Update #51 - Protocol and FPS improvements, better workspace

This commit is contained in:
lax1dude
2025-05-18 15:01:06 -07:00
parent 71c61e33fd
commit 325a6826bf
1191 changed files with 9266 additions and 187695 deletions

View File

@ -19,7 +19,7 @@ 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.socket.protocol.handshake.HandshakerHandler;
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;
@ -101,8 +101,11 @@ public class GuiScreenLANConnecting extends GuiScreen {
if(++renderCount > 1) {
RelayServerSocket sock;
if(relay == null) {
sock = RelayManager.relayManager.getWorkingRelay((str) -> ls.resetProgressAndMessage("Connecting: " + str), 0x02, code);
ls.resetProgressAndMessage("Connecting to '" + code + "'...");
sock = RelayManager.relayManager.getWorkingRelay((str) -> ls.displayLoadingString("Connecting: " + str), 0x02, code);
}else {
ls.resetProgressAndMessage("Connecting to '" + code + "'...");
ls.displayLoadingString("Connecting: " + relay.address);
sock = RelayManager.relayManager.connectHandshake(relay, 0x02, code);
}
if(sock == null) {
@ -125,7 +128,7 @@ public class GuiScreenLANConnecting extends GuiScreen {
networkManager.setNetHandler(new NetHandlerSingleplayerLogin(networkManager, mc, parent));
networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(),
EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(),
ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID));
HandshakerHandler.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID));
}
}
}

View File

@ -21,7 +21,7 @@ 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.socket.protocol.handshake.HandshakerHandler;
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;
@ -94,7 +94,7 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen {
this.networkManager.setNetHandler(new NetHandlerSingleplayerLogin(this.networkManager, this.mc, this.menu));
this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(),
EaglerProfile.getSkinPacket(3), EaglerProfile.getCapePacket(),
ConnectionHandshake.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID));
HandshakerHandler.getSPHandshakeProtocolData(), EaglercraftVersion.clientBrandUUID));
}
try {
this.networkManager.processReceivedPackets();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2022-2025 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
@ -366,6 +366,10 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
}
}
if(injectedController != null && injectedController.handlePacket(fullData, off)) {
continue;
}
ByteBuf nettyBuffer = Unpooled.buffer(fullData, fullData.length);
nettyBuffer.writerIndex(fullData.length);
nettyBuffer.readerIndex(off);
@ -399,6 +403,32 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager {
}
}
@Override
public void injectRawFrame(byte[] data) {
if(!isChannelOpen()) {
logger.error("Frame was injected on a closed connection");
return;
}
int len = data.length;
int fragmentSizeN1 = fragmentSize - 1;
if(len > fragmentSizeN1) {
int idx = 0;
do {
int readLen = len > fragmentSizeN1 ? fragmentSizeN1 : len;
byte[] frag = new byte[readLen + 1];
System.arraycopy(data, idx, frag, 1, readLen);
idx += readLen;
len -= readLen;
frag[0] = len == 0 ? (byte)0 : (byte)1;
PlatformWebRTC.clientLANSendPacket(frag);
}while(len > 0);
}else {
byte[] bytes = new byte[len + 1];
System.arraycopy(data, 0, bytes, 1, len);
PlatformWebRTC.clientLANSendPacket(bytes);
}
}
@Override
public void closeChannel(IChatComponent reason) {
if(!PlatformWebRTC.clientLANClosed()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2023-2025 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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2023-2025 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
@ -23,6 +23,7 @@ import java.util.List;
import com.google.common.collect.Lists;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.entity.player.EntityPlayer;
@ -33,8 +34,7 @@ import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.WorldServer;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.WorldSettings.GameType;
import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedCapeService;
import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedSkinService;
import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedTextureService;
import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService;
public class EaglerMinecraftServer extends MinecraftServer {
@ -48,8 +48,7 @@ public class EaglerMinecraftServer extends MinecraftServer {
protected WorldSettings newWorldSettings;
protected boolean paused;
protected EaglerSaveHandler saveHandler;
protected IntegratedSkinService skinService;
protected IntegratedCapeService capeService;
protected IntegratedTextureService textureService;
protected IntegratedVoiceService voiceService;
private long lastTPSUpdate = 0l;
@ -67,25 +66,22 @@ public class EaglerMinecraftServer extends MinecraftServer {
super(world);
Bootstrap.register();
this.saveHandler = new EaglerSaveHandler(savesDir, world);
this.skinService = new IntegratedSkinService(WorldsDB.newVFile(saveHandler.getWorldDirectory(), "eagler/skulls"));
this.capeService = new IntegratedCapeService();
EaglerPlayerList playerList = new EaglerPlayerList(this, viewDistance);
this.textureService = new IntegratedTextureService(playerList,
WorldsDB.newVFile(saveHandler.getWorldDirectory(), "eagler/skulls"));
this.voiceService = null;
this.setServerOwner(owner);
logger.info("server owner: " + owner);
this.setDemo(demo);
this.canCreateBonusChest(currentWorldSettings != null && currentWorldSettings.isBonusChestEnabled());
this.setBuildLimit(256);
this.setConfigManager(new EaglerPlayerList(this, viewDistance));
this.setConfigManager(playerList);
this.newWorldSettings = currentWorldSettings;
this.paused = false;
}
public IntegratedSkinService getSkinService() {
return skinService;
}
public IntegratedCapeService getCapeService() {
return capeService;
public IntegratedTextureService getTextureService() {
return textureService;
}
public IntegratedVoiceService getVoiceService() {
@ -166,12 +162,14 @@ public class EaglerMinecraftServer extends MinecraftServer {
this.currentTime += 50l;
this.tick();
++counterTicksPerSecond;
} else if (!singleThreadMode) {
EagUtils.sleep(1);
}
}
}
public void updateTimeLightAndEntities() {
this.skinService.flushCache();
this.textureService.flushCache();
super.updateTimeLightAndEntities();
}

View File

@ -44,8 +44,5 @@ public class EaglerPlayerList extends ServerConfigurationManager {
public void playerLoggedOut(EntityPlayerMP playerIn) {
super.playerLoggedOut(playerIn);
EaglerMinecraftServer svr = (EaglerMinecraftServer)getServerInstance();
svr.skinService.unregisterPlayer(playerIn.getUniqueID());
svr.capeService.unregisterPlayer(playerIn.getUniqueID());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
* Copyright (c) 2023-2025 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
@ -17,35 +17,56 @@
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.pkt.server.SPacketOtherSkinCustomV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
public class CustomSkullData {
public String skinURL;
public long lastHit;
public SkinPacketVersionCache skinData;
protected long lastHit;
protected byte[] textureDataV3;
protected byte[] textureDataV4;
public CustomSkullData(String skinURL, byte[] skinData) {
this.skinURL = skinURL;
this.lastHit = EagRuntime.steadyTimeMillis();
public CustomSkullData(byte[] skinData) {
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);
textureDataV3 = skinData;
lastHit = EagRuntime.steadyTimeMillis();
}
public byte[] getFullSkin() {
return ((SPacketOtherSkinCustomV3EAG)skinData.getV3()).customSkin;
return textureDataV3;
}
public GameMessagePacket getSkinPacket(EaglercraftUUID uuid, GamePluginMessageProtocol protocol) {
return SkinPacketVersionCache.rewriteUUID(skinData.get(protocol), uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
public GameMessagePacket getSkin(long uuidMost, long uuidLeast, GamePluginMessageProtocol protocol) {
switch(protocol) {
case V3:
return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, 0xFF, textureDataV3);
case V4:
if(textureDataV4 == null) {
textureDataV4 = SkinPacketVersionCache.convertToV4Raw(textureDataV3);
}
return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, 0xFF, textureDataV4);
default:
throw new IllegalStateException();
}
}
public GameMessagePacket getSkinV5(int requestId, GamePluginMessageProtocol protocol) {
if(protocol.ver >= 5) {
if(textureDataV4 == null) {
textureDataV4 = SkinPacketVersionCache.convertToV4Raw(textureDataV3);
}
return new SPacketOtherSkinCustomV5EAG(requestId, 0xFF, textureDataV4);
}else {
throw new IllegalStateException();
}
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2025 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.sp.server.WorldsDB;
public class CustomSkullLoader {
private static final byte[] skullNotFoundTexture = new byte[4096];
static {
for(int y = 0; y < 16; ++y) {
for(int x = 0; x < 64; ++x) {
int i = (y << 8) | (x << 2);
byte j = ((x + y) & 1) == 1 ? (byte)255 : 0;
skullNotFoundTexture[i] = (byte)255;
skullNotFoundTexture[i + 1] = j;
skullNotFoundTexture[i + 2] = 0;
skullNotFoundTexture[i + 3] = j;
}
}
}
private final VFile2 folder;
private final Map<String,CustomSkullData> customSkulls = new HashMap<>(64);
private long lastFlush = 0l;
public CustomSkullLoader(VFile2 folder) {
this.folder = folder;
}
private CustomSkullData loadSkullData0(String urlStr) {
byte[] data = WorldsDB.newVFile(folder, urlStr).getAllBytes();
if(data == null) {
return new CustomSkullData(skullNotFoundTexture);
}else {
return new CustomSkullData(data);
}
}
public CustomSkullData loadSkullData(String url) {
CustomSkullData sk = customSkulls.get(url);
if(sk == null) {
customSkulls.put(url, sk = loadSkullData0(url));
}else {
sk.lastHit = EagRuntime.steadyTimeMillis();
}
return sk;
}
private static final String hex = "0123456789abcdef";
public String installNewSkull(byte[] skullData) {
// set to 16384 to save a full 64x64 skin
if(skullData.length > 4096) {
byte[] tmp = skullData;
skullData = new byte[4096];
System.arraycopy(tmp, 0, skullData, 0, 4096);
}
SHA1Digest sha = new SHA1Digest();
sha.update(skullData, 0, skullData.length);
byte[] hash = new byte[20];
sha.doFinal(hash, 0);
char[] hashText = new char[40];
for(int i = 0; i < 20; ++i) {
hashText[i << 1] = hex.charAt((hash[i] & 0xF0) >> 4);
hashText[(i << 1) + 1] = hex.charAt(hash[i] & 0x0F);
}
String str = "skin-" + new String(hashText) + ".bmp";
customSkulls.put(str, new CustomSkullData(skullData));
WorldsDB.newVFile(folder, str).setAllBytes(skullData);
return str;
}
public void flushCache() {
long cur = EagRuntime.steadyTimeMillis();
if(cur - lastFlush > 300000l) {
lastFlush = cur;
Iterator<CustomSkullData> customSkullsItr = customSkulls.values().iterator();
while(customSkullsItr.hasNext()) {
if(cur - customSkullsItr.next().lastHit > 900000l) {
customSkullsItr.remove();
}
}
}
}
}

View File

@ -1,62 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
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;
public class IntegratedCapePackets {
public static final int PACKET_MY_CAPE_PRESET = 0x01;
public static final int PACKET_MY_CAPE_CUSTOM = 0x02;
public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedCapeService capeService) throws IOException {
if(bs.length == 0) {
throw new IOException("Zero-length packet recieved");
}
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 = 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");
}
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);
}
capeService.registerEaglercraftPlayer(clientUUID, generatedPacket);
}
public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedCapeService capeService) {
capeService.registerEaglercraftPlayer(clientUUID, new SPacketOtherCapePresetEAG(clientUUID.msb, clientUUID.lsb, 0));
}
}

View File

@ -1,65 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.io.IOException;
import java.util.HashMap;
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.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
import net.minecraft.entity.player.EntityPlayerMP;
public class IntegratedCapeService {
public static final Logger logger = LogManager.getLogger("IntegratedCapeService");
public static final int masterRateLimitPerPlayer = 250;
private final Map<EaglercraftUUID, GameMessagePacket> capesCache = new HashMap<>();
public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) {
try {
IntegratedCapePackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this);
} catch (IOException e) {
logger.error("Invalid skin data packet recieved from player {}!", sender.getName());
logger.error(e);
sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin data packet recieved!");
}
}
public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, GameMessagePacket capePacket) {
capesCache.put(playerUUID, capePacket);
}
public void processGetOtherCape(EaglercraftUUID searchUUID, EntityPlayerMP sender) {
GameMessagePacket maybeCape = capesCache.get(searchUUID);
if(maybeCape == null) {
maybeCape = new SPacketOtherCapePresetEAG(searchUUID.msb, searchUUID.lsb, 0);
}
sender.playerNetServerHandler.sendEaglerMessage(maybeCape);
}
public void unregisterPlayer(EaglercraftUUID playerUUID) {
synchronized(capesCache) {
capesCache.remove(playerUUID);
}
}
}

View File

@ -1,120 +0,0 @@
/*
* Copyright (c) 2022-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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
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;
public class IntegratedSkinPackets {
public static final int PACKET_MY_SKIN_PRESET = 0x01;
public static final int PACKET_MY_SKIN_CUSTOM = 0x02;
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");
}
GameMessagePacket generatedPacketV3 = null;
GameMessagePacket generatedPacketV4 = null;
int skinModel = -1;
int packetType = (int)bs[0] & 0xFF;
switch(packetType) {
case PACKET_MY_SKIN_PRESET:
if(bs.length != 5) {
throw new IOException("Invalid length " + bs.length + " for preset skin packet");
}
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:
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);
}
break;
default:
throw new IOException("Unknown skin packet type: " + packetType);
}
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;
skinService.processPacketPlayerSkin(clientUUID, SkinPacketVersionCache.createPreset(clientUUID.msb, clientUUID.lsb, skinModel), skinModel);
}
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)] = (byte)0xFF;
}
}
}
public static void setAlphaForChestV4(byte[] skin64x64) {
if(skin64x64.length != 12288) {
throw new IllegalArgumentException("Skin is not 64x64!");
}
for(int y = 20; y < 32; ++y) {
for(int x = 16; x < 40; ++x) {
skin64x64[((y << 6) | x) * 3] |= 0x80;
}
}
}
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) {
byte[] str = new byte[string.length()];
for(int i = 0; i < str.length; ++i) {
str[i] = (byte)string.charAt(i);
}
return str;
}
public static EaglercraftUUID createEaglerURLSkinUUID(String skinUrl) {
return EaglercraftUUID.nameUUIDFromBytes(asciiString("EaglercraftSkinURL:" + skinUrl));
}
public static int getModelId(String modelName) {
return "slim".equalsIgnoreCase(modelName) ? 1 : 0;
}
}

View File

@ -1,210 +0,0 @@
/*
* Copyright (c) 2022-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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
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.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.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
public class IntegratedSkinService {
public static final Logger logger = LogManager.getLogger("IntegratedSkinService");
public static final byte[] skullNotFoundTexture = new byte[4096];
static {
for(int y = 0; y < 16; ++y) {
for(int x = 0; x < 64; ++x) {
int i = (y << 8) | (x << 2);
byte j = ((x + y) & 1) == 1 ? (byte)255 : 0;
skullNotFoundTexture[i] = (byte)255;
skullNotFoundTexture[i + 1] = j;
skullNotFoundTexture[i + 2] = 0;
skullNotFoundTexture[i + 3] = j;
}
}
}
public final VFile2 skullsDirectory;
public final Map<EaglercraftUUID,SkinPacketVersionCache> playerSkins = new HashMap<>();
public final Map<String,CustomSkullData> customSkulls = new HashMap<>();
private long lastFlush = 0l;
public IntegratedSkinService(VFile2 skullsDirectory) {
this.skullsDirectory = skullsDirectory;
}
public void processLoginPacket(byte[] packetData, EntityPlayerMP sender, int protocolVers) {
try {
IntegratedSkinPackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this, protocolVers);
} catch (IOException e) {
logger.error("Invalid skin data packet recieved from player {}!", sender.getName());
logger.error(e);
sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin data packet recieved!");
}
}
public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, EntityPlayerMP sender) {
SkinPacketVersionCache playerSkin = playerSkins.get(searchUUID);
GameMessagePacket toSend = null;
if(playerSkin != null) {
toSend = playerSkin.get(sender.playerNetServerHandler.getEaglerMessageProtocol());
}else {
toSend = IntegratedSkinPackets.makePresetResponse(searchUUID);
}
sender.playerNetServerHandler.sendEaglerMessage(toSend);
}
public void processPacketGetOtherSkin(EaglercraftUUID searchUUID, String urlStr, EntityPlayerMP sender) {
urlStr = urlStr.toLowerCase();
GameMessagePacket playerSkin;
if(!urlStr.startsWith("eagler://")) {
playerSkin = new SPacketOtherSkinPresetEAG(searchUUID.msb, searchUUID.lsb, 0);
}else {
urlStr = urlStr.substring(9);
if(urlStr.contains(VFile2.pathSeperator)) {
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 = EagRuntime.steadyTimeMillis();
}
playerSkin = sk.getSkinPacket(searchUUID, sender.playerNetServerHandler.getEaglerMessageProtocol());
}
}
sender.playerNetServerHandler.sendEaglerMessage(playerSkin);
}
public void processPacketPlayerSkin(EaglercraftUUID clientUUID, SkinPacketVersionCache generatedPacket, int skinModel) {
playerSkins.put(clientUUID, generatedPacket);
}
public void unregisterPlayer(EaglercraftUUID clientUUID) {
playerSkins.remove(clientUUID);
}
public void processPacketInstallNewSkin(byte[] skullData, EntityPlayerMP sender) {
if(!sender.canCommandSenderUseCommand(2, "give")) {
ChatComponentTranslation cc = new ChatComponentTranslation("command.skull.nopermission");
cc.getChatStyle().setColor(EnumChatFormatting.RED);
sender.addChatMessage(cc);
return;
}
String fileName = "eagler://" + installNewSkull(skullData);
NBTTagCompound rootTagCompound = new NBTTagCompound();
NBTTagCompound ownerTagCompound = new NBTTagCompound();
ownerTagCompound.setString("Name", "Eagler");
ownerTagCompound.setString("Id", EaglercraftUUID.nameUUIDFromBytes((("EaglerSkullUUID:" + fileName).getBytes(StandardCharsets.UTF_8))).toString());
NBTTagCompound propertiesTagCompound = new NBTTagCompound();
NBTTagList texturesTagList = new NBTTagList();
NBTTagCompound texturesTagCompound = new NBTTagCompound();
String texturesProp = "{\"textures\":{\"SKIN\":{\"url\":\"" + fileName + "\",\"metadata\":{\"model\":\"default\"}}}}";
texturesTagCompound.setString("Value", Base64.encodeBase64String(texturesProp.getBytes(StandardCharsets.UTF_8)));
texturesTagList.appendTag(texturesTagCompound);
propertiesTagCompound.setTag("textures", texturesTagList);
ownerTagCompound.setTag("Properties", propertiesTagCompound);
rootTagCompound.setTag("SkullOwner", ownerTagCompound);
NBTTagCompound displayTagCompound = new NBTTagCompound();
displayTagCompound.setString("Name", EnumChatFormatting.RESET + "Custom Eaglercraft Skull");
NBTTagList loreList = new NBTTagList();
loreList.appendTag(new NBTTagString(EnumChatFormatting.GRAY + (fileName.length() > 24 ? (fileName.substring(0, 22) + "...") : fileName)));
displayTagCompound.setTag("Lore", loreList);
rootTagCompound.setTag("display", displayTagCompound);
ItemStack stack = new ItemStack(Items.skull, 1, 3);
stack.setTagCompound(rootTagCompound);
boolean flag = sender.inventory.addItemStackToInventory(stack);
if (flag) {
sender.worldObj.playSoundAtEntity(sender, "random.pop", 0.2F,
((sender.getRNG().nextFloat() - sender.getRNG().nextFloat()) * 0.7F + 1.0F)
* 2.0F);
sender.inventoryContainer.detectAndSendChanges();
}
sender.addChatMessage(new ChatComponentTranslation("command.skull.feedback", fileName));
}
private static final String hex = "0123456789abcdef";
public String installNewSkull(byte[] skullData) {
// set to 16384 to save a full 64x64 skin
if(skullData.length > 4096) {
byte[] tmp = skullData;
skullData = new byte[4096];
System.arraycopy(tmp, 0, skullData, 0, 4096);
}
SHA1Digest sha = new SHA1Digest();
sha.update(skullData, 0, skullData.length);
byte[] hash = new byte[20];
sha.doFinal(hash, 0);
char[] hashText = new char[40];
for(int i = 0; i < 20; ++i) {
hashText[i << 1] = hex.charAt((hash[i] & 0xF0) >> 4);
hashText[(i << 1) + 1] = hex.charAt(hash[i] & 0x0F);
}
String str = "skin-" + new String(hashText) + ".bmp";
customSkulls.put(str, new CustomSkullData(str, skullData));
WorldsDB.newVFile(skullsDirectory, str).setAllBytes(skullData);
return str;
}
private CustomSkullData loadCustomSkull(String urlStr) {
byte[] data = WorldsDB.newVFile(skullsDirectory, urlStr).getAllBytes();
if(data == null) {
return new CustomSkullData(urlStr, skullNotFoundTexture);
}else {
return new CustomSkullData(urlStr, data);
}
}
public void flushCache() {
long cur = EagRuntime.steadyTimeMillis();
if(cur - lastFlush > 300000l) {
lastFlush = cur;
Iterator<CustomSkullData> customSkullsItr = customSkulls.values().iterator();
while(customSkullsItr.hasNext()) {
if(cur - customSkullsItr.next().lastHit > 900000l) {
customSkullsItr.remove();
}
}
}
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2025 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
public class IntegratedTexturePackets {
public static PlayerTextureData handleTextureData(byte[] skinV1, byte[] capeV1) {
int skinId;
byte[] skinTextureDataV3;
eagler: {
if (skinV1 != null && skinV1.length > 0) {
int packetType = (int)skinV1[0] & 0xFF;
switch (packetType) {
case 0x01:
if (skinV1.length == 5) {
skinId = ((skinV1[1] & 0x7F) << 24) | ((skinV1[2] & 0xFF) << 16)
| ((skinV1[3] & 0xFF) << 8) | (skinV1[4] & 0xFF);
skinTextureDataV3 = null;
break eagler;
}
break;
case 0x02:
if (skinV1.length == 16386) {
skinId = -(Math.min((int) skinV1[1] & 0x7F, 0x7E) | 0x80) - 1;
skinTextureDataV3 = new byte[16384];
System.arraycopy(skinV1, 2, skinTextureDataV3, 0, 16384);
break eagler;
}
break;
default:
break;
}
}
skinId = 0;
skinTextureDataV3 = null;
}
int capeId;
byte[] capeTextureData;
eagler: {
if (capeV1 != null && capeV1.length > 0) {
int packetType = (int)capeV1[0] & 0xFF;
switch (packetType) {
case 0x01:
if(capeV1.length == 5) {
capeId = ((capeV1[1] & 0x7F) << 24) | ((capeV1[2] & 0xFF) << 16)
| ((capeV1[3] & 0xFF) << 8) | (capeV1[4] & 0xFF);
capeTextureData = null;
break eagler;
}
break;
case 0x02:
if (capeV1.length == 1174) {
capeId = -1;
capeTextureData = new byte[1173];
System.arraycopy(capeV1, 1, capeTextureData, 0, 1173);
break eagler;
}
break;
default:
break;
}
}
capeId = 0;
capeTextureData = null;
}
return new PlayerTextureData(skinId, skinTextureDataV3, null, capeId, capeTextureData);
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2025 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
import java.nio.charset.StandardCharsets;
import net.lax1dude.eaglercraft.v1_8.Base64;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherTexturesV5EAG;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerPlayerList;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
public class IntegratedTextureService {
private final EaglerPlayerList playerList;
private final CustomSkullLoader skullHandler;
public IntegratedTextureService(EaglerPlayerList playerList, VFile2 file) {
this.playerList = playerList;
this.skullHandler = new CustomSkullLoader(file);
}
public void handleRequestPlayerSkin(EntityPlayerMP requester, EaglercraftUUID uuid) {
EntityPlayerMP target = playerList.getPlayerByUUID(uuid);
if (target != null && target.textureData != null) {
requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getSkin(uuid.msb, uuid.lsb,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
} else {
requester.playerNetServerHandler.sendEaglerMessage(
new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, (uuid.hashCode() & 1) != 0 ? 1 : 0));
}
}
public void handleRequestPlayerCape(EntityPlayerMP requester, EaglercraftUUID uuid) {
EntityPlayerMP target = playerList.getPlayerByUUID(uuid);
if (target != null && target.textureData != null) {
requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getCape(uuid.msb, uuid.lsb,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
} else {
requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherCapePresetEAG(uuid.msb, uuid.lsb, 0));
}
}
public void handleRequestPlayerSkinV5(EntityPlayerMP requester, int requestId, EaglercraftUUID uuid) {
EntityPlayerMP target = playerList.getPlayerByUUID(uuid);
if (target != null && target.textureData != null) {
requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getSkinV5(requestId,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
} else {
requester.playerNetServerHandler.sendEaglerMessage(
new SPacketOtherSkinPresetV5EAG(requestId, (uuid.hashCode() & 1) != 0 ? 1 : 0));
}
}
public void handleRequestPlayerCapeV5(EntityPlayerMP requester, int requestId, EaglercraftUUID uuid) {
EntityPlayerMP target = playerList.getPlayerByUUID(uuid);
if (target != null && target.textureData != null) {
requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getCapeV5(requestId,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
} else {
requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherCapePresetV5EAG(requestId, 0));
}
}
public void handleRequestPlayerTexturesV5(EntityPlayerMP requester, int requestId, EaglercraftUUID uuid) {
EntityPlayerMP target = playerList.getPlayerByUUID(uuid);
if (target != null && target.textureData != null) {
requester.playerNetServerHandler.sendEaglerMessage(target.textureData.getTexturesV5(requestId,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
} else {
requester.playerNetServerHandler
.sendEaglerMessage(new SPacketOtherTexturesV5EAG(requestId, 0, null, 0, null));
}
}
public void handleRequestSkinByURL(EntityPlayerMP requester, EaglercraftUUID uuid, String url) {
url = url.toLowerCase();
if (url.startsWith("eagler://")) {
url = url.substring(9);
if (!url.contains(VFile2.pathSeperator)) {
CustomSkullData skull = skullHandler.loadSkullData(url);
if (skull != null) {
requester.playerNetServerHandler.sendEaglerMessage(skull.getSkin(uuid.msb, uuid.lsb,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
return;
}
}
}
requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(uuid.msb, uuid.lsb, 0));
}
public void handleRequestSkinByURLV5(EntityPlayerMP requester, int requestId, String url) {
url = url.toLowerCase();
if (url.startsWith("eagler://")) {
url = url.substring(9);
if (!url.contains(VFile2.pathSeperator)) {
CustomSkullData skull = skullHandler.loadSkullData(url);
if (skull != null) {
requester.playerNetServerHandler.sendEaglerMessage(skull.getSkinV5(requestId,
requester.playerNetServerHandler.getEaglerMessageProtocol()));
return;
}
}
}
requester.playerNetServerHandler.sendEaglerMessage(new SPacketOtherSkinPresetV5EAG(requestId, 0));
}
public void handleInstallNewSkin(EntityPlayerMP requester, byte[] skullData) {
if (!requester.canCommandSenderUseCommand(2, "give")) {
ChatComponentTranslation cc = new ChatComponentTranslation("command.skull.nopermission");
cc.getChatStyle().setColor(EnumChatFormatting.RED);
requester.addChatMessage(cc);
return;
}
String fileName = "eagler://" + skullHandler.installNewSkull(skullData);
NBTTagCompound rootTagCompound = new NBTTagCompound();
NBTTagCompound ownerTagCompound = new NBTTagCompound();
ownerTagCompound.setString("Name", "Eagler");
ownerTagCompound.setString("Id", EaglercraftUUID.nameUUIDFromBytes((("EaglerSkullUUID:" + fileName).getBytes(StandardCharsets.UTF_8))).toString());
NBTTagCompound propertiesTagCompound = new NBTTagCompound();
NBTTagList texturesTagList = new NBTTagList();
NBTTagCompound texturesTagCompound = new NBTTagCompound();
String texturesProp = "{\"textures\":{\"SKIN\":{\"url\":\"" + fileName + "\",\"metadata\":{\"model\":\"default\"}}}}";
texturesTagCompound.setString("Value", Base64.encodeBase64String(texturesProp.getBytes(StandardCharsets.UTF_8)));
texturesTagList.appendTag(texturesTagCompound);
propertiesTagCompound.setTag("textures", texturesTagList);
ownerTagCompound.setTag("Properties", propertiesTagCompound);
rootTagCompound.setTag("SkullOwner", ownerTagCompound);
NBTTagCompound displayTagCompound = new NBTTagCompound();
displayTagCompound.setString("Name", EnumChatFormatting.RESET + "Custom Eaglercraft Skull");
NBTTagList loreList = new NBTTagList();
loreList.appendTag(new NBTTagString(EnumChatFormatting.GRAY + (fileName.length() > 24 ? (fileName.substring(0, 22) + "...") : fileName)));
displayTagCompound.setTag("Lore", loreList);
rootTagCompound.setTag("display", displayTagCompound);
ItemStack stack = new ItemStack(Items.skull, 1, 3);
stack.setTagCompound(rootTagCompound);
boolean flag = requester.inventory.addItemStackToInventory(stack);
if (flag) {
requester.worldObj.playSoundAtEntity(requester, "random.pop", 0.2F,
((requester.getRNG().nextFloat() - requester.getRNG().nextFloat()) * 0.7F + 1.0F)
* 2.0F);
requester.inventoryContainer.detectAndSendChanges();
}
requester.addChatMessage(new ChatComponentTranslation("command.skull.feedback", fileName));
}
public void flushCache() {
skullHandler.flushCache();
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2025 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.skins;
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.SPacketOtherCapeCustomEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapeCustomV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV3EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinCustomV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherTexturesV5EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
public class PlayerTextureData {
private int skinId;
private byte[] skinTextureDataV3;
private byte[] skinTextureDataV4;
private int capeId;
private byte[] capeTextureData;
public PlayerTextureData(int skinId, byte[] skinTextureDataV3, byte[] skinTextureDataV4, int capeId,
byte[] capeTextureData) {
this.skinId = skinId;
this.skinTextureDataV3 = skinTextureDataV3;
this.skinTextureDataV4 = skinTextureDataV4;
this.capeId = capeId;
this.capeTextureData = capeTextureData;
}
private byte[] getTextureV3() {
if (skinTextureDataV3 != null) {
return skinTextureDataV3;
} else {
return skinTextureDataV3 = SkinPacketVersionCache.convertToV3Raw(skinTextureDataV4);
}
}
private byte[] getTextureV4() {
if (skinTextureDataV4 != null) {
return skinTextureDataV4;
} else {
return skinTextureDataV4 = SkinPacketVersionCache.convertToV4Raw(skinTextureDataV3);
}
}
public GameMessagePacket getSkin(long uuidMost, long uuidLeast, GamePluginMessageProtocol protocol) {
switch(protocol) {
case V3:
if (skinId < 0) {
return new SPacketOtherSkinCustomV3EAG(uuidMost, uuidLeast, -skinId + 1, getTextureV3());
} else {
return new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, skinId);
}
case V4:
if (skinId < 0) {
return new SPacketOtherSkinCustomV4EAG(uuidMost, uuidLeast, -skinId + 1, getTextureV4());
} else {
return new SPacketOtherSkinPresetEAG(uuidMost, uuidLeast, skinId);
}
default:
throw new IllegalStateException();
}
}
public GameMessagePacket getSkinV5(int requestId, GamePluginMessageProtocol protocol) {
if(protocol.ver >= 5) {
if (skinId < 0) {
return new SPacketOtherSkinCustomV5EAG(requestId, -skinId + 1, getTextureV4());
} else {
return new SPacketOtherSkinPresetV5EAG(requestId, skinId);
}
}else {
throw new IllegalStateException();
}
}
public GameMessagePacket getCape(long uuidMost, long uuidLeast, GamePluginMessageProtocol protocol) {
if(protocol.ver <= 4) {
if (capeId < 0) {
return new SPacketOtherCapeCustomEAG(uuidMost, uuidLeast, capeTextureData);
} else {
return new SPacketOtherCapePresetEAG(uuidMost, uuidLeast, capeId);
}
}else {
throw new IllegalStateException();
}
}
public GameMessagePacket getCapeV5(int requestId, GamePluginMessageProtocol protocol) {
if(protocol.ver >= 5) {
if (capeId < 0) {
return new SPacketOtherCapeCustomV5EAG(requestId, capeTextureData);
} else {
return new SPacketOtherCapePresetV5EAG(requestId, capeId);
}
}else {
throw new IllegalStateException();
}
}
public GameMessagePacket getTexturesV5(int requestId, GamePluginMessageProtocol protocol) {
if(protocol.ver >= 5) {
return new SPacketOtherTexturesV5EAG(requestId, skinId, skinId < 0 ? getTextureV4() : null, capeId,
capeId < 0 ? capeTextureData : null);
}else {
throw new IllegalStateException();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2022-2025 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
@ -31,6 +31,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf;
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
import net.lax1dude.eaglercraft.v1_8.socket.CompressionNotSupportedException;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.message.InjectedMessageController;
import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.minecraft.network.EnumConnectionState;
@ -53,6 +54,7 @@ public class IntegratedServerPlayerNetworkManager {
private int debugPacketCounter = 0;
private final List<byte[]> recievedPacketBuffer = new LinkedList<>();
private final boolean enableSendCompression;
protected InjectedMessageController injectedController = null;
private boolean firstPacket = true;
@ -70,6 +72,10 @@ public class IntegratedServerPlayerNetworkManager {
this.playerChannel = playerChannel;
this.enableSendCompression = !SingleplayerServerController.PLAYER_CHANNEL.equals(playerChannel);
}
public void setInjectedMessageController(InjectedMessageController controller) {
injectedController = controller;
}
public void connect() {
fragmentedPacket.clear();
@ -160,6 +166,10 @@ public class IntegratedServerPlayerNetworkManager {
++debugPacketCounter;
try {
if(injectedController != null && injectedController.handlePacket(fullData, 0)) {
continue;
}
ByteBuf nettyBuffer = Unpooled.buffer(fullData, fullData.length);
nettyBuffer.writerIndex(fullData.length);
PacketBuffer input = new PacketBuffer(nettyBuffer);
@ -274,6 +284,66 @@ public class IntegratedServerPlayerNetworkManager {
}
}
public void injectRawFrame(byte[] data) {
if(!isChannelOpen()) {
return;
}
if(enableSendCompression) {
int len = data.length;
if(len > compressionThreshold) {
if(compressedPacketTmp == null || compressedPacketTmp.length < len) {
compressedPacketTmp = new byte[len];
}
int cmpLen;
try {
cmpLen = EaglerZLIB.deflateFull(data, 0, len, compressedPacketTmp, 0, compressedPacketTmp.length);
}catch(IOException ex) {
logger.error("Failed to compress injected frame!");
logger.error(ex);
return;
}
byte[] compressedData = new byte[5 + cmpLen];
compressedData[0] = (byte)2;
compressedData[1] = (byte)((len >>> 24) & 0xFF);
compressedData[2] = (byte)((len >>> 16) & 0xFF);
compressedData[3] = (byte)((len >>> 8) & 0xFF);
compressedData[4] = (byte)(len & 0xFF);
System.arraycopy(compressedPacketTmp, 0, compressedData, 5, cmpLen);
if(compressedData.length > fragmentSize) {
int fragmentSizeN1 = fragmentSize - 1;
for (int j = 1; j < compressedData.length; j += fragmentSizeN1) {
byte[] fragData = new byte[((j + fragmentSizeN1 > (compressedData.length - 1)) ? ((compressedData.length - 1) % fragmentSizeN1) : fragmentSizeN1) + 1];
System.arraycopy(compressedData, j, fragData, 1, fragData.length - 1);
fragData[0] = (j + fragmentSizeN1 < compressedData.length) ? (byte) 1 : (byte) 2;
ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, fragData));
}
}else {
ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, compressedData));
}
}else {
int fragmentSizeN1 = fragmentSize - 1;
if(len > fragmentSizeN1) {
int idx = 0;
do {
int readLen = len > fragmentSizeN1 ? fragmentSizeN1 : len;
byte[] frag = new byte[readLen + 1];
System.arraycopy(data, idx, frag, 1, readLen);
idx += readLen;
len -= readLen;
frag[0] = len == 0 ? (byte)0 : (byte)1;
ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, frag));
}while(len > 0);
}else {
byte[] bytes = new byte[len + 1];
System.arraycopy(data, 0, bytes, 1, len);
ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, bytes));
}
}
}else {
ServerPlatformSingleplayer.sendPacket(new IPCPacketData(playerChannel, data));
}
}
public void setNetHandler(INetHandler nethandler) {
this.nethandler = nethandler;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer;
import net.minecraft.network.NetHandlerPlayServer;
public abstract class ServerMessageHandler implements GameMessageHandler {
protected final NetHandlerPlayServer netHandler;
protected final EaglerMinecraftServer server;
public ServerMessageHandler(NetHandlerPlayServer netHandler) {
this.netHandler = netHandler;
this.server = (EaglerMinecraftServer)netHandler.serverController;
}
public static ServerMessageHandler createServerHandler(int version, NetHandlerPlayServer netHandler) {
switch(version) {
case 3:
return new ServerV3MessageHandler(netHandler);
case 4:
return new ServerV4MessageHandler(netHandler);
case 5:
return new ServerV5MessageHandler(netHandler);
default:
throw new UnsupportedOperationException();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
* Copyright (c) 2024-2025 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
@ -17,58 +17,57 @@
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;
public class ServerV3MessageHandler implements GameMessageHandler {
private final NetHandlerPlayServer netHandler;
private final EaglerMinecraftServer server;
public class ServerV3MessageHandler extends ServerMessageHandler {
public ServerV3MessageHandler(NetHandlerPlayServer netHandler) {
this.netHandler = netHandler;
this.server = (EaglerMinecraftServer)netHandler.serverController;
super(netHandler);
}
public void handleClient(CPacketGetOtherCapeEAG packet) {
server.getCapeService().processGetOtherCape(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
server.getTextureService().handleRequestPlayerCape(netHandler.playerEntity,
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
}
public void handleClient(CPacketGetOtherSkinEAG packet) {
server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
server.getTextureService().handleRequestPlayerSkin(netHandler.playerEntity,
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
}
public void handleClient(CPacketGetSkinByURLEAG packet) {
server.getSkinService().processPacketGetOtherSkin(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url, netHandler.playerEntity);
server.getTextureService().handleRequestSkinByURL(netHandler.playerEntity,
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.url);
}
public void handleClient(CPacketInstallSkinSPEAG packet) {
server.getSkinService().processPacketInstallNewSkin(packet.customSkin, netHandler.playerEntity);
server.getTextureService().handleInstallNewSkin(netHandler.playerEntity, packet.customSkin);
}
public void handleClient(CPacketVoiceSignalConnectEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
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);
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 {
if (voiceSvc != null) {
if (packet.isPeerType) {
voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), netHandler.playerEntity);
} else {
voiceSvc.handleVoiceSignalPacketTypeDisconnect(netHandler.playerEntity);
}
}
@ -76,15 +75,17 @@ public class ServerV3MessageHandler implements GameMessageHandler {
public void handleClient(CPacketVoiceSignalICEEAG packet) {
IntegratedVoiceService voiceSvc = server.getVoiceService();
if(voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeICE(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.ice, netHandler.playerEntity);
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);
if (voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeRequest(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
netHandler.playerEntity);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
* Copyright (c) 2024-2025 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
@ -17,52 +17,21 @@
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.WrongPacketException;
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;
public class ServerV4MessageHandler implements GameMessageHandler {
private final NetHandlerPlayServer netHandler;
private final EaglerMinecraftServer server;
public class ServerV4MessageHandler extends ServerV3MessageHandler {
public ServerV4MessageHandler(NetHandlerPlayServer netHandler) {
this.netHandler = netHandler;
this.server = (EaglerMinecraftServer)netHandler.serverController;
super(netHandler);
}
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) {
throw new WrongPacketException();
}
public void handleClient(CPacketVoiceSignalDisconnectV4EAG packet) {
@ -74,30 +43,19 @@ public class ServerV4MessageHandler implements GameMessageHandler {
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);
if (voiceSvc != null) {
voiceSvc.handleVoiceSignalPacketTypeDisconnectPeer(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 {
EntityPlayerMP player = server.getConfigurationManager()
.getPlayerByUUID(new EaglercraftUUID(packet.playerUUIDMost, packet.playerUUIDLeast));
if (player != null && player.clientBrandUUID != null) {
netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId,
player.clientBrandUUID.msb, player.clientBrandUUID.lsb));
} else {
netHandler.sendEaglerMessage(new SPacketOtherPlayerClientUUIDV4EAG(packet.requestId, 0l, 0l));
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2024-2025 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.
*
*/
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.WrongPacketException;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*;
import net.minecraft.network.NetHandlerPlayServer;
public class ServerV5MessageHandler extends ServerV4MessageHandler {
public ServerV5MessageHandler(NetHandlerPlayServer netHandler) {
super(netHandler);
}
public void handleClient(CPacketGetOtherCapeEAG packet) {
throw new WrongPacketException();
}
public void handleClient(CPacketGetOtherSkinEAG packet) {
throw new WrongPacketException();
}
public void handleClient(CPacketGetSkinByURLEAG packet) {
throw new WrongPacketException();
}
public void handleClient(CPacketGetOtherCapeV5EAG packet) {
server.getTextureService().handleRequestPlayerCapeV5(netHandler.playerEntity, packet.requestId,
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
}
public void handleClient(CPacketGetOtherSkinV5EAG packet) {
server.getTextureService().handleRequestPlayerSkinV5(netHandler.playerEntity, packet.requestId,
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
}
public void handleClient(CPacketGetSkinByURLV5EAG packet) {
server.getTextureService().handleRequestSkinByURLV5(netHandler.playerEntity, packet.requestId, packet.url);
}
public void handleClient(CPacketGetOtherTexturesV5EAG packet) {
server.getTextureService().handleRequestPlayerTexturesV5(netHandler.playerEntity, packet.requestId,
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
}
}

View File

@ -32,6 +32,7 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
import net.lax1dude.eaglercraft.v1_8.voice.ExpiringSet;
import net.minecraft.entity.player.EntityPlayerMP;
//TODO: Rewrite to be more like EaglerXServer
public class IntegratedVoiceService {
public static final Logger logger = LogManager.getLogger("IntegratedVoiceService");
@ -151,22 +152,33 @@ public class IntegratedVoiceService {
}
GameMessagePacket v3p = null;
GameMessagePacket v4p = null;
for(EntityPlayerMP conn : voicePlayers.values()) {
if(conn.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) {
conn.playerNetServerHandler.sendEaglerMessage(v3p == null ? (v3p = new SPacketVoiceSignalConnectV3EAG(senderUuid.msb, senderUuid.lsb, true, false)) : v3p);
} else {
conn.playerNetServerHandler.sendEaglerMessage(v4p == null ? (v4p = new SPacketVoiceSignalConnectAnnounceV4EAG(senderUuid.msb, senderUuid.lsb)) : v4p);
}
}
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()));
for(EntityPlayerMP conn : voicePlayers.values()) {
EaglercraftUUID otherUuid = conn.getUniqueID();
if(conn != sender) {
if(conn.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) {
conn.playerNetServerHandler.sendEaglerMessage(v3p == null ? (v3p = new SPacketVoiceSignalConnectV3EAG(senderUuid.msb, senderUuid.lsb, true, false)) : v3p);
} else {
conn.playerNetServerHandler.sendEaglerMessage(v4p == null ? (v4p = new SPacketVoiceSignalConnectAnnounceV4EAG(senderUuid.msb, senderUuid.lsb)) : v4p);
}
}
userDatas.add(new SPacketVoiceSignalGlobalEAG.UserData(otherUuid.msb, otherUuid.lsb, conn.getName()));
}
SPacketVoiceSignalGlobalEAG packetToBroadcast = new SPacketVoiceSignalGlobalEAG(userDatas);
for (EntityPlayerMP userCon : voicePlayers.values()) {
userCon.playerNetServerHandler.sendEaglerMessage(packetToBroadcast);
}
boolean selfV3 = sender.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3;
for(EntityPlayerMP conn : voicePlayers.values()) {
EaglercraftUUID otherUuid = conn.getUniqueID();
if(conn != sender) {
if(selfV3) {
sender.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalConnectV3EAG(otherUuid.msb, otherUuid.lsb, true, false));
}else {
sender.playerNetServerHandler.sendEaglerMessage(new SPacketVoiceSignalConnectAnnounceV4EAG(otherUuid.msb, otherUuid.lsb));
}
}
}
}
public void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, byte[] str, EntityPlayerMP sender) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2023-2025 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
@ -78,6 +78,10 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana
byte[] next = recievedPacketBuffer.remove(0);
++debugPacketCounter;
try {
if(injectedController != null && injectedController.handlePacket(next, 0)) {
continue;
}
ByteBuf nettyBuffer = Unpooled.buffer(next, next.length);
nettyBuffer.writerIndex(next.length);
PacketBuffer input = new PacketBuffer(nettyBuffer);
@ -168,4 +172,14 @@ public class ClientIntegratedServerNetworkManager extends EaglercraftNetworkMana
public void clearRecieveQueue() {
recievedPacketBuffer.clear();
}
@Override
public void injectRawFrame(byte[] data) {
if(!isChannelOpen()) {
logger.error("Frame was injected on a closed connection");
return;
}
ClientPlatformSingleplayer.sendPacket(new IPCPacketData(address, data));
}
}

View File

@ -20,9 +20,7 @@ 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;
@ -73,15 +71,14 @@ public class NetHandlerSingleplayerLogin implements INetHandlerLoginClient {
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.setLANInfo(p);
NetHandlerPlayClient netHandler = new NetHandlerPlayClient(this.mc, this.previousGuiScreen, this.networkManager,
var1.getProfile(), mp);
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))));
this.networkManager.sendPacket(new C17PacketCustomPayload("EAG|MyUpdCert-1.8",
new PacketBuffer(Unpooled.buffer(b, b.length).writerIndex(b.length))));
}
}