mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #37 - Touch support without userscript, many other feats
This commit is contained in:
@ -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());
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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()) {
|
||||
|
@ -28,6 +28,7 @@ public interface RelayQuery {
|
||||
}
|
||||
}
|
||||
|
||||
void update();
|
||||
boolean isQueryOpen();
|
||||
boolean isQueryFailed();
|
||||
RateLimit isQueryRateLimit();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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>";
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 });
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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))));
|
||||
|
Reference in New Issue
Block a user