This commit is contained in:
eaglercraft 2024-05-18 23:52:27 -07:00
parent ddc90126af
commit f89f7486f5
50 changed files with 753 additions and 440 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
{"pluginName":"EaglercraftXBungee","pluginVersion":"1.1.0","pluginButton":"Download \"EaglerXBungee-1.1.0.jar\"","pluginFilename":"EaglerXBungee.zip"}
{"pluginName":"EaglercraftXBungee","pluginVersion":"1.2.0","pluginButton":"Download \"EaglerXBungee-1.2.0.jar\"","pluginFilename":"EaglerXBungee.zip"}

Binary file not shown.

View File

@ -64,8 +64,12 @@ public class PlatformApplication {
String str = glfwGetClipboardString(win);
return str == null ? "" : str;
}
public static void setLocalStorage(String name, byte[] data) {
setLocalStorage(name, data, true);
}
public static void setLocalStorage(String name, byte[] data, boolean hooks) {
if(data == null) {
(new File("_eagstorage."+name+".dat")).delete();
}else {
@ -76,8 +80,12 @@ public class PlatformApplication {
}
}
}
public static byte[] getLocalStorage(String data) {
return getLocalStorage(data, true);
}
public static byte[] getLocalStorage(String data, boolean hooks) {
File f = new File("_eagstorage."+data+".dat");
if(!f.isFile()) {
return null;

View File

@ -230,6 +230,10 @@ public class PlatformInput {
glfwSwapBuffers(win);
}
public static boolean isVSyncSupported() {
return true;
}
public static boolean wasResized() {
boolean b = windowResized;
windowResized = false;

View File

@ -8,6 +8,7 @@ import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry;
/**
@ -31,6 +32,8 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
public final List<DefaultServer> defaultServers = new ArrayList();
private final DesktopClientConfigAdapterHooks hooks = new DesktopClientConfigAdapterHooks();
@Override
public String getDefaultLocale() {
return "en_US";
@ -129,4 +132,32 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
return false;
}
@Override
public boolean isAllowFNAWSkins() {
return true;
}
@Override
public String getLocalStorageNamespace() {
return EaglercraftVersion.localStorageNamespace;
}
@Override
public IClientConfigAdapterHooks getHooks() {
return hooks;
}
private static class DesktopClientConfigAdapterHooks implements IClientConfigAdapterHooks {
@Override
public void callLocalStorageSavedHook(String key, String base64) {
}
@Override
public String callLocalStorageLoadHook(String key) {
return null;
}
}
}

View File

@ -49,6 +49,10 @@ public class Display {
PlatformInput.setVSync(enable);
}
public static boolean isVSyncSupported() {
return PlatformInput.isVSyncSupported();
}
public static void update() {
PlatformInput.update();
}

View File

@ -10,7 +10,7 @@ public class EaglercraftVersion {
/// Customize these to fit your fork:
public static final String projectForkName = "EaglercraftX";
public static final String projectForkVersion = "u29";
public static final String projectForkVersion = "u30";
public static final String projectForkVendor = "lax1dude";
public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8";
@ -20,7 +20,7 @@ public class EaglercraftVersion {
public static final String projectOriginName = "EaglercraftX";
public static final String projectOriginAuthor = "lax1dude";
public static final String projectOriginRevision = "1.8";
public static final String projectOriginVersion = "u29";
public static final String projectOriginVersion = "u30";
public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace
@ -31,7 +31,7 @@ public class EaglercraftVersion {
public static final boolean enableUpdateService = true;
public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client";
public static final int updateBundlePackageVersionInt = 29;
public static final int updateBundlePackageVersionInt = 30;
public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName;
@ -60,4 +60,6 @@ public class EaglercraftVersion {
public static final boolean forceDemoMode = false;
public static final String localStorageNamespace = "_eaglercraftX";
}

View File

@ -69,4 +69,11 @@ public interface IClientConfigAdapter {
boolean isEnableSignatureBadge();
boolean isAllowVoiceClient();
boolean isAllowFNAWSkins();
String getLocalStorageNamespace();
IClientConfigAdapterHooks getHooks();
}

View File

@ -1,8 +1,4 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts;
import org.teavm.jso.JSIndexer;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -19,12 +15,10 @@ import org.teavm.jso.JSProperty;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JSEaglercraftXOptsRelaysArray extends JSObject {
public interface IClientConfigAdapterHooks {
@JSIndexer
JSEaglercraftXOptsRelay get(int idx);
void callLocalStorageSavedHook(String key, String base64);
@JSProperty
int getLength();
String callLocalStorageLoadHook(String key);
}

View File

@ -13,6 +13,21 @@ import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.util.ResourceLocation;
/**
* Copyright (c) 2023 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 BlockVertexIDs implements IResourceManagerReloadListener {
private static final Logger logger = LogManager.getLogger("BlockVertexIDsCSV");

View File

@ -61,7 +61,7 @@ public class EaglerSkinTexture implements ITextureObject {
}
System.arraycopy(pixels, 0, this.pixels, 0, pixels.length);
if(textureId != -1) {
TextureUtil.uploadTextureImageAllocate(textureId, new ImageData(width, height, pixels, true), false, false);
TextureUtil.uploadTextureImageSub(textureId, new ImageData(width, height, pixels, true), 0, 0, false, false);
}
}

View File

@ -73,14 +73,20 @@ public class GuiScreenEditProfile extends GuiScreen {
}
private void updateOptions() {
DefaultSkins[] arr = DefaultSkins.defaultSkinsMap;
if(!EagRuntime.getConfiguration().isAllowFNAWSkins()) {
DefaultSkins[] arrNoFNAW = new DefaultSkins[arr.length - 5];
System.arraycopy(arr, 0, arrNoFNAW, 0, arrNoFNAW.length);
arr = arrNoFNAW;
}
int numCustom = EaglerProfile.customSkins.size();
String[] n = new String[numCustom + DefaultSkins.defaultSkinsMap.length];
String[] n = new String[numCustom + arr.length];
for(int i = 0; i < numCustom; ++i) {
n[i] = EaglerProfile.customSkins.get(i).name;
}
int numDefault = DefaultSkins.defaultSkinsMap.length;
int numDefault = arr.length;
for(int j = 0; j < numDefault; ++j) {
n[numCustom + j] = DefaultSkins.defaultSkinsMap[j].name;
n[numCustom + j] = arr[j].name;
}
dropDownOptions = n;
}
@ -106,6 +112,10 @@ public class GuiScreenEditProfile extends GuiScreen {
GlStateManager.translate(skinX + 2, skinY - 9, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
if(selectedSlot > dropDownOptions.length - 1) {
selectedSlot = 0;
}
int numberOfCustomSkins = EaglerProfile.customSkins.size();
int skid = selectedSlot - numberOfCustomSkins;
SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model : DefaultSkins.getSkinFromId(skid).model;

View File

@ -56,11 +56,15 @@ public class GuiScreenImportExportProfile extends GuiScreen {
FileChooserResult result = EagRuntime.getFileChooserResult();
if(result != null) {
mc.loadingScreen.eaglerShow(I18n.format("settingsBackup.importing.1"), "settingsBackup.importing.2");
ProfileImporter importer = new ProfileImporter(result.fileData);
try {
ProfileImporter importer = new ProfileImporter(result.fileData);
importer.readHeader();
mc.displayGuiScreen(new GuiScreenImportProfile(importer, back));
}catch(IOException ex) {
try {
importer.close();
} catch (IOException e) {
}
EagRuntime.debugPrintStackTrace(ex);
mc.displayGuiScreen(new GuiScreenGenericErrorMessage("settingsBackup.importing.failed.1", "settingsBackup.importing.failed.2", back));
}

View File

@ -62,6 +62,14 @@ public class GuiScreenImportProfile extends GuiScreen {
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 140, I18n.format("gui.cancel")));
}
@Override
public void onGuiClosed() {
try {
importer.close();
} catch (IOException e) {
}
}
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
if(!doImportProfile && !doImportSettings && !doImportServers && !doImportResourcePacks) {

View File

@ -69,98 +69,97 @@ public class ProfileExporter {
osb.write(new byte[]{(byte)255,(byte)255,(byte)255,(byte)255}); // this will be replaced with the file count
osb.write('G');
OutputStream os = EaglerZLIB.newGZIPOutputStream(osb);
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
os.write(new byte[]{(byte)0,(byte)0,(byte)0,(byte)14,(byte)101,(byte)112,(byte)107,(byte)47,(byte)112,(byte)114,(byte)111,
(byte)102,(byte)105,(byte)108,(byte)101,(byte)49,(byte)56,(byte)56}); // 14 + epk/profile188
os.write('>');
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
os.write(new byte[]{(byte)12,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)101,(byte)120,
(byte)112,(byte)111,(byte)114,(byte)116,(byte)115,(byte)0,(byte)0,(byte)0,(byte)1}); // 12 + file-exports + 1
os.write((doExportProfile ? 1 : 0) | (doExportSettings ? 2 : 0) | (doExportServers ? 4 : 0) | (doExportResourcePacks ? 8 : 0));
os.write('>');
int fileCount = 2;
if(doExportProfile) {
byte[] profileData = EaglerProfile.write();
if(profileData == null) {
throw new IOException("Could not write profile data!");
}
exportFileToEPK("_eaglercraftX.p", profileData, os);
++fileCount;
}
if(doExportSettings) {
logger.info("Exporting game settings...");
byte[] gameSettings = Minecraft.getMinecraft().gameSettings.writeOptions();
if(gameSettings == null) {
throw new IOException("Could not write game settings!");
}
exportFileToEPK("_eaglercraftX.g", gameSettings, os);
++fileCount;
logger.info("Exporting relay settings...");
byte[] relays = RelayManager.relayManager.write();
if(relays == null) {
throw new IOException("Could not write relay settings!");
}
exportFileToEPK("_eaglercraftX.r", relays, os);
++fileCount;
}
if(doExportServers) {
logger.info("Exporting server list...");
byte[] servers = ServerList.getServerList().writeServerList();
if(servers == null) {
throw new IOException("Could not write server list!");
}
exportFileToEPK("_eaglercraftX.s", servers, os);
++fileCount;
}
logger.info("Exporting certificates...");
UpdateCertificate cert = UpdateService.getClientCertificate();
if(cert != null) {
exportFileToEPK("certs/main.cert", cert.rawCertData, os);
++fileCount;
}
Collection<UpdateCertificate> updatesExport = UpdateService.getAvailableUpdates();
int cc = 0;
for(UpdateCertificate cert2 : updatesExport) {
exportFileToEPK("certs/c" + (cc++) + ".cert", cert2.rawCertData, os);
++fileCount;
}
if(doExportResourcePacks) {
logger.info("Exporting resource packs...");
byte[] packManifest = (new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")).getAllBytes();
if(packManifest != null) {
exportFileToEPK(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json", packManifest, os);
try(OutputStream os = EaglerZLIB.newGZIPOutputStream(osb)) {
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
os.write(new byte[]{(byte)0,(byte)0,(byte)0,(byte)14,(byte)101,(byte)112,(byte)107,(byte)47,(byte)112,(byte)114,(byte)111,
(byte)102,(byte)105,(byte)108,(byte)101,(byte)49,(byte)56,(byte)56}); // 14 + epk/profile188
os.write('>');
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
os.write(new byte[]{(byte)12,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)101,(byte)120,
(byte)112,(byte)111,(byte)114,(byte)116,(byte)115,(byte)0,(byte)0,(byte)0,(byte)1}); // 12 + file-exports + 1
os.write((doExportProfile ? 1 : 0) | (doExportSettings ? 2 : 0) | (doExportServers ? 4 : 0) | (doExportResourcePacks ? 8 : 0));
os.write('>');
if(doExportProfile) {
byte[] profileData = EaglerProfile.write();
if(profileData == null) {
throw new IOException("Could not write profile data!");
}
exportFileToEPK("_eaglercraftX.p", profileData, os);
++fileCount;
VFile2 baseDir = new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS);
List<VFile2> files = baseDir.listFiles(true);
logger.info("({} files to export)", files.size());
for(int i = 0, l = files.size(); i < l; ++i) {
VFile2 f = files.get(i);
if(f.getPath().equals(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")) {
continue;
}
exportFileToEPK(f.getPath(), f.getAllBytes(), os);
}
if(doExportSettings) {
logger.info("Exporting game settings...");
byte[] gameSettings = Minecraft.getMinecraft().gameSettings.writeOptions();
if(gameSettings == null) {
throw new IOException("Could not write game settings!");
}
exportFileToEPK("_eaglercraftX.g", gameSettings, os);
++fileCount;
logger.info("Exporting relay settings...");
byte[] relays = RelayManager.relayManager.write();
if(relays == null) {
throw new IOException("Could not write relay settings!");
}
exportFileToEPK("_eaglercraftX.r", relays, os);
++fileCount;
}
if(doExportServers) {
logger.info("Exporting server list...");
byte[] servers = ServerList.getServerList().writeServerList();
if(servers == null) {
throw new IOException("Could not write server list!");
}
exportFileToEPK("_eaglercraftX.s", servers, os);
++fileCount;
}
logger.info("Exporting certificates...");
UpdateCertificate cert = UpdateService.getClientCertificate();
if(cert != null) {
exportFileToEPK("certs/main.cert", cert.rawCertData, os);
++fileCount;
}
Collection<UpdateCertificate> updatesExport = UpdateService.getAvailableUpdates();
int cc = 0;
for(UpdateCertificate cert2 : updatesExport) {
exportFileToEPK("certs/c" + (cc++) + ".cert", cert2.rawCertData, os);
++fileCount;
}
if(doExportResourcePacks) {
logger.info("Exporting resource packs...");
byte[] packManifest = (new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")).getAllBytes();
if(packManifest != null) {
exportFileToEPK(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json", packManifest, os);
++fileCount;
if(i > 0 && i % 100 == 0) {
logger.info("Exported {} files", i);
VFile2 baseDir = new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS);
List<VFile2> files = baseDir.listFiles(true);
logger.info("({} files to export)", files.size());
for(int i = 0, l = files.size(); i < l; ++i) {
VFile2 f = files.get(i);
if(f.getPath().equals(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")) {
continue;
}
exportFileToEPK(f.getPath(), f.getAllBytes(), os);
++fileCount;
if(i > 0 && i % 100 == 0) {
logger.info("Exported {} files", i);
}
}
}
}
os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$
}
os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$
os.close();
osb.write(new byte[]{(byte)58,(byte)58,(byte)58,(byte)89,(byte)69,(byte)69,(byte)58,(byte)62}); // :::YEE:>
byte[] ret = osb.toByteArray();

View File

@ -1,5 +1,6 @@
package net.lax1dude.eaglercraft.v1_8.profile;
import java.io.Closeable;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
@ -29,7 +30,7 @@ import net.minecraft.client.multiplayer.ServerList;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ProfileImporter {
public class ProfileImporter implements Closeable {
private static final Logger logger = LogManager.getLogger("ProfileImporter");
@ -148,4 +149,9 @@ public class ProfileImporter {
}
logger.info("Import complete!");
}
@Override
public void close() throws IOException {
epkDecompiler.close();
}
}

View File

@ -468,4 +468,13 @@ public class RenderHighPoly extends RenderPlayer {
GlStateManager.popMatrix();
}
}
public void renderLivingAt(AbstractClientPlayer abstractclientplayer, double d0, double d1, double d2) {
if (abstractclientplayer.isEntityAlive() && abstractclientplayer.isPlayerSleeping()) {
super.renderLivingAt(abstractclientplayer, d0 - (double) abstractclientplayer.renderOffsetX,
d1 - (double) abstractclientplayer.renderOffsetY, d2 - (double) abstractclientplayer.renderOffsetZ);
} else {
super.renderLivingAt(abstractclientplayer, d0, d1, d2);
}
}
}

View File

@ -46,7 +46,7 @@ public enum SkinModel {
private SkinModel(int id, HighPolySkin highPoly) {
this.id = id;
this.width = 256;
this.height = 128;
this.height = 256;
this.profileSkinType = "eagler";
this.sanitize = true;
this.highPoly = highPoly;

View File

@ -156,7 +156,6 @@ public class GuiShareToLan extends GuiScreen {
this.mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(I18n.format("lanServer.opened")
.replace("$relay$", LANServerController.getCurrentURI()).replace("$code$", code)));
} else {
SingleplayerServerController.configureLAN(mc.theWorld.getWorldInfo().getGameType(), false);
this.mc.displayGuiScreen(new GuiScreenNoRelays(this, "noRelay.titleFail"));
}
}

View File

@ -340,9 +340,11 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
}
EaglerInputStream bi = new EaglerInputStream(fullData);
int i = (bi.read() << 24) | (bi.read() << 16) | (bi.read() << 8) | bi.read();
InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi);
fullData = new byte[i];
int r = IOUtils.readFully(inflaterInputStream, fullData);
int r;
try(InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi)) {
r = IOUtils.readFully(inflaterInputStream, fullData);
}
if (i != r) {
logger.warn("Decompressed packet expected size {} differs from actual size {}!", i, r);
}

View File

@ -479,7 +479,7 @@ public class EaglerIntegratedServerWorker {
while(true) {
mainLoop();
EagUtils.sleep(1l);
EagUtils.sleep(0l);
}
}catch(Throwable tt) {
if(tt instanceof ReportedException) {

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.export;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
@ -24,7 +25,7 @@ import net.lax1dude.eaglercraft.v1_8.IOUtils;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class EPKDecompiler {
public class EPKDecompiler implements Closeable {
public static class FileEntry {
public final String type;
@ -178,4 +179,9 @@ public class EPKDecompiler {
return new String(charIn);
}
@Override
public void close() throws IOException {
zis.close();
}
}

View File

@ -40,44 +40,45 @@ public class WorldConverterEPK {
folderName += "_";
worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
}
EPKDecompiler dc = new EPKDecompiler(archiveContents);
EPKDecompiler.FileEntry f = null;
int lastProgUpdate = 0;
int prog = 0;
String hasReadType = null;
boolean has152Format = false;
int cnt = 0;
while((f = dc.readFile()) != null) {
byte[] b = f.data;
if(hasReadType == null) {
if (f.type.equals("HEAD") && f.name.equals("file-type")
&& ((hasReadType = EPKDecompiler.readASCII(f.data)).equals("epk/world188")
|| (has152Format = hasReadType.equals("epk/world152")))) {
if(has152Format) {
logger.warn("World type detected as 1.5.2, it will be converted to 1.8.8 format");
try(EPKDecompiler dc = new EPKDecompiler(archiveContents)) {
EPKDecompiler.FileEntry f = null;
int lastProgUpdate = 0;
int prog = 0;
String hasReadType = null;
boolean has152Format = false;
int cnt = 0;
while((f = dc.readFile()) != null) {
byte[] b = f.data;
if(hasReadType == null) {
if (f.type.equals("HEAD") && f.name.equals("file-type")
&& ((hasReadType = EPKDecompiler.readASCII(f.data)).equals("epk/world188")
|| (has152Format = hasReadType.equals("epk/world152")))) {
if(has152Format) {
logger.warn("World type detected as 1.5.2, it will be converted to 1.8.8 format");
}
continue;
}else {
throw new IOException("file does not contain a singleplayer 1.5.2 or 1.8.8 world!");
}
continue;
}else {
throw new IOException("file does not contain a singleplayer 1.5.2 or 1.8.8 world!");
}
}
if(f.type.equals("FILE")) {
if(f.name.equals("level.dat") || f.name.equals("level.dat_old")) {
NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b));
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
EaglerOutputStream tmp = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(worldDatNBT, tmp);
b = tmp.toByteArray();
}
VFile2 ff = new VFile2(worldDir, f.name);
ff.setAllBytes(b);
prog += b.length;
++cnt;
if(prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
logger.info("Extracted {} files, {} bytes from EPK...", cnt, prog);
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.1", prog);
if(f.type.equals("FILE")) {
if(f.name.equals("level.dat") || f.name.equals("level.dat_old")) {
NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b));
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
EaglerOutputStream tmp = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(worldDatNBT, tmp);
b = tmp.toByteArray();
}
VFile2 ff = new VFile2(worldDir, f.name);
ff.setAllBytes(b);
prog += b.length;
++cnt;
if(prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
logger.info("Extracted {} files, {} bytes from EPK...", cnt, prog);
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.1", prog);
}
}
}
}

View File

@ -50,111 +50,113 @@ public class WorldConverterMCA {
folderName += "_";
worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
}
ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents));
ZipEntry folderNameFile = null;
List<char[]> fileNames = new ArrayList<>();
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());
try(ZipInputStream zis = new ZipInputStream(new EaglerInputStream(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];
zis = new ZipInputStream(new EaglerInputStream(archiveContents));
ZipEntry f = null;
int lastProgUpdate = 0;
int prog = 0;
byte[] bb = new byte[16384];
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;
EaglerOutputStream baos = new EaglerOutputStream();
int len;
while ((len = zis.read(bb)) != -1) {
baos.write(bb, 0, len);
}
baos.close();
byte[] b = baos.toByteArray();
String fileName = f.getName().substring(folderPrefixOffset);
if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) {
NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b));
NBTTagCompound gameRulesNBT = worldDatNBT.getCompoundTag("Data").getCompoundTag("GameRules");
gameRulesNBT.setString("loadSpawnChunks", (gameRules & 2) != 0 ? "true" : "false");
String s = (gameRules & 1) != 0 ? "true" : "false";
gameRulesNBT.setString("bedSpawnPoint", s);
gameRulesNBT.setString("clickToRide", "false");
gameRulesNBT.setString("clickToSit", s);
gameRulesNBT.setString("colorCodes", s);
gameRulesNBT.setString("doSignEditing", s);
worldDatNBT.getCompoundTag("Data").setTag("GameRules", gameRulesNBT);
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
EaglerOutputStream bo = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(worldDatNBT, bo);
b = bo.toByteArray();
VFile2 ff = new VFile2(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/"))) {
VFile2 chunkFolder = new VFile2(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0"));
RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length));
int loadChunksCount = 0;
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", 10)) {
throw new IOException("Chunk is missing level data!");
try(ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) {
ZipEntry f = null;
int lastProgUpdate = 0;
int prog = 0;
byte[] bb = new byte[16384];
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;
EaglerOutputStream baos = new EaglerOutputStream();
int len;
while ((len = zis.read(bb)) != -1) {
baos.write(bb, 0, len);
}
baos.close();
byte[] b = baos.toByteArray();
String fileName = f.getName().substring(folderPrefixOffset);
if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) {
NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b));
NBTTagCompound gameRulesNBT = worldDatNBT.getCompoundTag("Data").getCompoundTag("GameRules");
gameRulesNBT.setString("loadSpawnChunks", (gameRules & 2) != 0 ? "true" : "false");
String s = (gameRules & 1) != 0 ? "true" : "false";
gameRulesNBT.setString("bedSpawnPoint", s);
gameRulesNBT.setString("clickToRide", "false");
gameRulesNBT.setString("clickToSit", s);
gameRulesNBT.setString("colorCodes", s);
gameRulesNBT.setString("doSignEditing", s);
worldDatNBT.getCompoundTag("Data").setTag("GameRules", gameRulesNBT);
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
EaglerOutputStream bo = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(worldDatNBT, bo);
b = bo.toByteArray();
VFile2 ff = new VFile2(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/"))) {
VFile2 chunkFolder = new VFile2(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0"));
RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length));
int loadChunksCount = 0;
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", 10)) {
throw new IOException("Chunk is missing level data!");
}
chunkLevel = chunkNBT.getCompoundTag("Level");
}catch(Throwable t) {
logger.error("{}: Could not read chunk: {}, {}", fileName, j, k);
logger.error(t);
continue;
}
chunkLevel = chunkNBT.getCompoundTag("Level");
}catch(Throwable t) {
logger.error("{}: Could not read chunk: {}, {}", fileName, j, k);
logger.error(t);
continue;
int chunkX = chunkLevel.getInteger("xPos");
int chunkZ = chunkLevel.getInteger("zPos");
VFile2 chunkOut = new VFile2(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat");
if(chunkOut.exists()) {
logger.error("{}: Chunk already exists: {}", fileName, chunkOut.getPath());
continue;
}
EaglerOutputStream bao = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(chunkNBT, bao);
b = bao.toByteArray();
chunkOut.setAllBytes(b);
prog += b.length;
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog);
}
++loadChunksCount;
}
int chunkX = chunkLevel.getInteger("xPos");
int chunkZ = chunkLevel.getInteger("zPos");
VFile2 chunkOut = new VFile2(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat");
if(chunkOut.exists()) {
logger.error("{}: Chunk already exists: {}", fileName, chunkOut.getPath());
continue;
}
EaglerOutputStream bao = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(chunkNBT, bao);
b = bao.toByteArray();
chunkOut.setAllBytes(b);
prog += b.length;
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog);
}
++loadChunksCount;
}
}
logger.info("{}: Imported {} chunks successfully ({} bytes)", fileName, loadChunksCount, prog);
} else if (fileName.startsWith("playerdata/") || fileName.startsWith("stats/")) {
//TODO: LAN player inventories
} else if (fileName.startsWith("data/") || fileName.startsWith("players/") || fileName.startsWith("eagler/skulls/")) {
VFile2 ff = new VFile2(worldDir, fileName);
ff.setAllBytes(b);
prog += b.length;
} else if (!fileName.equals("level.dat_mcr") && !fileName.equals("session.lock")) {
logger.info("Skipping file: {}", fileName);
}
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog);
}
logger.info("{}: Imported {} chunks successfully ({} bytes)", fileName, loadChunksCount, prog);
} else if (fileName.startsWith("playerdata/") || fileName.startsWith("stats/")) {
//TODO: LAN player inventories
} else if (fileName.startsWith("data/") || fileName.startsWith("players/") || fileName.startsWith("eagler/skulls/")) {
VFile2 ff = new VFile2(worldDir, fileName);
ff.setAllBytes(b);
prog += b.length;
} else if (!fileName.equals("level.dat_mcr") && !fileName.equals("session.lock")) {
logger.info("Skipping file: {}", fileName);
}
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog);
}
}
logger.info("MCA was successfully extracted into directory \"{}\"", worldDir.getPath());
@ -172,129 +174,130 @@ public class WorldConverterMCA {
public static byte[] exportWorld(String folderName) throws IOException {
EaglerOutputStream bao = new EaglerOutputStream();
ZipOutputStream zos = new ZipOutputStream(bao);
zos.setComment("contains backup of world '" + folderName + "'");
VFile2 worldFolder = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
logger.info("Exporting world directory \"{}\" as MCA", worldFolder.getPath());
VFile2 vf = new VFile2(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 VFile2(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;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
String[] srcFolderNames = new String[] { "level0", "level-1", "level1" };
String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" };
List<VFile2> fileList;
for(int i = 0; i < 3; ++i) {
vf = new VFile2(worldFolder, srcFolderNames[i]);
fileList = vf.listFiles(true);
String regionFolder = folderName + dstFolderNames[i];
logger.info("Converting chunks in \"{}\" as MCA to \"{}\"...", vf.getPath(), regionFolder);
Map<String,RegionFile> regionFiles = new HashMap();
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 chunkFile = fileList.get(k);
NBTTagCompound chunkNBT;
NBTTagCompound chunkLevel;
try {
b = chunkFile.getAllBytes();
chunkNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b));
if(!chunkNBT.hasKey("Level", 10)) {
throw new IOException("Chunk is missing level data!");
VFile2 worldFolder;
try(ZipOutputStream zos = new ZipOutputStream(bao)) {
zos.setComment("contains backup of world '" + folderName + "'");
worldFolder = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
logger.info("Exporting world directory \"{}\" as MCA", worldFolder.getPath());
VFile2 vf = new VFile2(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 VFile2(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;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
String[] srcFolderNames = new String[] { "level0", "level-1", "level1" };
String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" };
List<VFile2> fileList;
for(int i = 0; i < 3; ++i) {
vf = new VFile2(worldFolder, srcFolderNames[i]);
fileList = vf.listFiles(true);
String regionFolder = folderName + dstFolderNames[i];
logger.info("Converting chunks in \"{}\" as MCA to \"{}\"...", vf.getPath(), regionFolder);
Map<String,RegionFile> regionFiles = new HashMap();
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 chunkFile = fileList.get(k);
NBTTagCompound chunkNBT;
NBTTagCompound chunkLevel;
try {
b = chunkFile.getAllBytes();
chunkNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b));
if(!chunkNBT.hasKey("Level", 10)) {
throw new IOException("Chunk is missing level data!");
}
chunkLevel = chunkNBT.getCompoundTag("Level");
}catch(IOException t) {
logger.error("Could not read chunk: {}", chunkFile.getPath());
logger.error(t);
continue;
}
chunkLevel = chunkNBT.getCompoundTag("Level");
}catch(IOException t) {
logger.error("Could not read chunk: {}", chunkFile.getPath());
logger.error(t);
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) {
logger.error("Could not write chunk to {}: {}", regionFileName, chunkFile.getPath());
logger.error(t);
continue;
}
prog += b.length;
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
if(regionFiles.isEmpty()) {
logger.info("No region files were generated");
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) {
logger.error("Could not write chunk to {}: {}", regionFileName, chunkFile.getPath());
logger.error(t);
continue;
for(Entry<String,RegionFile> etr : regionFiles.entrySet()) {
String regionPath = regionFolder + etr.getKey();
logger.info("Writing region file: {}", regionPath);
zos.putNextEntry(new ZipEntry(regionPath));
zos.write(etr.getValue().getFile().getByteArray());
}
}
logger.info("Copying extra world data...");
fileList = (new VFile2(worldFolder, "data")).listFiles(false);
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 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;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
if(regionFiles.isEmpty()) {
logger.info("No region files were generated");
continue;
fileList = (new VFile2(worldFolder, "players")).listFiles(false);
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 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;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
for(Entry<String,RegionFile> etr : regionFiles.entrySet()) {
String regionPath = regionFolder + etr.getKey();
logger.info("Writing region file: {}", regionPath);
zos.putNextEntry(new ZipEntry(regionPath));
zos.write(etr.getValue().getFile().getByteArray());
fileList = (new VFile2(worldFolder, "eagler/skulls")).listFiles(false);
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 dataFile = fileList.get(k);
zos.putNextEntry(new ZipEntry(folderName + "/eagler/skulls/" + dataFile.getName()));
b = dataFile.getAllBytes();
zos.write(b);
prog += b.length;
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
}
logger.info("Copying extra world data...");
fileList = (new VFile2(worldFolder, "data")).listFiles(false);
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 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;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
fileList = (new VFile2(worldFolder, "players")).listFiles(false);
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 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;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
fileList = (new VFile2(worldFolder, "eagler/skulls")).listFiles(false);
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 dataFile = fileList.get(k);
zos.putNextEntry(new ZipEntry(folderName + "/eagler/skulls/" + dataFile.getName()));
b = dataFile.getAllBytes();
zos.write(b);
prog += b.length;
if (prog - lastProgUpdate > 25000) {
lastProgUpdate = prog;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
zos.close();
logger.info("World directory \"{}\" was successfully exported as MCA", worldFolder.getPath());
return bao.toByteArray();
}

View File

@ -243,9 +243,9 @@ public class IntegratedServerPlayerNetworkManager {
temporaryOutputStream.write((len >> 16) & 0xFF);
temporaryOutputStream.write((len >> 8) & 0xFF);
temporaryOutputStream.write(len & 0xFF);
OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream);
temporaryBuffer.readBytes(os, len);
os.close();
try(OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream)) {
temporaryBuffer.readBytes(os, len);
}
compressedData = temporaryOutputStream.toByteArray();
}catch(IOException ex) {
logger.error("Failed to compress packet {}!", pkt.getClass().getSimpleName());

View File

@ -64,7 +64,7 @@ public class RelayUpdateChecker {
for(net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry etr : EagRuntime.getConfiguration().getRelays()) {
relaysList.add(new RelayEntry(etr.address));
}
byte[] b = PlatformApplication.getLocalStorage("lastRelayUpdate");
byte[] b = PlatformApplication.getLocalStorage("lastRelayUpdate", false);
if(b != null) {
try {
lastUpdateCheck = (new DataInputStream(new EaglerInputStream(b))).readLong();
@ -79,7 +79,7 @@ public class RelayUpdateChecker {
try {
EaglerOutputStream bao = new EaglerOutputStream(8);
(new DataOutputStream(bao)).writeLong(lastUpdateCheck);
PlatformApplication.setLocalStorage("lastRelayUpdate", bao.toByteArray());
PlatformApplication.setLocalStorage("lastRelayUpdate", bao.toByteArray(), false);
} catch (IOException e) {
}
for (int i = 0, l = relaysList.size(); i < l; ++i) {

View File

@ -148,7 +148,16 @@ public class UpdateCertificate {
throw new CertificateInvalidException("SHA256 checksum of signature payload is invalid!");
}
return new UpdateCertificate(certData, EaglerZLIB.newGZIPInputStream(new EaglerInputStream(signaturePayload)), vers);
UpdateCertificate cert;
try(InputStream gis = EaglerZLIB.newGZIPInputStream(new EaglerInputStream(signaturePayload))) {
cert = new UpdateCertificate(certData, gis, vers);
}
if(System.currentTimeMillis() < cert.sigTimestamp) {
throw new CertificateInvalidException("Update certificate timestamp is from the future!?");
}
return cert;
}
private UpdateCertificate(byte[] certData, InputStream is, int sigVers) throws IOException {

View File

@ -94,7 +94,7 @@ public class UpdateService {
}
}
}
byte[] latestUpdate = PlatformApplication.getLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey);
byte[] latestUpdate = PlatformApplication.getLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey, false);
if(latestUpdate != null) {
addCertificateToSet(latestUpdate, false);
}
@ -150,7 +150,7 @@ public class UpdateService {
latestUpdateFound = cert;
if (saveLatest) {
PlatformApplication.setLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey,
certificateData);
certificateData, false);
}
}
}else if(EagRuntime.getConfiguration().isLogInvalidCerts()) {

View File

@ -21,6 +21,7 @@ import com.google.common.collect.Lists;
import net.lax1dude.eaglercraft.v1_8.Display;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion;
import net.lax1dude.eaglercraft.v1_8.HString;
import net.lax1dude.eaglercraft.v1_8.IOUtils;
@ -788,7 +789,17 @@ public class Minecraft implements IThreadListener {
long l = System.nanoTime();
this.mcProfiler.startSection("tick");
for (int j = 0; j < this.timer.elapsedTicks; ++j) {
if (this.timer.elapsedTicks > 1) {
long watchdog = System.currentTimeMillis();
for (int j = 0; j < this.timer.elapsedTicks; ++j) {
this.runTick();
long millis = System.currentTimeMillis();
if (millis - watchdog > 50l) {
watchdog = millis;
EagUtils.sleep(0l);
}
}
} else if (this.timer.elapsedTicks == 1) {
this.runTick();
}
@ -876,7 +887,11 @@ public class Minecraft implements IThreadListener {
public void updateDisplay() {
this.mcProfiler.startSection("display_update");
Display.setVSync(this.gameSettings.enableVsync);
if (Display.isVSyncSupported()) {
Display.setVSync(this.gameSettings.enableVsync);
} else {
this.gameSettings.enableVsync = false;
}
Display.update();
this.mcProfiler.endSection();
this.checkWindowResize();

View File

@ -49,6 +49,7 @@ import net.minecraft.util.ResourceLocation;
*/
public class SoundHandler implements IResourceManagerReloadListener, ITickable {
private static final Logger logger = LogManager.getLogger();
private static final Logger tipLogger = LogManager.getLogger("EaglercraftX");
public static final SoundPoolEntry missing_sound = new SoundPoolEntry(new ResourceLocation("meta:missing_sound"),
0.0D, 0.0D, false);
private final SoundRegistry sndRegistry = new SoundRegistry();
@ -83,6 +84,10 @@ public class SoundHandler implements IResourceManagerReloadListener, ITickable {
}
}
if (this.sndRegistry.getObject(new ResourceLocation("minecraft:sounds/music/game/calm1.ogg")) == null) {
tipLogger.info(
"Download this resource pack if you want music: https://bafybeiayojww5jfyzvlmtuk7l5ufkt7nlfto7mhwmzf2vs4bvsjd5ouiuq.ipfs.nftstorage.link/?filename=Music_For_Eaglercraft.zip");
}
}
public static class SoundMap {

View File

@ -4,6 +4,7 @@ import java.util.Map;
import com.google.common.collect.Maps;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper;
import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer;
@ -218,8 +219,12 @@ public class RenderManager {
this.skinMap.put("default", this.playerRenderer);
this.skinMap.put("slim", new RenderPlayer(this, true, false));
this.skinMap.put("zombie", new RenderPlayer(this, false, true));
this.eaglerRenderer = new RenderHighPoly(this, this.playerRenderer.getMainModel(),
this.playerRenderer.shadowSize);
if (EagRuntime.getConfiguration().isAllowFNAWSkins()) {
this.eaglerRenderer = new RenderHighPoly(this, this.playerRenderer.getMainModel(),
this.playerRenderer.shadowSize);
} else {
this.eaglerRenderer = this.playerRenderer;
}
this.skinMap.put("eagler",
Minecraft.getMinecraft().gameSettings.enableFNAWSkins ? this.eaglerRenderer : this.playerRenderer);
}

View File

@ -835,7 +835,7 @@ public abstract class ServerConfigurationManager {
worldIn.theItemInWorldManager.initializeGameType(parWorld.getWorldInfo().getGameType());
} else {
parEntityPlayerMP2.theItemInWorldManager.setGameType(lanGamemode);
worldIn.theItemInWorldManager.setGameType(lanGamemode);
}
}

View File

@ -95,34 +95,66 @@ public class PlatformApplication {
private static native void setClipboard0(String str);
public static void setLocalStorage(String name, byte[] data) {
setLocalStorage(name, data, true);
}
public static void setLocalStorage(String name, byte[] data, boolean hooks) {
IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter();
String eagName = adapter.getLocalStorageNamespace() + "." + name;
String b64 = Base64.encodeBase64String(data);
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
if(data != null) {
s.setItem("_eaglercraftX." + name, Base64.encodeBase64String(data));
s.setItem(eagName, b64);
}else {
s.removeItem("_eaglercraftX." + name);
s.removeItem(eagName);
}
}
}catch(Throwable t) {
}
if(hooks) {
adapter.getHooks().callLocalStorageSavedHook(name, b64);
}
}
public static byte[] getLocalStorage(String name) {
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
String str = s.getItem("_eaglercraftX." + name);
if(str != null) {
return Base64.decodeBase64(str);
return getLocalStorage(name, true);
}
public static byte[] getLocalStorage(String name, boolean hooks) {
IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter();
String eagName = adapter.getLocalStorageNamespace() + "." + name;
byte[] hooked = null;
if(hooks) {
String hookedStr = adapter.getHooks().callLocalStorageLoadHook(eagName);
if(hookedStr != null) {
try {
hooked = Base64.decodeBase64(hookedStr);
}catch(Throwable t) {
PlatformRuntime.logger.error("Invalid Base64 recieved from local storage hook!");
hooked = null;
}
}
}
if(hooked == null) {
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
String str = s.getItem(eagName);
if(str != null) {
return Base64.decodeBase64(str);
}else {
return null;
}
}else {
return null;
}
}else {
}catch(Throwable t) {
return null;
}
}catch(Throwable t) {
return null;
}else {
return hooked;
}
}

View File

@ -30,16 +30,18 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.util.MathHelper;
/**
* Copyright (c) 2022-2023 LAX1DUDE. All Rights Reserved.
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
* 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 PlatformAudio {

View File

@ -102,8 +102,9 @@ public class PlatformInput {
public static boolean keyboardLockSupported = false;
public static boolean lockKeys = false;
private static boolean vsync = true;
private static boolean vsyncSupport = false;
@JSBody(params = { }, script = "window.onbeforeunload = () => {return false;};")
private static native void onBeforeCloseRegister();
@ -252,7 +253,18 @@ public class PlatformInput {
mouseDY = 0.0D;
}
});
onBeforeCloseRegister();
try {
onBeforeCloseRegister();
}catch(Throwable t) {
}
try {
asyncRequestAnimationFrame();
vsyncSupport = true;
}catch(Throwable t) {
PlatformRuntime.logger.error("VSync is not supported on this browser!");
}
fullscreenQuery = fullscreenMediaQuery();
if (keyboardLockSupported = checkKeyboardLockSupported()) {
@ -300,6 +312,9 @@ public class PlatformInput {
vsync = enable;
}
@JSBody(params = { "doc" }, script = "return (doc.visibilityState === \"visible\");")
private static native boolean getVisibilityState(JSObject doc);
public static void update() {
double r = win.getDevicePixelRatio();
int w = PlatformRuntime.parent.getClientWidth();
@ -320,10 +335,14 @@ public class PlatformInput {
PlatformRuntime.lastFrame = t;
}
}
if(vsync) {
asyncRequestAnimationFrame();
if(getVisibilityState(win.getDocument())) {
if(vsyncSupport && vsync) {
asyncRequestAnimationFrame();
}else {
EagUtils.sleep(0l);
}
}else {
EagUtils.sleep(0l);
EagUtils.sleep(50l);
}
}
@ -348,6 +367,10 @@ public class PlatformInput {
}, 50);
}
public static boolean isVSyncSupported() {
return vsyncSupport;
}
static void initFramebuffer(WebGL2RenderingContext ctx, WebGLFramebuffer fbo, int sw, int sh) {
context = ctx;
mainFramebuffer = fbo;
@ -599,7 +622,7 @@ public class PlatformInput {
keyEvents.clear();
}
@JSBody(params = {}, script = "return window.matchMedia('(display-mode: fullscreen)');")
@JSBody(params = {}, script = "return window.matchMedia(\"(display-mode: fullscreen)\");")
private static native JSObject fullscreenMediaQuery();
@JSBody(params = { "mediaQuery" }, script = "return mediaQuery.matches;")

View File

@ -9,6 +9,7 @@ import org.teavm.jso.JSBody;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.browser.Window;
import org.teavm.jso.core.JSArrayReader;
import org.teavm.jso.core.JSError;
import org.teavm.jso.dom.css.CSSStyleDeclaration;
import org.teavm.jso.dom.html.HTMLCanvasElement;
@ -21,7 +22,6 @@ import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsAssetsURI;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsAssetsURIsArray;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot;
import net.lax1dude.eaglercraft.v1_8.log4j.ILogRedirector;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
@ -87,7 +87,7 @@ public class ClientMain {
if(epkSingleURL != null) {
configEPKFiles = new EPKFileEntry[] { new EPKFileEntry(epkSingleURL, "") };
}else {
JSEaglercraftXOptsAssetsURIsArray epkURLs = eaglercraftOpts.getAssetsURIArray();
JSArrayReader<JSEaglercraftXOptsAssetsURI> epkURLs = eaglercraftOpts.getAssetsURIArray();
int len = epkURLs.getLength();
if(len == 0) {
throw new JSONException("assetsURI array cannot be empty!");

View File

@ -13,6 +13,7 @@ import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.dom.html.HTMLElement;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
/**
@ -63,13 +64,13 @@ public class DebugConsoleWindow {
destroyWindow();
}
});
if("true".equals(parent.getLocalStorage().getItem("_eaglercraftX.showDebugConsole"))) {
if("true".equals(parent.getLocalStorage().getItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole"))) {
showDebugConsole0();
}
}
public static void showDebugConsole() {
parent.getLocalStorage().setItem("_eaglercraftX.showDebugConsole", "true");
parent.getLocalStorage().setItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole", "true");
showDebugConsole0();
}

View File

@ -10,13 +10,14 @@ import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
import org.json.JSONArray;
import org.json.JSONObject;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSArrayReader;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsHooks;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRelay;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRelaysArray;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsServer;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsServersArray;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry;
/**
@ -56,6 +57,9 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
private boolean checkRelaysForUpdates = false;
private boolean enableSignatureBadge = false;
private boolean allowVoiceClient = true;
private boolean allowFNAWSkins = true;
private String localStorageNamespace = "_eaglercraftX";
private final TeaVMClientConfigAdapterHooks hooks = new TeaVMClientConfigAdapterHooks();
public void loadNative(JSObject jsObject) {
integratedServerOpts = new JSONObject();
@ -75,6 +79,12 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getLogInvalidCerts(false);
enableSignatureBadge = eaglercraftXOpts.getEnableSignatureBadge(false);
allowVoiceClient = eaglercraftXOpts.getAllowVoiceClient(true);
allowFNAWSkins = eaglercraftXOpts.getAllowFNAWSkins(true);
localStorageNamespace = eaglercraftXOpts.getLocalStorageNamespace(EaglercraftVersion.localStorageNamespace);
JSEaglercraftXOptsHooks hooksObj = eaglercraftXOpts.getHooks();
if(hooksObj != null) {
hooks.loadHooks(hooksObj);
}
integratedServerOpts.put("worldsDB", worldsDB);
integratedServerOpts.put("demoMode", demoMode);
@ -82,8 +92,9 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
integratedServerOpts.put("allowUpdateSvc", isAllowUpdateSvc);
integratedServerOpts.put("allowUpdateDL", isAllowUpdateDL);
integratedServerOpts.put("allowVoiceClient", allowVoiceClient);
integratedServerOpts.put("allowFNAWSkins", allowFNAWSkins);
JSEaglercraftXOptsServersArray serversArray = eaglercraftXOpts.getServers();
JSArrayReader<JSEaglercraftXOptsServer> serversArray = eaglercraftXOpts.getServers();
if(serversArray != null) {
for(int i = 0, l = serversArray.getLength(); i < l; ++i) {
JSEaglercraftXOptsServer serverEntry = serversArray.get(i);
@ -95,7 +106,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
}
}
JSEaglercraftXOptsRelaysArray relaysArray = eaglercraftXOpts.getRelays();
JSArrayReader<JSEaglercraftXOptsRelay> relaysArray = eaglercraftXOpts.getRelays();
if(relaysArray != null) {
boolean gotAPrimary = false;
for(int i = 0, l = relaysArray.getLength(); i < l; ++i) {
@ -162,6 +173,8 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftOpts.optBoolean("logInvalidCerts", false);
enableSignatureBadge = eaglercraftOpts.optBoolean("enableSignatureBadge", false);
allowVoiceClient = eaglercraftOpts.optBoolean("allowVoiceClient", true);
allowFNAWSkins = eaglercraftOpts.optBoolean("allowFNAWSkins", true);
localStorageNamespace = eaglercraftOpts.optString("localStorageNamespace", EaglercraftVersion.localStorageNamespace);
JSONArray serversArray = eaglercraftOpts.optJSONArray("servers");
if(serversArray != null) {
for(int i = 0, l = serversArray.length(); i < l; ++i) {
@ -309,6 +322,21 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
return allowVoiceClient;
}
@Override
public boolean isAllowFNAWSkins() {
return allowFNAWSkins;
}
@Override
public String getLocalStorageNamespace() {
return localStorageNamespace;
}
@Override
public IClientConfigAdapterHooks getHooks() {
return hooks;
}
@Override
public String toString() {
JSONObject jsonObject = new JSONObject();
@ -327,6 +355,8 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
jsonObject.put("checkRelaysForUpdates", checkRelaysForUpdates);
jsonObject.put("enableSignatureBadge", enableSignatureBadge);
jsonObject.put("allowVoiceClient", allowVoiceClient);
jsonObject.put("allowFNAWSkins", allowFNAWSkins);
jsonObject.put("localStorageNamespace", localStorageNamespace);
JSONArray serversArr = new JSONArray();
for(int i = 0, l = defaultServers.size(); i < l; ++i) {
DefaultServer srv = defaultServers.get(i);

View File

@ -0,0 +1,100 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import java.util.function.Supplier;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.browser.Window;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsHooks;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* 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.
*
*/
public class TeaVMClientConfigAdapterHooks implements IClientConfigAdapterHooks {
private static final Logger logger = LogManager.getLogger("TeaVMClientConfigAdapterHooks");
private LocalStorageSaveHook saveHook = null;
private LocalStorageLoadHook loadHook = null;
@JSFunctor
private static interface LocalStorageSaveHook extends JSObject {
void call(String key, String base64);
}
@Override
public void callLocalStorageSavedHook(String key, String base64) {
if(saveHook != null) {
callHookSafe("localStorageSaved", () -> {
saveHook.call(key, base64);
});
}
}
@JSFunctor
private static interface LocalStorageLoadHook extends JSObject {
String call(String key);
}
@Override
public String callLocalStorageLoadHook(String key) {
if(loadHook != null) {
return (String)callHookSafeWithReturn("localStorageLoaded", () -> {
return loadHook.call(key);
});
}else {
return null;
}
}
private static void callHookSafe(String identifer, Runnable hooker) {
Window.setTimeout(() -> {
try {
hooker.run();
}catch(Throwable t) {
logger.error("Caught exception while invoking eaglercraftXOpts \"{}\" hook!", identifer);
logger.error(t);
}
}, 0);
}
@Async
private static native Object callHookSafeWithReturn(String identifer, Supplier<Object> hooker);
private static void callHookSafeWithReturn(String identifer, Supplier<Object> hooker, final AsyncCallback<Object> cb) {
Window.setTimeout(() -> {
Object res = null;
try {
res = hooker.get();
}catch(Throwable t) {
logger.error("Caught exception while invoking eaglercraftXOpts \"{}\" hook!", identifer);
logger.error(t);
}finally {
cb.complete(res);
}
}, 0);
}
public void loadHooks(JSEaglercraftXOptsHooks hooks) {
saveHook = (LocalStorageSaveHook)hooks.getLocalStorageSavedHook();
loadHook = (LocalStorageLoadHook)hooks.getLocalStorageLoadedHook();
}
}

View File

@ -1,8 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts;
import org.teavm.jso.JSIndexer;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -19,12 +18,12 @@ import org.teavm.jso.JSProperty;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JSEaglercraftXOptsAssetsURIsArray extends JSObject {
public abstract class JSEaglercraftXOptsHooks implements JSObject {
@JSIndexer
JSEaglercraftXOptsAssetsURI get(int idx);
@JSBody(script = "return (typeof this.localStorageSaved === \"function\") ? this.localStorageSaved : null;")
public native JSObject getLocalStorageSavedHook();
@JSProperty
int getLength();
@JSBody(script = "return (typeof this.localStorageLoaded === \"function\") ? this.localStorageLoaded : null;")
public native JSObject getLocalStorageLoadedHook();
}

View File

@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSArrayReader;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -27,7 +28,7 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject {
public native String getAssetsURI();
@JSBody(script = "return (typeof this.assetsURI === \"object\") ? this.assetsURI : null;")
public native JSEaglercraftXOptsAssetsURIsArray getAssetsURIArray();
public native JSArrayReader<JSEaglercraftXOptsAssetsURI> getAssetsURIArray();
@JSBody(params = { "def" }, script = "return (typeof this.lang === \"string\") ? this.lang : def;")
public native String getLang(String defaultValue);
@ -48,10 +49,10 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject {
public native boolean getDemoMode(boolean defaultValue);
@JSBody(script = "return (typeof this.servers === \"object\") ? this.servers : null;")
public native JSEaglercraftXOptsServersArray getServers();
public native JSArrayReader<JSEaglercraftXOptsServer> getServers();
@JSBody(script = "return (typeof this.relays === \"object\") ? this.relays : null;")
public native JSEaglercraftXOptsRelaysArray getRelays();
public native JSArrayReader<JSEaglercraftXOptsRelay> getRelays();
@JSBody(params = { "def" }, script = "return (typeof this.checkShaderGLErrors === \"boolean\") ? this.checkShaderGLErrors : def;")
public native boolean getCheckShaderGLErrors(boolean defaultValue);
@ -83,4 +84,13 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject {
@JSBody(params = { "def" }, script = "return (typeof this.allowVoiceClient === \"boolean\") ? this.allowVoiceClient : def;")
public native boolean getAllowVoiceClient(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowFNAWSkins === \"boolean\") ? this.allowFNAWSkins : def;")
public native boolean getAllowFNAWSkins(boolean defaultValue);
@JSBody(script = "return (typeof this.hooks === \"object\") ? this.hooks : null;")
public native JSEaglercraftXOptsHooks getHooks();
@JSBody(params = { "def" }, script = "return (typeof this.localStorageNamespace === \"string\") ? this.localStorageNamespace : def;")
public native String getLocalStorageNamespace(String defaultValue);
}

View File

@ -1,30 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts;
import org.teavm.jso.JSIndexer;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
/**
* 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.
*
*/
public interface JSEaglercraftXOptsServersArray extends JSObject {
@JSIndexer
JSEaglercraftXOptsServer get(int idx);
@JSProperty
int getLength();
}