mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #28 - Added capes, voice chat, FNAW skins, and fixes
This commit is contained in:
@ -140,4 +140,7 @@ public class GuiScreenAddRelay extends GuiScreen {
|
||||
super.drawScreen(par1, par2, par3);
|
||||
}
|
||||
|
||||
public boolean blockPTTKey() {
|
||||
return this.serverName.isFocused() || this.serverAddress.isFocused();
|
||||
}
|
||||
}
|
@ -117,7 +117,7 @@ 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()));
|
||||
networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ 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()));
|
||||
this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket()));
|
||||
}
|
||||
try {
|
||||
this.networkManager.processReceivedPackets();
|
||||
|
@ -195,4 +195,8 @@ public class GuiShareToLan extends GuiScreen {
|
||||
super.updateScreen();
|
||||
this.codeTextField.updateCursorCounter();
|
||||
}
|
||||
|
||||
public boolean blockPTTKey() {
|
||||
return this.codeTextField.isFocused();
|
||||
}
|
||||
}
|
@ -361,6 +361,9 @@ public class EaglerIntegratedServerWorker {
|
||||
case IPCPacket17ConfigureLAN.ID: {
|
||||
|
||||
IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN)ipc;
|
||||
if(!pkt.iceServers.isEmpty() && ServerPlatformSingleplayer.getClientConfigAdapter().isAllowVoiceClient()) {
|
||||
currentProcess.enableVoice(pkt.iceServers.toArray(new String[pkt.iceServers.size()]));
|
||||
}
|
||||
currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats); // don't use iceServers
|
||||
|
||||
break;
|
||||
|
@ -16,7 +16,9 @@ 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.voice.IntegratedVoiceService;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
@ -45,6 +47,8 @@ public class EaglerMinecraftServer extends MinecraftServer {
|
||||
protected boolean paused;
|
||||
protected EaglerSaveHandler saveHandler;
|
||||
protected IntegratedSkinService skinService;
|
||||
protected IntegratedCapeService capeService;
|
||||
protected IntegratedVoiceService voiceService;
|
||||
|
||||
private long lastTPSUpdate = 0l;
|
||||
|
||||
@ -62,6 +66,8 @@ public class EaglerMinecraftServer extends MinecraftServer {
|
||||
Bootstrap.register();
|
||||
this.saveHandler = new EaglerSaveHandler(savesDir, world);
|
||||
this.skinService = new IntegratedSkinService(new VFile2(saveHandler.getWorldDirectory(), "eagler/skulls"));
|
||||
this.capeService = new IntegratedCapeService();
|
||||
this.voiceService = null;
|
||||
this.setServerOwner(owner);
|
||||
logger.info("server owner: " + owner);
|
||||
this.setDemo(demo);
|
||||
@ -76,6 +82,27 @@ public class EaglerMinecraftServer extends MinecraftServer {
|
||||
return skinService;
|
||||
}
|
||||
|
||||
public IntegratedCapeService getCapeService() {
|
||||
return capeService;
|
||||
}
|
||||
|
||||
public IntegratedVoiceService getVoiceService() {
|
||||
return voiceService;
|
||||
}
|
||||
|
||||
public void enableVoice(String[] iceServers) {
|
||||
if(iceServers != null) {
|
||||
if(voiceService != null) {
|
||||
voiceService.changeICEServers(iceServers);
|
||||
}else {
|
||||
voiceService = new IntegratedVoiceService(iceServers);
|
||||
for(EntityPlayerMP player : getConfigurationManager().func_181057_v()) {
|
||||
voiceService.handlePlayerLoggedIn(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setBaseServerProperties(EnumDifficulty difficulty, GameType gamemode) {
|
||||
this.difficulty = difficulty;
|
||||
this.gamemode = gamemode;
|
||||
|
@ -43,6 +43,8 @@ public class EaglerPlayerList extends ServerConfigurationManager {
|
||||
|
||||
public void playerLoggedOut(EntityPlayerMP playerIn) {
|
||||
super.playerLoggedOut(playerIn);
|
||||
((EaglerMinecraftServer)getServerInstance()).skinService.unregisterPlayer(playerIn.getUniqueID());
|
||||
EaglerMinecraftServer svr = (EaglerMinecraftServer)getServerInstance();
|
||||
svr.skinService.unregisterPlayer(playerIn.getUniqueID());
|
||||
svr.capeService.unregisterPlayer(playerIn.getUniqueID());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,110 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
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));
|
||||
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);
|
||||
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, IntegratedCapePackets.makePresetResponse(clientUUID, 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
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.netty.Unpooled;
|
||||
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.
|
||||
*
|
||||
* 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 IntegratedCapeService {
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("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!");
|
||||
}
|
||||
}
|
||||
|
||||
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, byte[] capePacket) {
|
||||
capesCache.put(playerUUID, capePacket);
|
||||
}
|
||||
|
||||
public void processGetOtherCape(EaglercraftUUID searchUUID, EntityPlayerMP sender) {
|
||||
byte[] maybeCape = capesCache.get(searchUUID);
|
||||
if(maybeCape == null) {
|
||||
maybeCape = IntegratedCapePackets.makePresetResponse(searchUUID, 0);
|
||||
}
|
||||
sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(maybeCape, maybeCape.length).writerIndex(maybeCape.length))));
|
||||
}
|
||||
|
||||
public void unregisterPlayer(EaglercraftUUID playerUUID) {
|
||||
synchronized(capesCache) {
|
||||
capesCache.remove(playerUUID);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,255 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.sp.server.voice;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
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.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.
|
||||
*
|
||||
* 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 IntegratedVoiceService {
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("IntegratedVoiceService");
|
||||
|
||||
public static final String CHANNEL = "EAG|Voice-1.8";
|
||||
|
||||
private byte[] 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);
|
||||
}
|
||||
|
||||
public void changeICEServers(String[] iceServers) {
|
||||
iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers);
|
||||
}
|
||||
|
||||
private static class VoicePair {
|
||||
|
||||
private final EaglercraftUUID uuid1;
|
||||
private final EaglercraftUUID uuid2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid1.hashCode() ^ uuid2.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
VoicePair other = (VoicePair) obj;
|
||||
return (uuid1.equals(other.uuid1) && uuid2.equals(other.uuid2))
|
||||
|| (uuid1.equals(other.uuid2) && uuid2.equals(other.uuid1));
|
||||
}
|
||||
|
||||
private VoicePair(EaglercraftUUID uuid1, EaglercraftUUID uuid2) {
|
||||
this.uuid1 = uuid1;
|
||||
this.uuid2 = uuid2;
|
||||
}
|
||||
|
||||
private boolean anyEquals(EaglercraftUUID uuid) {
|
||||
return uuid1.equals(uuid) || uuid2.equals(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedIn(EntityPlayerMP player) {
|
||||
player.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(
|
||||
Unpooled.buffer(iceServersPacket, iceServersPacket.length).writerIndex(iceServersPacket.length))));
|
||||
}
|
||||
|
||||
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) {
|
||||
EaglercraftUUID senderUUID = sender.getUniqueID();
|
||||
if (senderUUID.equals(player))
|
||||
return; // prevent duplicates
|
||||
if (!voicePlayers.containsKey(senderUUID))
|
||||
return;
|
||||
EntityPlayerMP targetPlayerCon = voicePlayers.get(player);
|
||||
if (targetPlayerCon == null)
|
||||
return;
|
||||
VoicePair newPair = new VoicePair(player, senderUUID);
|
||||
if (voicePairs.contains(newPair))
|
||||
return; // already paired
|
||||
ExpiringSet<EaglercraftUUID> senderRequestSet = voiceRequests.get(senderUUID);
|
||||
if (senderRequestSet == null) {
|
||||
voiceRequests.put(senderUUID, senderRequestSet = new ExpiringSet<>(2000));
|
||||
}
|
||||
if (!senderRequestSet.add(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if other has requested earlier
|
||||
ExpiringSet<EaglercraftUUID> theSet;
|
||||
if ((theSet = voiceRequests.get(player)) != null && theSet.contains(senderUUID)) {
|
||||
theSet.remove(senderUUID);
|
||||
if (theSet.isEmpty())
|
||||
voiceRequests.remove(player);
|
||||
senderRequestSet.remove(player);
|
||||
if (senderRequestSet.isEmpty())
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) {
|
||||
if (voicePlayers.containsKey(sender.getUniqueID())) {
|
||||
return;
|
||||
}
|
||||
boolean hasNoOtherPlayers = voicePlayers.isEmpty();
|
||||
voicePlayers.put(sender.getUniqueID(), sender);
|
||||
if (hasNoOtherPlayers) {
|
||||
return;
|
||||
}
|
||||
byte[] packetToBroadcast = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
|
||||
for (EntityPlayerMP userCon : voicePlayers.values()) {
|
||||
userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled
|
||||
.buffer(packetToBroadcast, packetToBroadcast.length).writerIndex(packetToBroadcast.length))));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, String str, EntityPlayerMP sender) {
|
||||
VoicePair pair = new VoicePair(player, sender.getUniqueID());
|
||||
EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
|
||||
if (pass != null) {
|
||||
pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
|
||||
IntegratedVoiceSignalPackets.makeVoiceSignalPacketICE(sender.getUniqueID(), str)));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDesc(EaglercraftUUID player, String str, EntityPlayerMP sender) {
|
||||
VoicePair pair = new VoicePair(player, sender.getUniqueID());
|
||||
EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
|
||||
if (pass != null) {
|
||||
pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL,
|
||||
IntegratedVoiceSignalPackets.makeVoiceSignalPacketDesc(sender.getUniqueID(), str)));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID player, EntityPlayerMP sender) {
|
||||
if (player != null) {
|
||||
if (!voicePlayers.containsKey(player)) {
|
||||
return;
|
||||
}
|
||||
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)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removeUser(sender.getUniqueID());
|
||||
}
|
||||
}
|
||||
|
||||
public void removeUser(EaglercraftUUID user) {
|
||||
if (voicePlayers.remove(user) == null) {
|
||||
return;
|
||||
}
|
||||
voiceRequests.remove(user);
|
||||
if (voicePlayers.size() > 0) {
|
||||
byte[] voicePlayersPkt = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
|
||||
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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] userDisconnectPacket = null;
|
||||
Iterator<VoicePair> pairsItr = voicePairs.iterator();
|
||||
while (pairsItr.hasNext()) {
|
||||
VoicePair voicePair = pairsItr.next();
|
||||
EaglercraftUUID target = null;
|
||||
if (voicePair.uuid1.equals(user)) {
|
||||
target = voicePair.uuid2;
|
||||
} else if (voicePair.uuid2.equals(user)) {
|
||||
target = voicePair.uuid1;
|
||||
}
|
||||
if (target != null) {
|
||||
pairsItr.remove();
|
||||
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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user