Update #37 - Touch support without userscript, many other feats

This commit is contained in:
lax1dude
2024-09-21 20:17:42 -07:00
parent 173727c8c4
commit ec1ab8ece3
683 changed files with 62074 additions and 8996 deletions

View File

@ -3,9 +3,11 @@ package net.lax1dude.eaglercraft.v1_8.sp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
@ -56,34 +58,59 @@ public class SingleplayerServerController implements ISaveFormat {
private static boolean loggingState = true;
private static String worldStatusString = "";
private static float worldStatusProgress = 0.0f;
private static final LinkedList<IPCPacket15Crashed> exceptions = new LinkedList();
private static final LinkedList<IPCPacket15Crashed> exceptions = new LinkedList<>();
private static final Set<Integer> issuesDetected = new HashSet<>();
public static final SingleplayerServerController instance = new SingleplayerServerController();
public static final Logger logger = LogManager.getLogger("SingleplayerServerController");
public static final List<SaveFormatComparator> saveListCache = new ArrayList();
public static final Map<String, WorldInfo> saveListMap = new HashMap();
public static final List<NBTTagCompound> saveListNBT = new ArrayList();
public static final List<SaveFormatComparator> saveListCache = new ArrayList<>();
public static final Map<String, WorldInfo> saveListMap = new HashMap<>();
public static final List<NBTTagCompound> saveListNBT = new ArrayList<>();
private static boolean isPaused = false;
private static List<String> integratedServerTPS = new ArrayList();
private static List<String> integratedServerTPS = new ArrayList<>();
private static long integratedServerLastTPSUpdate = 0;
public static final ClientIntegratedServerNetworkManager localPlayerNetworkManager = new ClientIntegratedServerNetworkManager(PLAYER_CHANNEL);
private static final List<String> openLANChannels = new ArrayList();
private static final List<String> openLANChannels = new ArrayList<>();
private static final IPCPacketManager packetManagerInstance = new IPCPacketManager();
private SingleplayerServerController() {
}
public static void startIntegratedServerWorker() {
public static void startIntegratedServerWorker(boolean forceSingleThread) {
if(statusState == IntegratedServerState.WORLD_WORKER_NOT_RUNNING) {
exceptions.clear();
issuesDetected.clear();
statusState = IntegratedServerState.WORLD_WORKER_BOOTING;
loggingState = true;
ClientPlatformSingleplayer.startIntegratedServer();
boolean singleThreadSupport = ClientPlatformSingleplayer.isSingleThreadModeSupported();
if(!singleThreadSupport && forceSingleThread) {
throw new UnsupportedOperationException("Single thread mode is not supported!");
}
if(forceSingleThread || !singleThreadSupport) {
ClientPlatformSingleplayer.startIntegratedServer(forceSingleThread);
}else {
try {
ClientPlatformSingleplayer.startIntegratedServer(forceSingleThread);
}catch(Throwable t) {
logger.error("Failed to start integrated server worker");
logger.error(t);
logger.error("Attempting to use single thread mode");
exceptions.clear();
issuesDetected.clear();
statusState = IntegratedServerState.WORLD_WORKER_BOOTING;
loggingState = true;
ClientPlatformSingleplayer.startIntegratedServer(true);
}
}
}
}
public static boolean isIssueDetected(int issue) {
return issuesDetected.contains(issue);
}
public static boolean isIntegratedServerWorkerStarted() {
return statusState != IntegratedServerState.WORLD_WORKER_NOT_RUNNING && statusState != IntegratedServerState.WORLD_WORKER_BOOTING;
}
@ -196,7 +223,7 @@ public class SingleplayerServerController implements ISaveFormat {
}
public static long getTPSAge() {
return System.currentTimeMillis() - integratedServerLastTPSUpdate;
return EagRuntime.steadyTimeMillis() - integratedServerLastTPSUpdate;
}
public static boolean hangupEaglercraftServer() {
@ -269,7 +296,11 @@ public class SingleplayerServerController implements ISaveFormat {
boolean logWindowState = PlatformApplication.isShowingDebugConsole();
if(loggingState != logWindowState) {
loggingState = logWindowState;
sendIPCPacket(new IPCPacket21EnableLogging(logWindowState));
sendIPCPacket(new IPCPacket1BEnableLogging(logWindowState));
}
if(ClientPlatformSingleplayer.isRunningSingleThreadMode()) {
ClientPlatformSingleplayer.updateSingleThreadMode();
}
LANServerController.updateLANServer();
@ -385,17 +416,22 @@ public class SingleplayerServerController implements ISaveFormat {
if(pkt.opCode == IPCPacket14StringList.SERVER_TPS) {
integratedServerTPS.clear();
integratedServerTPS.addAll(pkt.stringList);
integratedServerLastTPSUpdate = System.currentTimeMillis();
integratedServerLastTPSUpdate = EagRuntime.steadyTimeMillis();
}else {
logger.warn("Strange string list type {} recieved!", pkt.opCode);
}
break;
}
case IPCPacket20LoggerMessage.ID: {
IPCPacket20LoggerMessage pkt = (IPCPacket20LoggerMessage)ipc;
case IPCPacket1ALoggerMessage.ID: {
IPCPacket1ALoggerMessage pkt = (IPCPacket1ALoggerMessage)ipc;
PlatformApplication.addLogMessage(pkt.logMessage, pkt.isError);
break;
}
case IPCPacket1CIssueDetected.ID: {
IPCPacket1CIssueDetected pkt = (IPCPacket1CIssueDetected)ipc;
issuesDetected.add(pkt.issueID);
break;
}
default:
throw new RuntimeException("Unexpected IPC packet type recieved on client: " + ipc.id());
}

View File

@ -3,9 +3,8 @@ package net.lax1dude.eaglercraft.v1_8.sp;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketInstallSkinSPEAG;
import net.minecraft.client.Minecraft;
import net.minecraft.network.play.client.C17PacketCustomPayload;
import net.minecraft.util.ChatComponentTranslation;
/**
@ -44,9 +43,9 @@ public class SkullCommand {
if(fr == null || mc.thePlayer == null || mc.thePlayer.sendQueue == null) {
return;
}
ImageData loaded = ImageData.loadImageFile(fr.fileData);
ImageData loaded = ImageData.loadImageFile(fr.fileData, ImageData.getMimeFromType(fr.fileName));
if(loaded == null) {
mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentTranslation("command.skull.error.invalid.png"));
mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentTranslation("command.skull.error.invalid.format"));
return;
}
if(loaded.width != 64 || loaded.height > 64) {
@ -62,7 +61,7 @@ public class SkullCommand {
rawSkin[j + 2] = (byte)(k >>> 8);
rawSkin[j + 3] = (byte)(k & 0xFF);
}
mc.thePlayer.sendQueue.addToSendQueue(new C17PacketCustomPayload("EAG|Skins-1.8", SkinPackets.writeCreateCustomSkull(rawSkin)));
mc.thePlayer.sendQueue.sendEaglerMessage(new CPacketInstallSkinSPEAG(rawSkin));
}
}

View File

@ -1,58 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraft.client.resources.I18n;
/**
* 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 GuiIntegratedServerStartup extends GuiScreen {
private final GuiScreen backScreen;
private static final String[] dotDotDot = new String[] { "", ".", "..", "..." };
private int counter = 0;
public GuiIntegratedServerStartup(GuiScreen backScreen) {
this.backScreen = backScreen;
}
protected void keyTyped(char parChar1, int parInt1) {
}
public void initGui() {
this.buttonList.clear();
}
public void updateScreen() {
++counter;
if(counter > 1 && SingleplayerServerController.isIntegratedServerWorkerStarted()) {
mc.displayGuiScreen(new GuiSelectWorld(backScreen));
}else if(counter == 2) {
SingleplayerServerController.startIntegratedServerWorker();
}
}
public void drawScreen(int i, int j, float f) {
this.drawBackground(0);
String txt = I18n.format("singleplayer.integratedStartup");
int w = this.fontRendererObj.getStringWidth(txt);
this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((System.currentTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215);
super.drawScreen(i, j, f);
}
}

View File

@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.Keyboard;
import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
@ -143,4 +144,16 @@ public class GuiScreenAddRelay extends GuiScreen {
public boolean blockPTTKey() {
return this.serverName.isFocused() || this.serverAddress.isFocused();
}
@Override
public boolean showCopyPasteButtons() {
return this.serverName.isFocused() || this.serverAddress.isFocused();
}
@Override
public void fireInputEvent(EnumInputEvent event, String param) {
this.serverName.fireInputEvent(event, param);
this.serverAddress.fireInputEvent(event, param);
}
}

View File

@ -1,9 +1,14 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.WorkerStartupFailedException;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket1CIssueDetected;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraft.client.resources.I18n;
/**
@ -24,46 +29,76 @@ import net.minecraft.client.resources.I18n;
public class GuiScreenDemoIntegratedServerStartup extends GuiScreen {
private final GuiScreen contScreen;
private final boolean singleThread;
private static final String[] dotDotDot = new String[] { "", ".", "..", "..." };
private int counter = 0;
private GuiButton cancelButton;
public GuiScreenDemoIntegratedServerStartup(GuiScreen contScreen) {
this.contScreen = contScreen;
this.singleThread = false;
}
protected void keyTyped(char parChar1, int parInt1) {
public GuiScreenDemoIntegratedServerStartup(GuiScreen contScreen, boolean singleThread) {
this.contScreen = contScreen;
this.singleThread = singleThread;
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(cancelButton = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask")));
cancelButton.visible = false;
}
public void updateScreen() {
++counter;
if(counter == 2) {
try {
SingleplayerServerController.startIntegratedServerWorker();
SingleplayerServerController.startIntegratedServerWorker(singleThread);
}catch(WorkerStartupFailedException ex) {
mc.displayGuiScreen(new GuiScreenIntegratedServerFailed(ex.getMessage(), new GuiScreenDemoIntegratedServerFailed()));
return;
}
}else if(counter > 2) {
if(counter > 100 && SingleplayerServerController.canKillWorker() && !singleThread) {
cancelButton.visible = true;
}
IPCPacket15Crashed[] crashReport = SingleplayerServerController.worldStatusErrors();
if(crashReport != null) {
mc.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiScreenDemoIntegratedServerFailed(), "singleplayer.failed.notStarted", crashReport));
}else if(SingleplayerServerController.isIntegratedServerWorkerStarted()) {
mc.displayGuiScreen(contScreen);
GuiScreen cont = contScreen;
if(SingleplayerServerController.isRunningSingleThreadMode()) {
cont = new GuiScreenIntegratedServerFailed("singleplayer.failed.singleThreadWarning.1", "singleplayer.failed.singleThreadWarning.2", cont);
} else if (!EagRuntime.getConfiguration().isRamdiskMode()
&& SingleplayerServerController.isIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE)
&& SingleplayerServerController.canKillWorker()) {
cont = new GuiScreenRAMDiskModeDetected(cont);
}
mc.displayGuiScreen(cont);
}
}
}
protected void actionPerformed(GuiButton parGuiButton) {
if(parGuiButton.id == 0) {
SingleplayerServerController.killWorker();
mc.displayGuiScreen(new GuiScreenDemoIntegratedServerStartup(contScreen, true));
}
}
public void drawScreen(int i, int j, float f) {
this.drawBackground(0);
String txt = I18n.format("singleplayer.integratedStartup");
int w = this.fontRendererObj.getStringWidth(txt);
this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((System.currentTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215);
this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((EagRuntime.steadyTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215);
super.drawScreen(i, j, f);
}
public boolean canCloseGui() {
return false;
}
}

View File

@ -88,7 +88,7 @@ public class GuiScreenIntegratedServerBusy extends GuiScreen {
}
public void initGui() {
if(startStartTime == 0) this.startStartTime = System.currentTimeMillis();
if(startStartTime == 0) this.startStartTime = EagRuntime.steadyTimeMillis();
areYouSure = 0;
this.buttonList.add(killTask = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask")));
killTask.enabled = false;
@ -102,7 +102,7 @@ public class GuiScreenIntegratedServerBusy extends GuiScreen {
this.drawDefaultBackground();
int top = this.height / 3;
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
String str = I18n.format(currentStatus);
@ -128,7 +128,7 @@ public class GuiScreenIntegratedServerBusy extends GuiScreen {
}
public void updateScreen() {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - startStartTime > 6000l && SingleplayerServerController.canKillWorker()) {
killTask.enabled = true;
}
@ -164,4 +164,8 @@ public class GuiScreenIntegratedServerBusy extends GuiScreen {
return false;
}
public boolean canCloseGui() {
return false;
}
}

View File

@ -2,7 +2,6 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.resources.I18n;
/**
@ -33,8 +32,7 @@ public class GuiScreenIntegratedServerCrashed extends GuiScreen {
public void initGui() {
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height - 50, I18n.format("singleplayer.crashed.continue")));
ScaledResolution res = new ScaledResolution(mc);
int i = res.getScaleFactor();
int i = mc.scaledResolution.getScaleFactor();
CrashScreen.showCrashReportOverlay(crashReport, 90 * i, 60 * i, (width - 180) * i, (height - 130) * i);
}

View File

@ -1,6 +1,9 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
@ -40,6 +43,9 @@ public class GuiScreenIntegratedServerFailed extends GuiScreen {
public void initGui() {
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("singleplayer.crashed.continue")));
if(!ClientPlatformSingleplayer.isRunningSingleThreadMode() && ClientPlatformSingleplayer.isSingleThreadModeSupported()) {
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 126, I18n.format("singleplayer.crashed.singleThreadCont")));
}
}
public void drawScreen(int par1, int par2, float par3) {
@ -52,6 +58,12 @@ public class GuiScreenIntegratedServerFailed extends GuiScreen {
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
this.mc.displayGuiScreen(cont);
}else if(par1GuiButton.id == 1) {
if(SingleplayerServerController.canKillWorker()) {
SingleplayerServerController.killWorker();
}
this.mc.displayGuiScreen(new GuiScreenIntegratedServerStartup(new GuiMainMenu(), true));
}
}
}

View File

@ -1,8 +1,11 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.WorkerStartupFailedException;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket1CIssueDetected;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSelectWorld;
@ -26,31 +29,42 @@ import net.minecraft.client.resources.I18n;
public class GuiScreenIntegratedServerStartup extends GuiScreen {
private final GuiScreen backScreen;
private final boolean singleThread;
private static final String[] dotDotDot = new String[] { "", ".", "..", "..." };
private int counter = 0;
private GuiButton cancelButton;
public GuiScreenIntegratedServerStartup(GuiScreen backScreen) {
this.backScreen = backScreen;
this.singleThread = false;
}
protected void keyTyped(char parChar1, int parInt1) {
public GuiScreenIntegratedServerStartup(GuiScreen backScreen, boolean singleThread) {
this.backScreen = backScreen;
this.singleThread = singleThread;
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(cancelButton = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask")));
cancelButton.visible = false;
}
public void updateScreen() {
++counter;
if(counter == 2) {
try {
SingleplayerServerController.startIntegratedServerWorker();
SingleplayerServerController.startIntegratedServerWorker(singleThread);
}catch(WorkerStartupFailedException ex) {
mc.displayGuiScreen(new GuiScreenIntegratedServerFailed(ex.getMessage(), new GuiMainMenu()));
return;
}
}else if(counter > 2) {
if(counter > 100 && SingleplayerServerController.canKillWorker() && !singleThread) {
cancelButton.visible = true;
}
IPCPacket15Crashed[] crashReport = SingleplayerServerController.worldStatusErrors();
if(crashReport != null) {
mc.displayGuiScreen(GuiScreenIntegratedServerBusy.createException(new GuiMainMenu(), "singleplayer.failed.notStarted", crashReport));
@ -58,18 +72,33 @@ public class GuiScreenIntegratedServerStartup extends GuiScreen {
GuiScreen cont = new GuiSelectWorld(backScreen);
if(SingleplayerServerController.isRunningSingleThreadMode()) {
cont = new GuiScreenIntegratedServerFailed("singleplayer.failed.singleThreadWarning.1", "singleplayer.failed.singleThreadWarning.2", cont);
} else if (!EagRuntime.getConfiguration().isRamdiskMode()
&& SingleplayerServerController.isIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE)
&& SingleplayerServerController.canKillWorker()) {
cont = new GuiScreenRAMDiskModeDetected(cont);
}
mc.displayGuiScreen(cont);
}
}
}
protected void actionPerformed(GuiButton parGuiButton) {
if(parGuiButton.id == 0) {
SingleplayerServerController.killWorker();
mc.displayGuiScreen(new GuiScreenIntegratedServerStartup(new GuiMainMenu(), true));
}
}
public void drawScreen(int i, int j, float f) {
this.drawBackground(0);
String txt = I18n.format("singleplayer.integratedStartup");
int w = this.fontRendererObj.getStringWidth(txt);
this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((System.currentTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215);
this.drawString(this.fontRendererObj, txt + dotDotDot[(int)((EagRuntime.steadyTimeMillis() / 300L) % 4L)], (this.width - w) / 2, this.height / 2 - 50, 16777215);
super.drawScreen(i, j, f);
}
public boolean canCloseGui() {
return false;
}
}

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.Keyboard;
import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
@ -88,4 +89,14 @@ public class GuiScreenLANConnect extends GuiScreen {
}
}
@Override
public boolean showCopyPasteButtons() {
return codeTextField.isFocused();
}
@Override
public void fireInputEvent(EnumInputEvent event, String param) {
codeTextField.fireInputEvent(event, param);
}
}

View File

@ -1,7 +1,9 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake;
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServer;
@ -120,9 +122,15 @@ public class GuiScreenLANConnecting extends GuiScreen {
this.mc.clearTitles();
networkManager.setConnectionState(EnumConnectionState.LOGIN);
networkManager.setNetHandler(new NetHandlerSingleplayerLogin(networkManager, mc, parent));
networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket()));
networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(),
EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(),
ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID));
}
}
}
public boolean canCloseGui() {
return false;
}
}

View File

@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.Keyboard;
import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult;
import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiCreateWorld;
@ -144,10 +145,21 @@ public class GuiScreenNameWorldImport extends GuiScreen {
this.theGuiTextField.drawTextBox();
}else {
definetlyTimeToImport = true;
long dots = (System.currentTimeMillis() / 500l) % 4l;
long dots = (EagRuntime.steadyTimeMillis() / 500l) % 4l;
String str = I18n.format("singleplayer.import.reading", world.fileName);
this.drawString(fontRendererObj, str + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(str)) / 2, this.height / 3 + 10, 0xFFFFFF);
}
super.drawScreen(par1, par2, par3);
}
@Override
public boolean showCopyPasteButtons() {
return theGuiTextField.isFocused();
}
@Override
public void fireInputEvent(EnumInputEvent event, String param) {
theGuiTextField.fireInputEvent(event, param);
}
}

View File

@ -0,0 +1,55 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
/**
* 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 GuiScreenRAMDiskModeDetected extends GuiScreen {
private GuiScreen cont;
public GuiScreenRAMDiskModeDetected(GuiScreen cont) {
this.cont = cont;
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 106, I18n.format("singleplayer.ramdiskdetected.continue")));
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 136, I18n.format("singleplayer.ramdiskdetected.singleThreadCont")));
}
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.ramdiskdetected.title"), this.width / 2, 70, 11184810);
this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.ramdiskdetected.text0"), this.width / 2, 90, 16777215);
this.drawCenteredString(fontRendererObj, I18n.format("singleplayer.ramdiskdetected.text1"), this.width / 2, 105, 16777215);
super.drawScreen(par1, par2, par3);
}
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
this.mc.displayGuiScreen(cont);
}else if(par1GuiButton.id == 1) {
SingleplayerServerController.killWorker();
mc.displayGuiScreen(new GuiScreenIntegratedServerStartup(new GuiMainMenu(), true));
}
}
}

View File

@ -98,7 +98,7 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback {
selected = 0;
}
} else if(btn.id == 4) {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastRefresh > 700l) {
lastRefresh = millis;
slots.relayManager.ping();
@ -106,14 +106,14 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback {
lastRefresh += 60l;
} else if(btn.id == 5) {
slots.relayManager.loadDefaults();
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastRefresh > 700l) {
lastRefresh = millis;
slots.relayManager.ping();
}
lastRefresh += 60l;
} else if(btn.id == 6) {
EagRuntime.downloadFileWithName("EaglerSPRelay.zip", EagRuntime.getResourceBytes("relay_download.zip"));
EagRuntime.downloadFileWithName("EaglerSPRelay.zip", EagRuntime.getRequiredResourceBytes("relay_download.zip"));
}
}
@ -215,4 +215,10 @@ public class GuiScreenRelay extends GuiScreen implements GuiYesNoCallback {
this.slots.handleMouseInput();
}
@Override
public void handleTouchInput() throws IOException {
super.handleTouchInput();
this.slots.handleTouchInput();
}
}

View File

@ -2,7 +2,10 @@ package net.lax1dude.eaglercraft.v1_8.sp.gui;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager;
import net.lax1dude.eaglercraft.v1_8.sp.socket.NetHandlerSingleplayerLogin;
@ -47,7 +50,7 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen {
}
public void initGui() {
if(startStartTime == 0) this.startStartTime = System.currentTimeMillis();
if(startStartTime == 0) this.startStartTime = EagRuntime.steadyTimeMillis();
this.buttonList.add(killTask = new GuiButton(0, this.width / 2 - 100, this.height / 3 + 50, I18n.format("singleplayer.busy.killTask")));
killTask.enabled = false;
}
@ -57,7 +60,7 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen {
float f = 2.0f;
int top = this.height / 3;
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
long dots = (millis / 500l) % 4l;
this.drawString(fontRendererObj, message + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), (this.width - this.fontRendererObj.getStringWidth(message)) / 2, top + 10, 0xFFFFFF);
@ -88,7 +91,9 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen {
this.mc.clearTitles();
this.networkManager.setConnectionState(EnumConnectionState.LOGIN);
this.networkManager.setNetHandler(new NetHandlerSingleplayerLogin(this.networkManager, this.mc, this.menu));
this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket()));
this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(),
EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(),
ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID));
}
try {
this.networkManager.processReceivedPackets();
@ -106,7 +111,7 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen {
}
}
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - startStartTime > 6000l && SingleplayerServerController.canKillWorker()) {
killTask.enabled = true;
}
@ -124,4 +129,9 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen {
public boolean shouldHangupIntegratedServer() {
return false;
}
public boolean canCloseGui() {
return false;
}
}

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.sp.gui;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController;
import net.minecraft.client.LoadingScreenRenderer;
@ -198,4 +199,15 @@ public class GuiShareToLan extends GuiScreen {
public boolean blockPTTKey() {
return this.codeTextField.isFocused();
}
@Override
public boolean showCopyPasteButtons() {
return this.codeTextField.isFocused();
}
@Override
public void fireInputEvent(EnumInputEvent event, String param) {
this.codeTextField.fireInputEvent(event, param);
}
}

View File

@ -27,10 +27,11 @@ public class GuiSlider2 extends GuiButton {
/** Is this slider control being dragged. */
public boolean dragging = false;
public GuiSlider2(int par1, int par2, int par3, int par4, int par5, float par6, float par7) {
super(par1, par2, par3, par4, par5, (int)(par6 * par7 * 100.0F) + "%");
this.sliderValue = par6;
this.sliderMax = par7;
public GuiSlider2(int buttonId, int x, int y, int widthIn, int heightIn, float sliderValue, float sliderMax) {
super(buttonId, x, y, widthIn, heightIn, null);
this.sliderValue = sliderValue;
this.sliderMax = sliderMax;
this.displayString = updateDisplayString();
}
/**
@ -48,6 +49,7 @@ public class GuiSlider2 extends GuiButton {
protected void mouseDragged(Minecraft par1Minecraft, int par2, int par3) {
if (this.visible) {
if (this.dragging) {
float oldValue = sliderValue;
this.sliderValue = (float) (par2 - (this.xPosition + 4)) / (float) (this.width - 8);
if (this.sliderValue < 0.0F) {
@ -58,7 +60,11 @@ public class GuiSlider2 extends GuiButton {
this.sliderValue = 1.0F;
}
this.displayString = (int)(this.sliderValue * this.sliderMax * 100.0F) + "%";
if(oldValue != sliderValue) {
onChange();
}
this.displayString = updateDisplayString();
}
if(this.enabled) {
@ -75,6 +81,7 @@ public class GuiSlider2 extends GuiButton {
*/
public boolean mousePressed(Minecraft par1Minecraft, int par2, int par3) {
if (super.mousePressed(par1Minecraft, par2, par3)) {
float oldValue = sliderValue;
this.sliderValue = (float) (par2 - (this.xPosition + 4)) / (float) (this.width - 8);
if (this.sliderValue < 0.0F) {
@ -85,7 +92,11 @@ public class GuiSlider2 extends GuiButton {
this.sliderValue = 1.0F;
}
this.displayString = (int)(this.sliderValue * this.sliderMax * 100.0F) + "%";
if(oldValue != sliderValue) {
onChange();
}
this.displayString = updateDisplayString();
this.dragging = true;
return true;
} else {
@ -100,4 +111,17 @@ public class GuiSlider2 extends GuiButton {
public void mouseReleased(int par1, int par2) {
this.dragging = false;
}
protected String updateDisplayString() {
return (int)(this.sliderValue * this.sliderMax * 100.0F) + "%";
}
protected void onChange() {
}
public boolean isSliderTouchEvents() {
return true;
}
}

View File

@ -34,11 +34,11 @@ public class IPCPacket14StringList implements IPCPacketBase {
public final List<String> stringList;
public IPCPacket14StringList() {
stringList = new ArrayList();
stringList = new ArrayList<>();
}
public IPCPacket14StringList(int opcode, String[] list) {
stringList = new ArrayList();
stringList = new ArrayList<>(list.length);
for(int i = 0; i < list.length; ++i) {
String s = list[i].trim();
if(s.length() > 0) {
@ -49,7 +49,7 @@ public class IPCPacket14StringList implements IPCPacketBase {
}
public IPCPacket14StringList(int opcode, List<String> list) {
stringList = new ArrayList();
stringList = new ArrayList<>(list.size());
for(int i = 0, l = list.size(); i < l; ++i) {
String s = list.get(i).trim();
if(s.length() > 0) {

View File

@ -40,8 +40,8 @@ public class IPCPacket16NBTList implements IPCPacketBase {
public final List<NBTTagCompound> nbtTagList;
public IPCPacket16NBTList() {
tagList = new LinkedList();
nbtTagList = new LinkedList();
tagList = new LinkedList<>();
nbtTagList = new LinkedList<>();
}
public IPCPacket16NBTList(int opcode, NBTTagCompound[] list) {
@ -49,7 +49,7 @@ public class IPCPacket16NBTList implements IPCPacketBase {
}
public IPCPacket16NBTList(int opcode, List<NBTTagCompound> list) {
tagList = new LinkedList();
tagList = new LinkedList<>();
nbtTagList = list;
for(int i = 0, size = list.size(); i < size; ++i) {
NBTTagCompound tag = list.get(i);

View File

@ -30,7 +30,7 @@ public class IPCPacket17ConfigureLAN implements IPCPacketBase {
public final List<String> iceServers;
public IPCPacket17ConfigureLAN() {
iceServers = new ArrayList();
iceServers = new ArrayList<>();
}
public IPCPacket17ConfigureLAN(int gamemode, boolean cheats, List<String> iceServers) {

View File

@ -19,22 +19,22 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPCPacket20LoggerMessage implements IPCPacketBase {
public class IPCPacket1ALoggerMessage implements IPCPacketBase {
public static final int ID = 0x20;
public static final int ID = 0x1A;
public String logMessage;
public boolean isError;
public IPCPacket20LoggerMessage() {
public IPCPacket1ALoggerMessage() {
}
public IPCPacket20LoggerMessage(String logMessage, boolean isError) {
public IPCPacket1ALoggerMessage(String logMessage, boolean isError) {
this.logMessage = logMessage;
this.isError = isError;
}
public IPCPacket20LoggerMessage(String logMessage) {
public IPCPacket1ALoggerMessage(String logMessage) {
this.logMessage = logMessage;
this.isError = false;
}

View File

@ -19,16 +19,16 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPCPacket21EnableLogging implements IPCPacketBase {
public class IPCPacket1BEnableLogging implements IPCPacketBase {
public static final int ID = 0x21;
public static final int ID = 0x1B;
public boolean enable;
public IPCPacket21EnableLogging() {
public IPCPacket1BEnableLogging() {
}
public IPCPacket21EnableLogging(boolean enable) {
public IPCPacket1BEnableLogging(boolean enable) {
this.enable = enable;
}

View File

@ -1,11 +1,11 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
package net.lax1dude.eaglercraft.v1_8.sp.ipc;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
* 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
@ -19,27 +19,39 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket05ClientSuccess extends IPacket {
public String clientId;
public IPacket05ClientSuccess() {
}
public IPacket05ClientSuccess(String clientId) {
this.clientId = clientId;
public class IPCPacket1CIssueDetected implements IPCPacketBase {
public static final int ID = 0x1C;
public static final int ISSUE_RAMDISK_MODE = 0x01;
public int issueID;
public IPCPacket1CIssueDetected() {
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
public IPCPacket1CIssueDetected(int issueID) {
this.issueID = issueID;
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, clientId);
@Override
public void deserialize(DataInput bin) throws IOException {
issueID = bin.readUnsignedByte();
}
public int packetLength() {
return 1 + clientId.length();
@Override
public void serialize(DataOutput bin) throws IOException {
bin.writeByte(issueID);
}
@Override
public int id() {
return ID;
}
@Override
public int size() {
return 1;
}
}

View File

@ -23,7 +23,7 @@ import java.util.function.Supplier;
*/
public class IPCPacketManager {
public static final HashMap<Integer, Supplier<IPCPacketBase>> mappings = new HashMap();
public static final HashMap<Integer, Supplier<IPCPacketBase>> mappings = new HashMap<>();
public final IPCInputStream IPC_INPUT_STREAM = new IPCInputStream();
public final IPCOutputStream IPC_OUTPUT_STREAM = new IPCOutputStream();
@ -55,8 +55,9 @@ public class IPCPacketManager {
mappings.put(IPCPacket17ConfigureLAN.ID, IPCPacket17ConfigureLAN::new);
mappings.put(IPCPacket18ClearPlayers.ID, IPCPacket18ClearPlayers::new);
mappings.put(IPCPacket19Autosave.ID, IPCPacket19Autosave::new);
mappings.put(IPCPacket20LoggerMessage.ID, IPCPacket20LoggerMessage::new);
mappings.put(IPCPacket21EnableLogging.ID, IPCPacket21EnableLogging::new);
mappings.put(IPCPacket1ALoggerMessage.ID, IPCPacket1ALoggerMessage::new);
mappings.put(IPCPacket1BEnableLogging.ID, IPCPacket1BEnableLogging::new);
mappings.put(IPCPacket1CIssueDetected.ID, IPCPacket1CIssueDetected::new);
mappings.put(IPCPacketFFProcessKeepAlive.ID, IPCPacketFFProcessKeepAlive::new);
}

View File

@ -1,5 +1,6 @@
package net.lax1dude.eaglercraft.v1_8.sp.lan;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.EaglerZLIB;
@ -75,15 +76,17 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
public static LANClientNetworkManager connectToWorld(RelayServerSocket sock, String displayCode, String displayRelay) {
PlatformWebRTC.clearLANClientState();
int connectState = PRE;
IPacket pkt;
RelayPacket pkt;
mainLoop: while(!sock.isClosed()) {
PlatformWebRTC.runScheduledTasks();
sock.update();
if((pkt = sock.readPacket()) != null) {
if(pkt instanceof IPacket00Handshake) {
if(pkt instanceof RelayPacket00Handshake) {
if(connectState == PRE) {
// %%%%%% Process IPacket00Handshake %%%%%%
logger.info("Relay [{}|{}] recieved handshake, client id: {}", displayRelay, displayCode, ((IPacket00Handshake)pkt).connectionCode);
logger.info("Relay [{}|{}] recieved handshake, client id: {}", displayRelay, displayCode, ((RelayPacket00Handshake)pkt).connectionCode);
connectState = INIT;
}else {
@ -91,17 +94,17 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
logger.error("Relay [{}|{}] unexpected packet: IPacket00Handshake in state {}", displayRelay, displayCode, initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacket01ICEServers) {
}else if(pkt instanceof RelayPacket01ICEServers) {
if(connectState == INIT) {
// %%%%%% Process IPacket01ICEServers %%%%%%
IPacket01ICEServers ipkt = (IPacket01ICEServers) pkt;
RelayPacket01ICEServers ipkt = (RelayPacket01ICEServers) pkt;
// print servers
logger.info("Relay [{}|{}] provided ICE servers:", displayRelay, displayCode);
List<String> servers = new ArrayList();
for(ICEServerSet.RelayServer srv : ipkt.servers) {
List<String> servers = new ArrayList<>();
for(RelayPacket01ICEServers.RelayServer srv : ipkt.servers) {
logger.info("Relay [{}|{}] {}: {}", displayRelay, displayCode, srv.type.name(), srv.address);
servers.add(srv.getICEString());
}
@ -110,20 +113,21 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
PlatformWebRTC.clientLANSetICEServersAndConnect(servers.toArray(new String[servers.size()]));
// await result
long lm = System.currentTimeMillis();
long lm = EagRuntime.steadyTimeMillis();
do {
PlatformWebRTC.runScheduledTasks();
String c = PlatformWebRTC.clientLANAwaitDescription();
if(c != null) {
logger.info("Relay [{}|{}] client sent description", displayRelay, displayCode);
// 'this.descriptionHandler' was called, send result:
sock.writePacket(new IPacket04Description("", c));
sock.writePacket(new RelayPacket04Description("", c));
connectState = SENT_DESCRIPTION;
continue mainLoop;
}
EagUtils.sleep(20l);
}while(System.currentTimeMillis() - lm < 5000l);
}while(EagRuntime.steadyTimeMillis() - lm < 5000l);
// no description was sent
sock.close();
@ -135,34 +139,35 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
logger.error("Relay [{}|{}] unexpected packet: IPacket01ICEServers in state {}", displayRelay, displayCode, initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacket03ICECandidate) {
}else if(pkt instanceof RelayPacket03ICECandidate) {
if(connectState == SENT_ICE_CANDIDATE) {
// %%%%%% Process IPacket03ICECandidate %%%%%%
IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt;
RelayPacket03ICECandidate ipkt = (RelayPacket03ICECandidate) pkt;
// process
logger.info("Relay [{}|{}] recieved server ICE candidate", displayRelay, displayCode);
PlatformWebRTC.clientLANSetICECandidate(ipkt.candidate);
PlatformWebRTC.clientLANSetICECandidate(ipkt.getCandidateString());
// await result
long lm = System.currentTimeMillis();
long lm = EagRuntime.steadyTimeMillis();
do {
PlatformWebRTC.runScheduledTasks();
if(PlatformWebRTC.clientLANAwaitChannel()) {
logger.info("Relay [{}|{}] client opened data channel", displayRelay, displayCode);
// 'this.remoteDataChannelHandler' was called, success
sock.writePacket(new IPacket05ClientSuccess(ipkt.peerId));
sock.writePacket(new RelayPacket05ClientSuccess(ipkt.peerId));
sock.close();
return new LANClientNetworkManager(displayCode, displayRelay);
}
EagUtils.sleep(20l);
}while(System.currentTimeMillis() - lm < 5000l);
}while(EagRuntime.steadyTimeMillis() - lm < 5000l);
// no channel was opened
sock.writePacket(new IPacket06ClientFailure(ipkt.peerId));
sock.writePacket(new RelayPacket06ClientFailure(ipkt.peerId));
sock.close();
logger.error("Relay [{}|{}] client open data channel timeout", displayRelay, displayCode);
return null;
@ -172,32 +177,33 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
logger.error("Relay [{}|{}] unexpected packet: IPacket03ICECandidate in state {}", displayRelay, displayCode, initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacket04Description) {
}else if(pkt instanceof RelayPacket04Description) {
if(connectState == SENT_DESCRIPTION) {
// %%%%%% Process IPacket04Description %%%%%%
IPacket04Description ipkt = (IPacket04Description) pkt;
RelayPacket04Description ipkt = (RelayPacket04Description) pkt;
// process
logger.info("Relay [{}|{}] recieved server description", displayRelay, displayCode);
PlatformWebRTC.clientLANSetDescription(ipkt.description);
PlatformWebRTC.clientLANSetDescription(ipkt.getDescriptionString());
// await result
long lm = System.currentTimeMillis();
long lm = EagRuntime.steadyTimeMillis();
do {
PlatformWebRTC.runScheduledTasks();
String c = PlatformWebRTC.clientLANAwaitICECandidate();
if(c != null) {
logger.info("Relay [{}|{}] client sent ICE candidate", displayRelay, displayCode);
// 'this.iceCandidateHandler' was called, send result:
sock.writePacket(new IPacket03ICECandidate("", c));
sock.writePacket(new RelayPacket03ICECandidate("", c));
connectState = SENT_ICE_CANDIDATE;
continue mainLoop;
}
EagUtils.sleep(20l);
}while(System.currentTimeMillis() - lm < 5000l);
}while(EagRuntime.steadyTimeMillis() - lm < 5000l);
// no ice candidates were sent
sock.close();
@ -209,12 +215,12 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
logger.error("Relay [{}|{}] unexpected packet: IPacket04Description in state {}", displayRelay, displayCode, initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacketFFErrorCode) {
}else if(pkt instanceof RelayPacketFFErrorCode) {
// %%%%%% Process IPacketFFErrorCode %%%%%%
IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt;
logger.error("Relay [{}|{}] connection failed: {}({}): {}", displayRelay, displayCode, IPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc);
RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode) pkt;
logger.error("Relay [{}|{}] connection failed: {}({}): {}", displayRelay, displayCode, RelayPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc);
Throwable t;
while((t = sock.getException()) != null) {
logger.error(t);
@ -291,7 +297,7 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
return !clientDisconnected;
}
private List<byte[]> fragmentedPacket = new ArrayList();
private List<byte[]> fragmentedPacket = new ArrayList<>();
@Override
public void processReceivedPackets() throws IOException {

View File

@ -3,6 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.lan;
import java.util.Iterator;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
@ -10,8 +11,8 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket03ICECandidate;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket04Description;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket03ICECandidate;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket04Description;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
@ -47,12 +48,12 @@ class LANClientPeer {
protected void handleICECandidates(String candidates) {
if(state == SENT_DESCRIPTION) {
PlatformWebRTC.serverLANPeerICECandidates(clientId, candidates);
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
do {
LANPeerEvent evt;
if((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null) {
if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
LANServerController.lanRelaySocket.writePacket(new IPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates));
LANServerController.lanRelaySocket.writePacket(new RelayPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates));
state = SENT_ICE_CANDIDATE;
return;
}else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) {
@ -64,7 +65,7 @@ class LANClientPeer {
return;
}
EagUtils.sleep(20l);
}while(System.currentTimeMillis() - millis < 5000l);
}while(EagRuntime.steadyTimeMillis() - millis < 5000l);
logger.error("Getting server ICE candidates for '{}' timed out!", clientId);
disconnect();
}else {
@ -75,12 +76,12 @@ class LANClientPeer {
protected void handleDescription(String description) {
if(state == PRE) {
PlatformWebRTC.serverLANPeerDescription(clientId, description);
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
do {
LANPeerEvent evt;
if((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null) {
if(evt instanceof LANPeerEvent.LANPeerDescriptionEvent) {
LANServerController.lanRelaySocket.writePacket(new IPacket04Description(clientId, ((LANPeerEvent.LANPeerDescriptionEvent)evt).description));
LANServerController.lanRelaySocket.writePacket(new RelayPacket04Description(clientId, ((LANPeerEvent.LANPeerDescriptionEvent)evt).description));
state = SENT_DESCRIPTION;
return;
}else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) {
@ -92,7 +93,7 @@ class LANClientPeer {
return;
}
EagUtils.sleep(20l);
}while(System.currentTimeMillis() - millis < 5000l);
}while(EagRuntime.steadyTimeMillis() - millis < 5000l);
logger.error("Getting server description for '{}' timed out!", clientId);
disconnect();
}else {
@ -102,7 +103,7 @@ class LANClientPeer {
protected void handleSuccess() {
if(state == SENT_ICE_CANDIDATE) {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
do {
LANPeerEvent evt;
while((evt = PlatformWebRTC.serverLANGetEvent(clientId)) != null && evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
@ -122,7 +123,7 @@ class LANClientPeer {
return;
}
EagUtils.sleep(20l);
}while(System.currentTimeMillis() - millis < 5000l);
}while(EagRuntime.steadyTimeMillis() - millis < 5000l);
logger.error("Getting server description for '{}' timed out!", clientId);
disconnect();
}else {

View File

@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
@ -34,7 +35,7 @@ public class LANServerController {
public static final Logger logger = LogManager.getLogger("LANServerController");
public static final List<String> currentICEServers = new ArrayList();
public static final List<String> currentICEServers = new ArrayList<>();
static RelayServerSocket lanRelaySocket = null;
@ -49,25 +50,26 @@ public class LANServerController {
return null;
}else {
progressCallback.accept("Opening: " + sock.getURI());
IPacket00Handshake hs = (IPacket00Handshake)sock.readPacket();
RelayPacket00Handshake hs = (RelayPacket00Handshake)sock.readPacket();
lanRelaySocket = sock;
String code = hs.connectionCode;
logger.info("Relay [{}] connected as 'server', code: {}", sock.getURI(), code);
progressCallback.accept("Opened '" + code + "' on " + sock.getURI());
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
do {
sock.update();
if(sock.isClosed()) {
logger.info("Relay [{}] connection lost", sock.getURI());
lanRelaySocket = null;
return null;
}
IPacket pkt = sock.readPacket();
RelayPacket pkt = sock.readPacket();
if(pkt != null) {
if(pkt instanceof IPacket01ICEServers) {
IPacket01ICEServers ipkt = (IPacket01ICEServers)pkt;
if(pkt instanceof RelayPacket01ICEServers) {
RelayPacket01ICEServers ipkt = (RelayPacket01ICEServers)pkt;
logger.info("Relay [{}] provided ICE servers:", sock.getURI());
currentICEServers.clear();
for(net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.ICEServerSet.RelayServer srv : ipkt.servers) {
for(net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket01ICEServers.RelayServer srv : ipkt.servers) {
logger.info("Relay [{}] {}: {}", sock.getURI(), srv.type.name(), srv.address);
currentICEServers.add(srv.getICEString());
}
@ -80,7 +82,7 @@ public class LANServerController {
}
}
EagUtils.sleep(50l);
}while(System.currentTimeMillis() - millis < 1000l);
}while(EagRuntime.steadyTimeMillis() - millis < 1000l);
logger.info("Relay [{}] relay provide ICE servers timeout", sock.getURI());
closeLAN();
return null;
@ -131,54 +133,55 @@ public class LANServerController {
return lanRelaySocket != null;
}
private static final Map<String, LANClientPeer> clients = new HashMap();
private static final Map<String, LANClientPeer> clients = new HashMap<>();
public static void updateLANServer() {
if(lanRelaySocket != null) {
IPacket pkt;
lanRelaySocket.update();
RelayPacket pkt;
while((pkt = lanRelaySocket.readPacket()) != null) {
if(pkt instanceof IPacket02NewClient) {
IPacket02NewClient ipkt = (IPacket02NewClient) pkt;
if(pkt instanceof RelayPacket02NewClient) {
RelayPacket02NewClient ipkt = (RelayPacket02NewClient) pkt;
if(clients.containsKey(ipkt.clientId)) {
logger.error("Relay [{}] relay provided duplicate client '{}'", lanRelaySocket.getURI(), ipkt.clientId);
}else {
clients.put(ipkt.clientId, new LANClientPeer(ipkt.clientId));
}
}else if(pkt instanceof IPacket03ICECandidate) {
IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt;
}else if(pkt instanceof RelayPacket03ICECandidate) {
RelayPacket03ICECandidate ipkt = (RelayPacket03ICECandidate) pkt;
LANClientPeer c = clients.get(ipkt.peerId);
if(c != null) {
c.handleICECandidates(ipkt.candidate);
c.handleICECandidates(ipkt.getCandidateString());
}else {
logger.error("Relay [{}] relay sent IPacket03ICECandidate for unknown client '{}'", lanRelaySocket.getURI(), ipkt.peerId);
}
}else if(pkt instanceof IPacket04Description) {
IPacket04Description ipkt = (IPacket04Description) pkt;
}else if(pkt instanceof RelayPacket04Description) {
RelayPacket04Description ipkt = (RelayPacket04Description) pkt;
LANClientPeer c = clients.get(ipkt.peerId);
if(c != null) {
c.handleDescription(ipkt.description);
c.handleDescription(ipkt.getDescriptionString());
}else {
logger.error("Relay [{}] relay sent IPacket04Description for unknown client '{}'", lanRelaySocket.getURI(), ipkt.peerId);
}
}else if(pkt instanceof IPacket05ClientSuccess) {
IPacket05ClientSuccess ipkt = (IPacket05ClientSuccess) pkt;
}else if(pkt instanceof RelayPacket05ClientSuccess) {
RelayPacket05ClientSuccess ipkt = (RelayPacket05ClientSuccess) pkt;
LANClientPeer c = clients.get(ipkt.clientId);
if(c != null) {
c.handleSuccess();
}else {
logger.error("Relay [{}] relay sent IPacket05ClientSuccess for unknown client '{}'", lanRelaySocket.getURI(), ipkt.clientId);
}
}else if(pkt instanceof IPacket06ClientFailure) {
IPacket06ClientFailure ipkt = (IPacket06ClientFailure) pkt;
}else if(pkt instanceof RelayPacket06ClientFailure) {
RelayPacket06ClientFailure ipkt = (RelayPacket06ClientFailure) pkt;
LANClientPeer c = clients.get(ipkt.clientId);
if(c != null) {
c.handleFailure();
}else {
logger.error("Relay [{}] relay sent IPacket06ClientFailure for unknown client '{}'", lanRelaySocket.getURI(), ipkt.clientId);
}
}else if(pkt instanceof IPacketFFErrorCode) {
IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt;
logger.error("Relay [{}] error code thrown: {}({}): {}", lanRelaySocket.getURI(), IPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc);
}else if(pkt instanceof RelayPacketFFErrorCode) {
RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode) pkt;
logger.error("Relay [{}] error code thrown: {}({}): {}", lanRelaySocket.getURI(), RelayPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc);
Throwable t;
while((t = lanRelaySocket.getException()) != null) {
logger.error(t);
@ -186,6 +189,7 @@ public class LANServerController {
}else {
logger.error("Relay [{}] unexpected packet: {}", lanRelaySocket.getURI(), pkt.getClass().getSimpleName());
}
lanRelaySocket.update();
}
if(lanRelaySocket.isClosed()) {
lanRelaySocket = null;

View File

@ -9,11 +9,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServer;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQuery;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket07LocalWorlds;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
@ -32,15 +33,15 @@ import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket07LocalWorlds;
*/
public class LANServerList {
private final List<LanServer> lanServersList = new LinkedList();
private final Map<String, RelayWorldsQuery> lanServersQueryList = new LinkedHashMap();
private final Set<String> deadURIs = new HashSet();
private final List<LanServer> lanServersList = new LinkedList<>();
private final Map<String, RelayWorldsQuery> lanServersQueryList = new LinkedHashMap<>();
private final Set<String> deadURIs = new HashSet<>();
private long lastRefresh = 0l;
private int refreshCounter = 0;
public boolean update() {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastRefresh > 20000l) {
if(++refreshCounter < 10) {
refresh();
@ -54,6 +55,7 @@ public class LANServerList {
Entry<String,RelayWorldsQuery> etr = itr.next();
String uri = etr.getKey();
RelayWorldsQuery q = etr.getValue();
q.update();
if(!q.isQueryOpen()) {
itr.remove();
if(q.isQueryFailed()) {
@ -75,9 +77,9 @@ public class LANServerList {
}
}
if(rl != null) {
Iterator<IPacket07LocalWorlds.LocalWorld> itr3 = q.getWorlds().iterator();
Iterator<RelayPacket07LocalWorlds.LocalWorld> itr3 = q.getWorlds().iterator();
yee: while(itr3.hasNext()) {
IPacket07LocalWorlds.LocalWorld l = itr3.next();
RelayPacket07LocalWorlds.LocalWorld l = itr3.next();
itr2 = lanServersList.iterator();
while(itr2.hasNext()) {
LanServer l2 = itr2.next();
@ -116,7 +118,7 @@ public class LANServerList {
}
private void refresh() {
lastRefresh = System.currentTimeMillis();
lastRefresh = EagRuntime.steadyTimeMillis();
if(PlatformWebRTC.supported()) {
for(int i = 0, l = RelayManager.relayManager.count(); i < l; ++i) {
RelayServer srv = RelayManager.relayManager.get(i);

View File

@ -1,11 +1,10 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IRelayLogger;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
* 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
@ -19,27 +18,37 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket06ClientFailure extends IPacket {
public String clientId;
public IPacket06ClientFailure() {
}
public IPacket06ClientFailure(String clientId) {
this.clientId = clientId;
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
public class RelayLoggerImpl implements IRelayLogger {
private final Logger impl;
public RelayLoggerImpl(Logger impl) {
this.impl = impl;
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, clientId);
@Override
public void debug(String msg, Object... args) {
impl.debug(msg, args);
}
public int packetLength() {
return 1 + clientId.length();
@Override
public void info(String msg, Object... args) {
impl.debug(msg, args);
}
@Override
public void warn(String msg, Object... args) {
impl.warn(msg, args);
}
@Override
public void error(String msg, Object... args) {
impl.error(msg, args);
}
@Override
public void error(Throwable th) {
impl.error(th);
}
}

View File

@ -14,9 +14,9 @@ import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket00Handshake;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacketFFErrorCode;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacketFFErrorCode;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
@ -43,7 +43,7 @@ public class RelayManager {
public static final RelayManager relayManager = new RelayManager();
public static final int preferredRelayVersion = 1;
private final List<RelayServer> relays = new ArrayList();
private final List<RelayServer> relays = new ArrayList<>();
private long lastPingThrough = 0l;
public void load(byte[] relayConfig) {
@ -180,7 +180,7 @@ public class RelayManager {
}
public void ping() {
lastPingThrough = System.currentTimeMillis();
lastPingThrough = EagRuntime.steadyTimeMillis();
for(int i = 0, l = relays.size(); i < l; ++i) {
relays.get(i).ping();
}
@ -274,16 +274,18 @@ public class RelayManager {
public RelayServerSocket connectHandshake(RelayServer relay, int type, String code) {
RelayServerSocket sock = relay.openSocket();
while(!sock.isClosed()) {
sock.update();
if(sock.isOpen()) {
sock.writePacket(new IPacket00Handshake(type, preferredRelayVersion, code));
sock.writePacket(new RelayPacket00Handshake(type, preferredRelayVersion, code));
while(!sock.isClosed()) {
IPacket pkt = sock.nextPacket();
sock.update();
RelayPacket pkt = sock.nextPacket();
if(pkt != null) {
if(pkt instanceof IPacket00Handshake) {
if(pkt instanceof RelayPacket00Handshake) {
return sock;
}else if(pkt instanceof IPacketFFErrorCode) {
IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt;
logger.error("Relay [{}] failed: {}({}): {}", relay.address, IPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc);
}else if(pkt instanceof RelayPacketFFErrorCode) {
RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode) pkt;
logger.error("Relay [{}] failed: {}({}): {}", relay.address, RelayPacketFFErrorCode.code2string(ipkt.code), ipkt.code, ipkt.desc);
Throwable t;
while((t = sock.getException()) != null) {
logger.error(t);
@ -309,12 +311,12 @@ public class RelayManager {
return null;
}
private final List<RelayServer> brokenServers = new LinkedList();
private final List<RelayServer> brokenServers = new LinkedList<>();
public RelayServerSocket getWorkingRelay(Consumer<String> progressCallback, int type, String code) {
brokenServers.clear();
if(!relays.isEmpty()) {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastPingThrough < 10000l) {
RelayServer relay = getPrimary();
if(relay.getPing() > 0l && relay.getPingCompatible().isCompatible()) {

View File

@ -28,6 +28,7 @@ public interface RelayQuery {
}
}
void update();
boolean isQueryOpen();
boolean isQueryFailed();
RateLimit isQueryRateLimit();

View File

@ -0,0 +1,225 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket69Pong;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket70SpecialUpdate;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacketFFErrorCode;
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. 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 RelayQueryImpl implements RelayQuery {
private static final Logger logger = LogManager.getLogger("RelayQuery");
private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket"));
private final IWebSocketClient sock;
private final String uri;
private boolean failed;
private boolean hasSentHandshake = false;
private boolean hasRecievedAnyData = false;
private int vers = -1;
private String comment = "<no comment>";
private String brand = "<no brand>";
private long connectionOpenedAt;
private long connectionPingStart = -1;
private long connectionPingTimer = -1;
private RateLimit rateLimitStatus = RateLimit.NONE;
private VersionMismatch versError = VersionMismatch.UNKNOWN;
public RelayQueryImpl(String uri) {
this.uri = uri;
IWebSocketClient s;
try {
connectionOpenedAt = EagRuntime.steadyTimeMillis();
s = PlatformNetworking.openWebSocketUnsafe(uri);
}catch(Throwable t) {
connectionOpenedAt = 0l;
sock = null;
failed = true;
return;
}
sock = s;
}
@Override
public void update() {
if(sock == null) return;
if(sock.availableStringFrames() > 0) {
logger.warn("[{}] discarding {} string frames recieved on a binary connection", uri, sock.availableStringFrames());
sock.clearStringFrames();
}
List<IWebSocketFrame> frames = sock.getNextBinaryFrames();
if(frames != null) {
for(int i = 0, l = frames.size(); i < l; ++i) {
hasRecievedAnyData = true;
byte[] arr = frames.get(i).getByteArray();
if(arr.length == 2 && arr[0] == (byte)0xFC) {
if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) {
rateLimitStatus = RateLimit.BLOCKED;
RelayServerRateLimitTracker.setLimited(RelayQueryImpl.this.uri);
}else if(arr[1] == (byte)0x02) {
rateLimitStatus = RateLimit.NOW_LOCKED;
RelayServerRateLimitTracker.setLimitedLocked(RelayQueryImpl.this.uri);
}else {
rateLimitStatus = RateLimit.LOCKED;
RelayServerRateLimitTracker.setLocked(RelayQueryImpl.this.uri);
}
failed = true;
sock.close();
}else {
try {
RelayPacket pkt = RelayPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)), loggerImpl);
if(pkt instanceof RelayPacket69Pong) {
RelayPacket69Pong ipkt = (RelayPacket69Pong)pkt;
versError = VersionMismatch.COMPATIBLE;
if(connectionPingTimer == -1) {
connectionPingTimer = frames.get(i).getTimestamp() - connectionPingStart;
}
vers = ipkt.protcolVersion;
comment = ipkt.comment;
brand = ipkt.brand;
failed = false;
sock.close();
return;
}else if(pkt instanceof RelayPacket70SpecialUpdate) {
RelayPacket70SpecialUpdate ipkt = (RelayPacket70SpecialUpdate)pkt;
if(ipkt.operation == RelayPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) {
UpdateService.addCertificateToSet(ipkt.updatePacket);
}
}else if(pkt instanceof RelayPacketFFErrorCode) {
RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode)pkt;
if(ipkt.code == RelayPacketFFErrorCode.TYPE_PROTOCOL_VERSION) {
String s1 = ipkt.desc.toLowerCase();
if(s1.contains("outdated client") || s1.contains("client outdated")) {
versError = VersionMismatch.CLIENT_OUTDATED;
}else if(s1.contains("outdated server") || s1.contains("server outdated") ||
s1.contains("outdated relay") || s1.contains("server relay")) {
versError = VersionMismatch.RELAY_OUTDATED;
}else {
versError = VersionMismatch.UNKNOWN;
}
}
logger.error("[{}] Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc);
failed = true;
sock.close();
return;
}else {
throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'");
}
} catch (IOException e) {
logger.error("Relay query error: {}", e.toString());
logger.error(e);
failed = true;
sock.close();
return;
}
}
}
}
if(sock.isOpen() && !hasSentHandshake) {
hasSentHandshake = true;
try {
connectionPingStart = EagRuntime.steadyTimeMillis();
sock.send(RelayPacket.writePacket(new RelayPacket00Handshake(0x03, RelayManager.preferredRelayVersion, ""), loggerImpl));
} catch (IOException e) {
logger.error("Failed to write handshake: {}", e.toString());
logger.error(e);
sock.close();
failed = true;
}
}
if(sock.isClosed()) {
if(!hasRecievedAnyData) {
failed = true;
rateLimitStatus = RelayServerRateLimitTracker.isLimitedLong(uri);
}
}
if(EagRuntime.steadyTimeMillis() - connectionOpenedAt > 10000l) {
logger.error("Terminating connection that was open for too long: {}", uri);
sock.close();
failed = true;
}
}
@Override
public boolean isQueryOpen() {
return sock != null && !sock.isClosed();
}
@Override
public boolean isQueryFailed() {
return failed || sock == null || sock.getState() == EnumEaglerConnectionState.FAILED;
}
@Override
public RateLimit isQueryRateLimit() {
return rateLimitStatus;
}
@Override
public void close() {
if(sock != null) {
sock.close();
}
}
@Override
public int getVersion() {
return vers;
}
@Override
public String getComment() {
return comment;
}
@Override
public String getBrand() {
return brand;
}
@Override
public long getPing() {
return connectionPingTimer < 1 ? 1 : connectionPingTimer;
}
@Override
public VersionMismatch getCompatible() {
return versError;
}
}

View File

@ -0,0 +1,75 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. 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 RelayQueryRateLimitDummy implements RelayQuery {
private final RateLimit type;
public RelayQueryRateLimitDummy(RateLimit type) {
this.type = type;
}
@Override
public void update() {
}
@Override
public boolean isQueryOpen() {
return false;
}
@Override
public boolean isQueryFailed() {
return true;
}
@Override
public RateLimit isQueryRateLimit() {
return type;
}
@Override
public void close() {
}
@Override
public int getVersion() {
return RelayManager.preferredRelayVersion;
}
@Override
public String getComment() {
return "this query was rate limited";
}
@Override
public String getBrand() {
return "lax1dude";
}
@Override
public long getPing() {
return 0l;
}
@Override
public VersionMismatch getCompatible() {
return VersionMismatch.COMPATIBLE;
}
}

View File

@ -1,5 +1,6 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery.VersionMismatch;
@ -105,23 +106,26 @@ public class RelayServer {
}
public void update() {
if(query != null && !query.isQueryOpen()) {
if(query.isQueryFailed()) {
queriedVersion = -1;
queriedComment = null;
queriedVendor = null;
queriedCompatible = VersionMismatch.UNKNOWN;
ping = 0l;
}else {
queriedVersion = query.getVersion();
queriedComment = query.getComment();
queriedVendor = query.getBrand();
ping = query.getPing();
queriedCompatible = query.getCompatible();
workingPing = ping;
if(query != null) {
query.update();
if(!query.isQueryOpen()) {
if(query.isQueryFailed()) {
queriedVersion = -1;
queriedComment = null;
queriedVendor = null;
queriedCompatible = VersionMismatch.UNKNOWN;
ping = 0l;
}else {
queriedVersion = query.getVersion();
queriedComment = query.getComment();
queriedVendor = query.getBrand();
ping = query.getPing();
queriedCompatible = query.getCompatible();
workingPing = ping;
}
lastPing = EagRuntime.steadyTimeMillis();
query = null;
}
lastPing = System.currentTimeMillis();
query = null;
}
}

View File

@ -0,0 +1,98 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.util.HashMap;
import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
/**
* 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 RelayServerRateLimitTracker {
private static final Map<String,Long> relayQueryLimited = new HashMap<>();
private static final Map<String,Long> relayQueryLocked = new HashMap<>();
public static void setLimited(String str) {
synchronized(relayQueryLimited) {
relayQueryLimited.put(str, EagRuntime.steadyTimeMillis());
}
}
public static void setLocked(String str) {
synchronized(relayQueryLocked) {
relayQueryLocked.put(str, EagRuntime.steadyTimeMillis());
}
}
public static void setLimitedLocked(String str) {
long now = EagRuntime.steadyTimeMillis();
synchronized(relayQueryLimited) {
relayQueryLimited.put(str, now);
}
synchronized(relayQueryLocked) {
relayQueryLocked.put(str, now);
}
}
public static RelayQuery.RateLimit isLimited(String str) {
long now = EagRuntime.steadyTimeMillis();
synchronized(relayQueryLocked) {
Long l = relayQueryLocked.get(str);
if(l != null && now - l.longValue() < 60000l) {
return RelayQuery.RateLimit.LOCKED;
}
}
synchronized(relayQueryLimited) {
Long l = relayQueryLimited.get(str);
if(l != null && now - l.longValue() < 10000l) {
return RelayQuery.RateLimit.BLOCKED;
}
}
return RelayQuery.RateLimit.NONE;
}
public static RelayQuery.RateLimit isLimitedLong(String str) {
long now = EagRuntime.steadyTimeMillis();
synchronized(relayQueryLocked) {
Long l = relayQueryLocked.get(str);
if(l != null && now - l.longValue() < 400000l) {
return RelayQuery.RateLimit.LOCKED;
}
}
synchronized(relayQueryLimited) {
Long l = relayQueryLimited.get(str);
if(l != null && now - l.longValue() < 900000l) {
return RelayQuery.RateLimit.BLOCKED;
}
}
return RelayQuery.RateLimit.NONE;
}
public static RelayQuery.RateLimit isLimitedEver(String str) {
synchronized(relayQueryLocked) {
if(relayQueryLocked.containsKey(str)) {
return RelayQuery.RateLimit.LOCKED;
}
}
synchronized(relayQueryLimited) {
if(relayQueryLimited.containsKey(str)) {
return RelayQuery.RateLimit.BLOCKED;
}
}
return RelayQuery.RateLimit.NONE;
}
}

View File

@ -1,6 +1,6 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
@ -19,6 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket;
*/
public interface RelayServerSocket {
void update();
boolean isOpen();
boolean isClosed();
void close();
@ -26,10 +27,10 @@ public interface RelayServerSocket {
boolean isFailed();
Throwable getException();
void writePacket(IPacket pkt);
void writePacket(RelayPacket pkt);
IPacket readPacket();
IPacket nextPacket();
RelayPacket readPacket();
RelayPacket nextPacket();
RelayQuery.RateLimit getRatelimitHistory();

View File

@ -0,0 +1,173 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket70SpecialUpdate;
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. 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 RelayServerSocketImpl implements RelayServerSocket {
private static final Logger logger = LogManager.getLogger("RelayServerSocket");
private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket"));
private final IWebSocketClient sock;
private final String uri;
private boolean hasRecievedAnyData = false;
private boolean failed = false;
private final List<Throwable> exceptions = new LinkedList<>();
private final List<RelayPacket> packets = new LinkedList<>();
public RelayServerSocketImpl(String uri, int timeout) {
this.uri = uri;
IWebSocketClient s;
try {
s = PlatformNetworking.openWebSocketUnsafe(uri);
}catch(Throwable t) {
exceptions.add(t);
sock = null;
failed = true;
return;
}
sock = s;
}
@Override
public void update() {
if(sock == null) return;
if(sock.availableStringFrames() > 0) {
logger.warn("[{}] discarding {} string frames recieved on a binary connection", uri, sock.availableStringFrames());
sock.clearStringFrames();
}
List<IWebSocketFrame> frames = sock.getNextBinaryFrames();
if(frames != null) {
for(int i = 0, l = frames.size(); i < l; ++i) {
hasRecievedAnyData = true;
try {
RelayPacket pkt = RelayPacket.readPacket(new DataInputStream(frames.get(i).getInputStream()), loggerImpl);
if(pkt instanceof RelayPacket70SpecialUpdate) {
RelayPacket70SpecialUpdate ipkt = (RelayPacket70SpecialUpdate)pkt;
if(ipkt.operation == RelayPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) {
UpdateService.addCertificateToSet(ipkt.updatePacket);
}
}else {
packets.add(pkt);
}
} catch (IOException e) {
exceptions.add(e);
logger.error("[{}] Relay Socket Error: {}", uri, e.toString());
EagRuntime.debugPrintStackTrace(e);
failed = true;
sock.close();
return;
}
}
}
if(sock.isClosed()) {
if (!hasRecievedAnyData) {
failed = true;
}
}
}
@Override
public boolean isOpen() {
return sock != null && sock.isOpen();
}
@Override
public boolean isClosed() {
return sock == null || sock.isClosed();
}
@Override
public void close() {
if(sock != null) {
sock.close();
}
}
@Override
public boolean isFailed() {
return failed || sock == null || sock.getState() == EnumEaglerConnectionState.FAILED;
}
@Override
public Throwable getException() {
if(!exceptions.isEmpty()) {
return exceptions.remove(0);
}else {
return null;
}
}
@Override
public void writePacket(RelayPacket pkt) {
if(sock != null) {
try {
sock.send(RelayPacket.writePacket(pkt, loggerImpl));
} catch (Throwable e) {
logger.error("Relay connection error: {}", e.toString());
EagRuntime.debugPrintStackTrace(e);
exceptions.add(e);
sock.close();
}
}
}
@Override
public RelayPacket readPacket() {
if(!packets.isEmpty()) {
return packets.remove(0);
}else {
return null;
}
}
@Override
public RelayPacket nextPacket() {
if(!packets.isEmpty()) {
return packets.get(0);
}else {
return null;
}
}
@Override
public RelayQuery.RateLimit getRatelimitHistory() {
return RelayServerRateLimitTracker.isLimitedEver(uri);
}
@Override
public String getURI() {
return uri;
}
}

View File

@ -0,0 +1,80 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. 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 RelayServerSocketRateLimitDummy implements RelayServerSocket {
private final RelayQuery.RateLimit limit;
public RelayServerSocketRateLimitDummy(RelayQuery.RateLimit limit) {
this.limit = limit;
}
@Override
public void update() {
}
@Override
public boolean isOpen() {
return false;
}
@Override
public boolean isClosed() {
return true;
}
@Override
public void close() {
}
@Override
public boolean isFailed() {
return true;
}
@Override
public Throwable getException() {
return null;
}
@Override
public void writePacket(RelayPacket pkt) {
}
@Override
public RelayPacket readPacket() {
return null;
}
@Override
public RelayPacket nextPacket() {
return null;
}
@Override
public RelayQuery.RateLimit getRatelimitHistory() {
return limit;
}
@Override
public String getURI() {
return "<disconnected>";
}
}

View File

@ -3,7 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery.VersionMismatch;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket07LocalWorlds;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
@ -22,12 +22,13 @@ import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket07LocalWorlds;
*/
public interface RelayWorldsQuery {
void update();
boolean isQueryOpen();
boolean isQueryFailed();
RelayQuery.RateLimit isQueryRateLimit();
void close();
List<IPacket07LocalWorlds.LocalWorld> getWorlds();
List<RelayPacket07LocalWorlds.LocalWorld> getWorlds();
VersionMismatch getCompatible();

View File

@ -0,0 +1,197 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery.RateLimit;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket00Handshake;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket70SpecialUpdate;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacketFFErrorCode;
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. 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 RelayWorldsQueryImpl implements RelayWorldsQuery {
private static final Logger logger = LogManager.getLogger("RelayWorldsQuery");
private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket"));
private final IWebSocketClient sock;
private final String uri;
private boolean failed;
private long openedAt;
private boolean hasSentHandshake = false;
private boolean hasRecievedAnyData = false;
private RelayQuery.RateLimit rateLimitStatus = RelayQuery.RateLimit.NONE;
private RelayQuery.VersionMismatch versError = RelayQuery.VersionMismatch.UNKNOWN;
private List<RelayPacket07LocalWorlds.LocalWorld> worlds = null;
public RelayWorldsQueryImpl(String uri) {
this.uri = uri;
IWebSocketClient s;
try {
openedAt = EagRuntime.steadyTimeMillis();
s = PlatformNetworking.openWebSocketUnsafe(uri);
}catch(Throwable t) {
sock = null;
failed = true;
return;
}
sock = s;
}
@Override
public void update() {
if(sock == null) return;
if(sock.availableStringFrames() > 0) {
logger.warn("[{}] discarding {} string frames recieved on a binary connection", uri, sock.availableStringFrames());
sock.clearStringFrames();
}
List<IWebSocketFrame> frames = sock.getNextBinaryFrames();
if(frames != null) {
for(int i = 0, l = frames.size(); i < l; ++i) {
hasRecievedAnyData = true;
byte[] arr = frames.get(i).getByteArray();
if(arr.length == 2 && arr[0] == (byte)0xFC) {
if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) {
rateLimitStatus = RateLimit.BLOCKED;
RelayServerRateLimitTracker.setLimited(RelayWorldsQueryImpl.this.uri);
}else if(arr[1] == (byte)0x02) {
rateLimitStatus = RateLimit.NOW_LOCKED;
RelayServerRateLimitTracker.setLimitedLocked(RelayWorldsQueryImpl.this.uri);
}else {
rateLimitStatus = RateLimit.LOCKED;
RelayServerRateLimitTracker.setLocked(RelayWorldsQueryImpl.this.uri);
}
failed = true;
sock.close();
}else {
try {
RelayPacket pkt = RelayPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)), loggerImpl);
if(pkt instanceof RelayPacket07LocalWorlds) {
worlds = ((RelayPacket07LocalWorlds)pkt).worldsList;
sock.close();
failed = false;
return;
}else if(pkt instanceof RelayPacket70SpecialUpdate) {
RelayPacket70SpecialUpdate ipkt = (RelayPacket70SpecialUpdate)pkt;
if(ipkt.operation == RelayPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) {
UpdateService.addCertificateToSet(ipkt.updatePacket);
}
}else if(pkt instanceof RelayPacketFFErrorCode) {
RelayPacketFFErrorCode ipkt = (RelayPacketFFErrorCode)pkt;
if(ipkt.code == RelayPacketFFErrorCode.TYPE_PROTOCOL_VERSION) {
String s1 = ipkt.desc.toLowerCase();
if(s1.contains("outdated client") || s1.contains("client outdated")) {
versError = RelayQuery.VersionMismatch.CLIENT_OUTDATED;
}else if(s1.contains("outdated server") || s1.contains("server outdated") ||
s1.contains("outdated relay") || s1.contains("server relay")) {
versError = RelayQuery.VersionMismatch.RELAY_OUTDATED;
}else {
versError = RelayQuery.VersionMismatch.UNKNOWN;
}
}
logger.error("[{}] Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc);
failed = true;
sock.close();
return;
}else {
throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'");
}
} catch (IOException e) {
logger.error("Relay World Query Error: {}", e.toString());
EagRuntime.debugPrintStackTrace(e);
failed = true;
sock.close();
return;
}
}
}
}
if(sock.isOpen() && !hasSentHandshake) {
hasSentHandshake = true;
try {
sock.send(RelayPacket.writePacket(new RelayPacket00Handshake(0x04, RelayManager.preferredRelayVersion, ""), loggerImpl));
} catch (IOException e) {
logger.error("Failed to write handshake: {}", e.toString());
logger.error(e);
sock.close();
failed = true;
}
}
if(sock.isClosed()) {
if(!hasRecievedAnyData) {
failed = true;
rateLimitStatus = RelayServerRateLimitTracker.isLimitedLong(uri);
}
}else {
if(EagRuntime.steadyTimeMillis() - openedAt > 10000l) {
logger.error("Terminating connection that was open for too long: {}", uri);
sock.close();
failed = true;
}
}
}
@Override
public boolean isQueryOpen() {
return sock != null && !sock.isClosed();
}
@Override
public boolean isQueryFailed() {
return failed || sock == null || sock.getState() == EnumEaglerConnectionState.FAILED;
}
@Override
public RelayQuery.RateLimit isQueryRateLimit() {
return rateLimitStatus;
}
@Override
public void close() {
if(sock != null) {
sock.close();
}
}
@Override
public List<RelayPacket07LocalWorlds.LocalWorld> getWorlds() {
return worlds;
}
@Override
public RelayQuery.VersionMismatch getCompatible() {
return versError;
}
}

View File

@ -0,0 +1,64 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay;
import java.util.ArrayList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.RelayPacket07LocalWorlds;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. 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 RelayWorldsQueryRateLimitDummy implements RelayWorldsQuery {
private final RelayQuery.RateLimit rateLimit;
public RelayWorldsQueryRateLimitDummy(RelayQuery.RateLimit rateLimit) {
this.rateLimit = rateLimit;
}
@Override
public void update() {
}
@Override
public boolean isQueryOpen() {
return false;
}
@Override
public boolean isQueryFailed() {
return true;
}
@Override
public RelayQuery.RateLimit isQueryRateLimit() {
return rateLimit;
}
@Override
public void close() {
}
@Override
public List<RelayPacket07LocalWorlds.LocalWorld> getWorlds() {
return new ArrayList<>(0);
}
@Override
public RelayQuery.VersionMismatch getCompatible() {
return RelayQuery.VersionMismatch.COMPATIBLE;
}
}

View File

@ -1,55 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
/**
* Copyright (c) 2022 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 ICEServerSet {
public static enum RelayType {
STUN, TURN;
}
public static class RelayServer {
public final RelayType type;
public final String address;
public final String username;
public final String password;
protected RelayServer(RelayType type, String address, String username, String password) {
this.type = type;
this.address = address;
this.username = username;
this.password = password;
}
protected RelayServer(RelayType type, String address) {
this.type = type;
this.address = address;
this.username = null;
this.password = null;
}
public String getICEString() {
if(username == null) {
return address;
}else {
return address + ";" + username + ";" + password;
}
}
}
}

View File

@ -1,165 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2022 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 IPacket {
private static final Logger logger = LogManager.getLogger("RelayPacket");
private static final Map<Integer,Class<? extends IPacket>> definedPacketClasses = new HashMap();
private static final Map<Class<? extends IPacket>,Integer> definedPacketIds = new HashMap();
private static void register(int id, Class<? extends IPacket> clazz) {
definedPacketClasses.put(id, clazz);
definedPacketIds.put(clazz, id);
}
static {
register(0x00, IPacket00Handshake.class);
register(0x01, IPacket01ICEServers.class);
register(0x02, IPacket02NewClient.class);
register(0x03, IPacket03ICECandidate.class);
register(0x04, IPacket04Description.class);
register(0x05, IPacket05ClientSuccess.class);
register(0x06, IPacket06ClientFailure.class);
register(0x07, IPacket07LocalWorlds.class);
register(0x69, IPacket69Pong.class);
register(0x70, IPacket70SpecialUpdate.class);
register(0xFE, IPacketFEDisconnectClient.class);
register(0xFF, IPacketFFErrorCode.class);
}
public static IPacket readPacket(DataInputStream input) throws IOException {
int i = input.read();
try {
Class<? extends IPacket> clazz = definedPacketClasses.get(i);
if(clazz == null) {
throw new IOException("Unknown packet type: " + i);
}
IPacket pkt = clazz.newInstance();
pkt.read(input);
return pkt;
} catch (InstantiationException | IllegalAccessException e) {
throw new IOException("Unknown packet type: " + i);
}
}
public static byte[] writePacket(IPacket packet) throws IOException {
Integer i = definedPacketIds.get(packet.getClass());
if(i != null) {
int len = packet.packetLength();
EaglerOutputStream bao = len == -1 ? new EaglerOutputStream() :
new EaglerOutputStream(len + 1);
bao.write(i);
packet.write(new DataOutputStream(bao));
byte[] ret = bao.toByteArray();
if(len != -1 && ret.length != len + 1) {
logger.error("writePacket buffer for packet {} {} by {} bytes", packet.getClass().getSimpleName(),
(len + 1 < ret.length ? "overflowed" : "underflowed"),
(len + 1 < ret.length ? ret.length - len - 1 : len + 1 - ret.length));
}
return ret;
}else {
throw new IOException("Unknown packet type: " + packet.getClass().getSimpleName());
}
}
public void read(DataInputStream input) throws IOException {
}
public void write(DataOutputStream output) throws IOException {
}
public int packetLength() {
return -1;
}
public static String readASCII(InputStream is, int len) throws IOException {
char[] ret = new char[len];
for(int i = 0; i < len; ++i) {
int j = is.read();
if(j < 0) {
return null;
}
ret[i] = (char)j;
}
return new String(ret);
}
public static void writeASCII(OutputStream is, String txt) throws IOException {
for(int i = 0, l = txt.length(); i < l; ++i) {
is.write((int)txt.charAt(i));
}
}
public static String readASCII8(InputStream is) throws IOException {
int i = is.read();
if(i < 0) {
return null;
}else {
return readASCII(is, i);
}
}
public static void writeASCII8(OutputStream is, String txt) throws IOException {
if(txt == null) {
is.write(0);
}else {
int l = txt.length();
is.write(l);
for(int i = 0; i < l; ++i) {
is.write((int)txt.charAt(i));
}
}
}
public static String readASCII16(InputStream is) throws IOException {
int hi = is.read();
int lo = is.read();
if(hi < 0 || lo < 0) {
return null;
}else {
return readASCII(is, (hi << 8) | lo);
}
}
public static void writeASCII16(OutputStream is, String txt) throws IOException {
if(txt == null) {
is.write(0);
is.write(0);
}else {
int l = txt.length();
is.write((l >>> 8) & 0xFF);
is.write(l & 0xFF);
for(int i = 0; i < l; ++i) {
is.write((int)txt.charAt(i));
}
}
}
}

View File

@ -1,57 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Copyright (c) 2022 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 IPacket00Handshake extends IPacket {
public int connectionType = 0;
public int connectionVersion = 1;
public String connectionCode = null;
public IPacket00Handshake() {
}
public IPacket00Handshake(int connectionType, int connectionVersion,
String connectionCode) {
this.connectionType = connectionType;
this.connectionVersion = connectionVersion;
this.connectionCode = connectionCode;
}
@Override
public void read(DataInputStream input) throws IOException {
connectionType = input.read();
connectionVersion = input.read();
connectionCode = IPacket.readASCII8(input);
}
@Override
public void write(DataOutputStream output) throws IOException {
output.write(connectionType);
output.write(connectionVersion);
IPacket.writeASCII8(output, connectionCode);
}
@Override
public int packetLength() {
return 1 + 1 + (connectionCode != null ? 1 + connectionCode.length() : 0);
}
}

View File

@ -1,53 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
/**
* Copyright (c) 2022 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 IPacket01ICEServers extends IPacket {
public final Collection<ICEServerSet.RelayServer> servers;
public IPacket01ICEServers() {
servers = new ArrayList();
}
public void read(DataInputStream input) throws IOException {
servers.clear();
int l = input.readUnsignedShort();
for(int i = 0; i < l; ++i) {
char type = (char)input.read();
ICEServerSet.RelayType typeEnum;
if(type == 'S') {
typeEnum = ICEServerSet.RelayType.STUN;
}else if(type == 'T') {
typeEnum = ICEServerSet.RelayType.TURN;
}else {
throw new IOException("Unknown/Unsupported Relay Type: '" + type + "'");
}
servers.add(new ICEServerSet.RelayServer(
typeEnum,
readASCII16(input),
readASCII8(input),
readASCII8(input)
));
}
}
}

View File

@ -1,49 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Copyright (c) 2022 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 IPacket03ICECandidate extends IPacket {
public String peerId;
public String candidate;
public IPacket03ICECandidate(String peerId, String desc) {
this.peerId = peerId;
this.candidate = desc;
}
public IPacket03ICECandidate() {
}
public void read(DataInputStream input) throws IOException {
peerId = readASCII8(input);
candidate = readASCII16(input);
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, peerId);
writeASCII16(output, candidate);
}
public int packetLength() {
return 1 + peerId.length() + 2 + candidate.length();
}
}

View File

@ -1,49 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Copyright (c) 2022 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 IPacket04Description extends IPacket {
public String peerId;
public String description;
public IPacket04Description(String peerId, String desc) {
this.peerId = peerId;
this.description = desc;
}
public IPacket04Description() {
}
public void read(DataInputStream input) throws IOException {
peerId = readASCII8(input);
description = readASCII16(input);
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, peerId);
writeASCII16(output, description);
}
public int packetLength() {
return 1 + peerId.length() + 2 + description.length();
}
}

View File

@ -1,50 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Copyright (c) 2022 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 IPacket07LocalWorlds extends IPacket {
public static class LocalWorld {
public final String worldName;
public final String worldCode;
public LocalWorld(String worldName, String worldCode) {
this.worldName = worldName;
this.worldCode = worldCode;
}
}
public final List<LocalWorld> worldsList;
public IPacket07LocalWorlds() {
this.worldsList = new ArrayList();
}
public void read(DataInputStream input) throws IOException {
int l = input.read();
for(int i = 0; i < l; ++i) {
worldsList.add(new LocalWorld(readASCII8(input), readASCII8(input)));
}
}
}

View File

@ -1,45 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.IOException;
/**
* Copyright (c) 2022 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 IPacket69Pong extends IPacket {
public int protcolVersion;
public String comment;
public String brand;
public IPacket69Pong(int protcolVersion, String comment, String brand) {
if(comment.length() > 255) {
comment = comment.substring(0, 256);
}
this.protcolVersion = protcolVersion;
this.comment = comment;
this.brand = brand;
}
public IPacket69Pong() {
}
public void read(DataInputStream output) throws IOException {
protcolVersion = output.read();
comment = readASCII8(output);
brand = readASCII8(output);
}
}

View File

@ -1,55 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* 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 IPacket70SpecialUpdate extends IPacket {
public static final int OPERATION_UPDATE_CERTIFICATE = 0x69;
public int operation;
public byte[] updatePacket;
public IPacket70SpecialUpdate() {
}
public IPacket70SpecialUpdate(int operation, byte[] updatePacket) {
this.operation = operation;
this.updatePacket = updatePacket;
}
@Override
public void read(DataInputStream input) throws IOException {
operation = input.read();
updatePacket = new byte[input.readUnsignedShort()];
input.read(updatePacket);
}
@Override
public void write(DataOutputStream output) throws IOException {
output.write(operation);
output.writeShort(updatePacket.length);
output.write(updatePacket);
}
@Override
public int packetLength() {
return 3 + updatePacket.length;
}
}

View File

@ -1,67 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Copyright (c) 2022 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 IPacketFEDisconnectClient extends IPacket {
public static final int TYPE_FINISHED_SUCCESS = 0x00;
public static final int TYPE_FINISHED_FAILED = 0x01;
public static final int TYPE_TIMEOUT = 0x02;
public static final int TYPE_INVALID_OPERATION = 0x03;
public static final int TYPE_INTERNAL_ERROR = 0x04;
public static final int TYPE_SERVER_DISCONNECT = 0x05;
public static final int TYPE_UNKNOWN = 0xFF;
public String clientId;
public int code;
public String reason;
public IPacketFEDisconnectClient() {
}
public IPacketFEDisconnectClient(String clientId, int code, String reason) {
this.clientId = clientId;
this.code = code;
this.reason = reason;
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
code = input.read();
reason = readASCII16(input);
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, clientId);
output.write(code);
writeASCII16(output, reason);
}
public int packetLength() {
return -1;
}
public static final ByteBuffer ratelimitPacketTooMany = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x00 });
public static final ByteBuffer ratelimitPacketBlock = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x01 });
public static final ByteBuffer ratelimitPacketBlockLock = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x02 });
public static final ByteBuffer ratelimitPacketLocked = ByteBuffer.wrap(new byte[] { (byte)0xFC, (byte)0x03 });
}

View File

@ -1,82 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Copyright (c) 2022 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 IPacketFFErrorCode extends IPacket {
public static final int TYPE_INTERNAL_ERROR = 0x00;
public static final int TYPE_PROTOCOL_VERSION = 0x01;
public static final int TYPE_INVALID_PACKET = 0x02;
public static final int TYPE_ILLEGAL_OPERATION = 0x03;
public static final int TYPE_CODE_LENGTH = 0x04;
public static final int TYPE_INCORRECT_CODE = 0x05;
public static final int TYPE_SERVER_DISCONNECTED = 0x06;
public static final int TYPE_UNKNOWN_CLIENT = 0x07;
public static final String[] packetTypes = new String[0x08];
static {
packetTypes[TYPE_INTERNAL_ERROR] = "TYPE_INTERNAL_ERROR";
packetTypes[TYPE_PROTOCOL_VERSION] = "TYPE_PROTOCOL_VERSION";
packetTypes[TYPE_INVALID_PACKET] = "TYPE_INVALID_PACKET";
packetTypes[TYPE_ILLEGAL_OPERATION] = "TYPE_ILLEGAL_OPERATION";
packetTypes[TYPE_CODE_LENGTH] = "TYPE_CODE_LENGTH";
packetTypes[TYPE_INCORRECT_CODE] = "TYPE_INCORRECT_CODE";
packetTypes[TYPE_SERVER_DISCONNECTED] = "TYPE_SERVER_DISCONNECTED";
packetTypes[TYPE_UNKNOWN_CLIENT] = "TYPE_UNKNOWN_CLIENT";
}
public static String code2string(int i) {
if(i >= 0 || i < packetTypes.length) {
return packetTypes[i];
}else {
return "UNKNOWN";
}
}
public int code;
public String desc;
public IPacketFFErrorCode() {
}
public IPacketFFErrorCode(int code, String desc) {
this.code = code;
this.desc = desc;
}
@Override
public void read(DataInputStream input) throws IOException {
code = input.read();
desc = readASCII16(input);
}
@Override
public void write(DataOutputStream input) throws IOException {
input.write(code);
writeASCII16(input, desc);
}
@Override
public int packetLength() {
return 1 + 2 + desc.length();
}
}

View File

@ -71,7 +71,7 @@ public class EaglerChunkLoader extends AnvilChunkLoader {
@Override
public Chunk loadChunk(World var1, int var2, int var3) throws IOException {
VFile2 file = new VFile2(chunkDirectory, getChunkPath(var2, var3) + ".dat");
VFile2 file = WorldsDB.newVFile(chunkDirectory, getChunkPath(var2, var3) + ".dat");
if(!file.exists()) {
return null;
}
@ -93,7 +93,7 @@ public class EaglerChunkLoader extends AnvilChunkLoader {
this.writeChunkToNBT(var2, var1, chunkData);
NBTTagCompound fileData = new NBTTagCompound();
fileData.setTag("Level", chunkData);
VFile2 file = new VFile2(chunkDirectory, getChunkPath(var2.xPosition, var2.zPosition) + ".dat");
VFile2 file = WorldsDB.newVFile(chunkDirectory, getChunkPath(var2.xPosition, var2.zPosition) + ".dat");
try(OutputStream os = file.getOutputStream()) {
CompressedStreamTools.writeCompressed(fileData, os);
}

View File

@ -58,7 +58,7 @@ public class EaglerIntegratedServerWorker {
public static final EaglerSaveFormat saveFormat = new EaglerSaveFormat(EaglerSaveFormat.worldsFolder);
private static final Map<String, IntegratedServerPlayerNetworkManager> openChannels = new HashMap();
private static final Map<String, IntegratedServerPlayerNetworkManager> openChannels = new HashMap<>();
private static final IPCPacketManager packetManagerInstance = new IPCPacketManager();
@ -197,7 +197,7 @@ public class EaglerIntegratedServerWorker {
}
String[] worldsTxt = EaglerSaveFormat.worldsList.getAllLines();
if(worldsTxt != null) {
List<String> newWorlds = new ArrayList();
List<String> newWorlds = new ArrayList<>();
for(int i = 0; i < worldsTxt.length; ++i) {
String str = worldsTxt[i];
if(!str.equalsIgnoreCase(pkt.worldName)) {
@ -301,18 +301,18 @@ public class EaglerIntegratedServerWorker {
}else {
String[] worlds = EaglerSaveFormat.worldsList.getAllLines();
if(worlds == null) {
sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, new LinkedList<NBTTagCompound>()));
sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, new LinkedList<>()));
break;
}
LinkedHashSet<String> updatedList = new LinkedHashSet();
LinkedList<NBTTagCompound> sendListNBT = new LinkedList();
LinkedHashSet<String> updatedList = new LinkedHashSet<>();
LinkedList<NBTTagCompound> sendListNBT = new LinkedList<>();
boolean rewrite = false;
for(int i = 0; i < worlds.length; ++i) {
String w = worlds[i].trim();
if(w.length() > 0) {
VFile2 vf = new VFile2(EaglerSaveFormat.worldsFolder, w, "level.dat");
VFile2 vf = WorldsDB.newVFile(EaglerSaveFormat.worldsFolder, w, "level.dat");
if(!vf.exists()) {
vf = new VFile2(EaglerSaveFormat.worldsFolder, w, "level.dat_old");
vf = WorldsDB.newVFile(EaglerSaveFormat.worldsFolder, w, "level.dat_old");
}
if(vf.exists()) {
try(InputStream dat = vf.getInputStream()) {
@ -391,8 +391,8 @@ public class EaglerIntegratedServerWorker {
}
break;
}
case IPCPacket21EnableLogging.ID: {
enableLoggingRedirector(((IPCPacket21EnableLogging)ipc).enable);
case IPCPacket1BEnableLogging.ID: {
enableLoggingRedirector(((IPCPacket1BEnableLogging)ipc).enable);
break;
}
default:
@ -418,7 +418,7 @@ public class EaglerIntegratedServerWorker {
}
public static void sendLogMessagePacket(String txt, boolean err) {
sendIPCPacket(new IPCPacket20LoggerMessage(txt, err));
sendIPCPacket(new IPCPacket1ALoggerMessage(txt, err));
}
public static void sendIPCPacket(IPCPacketBase ipc) {
@ -454,12 +454,12 @@ public class EaglerIntegratedServerWorker {
currentProcess = null;
}
private static void mainLoop() {
private static void mainLoop(boolean singleThreadMode) {
processAsyncMessageQueue();
if(currentProcess != null) {
if(currentProcess.isServerRunning()) {
currentProcess.mainLoop();
currentProcess.mainLoop(singleThreadMode);
}
if(!currentProcess.isServerRunning()) {
currentProcess.stopServer();
@ -467,7 +467,9 @@ public class EaglerIntegratedServerWorker {
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID));
}
}else {
EagUtils.sleep(50l);
if(!singleThreadMode) {
EagUtils.sleep(50l);
}
}
}
@ -476,12 +478,16 @@ public class EaglerIntegratedServerWorker {
currentProcess = null;
logger.info("Starting EaglercraftX integrated server worker...");
if(ServerPlatformSingleplayer.getWorldsDatabase().isRamdisk()) {
sendIPCPacket(new IPCPacket1CIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE));
}
// signal thread startup successful
sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF));
while(true) {
mainLoop();
EagUtils.sleep(0l);
mainLoop(false);
ServerPlatformSingleplayer.immediateContinue();
}
}catch(Throwable tt) {
if(tt instanceof ReportedException) {
@ -506,4 +512,17 @@ public class EaglerIntegratedServerWorker {
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacketFFProcessKeepAlive.EXITED));
}
}
public static void singleThreadMain() {
logger.info("Starting EaglercraftX integrated server worker...");
if(ServerPlatformSingleplayer.getWorldsDatabase().isRamdisk()) {
sendIPCPacket(new IPCPacket1CIssueDetected(IPCPacket1CIssueDetected.ISSUE_RAMDISK_MODE));
}
sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF));
}
public static void singleThreadUpdate() {
mainLoop(true);
}
}

View File

@ -1,11 +1,12 @@
package net.lax1dude.eaglercraft.v1_8.sp.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import com.google.common.collect.Lists;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.entity.player.EntityPlayer;
@ -39,7 +40,7 @@ public class EaglerMinecraftServer extends MinecraftServer {
public static final Logger logger = EaglerIntegratedServerWorker.logger;
public static final VFile2 savesDir = new VFile2("worlds");
public static final VFile2 savesDir = WorldsDB.newVFile("worlds");
protected EnumDifficulty difficulty;
protected GameType gamemode;
@ -59,13 +60,13 @@ public class EaglerMinecraftServer extends MinecraftServer {
public static int counterTileUpdate = 0;
public static int counterLightUpdate = 0;
private final List<Runnable> scheduledTasks = new LinkedList();
private final List<Runnable> scheduledTasks = new LinkedList<>();
public EaglerMinecraftServer(String world, String owner, int viewDistance, WorldSettings currentWorldSettings, boolean demo) {
super(world);
Bootstrap.register();
this.saveHandler = new EaglerSaveHandler(savesDir, world);
this.skinService = new IntegratedSkinService(new VFile2(saveHandler.getWorldDirectory(), "eagler/skulls"));
this.skinService = new IntegratedSkinService(WorldsDB.newVFile(saveHandler.getWorldDirectory(), "eagler/skulls"));
this.capeService = new IntegratedCapeService();
this.voiceService = null;
this.setServerOwner(owner);
@ -131,7 +132,7 @@ public class EaglerMinecraftServer extends MinecraftServer {
EaglerIntegratedServerWorker.saveFormat.deleteWorldDirectory(getFolderName());
}
public void mainLoop() {
public void mainLoop(boolean singleThreadMode) {
long k = getCurrentTimeMillis();
this.sendTPSToClient(k);
if(paused && this.playersOnline.size() <= 1) {
@ -140,7 +141,7 @@ public class EaglerMinecraftServer extends MinecraftServer {
}
long j = k - this.currentTime;
if (j > 2000L && this.currentTime - this.timeOfLastWarning >= 15000L) {
if (j > (singleThreadMode ? 500L : 2000L) && this.currentTime - this.timeOfLastWarning >= (singleThreadMode ? 5000L : 15000L)) {
logger.warn(
"Can\'t keep up! Did the system time change, or is the server overloaded? Running {}ms behind, skipping {} tick(s)",
new Object[] { Long.valueOf(j), Long.valueOf(j / 50L) });
@ -177,13 +178,13 @@ public class EaglerMinecraftServer extends MinecraftServer {
if(millis - lastTPSUpdate > 1000l) {
lastTPSUpdate = millis;
if(serverRunning && this.worldServers != null) {
List<String> lst = new ArrayList<>(Arrays.asList(
List<String> lst = Lists.newArrayList(
"TPS: " + counterTicksPerSecond + "/20",
"Chunks: " + countChunksLoaded(this.worldServers) + "/" + countChunksTotal(this.worldServers),
"Entities: " + countEntities(this.worldServers) + "+" + countTileEntities(this.worldServers),
"R: " + counterChunkRead + ", G: " + counterChunkGenerate + ", W: " + counterChunkWrite,
"TU: " + counterTileUpdate + ", LU: " + counterLightUpdate
));
);
int players = countPlayerEntities(this.worldServers);
if(players > 1) {
lst.add("Players: " + players);
@ -252,7 +253,7 @@ public class EaglerMinecraftServer extends MinecraftServer {
public void setPaused(boolean p) {
paused = p;
if(!p) {
currentTime = System.currentTimeMillis();
currentTime = EagRuntime.steadyTimeMillis();
}
}

View File

@ -30,13 +30,13 @@ import net.minecraft.world.storage.WorldInfo;
*/
public class EaglerSaveFormat extends SaveFormatOld {
public static final VFile2 worldsList = new VFile2("worlds_list.txt");
public static final VFile2 worldsFolder = new VFile2("worlds");
public EaglerSaveFormat(VFile2 parFile) {
super(parFile);
}
public static final VFile2 worldsList = WorldsDB.newVFile("worlds_list.txt");
public static final VFile2 worldsFolder = WorldsDB.newVFile("worlds");
public String getName() {
return "eagler";
}
@ -46,7 +46,7 @@ public class EaglerSaveFormat extends SaveFormatOld {
}
public List<SaveFormatComparator> getSaveList() {
ArrayList arraylist = Lists.newArrayList();
ArrayList<SaveFormatComparator> arraylist = Lists.newArrayList();
if(worldsList.exists()) {
String[] lines = worldsList.getAllLines();
for (int i = 0; i < lines.length; ++i) {
@ -70,7 +70,7 @@ public class EaglerSaveFormat extends SaveFormatOld {
}
public void clearPlayers(String worldFolder) {
VFile2 file1 = new VFile2(this.savesDirectory, worldFolder, "player");
VFile2 file1 = WorldsDB.newVFile(this.savesDirectory, worldFolder, "player");
deleteFiles(file1.listFiles(true), null);
}
@ -80,12 +80,12 @@ public class EaglerSaveFormat extends SaveFormatOld {
public boolean duplicateWorld(String worldFolder, String displayName) {
String newFolderName = displayName.replaceAll("[\\./\"]", "_");
VFile2 newFolder = new VFile2(savesDirectory, newFolderName);
while((new VFile2(newFolder, "level.dat")).exists() || (new VFile2(newFolder, "level.dat_old")).exists()) {
VFile2 newFolder = WorldsDB.newVFile(savesDirectory, newFolderName);
while((WorldsDB.newVFile(newFolder, "level.dat")).exists() || (WorldsDB.newVFile(newFolder, "level.dat_old")).exists()) {
newFolderName += "_";
newFolder = new VFile2(savesDirectory, newFolderName);
newFolder = WorldsDB.newVFile(savesDirectory, newFolderName);
}
VFile2 oldFolder = new VFile2(this.savesDirectory, worldFolder);
VFile2 oldFolder = WorldsDB.newVFile(this.savesDirectory, worldFolder);
String oldPath = oldFolder.getPath();
int totalSize = 0;
int lastUpdate = 0;
@ -94,7 +94,7 @@ public class EaglerSaveFormat extends SaveFormatOld {
for(int i = 0, l = vfl.size(); i < l; ++i) {
VFile2 vf = vfl.get(i);
String fileNameRelative = vf.getPath().substring(oldPath.length() + 1);
totalSize += VFile2.copyFile(vf, new VFile2(finalNewFolder, fileNameRelative));
totalSize += VFile2.copyFile(vf, WorldsDB.newVFile(finalNewFolder, fileNameRelative));
if (totalSize - lastUpdate > 10000) {
lastUpdate = totalSize;
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.duplicating", totalSize);

View File

@ -29,7 +29,7 @@ public class EaglerSaveHandler extends SaveHandler {
}
public IChunkLoader getChunkLoader(WorldProvider provider) {
return new EaglerChunkLoader(new VFile2(this.getWorldDirectory(), "level" + provider.getDimensionId()));
return new EaglerChunkLoader(WorldsDB.newVFile(this.getWorldDirectory(), "level" + provider.getDimensionId()));
}
public void saveWorldInfoWithPlayer(WorldInfo worldInformation, NBTTagCompound tagCompound) {

View File

@ -1,10 +1,13 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
package net.lax1dude.eaglercraft.v1_8.sp.server;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.function.Supplier;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
* 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
@ -18,19 +21,12 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket02NewClient extends IPacket {
public String clientId;
public IPacket02NewClient(String clientId) {
this.clientId = clientId;
public class WorldsDB {
private static final Supplier<IEaglerFilesystem> fsGetter = ServerPlatformSingleplayer::getWorldsDatabase;
public static VFile2 newVFile(Object... path) {
return VFile2.create(fsGetter, path);
}
public IPacket02NewClient() {
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
}
}

View File

@ -7,8 +7,8 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.CRC32;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
import net.lax1dude.eaglercraft.v1_8.EaglerZLIB;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
@ -28,11 +28,16 @@ import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
public class EPKCompiler {
private final EaglerOutputStream os;
private final OutputStream dos;
private final CRC32 checkSum = new CRC32();
private int lengthIntegerOffset = 0;
private int totalFileCount = 0;
public EPKCompiler(String name, String owner, String type) {
this(name, owner, type, false, true, null);
}
public EPKCompiler(String name, String owner, String type, boolean gzip, boolean world, String commentStr) {
os = new EaglerOutputStream(0x200000);
try {
@ -44,10 +49,11 @@ public class EPKCompiler {
os.write(filename.length);
os.write(filename);
byte[] comment = ("\n\n # Eagler EPK v2.0 (c) " + EagRuntime.fixDateFormat(new SimpleDateFormat("yyyy")).format(d) + " " +
owner + "\n # export: on " + EagRuntime.fixDateFormat(new SimpleDateFormat("MM/dd/yyyy")).format(d) + " at " +
EagRuntime.fixDateFormat(new SimpleDateFormat("hh:mm:ss aa")).format(d) + "\n\n # world name: " + name + "\n\n")
.getBytes(StandardCharsets.UTF_8);
byte[] comment = (world ? ("\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") : commentStr).getBytes(StandardCharsets.UTF_8);
os.write((comment.length >>> 8) & 255);
os.write(comment.length & 255);
@ -58,40 +64,50 @@ public class EPKCompiler {
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
if(gzip) {
os.write('G'); // compression type: gzip
dos = EaglerZLIB.newGZIPOutputStream(os);
}else {
os.write('0'); // compression type: none
dos = os;
}
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,
dos.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
dos.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('>');
writeInt(typeBytes.length, dos);
dos.write(typeBytes); // write type
dos.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
if(world) {
dos.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
dos.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, dos);
dos.write(nameBytes); // write name
dos.write('>');
++totalFileCount;
}
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;
if(world && owner != null) {
dos.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
dos.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, dos);
dos.write(ownerBytes); // write owner
dos.write('>');
++totalFileCount;
}
}catch(IOException ex) {
throw new RuntimeException("This happened somehow", ex);
@ -105,19 +121,19 @@ public class EPKCompiler {
checkSum.update(dat, 0, dat.length);
long sum = checkSum.getValue();
os.write(new byte[]{(byte)70,(byte)73,(byte)76,(byte)69}); // FILE
dos.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);
dos.write(nameBytes.length);
dos.write(nameBytes);
writeInt(dat.length + 5, os);
writeInt((int)sum, os);
writeInt(dat.length + 5, dos);
writeInt((int)sum, dos);
os.write(dat);
dos.write(dat);
os.write(':');
os.write('>');
dos.write(':');
dos.write('>');
++totalFileCount;
@ -128,8 +144,9 @@ public class EPKCompiler {
public byte[] complete() {
try {
dos.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$
dos.close();
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();

View File

@ -10,6 +10,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerSaveFormat;
import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.storage.WorldInfo;
@ -37,7 +38,7 @@ public class WorldConverterEPK {
logger.info("Importing world \"{}\" from EPK", newName);
String folderName = newName.replaceAll("[\\./\"]", "_");
VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
while((new VFile2(worldDir, "level.dat")).exists() || (new VFile2(worldDir, "level.dat_old")).exists()) {
while(WorldsDB.newVFile(worldDir, "level.dat").exists() || WorldsDB.newVFile(worldDir, "level.dat_old").exists()) {
folderName += "_";
worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
}
@ -74,7 +75,7 @@ public class WorldConverterEPK {
CompressedStreamTools.writeCompressed(worldDatNBT, tmp);
b = tmp.toByteArray();
}
VFile2 ff = new VFile2(worldDir, f.name);
VFile2 ff = WorldsDB.newVFile(worldDir, f.name);
ff.setAllBytes(b);
prog += b.length;
++cnt;

View File

@ -19,6 +19,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerChunkLoader;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerSaveFormat;
import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB;
import net.minecraft.world.chunk.storage.RegionFile;
import net.minecraft.world.storage.WorldInfo;
import net.minecraft.nbt.CompressedStreamTools;
@ -47,7 +48,7 @@ public class WorldConverterMCA {
logger.info("Importing world \"{}\" from MCA", newName);
String folderName = newName.replaceAll("[\\./\"]", "_");
VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
while((new VFile2(worldDir, "level.dat")).exists() || (new VFile2(worldDir, "level.dat_old")).exists()) {
while(WorldsDB.newVFile(worldDir, "level.dat").exists() || WorldsDB.newVFile(worldDir, "level.dat_old").exists()) {
folderName += "_";
worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory();
}
@ -105,11 +106,11 @@ public class WorldConverterMCA {
EaglerOutputStream bo = new EaglerOutputStream();
CompressedStreamTools.writeCompressed(worldDatNBT, bo);
b = bo.toByteArray();
VFile2 ff = new VFile2(worldDir, fileName);
VFile2 ff = WorldsDB.newVFile(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"));
VFile2 chunkFolder = WorldsDB.newVFile(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) {
@ -130,7 +131,7 @@ public class WorldConverterMCA {
}
int chunkX = chunkLevel.getInteger("xPos");
int chunkZ = chunkLevel.getInteger("zPos");
VFile2 chunkOut = new VFile2(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat");
VFile2 chunkOut = WorldsDB.newVFile(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat");
if(chunkOut.exists()) {
logger.error("{}: Chunk already exists: {}", fileName, chunkOut.getPath());
continue;
@ -152,7 +153,7 @@ public class WorldConverterMCA {
} 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);
VFile2 ff = WorldsDB.newVFile(worldDir, fileName);
ff.setAllBytes(b);
prog += b.length;
} else if (!fileName.equals("level.dat_mcr") && !fileName.equals("session.lock")) {
@ -184,7 +185,7 @@ public class WorldConverterMCA {
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");
VFile2 vf = WorldsDB.newVFile(worldFolder, "level.dat");
byte[] b;
int lastProgUpdate = 0;
int prog = 0;
@ -196,7 +197,7 @@ public class WorldConverterMCA {
prog += b.length;
safe = true;
}
vf = new VFile2(worldFolder, "level.dat_old");
vf = WorldsDB.newVFile(worldFolder, "level.dat_old");
if(vf.exists()) {
zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old"));
b = vf.getAllBytes();
@ -212,11 +213,11 @@ public class WorldConverterMCA {
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]);
vf = WorldsDB.newVFile(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();
Map<String,RegionFile> regionFiles = new HashMap<>();
for(int k = 0, l = fileList.size(); k < l; ++k) {
VFile2 chunkFile = fileList.get(k);
NBTTagCompound chunkNBT;
@ -266,7 +267,7 @@ public class WorldConverterMCA {
}
}
logger.info("Copying extra world data...");
fileList = (new VFile2(worldFolder, "data")).listFiles(false);
fileList = WorldsDB.newVFile(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()));
@ -278,7 +279,7 @@ public class WorldConverterMCA {
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
fileList = (new VFile2(worldFolder, "players")).listFiles(false);
fileList = WorldsDB.newVFile(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()));
@ -290,7 +291,7 @@ public class WorldConverterMCA {
EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog);
}
}
fileList = (new VFile2(worldFolder, "eagler/skulls")).listFiles(false);
fileList = WorldsDB.newVFile(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()));

View File

@ -1,5 +1,12 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV3EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
@ -19,21 +26,25 @@ public class CustomSkullData {
public String skinURL;
public long lastHit;
public byte[] skinData;
public SkinPacketVersionCache skinData;
public CustomSkullData(String skinURL, byte[] skinData) {
this.skinURL = skinURL;
this.lastHit = System.currentTimeMillis();
this.skinData = skinData;
this.lastHit = EagRuntime.steadyTimeMillis();
if(skinData.length != 16384) {
byte[] fixed = new byte[16384];
System.arraycopy(skinData, 0, fixed, 0, skinData.length > fixed.length ? fixed.length : skinData.length);
skinData = fixed;
}
this.skinData = SkinPacketVersionCache.createCustomV3(0l, 0l, 0, skinData);
}
public byte[] getFullSkin() {
if(skinData.length == 16384) {
return skinData;
}
byte[] ret = new byte[16384];
System.arraycopy(skinData, 0, ret, 0, skinData.length > ret.length ? ret.length : skinData.length);
return ret;
return ((SPacketOtherSkinCustomV3EAG)skinData.getV3()).customSkin;
}
public GameMessagePacket getSkinPacket(EaglercraftUUID uuid, GamePluginMessageProtocol protocol) {
return SkinPacketVersionCache.rewriteUUID(skinData.get(protocol), uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
}
}

View File

@ -3,7 +3,9 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.minecraft.entity.player.EntityPlayerMP;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -24,56 +26,27 @@ public class IntegratedCapePackets {
public static final int PACKET_MY_CAPE_PRESET = 0x01;
public static final int PACKET_MY_CAPE_CUSTOM = 0x02;
public static final int PACKET_GET_OTHER_CAPE = 0x03;
public static final int PACKET_OTHER_CAPE_PRESET = 0x04;
public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05;
public static void processPacket(byte[] data, EntityPlayerMP sender, IntegratedCapeService capeService) throws IOException {
if(data.length == 0) {
throw new IOException("Zero-length packet recieved");
}
int packetId = (int)data[0] & 0xFF;
try {
switch(packetId) {
case PACKET_GET_OTHER_CAPE:
processGetOtherCape(data, sender, capeService);
break;
default:
throw new IOException("Unknown packet type " + packetId);
}
}catch(IOException ex) {
throw ex;
}catch(Throwable t) {
throw new IOException("Unhandled exception handling packet type " + packetId, t);
}
}
private static void processGetOtherCape(byte[] data, EntityPlayerMP sender, IntegratedCapeService capeService) throws IOException {
if(data.length != 17) {
throw new IOException("Invalid length " + data.length + " for skin request packet");
}
EaglercraftUUID searchUUID = IntegratedSkinPackets.bytesToUUID(data, 1);
capeService.processGetOtherCape(searchUUID, sender);
}
public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedCapeService capeService) throws IOException {
if(bs.length == 0) {
throw new IOException("Zero-length packet recieved");
}
byte[] generatedPacket;
GameMessagePacket generatedPacket;
int packetType = (int)bs[0] & 0xFF;
switch(packetType) {
case PACKET_MY_CAPE_PRESET:
if(bs.length != 5) {
throw new IOException("Invalid length " + bs.length + " for preset cape packet");
}
generatedPacket = IntegratedCapePackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
generatedPacket = new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
break;
case PACKET_MY_CAPE_CUSTOM:
if(bs.length != 1174) {
throw new IOException("Invalid length " + bs.length + " for custom cape packet");
}
generatedPacket = IntegratedCapePackets.makeCustomResponse(clientUUID, bs, 1, 1173);
byte[] capePixels = new byte[bs.length - 1];
System.arraycopy(bs, 1, capePixels, 0, capePixels.length);
generatedPacket = new SPacketOtherCapeCustomEAG(clientUUID.msb, clientUUID.lsb, capePixels);
break;
default:
throw new IOException("Unknown skin packet type: " + packetType);
@ -82,29 +55,7 @@ public class IntegratedCapePackets {
}
public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedCapeService capeService) {
capeService.registerEaglercraftPlayer(clientUUID, IntegratedCapePackets.makePresetResponse(clientUUID, 0));
capeService.registerEaglercraftPlayer(clientUUID, new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, 0));
}
public static byte[] makePresetResponse(EaglercraftUUID uuid, int presetId) {
byte[] ret = new byte[1 + 16 + 4];
ret[0] = (byte)PACKET_OTHER_CAPE_PRESET;
IntegratedSkinPackets.UUIDToBytes(uuid, ret, 1);
ret[17] = (byte)(presetId >>> 24);
ret[18] = (byte)(presetId >>> 16);
ret[19] = (byte)(presetId >>> 8);
ret[20] = (byte)(presetId & 0xFF);
return ret;
}
public static byte[] makeCustomResponse(EaglercraftUUID uuid, byte[] pixels) {
return makeCustomResponse(uuid, pixels, 0, pixels.length);
}
public static byte[] makeCustomResponse(EaglercraftUUID uuid, byte[] pixels, int offset, int length) {
byte[] ret = new byte[1 + 16 + length];
ret[0] = (byte)PACKET_OTHER_CAPE_CUSTOM;
IntegratedSkinPackets.UUIDToBytes(uuid, ret, 1);
System.arraycopy(pixels, offset, ret, 17, length);
return ret;
}
}

View File

@ -7,10 +7,9 @@ import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S3FPacketCustomPayload;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -33,19 +32,7 @@ public class IntegratedCapeService {
public static final int masterRateLimitPerPlayer = 250;
public static final String CHANNEL = "EAG|Capes-1.8";
private final Map<EaglercraftUUID, byte[]> capesCache = new HashMap();
public void processPacket(byte[] packetData, EntityPlayerMP sender) {
try {
IntegratedCapePackets.processPacket(packetData, sender, this);
} catch (IOException e) {
logger.error("Invalid skin request packet recieved from player {}!", sender.getName());
logger.error(e);
sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin request packet recieved!");
}
}
private final Map<EaglercraftUUID, GameMessagePacket> capesCache = new HashMap<>();
public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) {
try {
@ -57,16 +44,16 @@ public class IntegratedCapeService {
}
}
public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, byte[] capePacket) {
public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, GameMessagePacket capePacket) {
capesCache.put(playerUUID, capePacket);
}
public void processGetOtherCape(EaglercraftUUID searchUUID, EntityPlayerMP sender) {
byte[] maybeCape = capesCache.get(searchUUID);
GameMessagePacket maybeCape = capesCache.get(searchUUID);
if(maybeCape == null) {
maybeCape = IntegratedCapePackets.makePresetResponse(searchUUID, 0);
maybeCape = new SPacketOtherCapePresetEAG(searchUUID.msb, searchUUID.lsb, 0);
}
sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(maybeCape, maybeCape.length).writerIndex(maybeCape.length))));
sender.playerNetServerHandler.sendEaglerMessage(maybeCape);
}
public void unregisterPlayer(EaglercraftUUID playerUUID) {

View File

@ -3,7 +3,9 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.minecraft.entity.player.EntityPlayerMP;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
@ -24,76 +26,14 @@ public class IntegratedSkinPackets {
public static final int PACKET_MY_SKIN_PRESET = 0x01;
public static final int PACKET_MY_SKIN_CUSTOM = 0x02;
public static final int PACKET_GET_OTHER_SKIN = 0x03;
public static final int PACKET_OTHER_SKIN_PRESET = 0x04;
public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05;
public static final int PACKET_GET_SKIN_BY_URL = 0x06;
public static final int PACKET_INSTALL_NEW_SKIN = 0x07;
public static void processPacket(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException {
if(data.length == 0) {
throw new IOException("Zero-length packet recieved");
}
int packetId = (int)data[0] & 0xFF;
try {
switch(packetId) {
case PACKET_GET_OTHER_SKIN:
processGetOtherSkin(data, sender, skinService);
break;
case PACKET_GET_SKIN_BY_URL:
processGetOtherSkinByURL(data, sender, skinService);
break;
case PACKET_INSTALL_NEW_SKIN:
processInstallNewSkin(data, sender, skinService);
break;
default:
throw new IOException("Unknown packet type " + packetId);
}
}catch(IOException ex) {
throw ex;
}catch(Throwable t) {
throw new IOException("Unhandled exception handling packet type " + packetId, t);
}
}
private static void processGetOtherSkin(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException {
if(data.length != 17) {
throw new IOException("Invalid length " + data.length + " for skin request packet");
}
EaglercraftUUID searchUUID = bytesToUUID(data, 1);
skinService.processPacketGetOtherSkin(searchUUID, sender);
}
private static void processGetOtherSkinByURL(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException {
if(data.length < 20) {
throw new IOException("Invalid length " + data.length + " for skin request packet");
}
EaglercraftUUID searchUUID = bytesToUUID(data, 1);
int urlLength = (data[17] << 8) | data[18];
if(data.length < 19 + urlLength) {
throw new IOException("Invalid length " + data.length + " for skin request packet with " + urlLength + " length URL");
}
skinService.processPacketGetOtherSkin(searchUUID, bytesToAscii(data, 19, urlLength), sender);
}
private static void processInstallNewSkin(byte[] data, EntityPlayerMP sender, IntegratedSkinService skinService) throws IOException {
if(data.length < 3) {
throw new IOException("Invalid length " + data.length + " for skin data packet");
}
int dataLength = (data[1] << 8) | data[2];
byte[] dataBmp = new byte[dataLength];
if(data.length != dataLength + 3) {
throw new IOException("Invalid data length " + dataLength + " for " + data.length + " byte skin data packet");
}
System.arraycopy(data, 3, dataBmp, 0, dataLength);
skinService.processPacketInstallNewSkin(dataBmp, sender);
}
public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedSkinService skinService) throws IOException {
public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedSkinService skinService,
int protocolVers) throws IOException {
if(bs.length == 0) {
throw new IOException("Zero-length packet recieved");
}
byte[] generatedPacket;
GameMessagePacket generatedPacketV3 = null;
GameMessagePacket generatedPacketV4 = null;
int skinModel = -1;
int packetType = (int)bs[0] & 0xFF;
switch(packetType) {
@ -101,118 +41,63 @@ public class IntegratedSkinPackets {
if(bs.length != 5) {
throw new IOException("Invalid length " + bs.length + " for preset skin packet");
}
generatedPacket = makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
generatedPacketV3 = generatedPacketV4 = new SPacketOtherSkinPresetEAG(clientUUID.msb, clientUUID.lsb,
(bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
break;
case PACKET_MY_SKIN_CUSTOM:
byte[] pixels = new byte[16384];
if(bs.length != 2 + pixels.length) {
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
if(protocolVers <= 3) {
byte[] pixels = new byte[16384];
if(bs.length != 2 + pixels.length) {
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
}
setAlphaForChestV3(pixels);
System.arraycopy(bs, 2, pixels, 0, pixels.length);
generatedPacketV3 = new SPacketOtherSkinCustomV3EAG(clientUUID.msb, clientUUID.lsb, (skinModel = (int)bs[1] & 0xFF), pixels);
}else {
byte[] pixels = new byte[12288];
if(bs.length != 2 + pixels.length) {
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
}
setAlphaForChestV4(pixels);
System.arraycopy(bs, 2, pixels, 0, pixels.length);
generatedPacketV4 = new SPacketOtherSkinCustomV4EAG(clientUUID.msb, clientUUID.lsb, (skinModel = (int)bs[1] & 0xFF), pixels);
}
setAlphaForChest(pixels, (byte)255);
System.arraycopy(bs, 2, pixels, 0, pixels.length);
generatedPacket = makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), pixels);
break;
default:
throw new IOException("Unknown skin packet type: " + packetType);
}
skinService.processPacketPlayerSkin(clientUUID, generatedPacket, skinModel);
skinService.processPacketPlayerSkin(clientUUID, new SkinPacketVersionCache(generatedPacketV3, generatedPacketV4), skinModel);
}
public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedSkinService skinService) throws IOException {
int skinModel = (clientUUID.hashCode() & 1) != 0 ? 1 : 0;
byte[] generatedPacket = makePresetResponse(clientUUID, skinModel);
skinService.processPacketPlayerSkin(clientUUID, generatedPacket, skinModel);
skinService.processPacketPlayerSkin(clientUUID, SkinPacketVersionCache.createPreset(clientUUID.msb, clientUUID.lsb, skinModel), skinModel);
}
public static void setAlphaForChest(byte[] skin64x64, byte alpha) {
public static void setAlphaForChestV3(byte[] skin64x64) {
if(skin64x64.length != 16384) {
throw new IllegalArgumentException("Skin is not 64x64!");
}
for(int y = 20; y < 32; ++y) {
for(int x = 16; x < 40; ++x) {
skin64x64[(y << 8) | (x << 2)] = alpha;
skin64x64[(y << 8) | (x << 2)] = (byte)0xFF;
}
}
}
public static byte[] makePresetResponse(EaglercraftUUID uuid) {
return makePresetResponse(uuid, (uuid.hashCode() & 1) != 0 ? 1 : 0);
}
public static byte[] makePresetResponse(EaglercraftUUID uuid, int presetId) {
byte[] ret = new byte[1 + 16 + 4];
ret[0] = (byte)PACKET_OTHER_SKIN_PRESET;
UUIDToBytes(uuid, ret, 1);
ret[17] = (byte)(presetId >>> 24);
ret[18] = (byte)(presetId >>> 16);
ret[19] = (byte)(presetId >>> 8);
ret[20] = (byte)(presetId & 0xFF);
return ret;
}
public static byte[] makeCustomResponse(EaglercraftUUID uuid, int model, byte[] pixels) {
byte[] ret = new byte[1 + 16 + 1 + pixels.length];
ret[0] = (byte)PACKET_OTHER_SKIN_CUSTOM;
UUIDToBytes(uuid, ret, 1);
ret[17] = (byte)model;
System.arraycopy(pixels, 0, ret, 18, pixels.length);
return ret;
}
public static EaglercraftUUID bytesToUUID(byte[] bytes, int off) {
long msb = (((long) bytes[off] & 0xFFl) << 56l) | (((long) bytes[off + 1] & 0xFFl) << 48l)
| (((long) bytes[off + 2] & 0xFFl) << 40l) | (((long) bytes[off + 3] & 0xFFl) << 32l)
| (((long) bytes[off + 4] & 0xFFl) << 24l) | (((long) bytes[off + 5] & 0xFFl) << 16l)
| (((long) bytes[off + 6] & 0xFFl) << 8l) | ((long) bytes[off + 7] & 0xFFl);
long lsb = (((long) bytes[off + 8] & 0xFFl) << 56l) | (((long) bytes[off + 9] & 0xFFl) << 48l)
| (((long) bytes[off + 10] & 0xFFl) << 40l) | (((long) bytes[off + 11] & 0xFFl) << 32l)
| (((long) bytes[off + 12] & 0xFFl) << 24l) | (((long) bytes[off + 13] & 0xFFl) << 16l)
| (((long) bytes[off + 14] & 0xFFl) << 8l) | ((long) bytes[off + 15] & 0xFFl);
return new EaglercraftUUID(msb, lsb);
}
private static final String hex = "0123456789abcdef";
public static String bytesToString(byte[] bytes, int off, int len) {
char[] ret = new char[len << 1];
for(int i = 0; i < len; ++i) {
ret[i * 2] = hex.charAt((bytes[off + i] >> 4) & 0xF);
ret[i * 2 + 1] = hex.charAt(bytes[off + i] & 0xF);
public static void setAlphaForChestV4(byte[] skin64x64) {
if(skin64x64.length != 12288) {
throw new IllegalArgumentException("Skin is not 64x64!");
}
return new String(ret);
}
public static String bytesToAscii(byte[] bytes, int off, int len) {
char[] ret = new char[len];
for(int i = 0; i < len; ++i) {
ret[i] = (char)((int)bytes[off + i] & 0xFF);
for(int y = 20; y < 32; ++y) {
for(int x = 16; x < 40; ++x) {
skin64x64[((y << 6) | x) * 3] |= 0x80;
}
}
return new String(ret);
}
public static String bytesToAscii(byte[] bytes) {
return bytesToAscii(bytes, 0, bytes.length);
}
public static void UUIDToBytes(EaglercraftUUID uuid, byte[] bytes, int off) {
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
bytes[off] = (byte)(msb >>> 56l);
bytes[off + 1] = (byte)(msb >>> 48l);
bytes[off + 2] = (byte)(msb >>> 40l);
bytes[off + 3] = (byte)(msb >>> 32l);
bytes[off + 4] = (byte)(msb >>> 24l);
bytes[off + 5] = (byte)(msb >>> 16l);
bytes[off + 6] = (byte)(msb >>> 8l);
bytes[off + 7] = (byte)(msb & 0xFFl);
bytes[off + 8] = (byte)(lsb >>> 56l);
bytes[off + 9] = (byte)(lsb >>> 48l);
bytes[off + 10] = (byte)(lsb >>> 40l);
bytes[off + 11] = (byte)(lsb >>> 32l);
bytes[off + 12] = (byte)(lsb >>> 24l);
bytes[off + 13] = (byte)(lsb >>> 16l);
bytes[off + 14] = (byte)(lsb >>> 8l);
bytes[off + 15] = (byte)(lsb & 0xFFl);
public static SPacketOtherSkinPresetEAG makePresetResponse(EaglercraftUUID uuid) {
return new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, (uuid.hashCode() & 1) != 0 ? 1 : 0);
}
public static byte[] asciiString(String string) {
@ -231,21 +116,4 @@ public class IntegratedSkinPackets {
return "slim".equalsIgnoreCase(modelName) ? 1 : 0;
}
public static byte[] rewriteUUID(EaglercraftUUID newUUID, byte[] pkt) {
byte[] ret = new byte[pkt.length];
System.arraycopy(pkt, 0, ret, 0, pkt.length);
UUIDToBytes(newUUID, ret, 1);
return ret;
}
public static byte[] rewriteUUIDModel(EaglercraftUUID newUUID, byte[] pkt, int model) {
byte[] ret = new byte[pkt.length];
System.arraycopy(pkt, 0, ret, 0, pkt.length);
UUIDToBytes(newUUID, ret, 1);
if(ret[0] == (byte)PACKET_OTHER_SKIN_CUSTOM) {
ret[17] = (byte)model;
}
return ret;
}
}

View File

@ -7,17 +7,19 @@ import java.util.Iterator;
import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.Base64;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S3FPacketCustomPayload;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.nbt.NBTTagCompound;
@ -43,8 +45,6 @@ public class IntegratedSkinService {
public static final Logger logger = LogManager.getLogger("IntegratedSkinService");
public static final String CHANNEL = "EAG|Skins-1.8";
public static final byte[] skullNotFoundTexture = new byte[4096];
static {
@ -62,8 +62,8 @@ public class IntegratedSkinService {
public final VFile2 skullsDirectory;
public final Map<EaglercraftUUID,byte[]> playerSkins = new HashMap();
public final Map<String,CustomSkullData> customSkulls = new HashMap();
public final Map<EaglercraftUUID,SkinPacketVersionCache> playerSkins = new HashMap<>();
public final Map<String,CustomSkullData> customSkulls = new HashMap<>();
private long lastFlush = 0l;
@ -71,19 +71,9 @@ public class IntegratedSkinService {
this.skullsDirectory = skullsDirectory;
}
public void processPacket(byte[] packetData, EntityPlayerMP sender) {
public void processLoginPacket(byte[] packetData, EntityPlayerMP sender, int protocolVers) {
try {
IntegratedSkinPackets.processPacket(packetData, sender, this);
} catch (IOException e) {
logger.error("Invalid skin request packet recieved from player {}!", sender.getName());
logger.error(e);
sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin request packet recieved!");
}
}
public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) {
try {
IntegratedSkinPackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this);
IntegratedSkinPackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this, protocolVers);
} catch (IOException e) {
logger.error("Invalid skin data packet recieved from player {}!", sender.getName());
logger.error(e);
@ -92,36 +82,39 @@ public class IntegratedSkinService {
}
public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, EntityPlayerMP sender) {
byte[] playerSkin = playerSkins.get(searchUUID);
if(playerSkin == null) {
playerSkin = IntegratedSkinPackets.makePresetResponse(searchUUID);
SkinPacketVersionCache playerSkin = playerSkins.get(searchUUID);
GameMessagePacket toSend = null;
if(playerSkin != null) {
toSend = playerSkin.get(sender.playerNetServerHandler.getEaglerMessageProtocol());
}else {
toSend = IntegratedSkinPackets.makePresetResponse(searchUUID);
}
sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(playerSkin, playerSkin.length).writerIndex(playerSkin.length))));
sender.playerNetServerHandler.sendEaglerMessage(toSend);
}
public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, String urlStr, EntityPlayerMP sender) {
urlStr = urlStr.toLowerCase();
byte[] playerSkin;
GameMessagePacket playerSkin;
if(!urlStr.startsWith("eagler://")) {
playerSkin = IntegratedSkinPackets.makePresetResponse(searchUUID, 0);
playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0);
}else {
urlStr = urlStr.substring(9);
if(urlStr.contains(VFile2.pathSeperator)) {
playerSkin = IntegratedSkinPackets.makePresetResponse(searchUUID, 0);
playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0);
}else {
CustomSkullData sk = customSkulls.get(urlStr);
if(sk == null) {
customSkulls.put(urlStr, sk = loadCustomSkull(urlStr));
}else {
sk.lastHit = System.currentTimeMillis();
sk.lastHit = EagRuntime.steadyTimeMillis();
}
playerSkin = IntegratedSkinPackets.makeCustomResponse(searchUUID, 0, sk.getFullSkin());
playerSkin = sk.getSkinPacket(searchUUID, sender.playerNetServerHandler.getEaglerMessageProtocol());
}
}
sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(playerSkin, playerSkin.length).writerIndex(playerSkin.length))));
sender.playerNetServerHandler.sendEaglerMessage(playerSkin);
}
public void processPacketPlayerSkin(EaglercraftUUID clientUUID, byte[] generatedPacket, int skinModel) {
public void processPacketPlayerSkin(EaglercraftUUID clientUUID, SkinPacketVersionCache generatedPacket, int skinModel) {
playerSkins.put(clientUUID, generatedPacket);
}
@ -188,12 +181,12 @@ public class IntegratedSkinService {
}
String str = "skin-" + new String(hashText) + ".bmp";
customSkulls.put(str, new CustomSkullData(str, skullData));
(new VFile2(skullsDirectory, str)).setAllBytes(skullData);
WorldsDB.newVFile(skullsDirectory, str).setAllBytes(skullData);
return str;
}
private CustomSkullData loadCustomSkull(String urlStr) {
byte[] data = (new VFile2(skullsDirectory, urlStr)).getAllBytes();
byte[] data = WorldsDB.newVFile(skullsDirectory, urlStr).getAllBytes();
if(data == null) {
return new CustomSkullData(urlStr, skullNotFoundTexture);
}else {
@ -202,7 +195,7 @@ public class IntegratedSkinService {
}
public void flushCache() {
long cur = System.currentTimeMillis();
long cur = EagRuntime.steadyTimeMillis();
if(cur - lastFlush > 300000l) {
lastFlush = cur;
Iterator<CustomSkullData> customSkullsItr = customSkulls.values().iterator();

View File

@ -56,7 +56,7 @@ public class IntegratedServerPlayerNetworkManager {
private boolean firstPacket = true;
private List<byte[]> fragmentedPacket = new ArrayList();
private List<byte[]> fragmentedPacket = new ArrayList<>();
public static final int fragmentSize = 0xFF00;
public static final int compressionThreshold = 1024;
@ -124,8 +124,7 @@ public class IntegratedServerPlayerNetworkManager {
kickDAO.write(0x00);
kickDAO.write(msg.length());
for(int j = 0, l = msg.length(); j < l; ++j) {
kickDAO.write(0);
kickDAO.write(msg.codePointAt(j));
kickDAO.writeChar(msg.charAt(j));
}
}catch(IOException ex) {
throw new RuntimeException(ex);

View File

@ -0,0 +1,90 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer;
import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService;
import net.minecraft.network.NetHandlerPlayServer;
/**
* 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 ServerV3MessageHandler implements GameMessageHandler {
private final NetHandlerPlayServer netHandler;
private final EaglerMinecraftServer server;
public ServerV3MessageHandler(NetHandlerPlayServer netHandler) {
this.netHandler = netHandler;
this.server = (EaglerMinecraftServer)netHandler.serverController;
}
public void handleClient(CPacketGetOtherCapeEAG packet) {
server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
public void handleClient(CPacketGetOtherSkinEAG packet) {
server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
public void handleClient(CPacketGetSkinByURLEAG packet) {
server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity);
}
public void handleClient(CPacketInstallSkinSPEAG packet) {
server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity);
}
public void handleClient(CPacketVoiceSignalConnectEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeConnect(netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalDescEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.desc, netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalDisconnectV3EAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
if(packet.isPeerType) {
voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}else {
voiceSvc.handleVoiceSignalPacketTypeDisconnect(netHandler.playerEntity);
}
}
}
public void handleClient(CPacketVoiceSignalICEEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalRequestEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
}
}

View File

@ -0,0 +1,104 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherPlayerClientUUIDV4EAG;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer;
import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetHandlerPlayServer;
/**
* 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 ServerV4MessageHandler implements GameMessageHandler {
private final NetHandlerPlayServer netHandler;
private final EaglerMinecraftServer server;
public ServerV4MessageHandler(NetHandlerPlayServer netHandler) {
this.netHandler = netHandler;
this.server = (EaglerMinecraftServer)netHandler.serverController;
}
public void handleClient(CPacketGetOtherCapeEAG packet) {
server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
public void handleClient(CPacketGetOtherSkinEAG packet) {
server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
public void handleClient(CPacketGetSkinByURLEAG packet) {
server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity);
}
public void handleClient(CPacketInstallSkinSPEAG packet) {
server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity);
}
public void handleClient(CPacketVoiceSignalConnectEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeConnect(netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalDescEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeDesc(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.desc, netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalDisconnectV4EAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeDisconnect(netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalDisconnectPeerV4EAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalICEEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity);
}
}
public void handleClient(CPacketVoiceSignalRequestEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
}
}
public void handleClient(CPacketGetOtherClientUUIDV4EAG packet) {
EntityPlayerMP player = server.getConfigurationManager().getPlayerByUUID(new EaglercraftUUID(packet.playerUUIDMost, packet.playerUUIDLeast));
if(player != null && player.clientBrandUUID != null) {
netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, player.clientBrandUUID.msb, player.clientBrandUUID.lsb));
}else {
netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, 0l, 0l));
}
}
}

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.voice;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -10,11 +11,10 @@ import java.util.Set;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
import net.lax1dude.eaglercraft.v1_8.voice.ExpiringSet;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S3FPacketCustomPayload;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -35,20 +35,18 @@ public class IntegratedVoiceService {
public static final Logger logger = LogManager.getLogger("IntegratedVoiceService");
public static final String CHANNEL = "EAG|Voice-1.8";
private byte[] iceServersPacket;
private GameMessagePacket iceServersPacket;
private final Map<EaglercraftUUID, EntityPlayerMP> voicePlayers = new HashMap<>();
private final Map<EaglercraftUUID, ExpiringSet<EaglercraftUUID>> voiceRequests = new HashMap<>();
private final Set<VoicePair> voicePairs = new HashSet<>();
public IntegratedVoiceService(String[] iceServers) {
iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers);
iceServersPacket = new SPacketVoiceSignalAllowedEAG(true, iceServers);
}
public void changeICEServers(String[] iceServers) {
iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers);
iceServersPacket = new SPacketVoiceSignalAllowedEAG(true, iceServers);
}
private static class VoicePair {
@ -85,25 +83,14 @@ public class IntegratedVoiceService {
}
public void handlePlayerLoggedIn(EntityPlayerMP player) {
player.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(
Unpooled.buffer(iceServersPacket, iceServersPacket.length).writerIndex(iceServersPacket.length))));
player.playerNetServerHandler.sendEaglerMessage(iceServersPacket);
}
public void handlePlayerLoggedOut(EntityPlayerMP player) {
removeUser(player.getUniqueID());
}
public void processPacket(PacketBuffer packetData, EntityPlayerMP sender) {
try {
IntegratedVoiceSignalPackets.processPacket(packetData, sender, this);
} catch (IOException e) {
logger.error("Invalid voice signal packet recieved from player {}!", sender.getName());
logger.error(e);
sender.playerNetServerHandler.kickPlayerFromServer("Invalid voice signal packet recieved!");
}
}
void handleVoiceSignalPacketTypeRequest(EaglercraftUUID player, EntityPlayerMP sender) {
public void handleVoiceSignalPacketTypeRequest(EaglercraftUUID player, EntityPlayerMP sender) {
EaglercraftUUID senderUUID = sender.getUniqueID();
if (senderUUID.equals(player))
return; // prevent duplicates
@ -134,14 +121,24 @@ public class IntegratedVoiceService {
voiceRequests.remove(senderUUID);
// send each other add data
voicePairs.add(newPair);
targetPlayerCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
IntegratedVoiceSignalPackets.makeVoiceSignalPacketConnect(senderUUID, false)));
sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
IntegratedVoiceSignalPackets.makeVoiceSignalPacketConnect(player, true)));
if(targetPlayerCon.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) {
targetPlayerCon.playerNetServerHandler
.sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(senderUUID.msb, senderUUID.lsb, false, false));
}else {
targetPlayerCon.playerNetServerHandler
.sendEaglerMessage(new SPacketVoiceSignalConnectV4EAG(senderUUID.msb, senderUUID.lsb, false));
}
if(sender.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) {
sender.playerNetServerHandler
.sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(player.msb, player.lsb, false, true));
}else {
sender.playerNetServerHandler
.sendEaglerMessage(new SPacketVoiceSignalConnectV4EAG(player.msb, player.lsb, true));
}
}
}
void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) {
public void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) {
if (voicePlayers.containsKey(sender.getUniqueID())) {
return;
}
@ -150,63 +147,60 @@ public class IntegratedVoiceService {
if (hasNoOtherPlayers) {
return;
}
byte[] packetToBroadcast = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
Collection<SPacketVoiceSignalGlobalEAG.UserData> userDatas = new ArrayList<>(voicePlayers.size());
for(EntityPlayerMP player : voicePlayers.values()) {
EaglercraftUUID uuid = player.getUniqueID();
userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.msb, uuid.lsb, player.getName()));
}
SPacketVoiceSignalGlobalEAG packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas);
for (EntityPlayerMP userCon : voicePlayers.values()) {
userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled
.buffer(packetToBroadcast, packetToBroadcast.length).writerIndex(packetToBroadcast.length))));
userCon.playerNetServerHandler.sendEaglerMessage(packetToBroadcast);
}
}
void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, String str, EntityPlayerMP sender) {
VoicePair pair = new VoicePair(player, sender.getUniqueID());
public void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, byte[] str, EntityPlayerMP sender) {
EaglercraftUUID uuid = sender.getUniqueID();
VoicePair pair = new VoicePair(player, uuid);
EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
if (pass != null) {
pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
IntegratedVoiceSignalPackets.makeVoiceSignalPacketICE(sender.getUniqueID(), str)));
pass.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalICEEAG(uuid.msb, uuid.lsb, str));
}
}
void handleVoiceSignalPacketTypeDesc(EaglercraftUUID player, String str, EntityPlayerMP sender) {
VoicePair pair = new VoicePair(player, sender.getUniqueID());
public void handleVoiceSignalPacketTypeDesc(EaglercraftUUID player, byte[] str, EntityPlayerMP sender) {
EaglercraftUUID uuid = sender.getUniqueID();
VoicePair pair = new VoicePair(player, uuid);
EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
if (pass != null) {
pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
IntegratedVoiceSignalPackets.makeVoiceSignalPacketDesc(sender.getUniqueID(), str)));
pass.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDescEAG(uuid.msb, uuid.lsb, str));
}
}
void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID player, EntityPlayerMP sender) {
if (player != null) {
if (!voicePlayers.containsKey(player)) {
return;
public void handleVoiceSignalPacketTypeDisconnect(EntityPlayerMP sender) {
removeUser(sender.getUniqueID());
}
public void handleVoiceSignalPacketTypeDisconnectPeer(EaglercraftUUID player, EntityPlayerMP sender) {
if (!voicePlayers.containsKey(player)) {
return;
}
Iterator<VoicePair> pairsItr = voicePairs.iterator();
while (pairsItr.hasNext()) {
VoicePair voicePair = pairsItr.next();
EaglercraftUUID target = null;
if (voicePair.uuid1.equals(player)) {
target = voicePair.uuid2;
} else if (voicePair.uuid2.equals(player)) {
target = voicePair.uuid1;
}
byte[] userDisconnectPacket = null;
Iterator<VoicePair> pairsItr = voicePairs.iterator();
while (pairsItr.hasNext()) {
VoicePair voicePair = pairsItr.next();
EaglercraftUUID target = null;
if (voicePair.uuid1.equals(player)) {
target = voicePair.uuid2;
} else if (voicePair.uuid2.equals(player)) {
target = voicePair.uuid1;
}
if (target != null) {
pairsItr.remove();
EntityPlayerMP conn = voicePlayers.get(target);
if (conn != null) {
if (userDisconnectPacket == null) {
userDisconnectPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnect(player);
}
conn.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
new PacketBuffer(Unpooled.buffer(userDisconnectPacket, userDisconnectPacket.length)
.writerIndex(userDisconnectPacket.length))));
}
sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnectPB(target)));
if (target != null) {
pairsItr.remove();
EntityPlayerMP conn = voicePlayers.get(target);
if (conn != null) {
conn.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(player.msb, player.lsb));
}
sender.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(target.msb, target.lsb));
}
} else {
removeUser(sender.getUniqueID());
}
}
@ -216,16 +210,16 @@ public class IntegratedVoiceService {
}
voiceRequests.remove(user);
if (voicePlayers.size() > 0) {
byte[] voicePlayersPkt = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
Collection<SPacketVoiceSignalGlobalEAG.UserData> userDatas = new ArrayList<>(voicePlayers.size());
for(EntityPlayerMP player : voicePlayers.values()) {
EaglercraftUUID uuid = player.getUniqueID();
userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(uuid.msb, uuid.lsb, player.getName()));
}
SPacketVoiceSignalGlobalEAG packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas);
for (EntityPlayerMP userCon : voicePlayers.values()) {
if (!user.equals(userCon.getUniqueID())) {
userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
new PacketBuffer(Unpooled.buffer(voicePlayersPkt, voicePlayersPkt.length)
.writerIndex(voicePlayersPkt.length))));
}
userCon.playerNetServerHandler.sendEaglerMessage(packetToBroadcast);
}
}
byte[] userDisconnectPacket = null;
Iterator<VoicePair> pairsItr = voicePairs.iterator();
while (pairsItr.hasNext()) {
VoicePair voicePair = pairsItr.next();
@ -240,12 +234,7 @@ public class IntegratedVoiceService {
if (voicePlayers.size() > 0) {
EntityPlayerMP conn = voicePlayers.get(target);
if (conn != null) {
if (userDisconnectPacket == null) {
userDisconnectPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnect(user);
}
conn.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
new PacketBuffer(Unpooled.buffer(userDisconnectPacket, userDisconnectPacket.length)
.writerIndex(userDisconnectPacket.length))));
conn.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalDisconnectPeerEAG(user.msb, user.lsb));
}
}
}

View File

@ -1,198 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.voice;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf;
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.PacketBuffer;
/**
* 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 IntegratedVoiceSignalPackets {
static final int VOICE_SIGNAL_ALLOWED = 0;
static final int VOICE_SIGNAL_REQUEST = 0;
static final int VOICE_SIGNAL_CONNECT = 1;
static final int VOICE_SIGNAL_DISCONNECT = 2;
static final int VOICE_SIGNAL_ICE = 3;
static final int VOICE_SIGNAL_DESC = 4;
static final int VOICE_SIGNAL_GLOBAL = 5;
public static void processPacket(PacketBuffer buffer, EntityPlayerMP sender, IntegratedVoiceService voiceService) throws IOException {
int packetId = -1;
if(buffer.readableBytes() == 0) {
throw new IOException("Zero-length packet recieved");
}
try {
packetId = buffer.readUnsignedByte();
switch(packetId) {
case VOICE_SIGNAL_REQUEST: {
voiceService.handleVoiceSignalPacketTypeRequest(buffer.readUuid(), sender);
break;
}
case VOICE_SIGNAL_CONNECT: {
voiceService.handleVoiceSignalPacketTypeConnect(sender);
break;
}
case VOICE_SIGNAL_ICE: {
voiceService.handleVoiceSignalPacketTypeICE(buffer.readUuid(), buffer.readStringFromBuffer(32767), sender);
break;
}
case VOICE_SIGNAL_DESC: {
voiceService.handleVoiceSignalPacketTypeDesc(buffer.readUuid(), buffer.readStringFromBuffer(32767), sender);
break;
}
case VOICE_SIGNAL_DISCONNECT: {
voiceService.handleVoiceSignalPacketTypeDisconnect(buffer.readableBytes() > 0 ? buffer.readUuid() : null, sender);
break;
}
default: {
throw new IOException("Unknown packet type " + packetId);
}
}
if(buffer.readableBytes() > 0) {
throw new IOException("Voice packet is too long!");
}
}catch(IOException ex) {
throw ex;
}catch(Throwable t) {
throw new IOException("Unhandled exception handling voice packet type " + packetId, t);
}
}
static byte[] makeVoiceSignalPacketAllowed(boolean allowed, String[] iceServers) {
if (iceServers == null) {
byte[] ret = new byte[2];
ByteBuf wrappedBuffer = Unpooled.buffer(ret, ret.length);
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
wrappedBuffer.writeBoolean(allowed);
return ret;
}
byte[][] iceServersBytes = new byte[iceServers.length][];
int totalLen = 2 + PacketBuffer.getVarIntSize(iceServers.length);
for(int i = 0; i < iceServers.length; ++i) {
byte[] b = iceServersBytes[i] = iceServers[i].getBytes(StandardCharsets.UTF_8);
totalLen += PacketBuffer.getVarIntSize(b.length) + b.length;
}
byte[] ret = new byte[totalLen];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
wrappedBuffer.writeBoolean(allowed);
wrappedBuffer.writeVarIntToBuffer(iceServersBytes.length);
for(int i = 0; i < iceServersBytes.length; ++i) {
byte[] b = iceServersBytes[i];
wrappedBuffer.writeVarIntToBuffer(b.length);
wrappedBuffer.writeBytes(b);
}
return ret;
}
static byte[] makeVoiceSignalPacketGlobal(Collection<EntityPlayerMP> users) {
int cnt = users.size();
byte[][] displayNames = new byte[cnt][];
int i = 0;
for(EntityPlayerMP user : users) {
String name = user.getName();
if(name.length() > 16) name = name.substring(0, 16);
displayNames[i++] = name.getBytes(StandardCharsets.UTF_8);
}
int totalLength = 1 + PacketBuffer.getVarIntSize(cnt) + (cnt << 4);
for(i = 0; i < cnt; ++i) {
totalLength += PacketBuffer.getVarIntSize(displayNames[i].length) + displayNames[i].length;
}
byte[] ret = new byte[totalLength];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_GLOBAL);
wrappedBuffer.writeVarIntToBuffer(cnt);
for(EntityPlayerMP user : users) {
wrappedBuffer.writeUuid(user.getUniqueID());
}
for(i = 0; i < cnt; ++i) {
wrappedBuffer.writeVarIntToBuffer(displayNames[i].length);
wrappedBuffer.writeBytes(displayNames[i]);
}
return ret;
}
static PacketBuffer makeVoiceSignalPacketConnect(EaglercraftUUID player, boolean offer) {
byte[] ret = new byte[18];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
wrappedBuffer.writeUuid(player);
wrappedBuffer.writeBoolean(offer);
return wrappedBuffer;
}
static byte[] makeVoiceSignalPacketConnectAnnounce(EaglercraftUUID player) {
byte[] ret = new byte[17];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
wrappedBuffer.writeUuid(player);
return ret;
}
static byte[] makeVoiceSignalPacketDisconnect(EaglercraftUUID player) {
if(player == null) {
return new byte[] { (byte)VOICE_SIGNAL_DISCONNECT };
}
byte[] ret = new byte[17];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT);
wrappedBuffer.writeUuid(player);
return ret;
}
static PacketBuffer makeVoiceSignalPacketDisconnectPB(EaglercraftUUID player) {
if(player == null) {
byte[] ret = new byte[1];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT);
return wrappedBuffer;
}
byte[] ret = new byte[17];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT);
wrappedBuffer.writeUuid(player);
return wrappedBuffer;
}
static PacketBuffer makeVoiceSignalPacketICE(EaglercraftUUID player, String str) {
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
byte[] ret = new byte[17 + PacketBuffer.getVarIntSize(strBytes.length) + strBytes.length];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_ICE);
wrappedBuffer.writeUuid(player);
wrappedBuffer.writeVarIntToBuffer(strBytes.length);
wrappedBuffer.writeBytes(strBytes);
return wrappedBuffer;
}
static PacketBuffer makeVoiceSignalPacketDesc(EaglercraftUUID player, String str) {
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
byte[] ret = new byte[17 + PacketBuffer.getVarIntSize(strBytes.length) + strBytes.length];
PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length));
wrappedBuffer.writeByte(VOICE_SIGNAL_DESC);
wrappedBuffer.writeUuid(player);
wrappedBuffer.writeVarIntToBuffer(strBytes.length);
wrappedBuffer.writeBytes(strBytes);
return wrappedBuffer;
}
}

View File

@ -1,7 +1,12 @@
package net.lax1dude.eaglercraft.v1_8.sp.socket;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.client.GameProtocolMessageController;
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiDisconnected;
@ -15,6 +20,7 @@ import net.minecraft.network.login.server.S01PacketEncryptionRequest;
import net.minecraft.network.login.server.S02PacketLoginSuccess;
import net.minecraft.network.login.server.S03PacketEnableCompression;
import net.minecraft.network.play.client.C17PacketCustomPayload;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IChatComponent;
/**
@ -38,6 +44,8 @@ public class NetHandlerSingleplayerLogin implements INetHandlerLoginClient {
private final GuiScreen previousGuiScreen;
private final EaglercraftNetworkManager networkManager;
private static final Logger logger = LogManager.getLogger("NetHandlerSingleplayerLogin");
public NetHandlerSingleplayerLogin(EaglercraftNetworkManager parNetworkManager, Minecraft mcIn, GuiScreen parGuiScreen) {
this.networkManager = parNetworkManager;
this.mc = mcIn;
@ -57,7 +65,19 @@ public class NetHandlerSingleplayerLogin implements INetHandlerLoginClient {
@Override
public void handleLoginSuccess(S02PacketLoginSuccess var1) {
this.networkManager.setConnectionState(EnumConnectionState.PLAY);
this.networkManager.setNetHandler(new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager, var1.getProfile()));
int p = var1.getSelectedProtocol();
GamePluginMessageProtocol mp = GamePluginMessageProtocol.getByVersion(p);
if(mp == null) {
this.networkManager.closeChannel(new ChatComponentText("Unknown protocol selected: " + p));
return;
}
logger.info("Server is using protocol: {}", p);
NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager, var1.getProfile());
netHandler.setEaglerMessageController(
new GameProtocolMessageController(mp, GamePluginMessageConstants.CLIENT_TO_SERVER,
GameProtocolMessageController.createClientHandler(p, netHandler),
(ch, msg) -> netHandler.addToSendQueue(new C17PacketCustomPayload(ch, msg))));
this.networkManager.setNetHandler(netHandler);
byte[] b = UpdateService.getClientSignatureData();
if(b != null) {
this.networkManager.sendPacket(new C17PacketCustomPayload("EAG|MyUpdCert-1.8", new PacketBuffer(Unpooled.buffer(b, b.length).writerIndex(b.length))));