mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-28 02:48:14 -05:00
Update #51 - Protocol and FPS improvements, better workspace
This commit is contained in:
@ -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
|
||||
@ -18,13 +18,10 @@ package net.lax1dude.eaglercraft.v1_8.voice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
@ -32,32 +29,21 @@ import net.lax1dude.eaglercraft.v1_8.Keyboard;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformVoiceClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.*;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketVoiceSignalGlobalEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
public class VoiceClientController {
|
||||
|
||||
public static final String SIGNAL_CHANNEL = "EAG|Voice-1.8";
|
||||
|
||||
static final Logger logger = LogManager.getLogger("VoiceClientController");
|
||||
|
||||
private static boolean clientSupport = false;
|
||||
private static boolean serverSupport = false;
|
||||
private static Consumer<GameMessagePacket> packetSendCallback = null;
|
||||
private static int protocolVersion = -1;
|
||||
private static EnumVoiceChannelType voiceChannel = EnumVoiceChannelType.NONE;
|
||||
private static final HashSet<EaglercraftUUID> nearbyPlayers = new HashSet<>();
|
||||
private static final ExpiringSet<EaglercraftUUID> recentlyNearbyPlayers = new ExpiringSet<>(5000, uuid -> {
|
||||
if (!nearbyPlayers.contains(uuid)) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
});
|
||||
private static final Map<EaglercraftUUID, String> uuidToNameLookup = new HashMap<>(256);
|
||||
private static VoiceClientInstance voiceClient = null;
|
||||
|
||||
static EnumVoiceChannelType lastVoiceChannel = EnumVoiceChannelType.NONE;
|
||||
|
||||
public static boolean isSupported() {
|
||||
return isClientSupported() && isServerSupported();
|
||||
@ -78,202 +64,118 @@ public class VoiceClientController {
|
||||
}
|
||||
|
||||
public static void initializeVoiceClient(Consumer<GameMessagePacket> signalSendCallbackIn, int proto) {
|
||||
if (!isClientSupported()) return;
|
||||
packetSendCallback = signalSendCallbackIn;
|
||||
protocolVersion = proto;
|
||||
uuidToNameLookup.clear();
|
||||
if (getVoiceChannel() != EnumVoiceChannelType.NONE) sendInitialVoice();
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeGlobal(EaglercraftUUID[] voicePlayers, String[] voiceNames) {
|
||||
uuidToNameLookup.clear();
|
||||
for (int i = 0; i < voicePlayers.length; i++) {
|
||||
if(voiceNames != null) {
|
||||
uuidToNameLookup.put(voicePlayers[i], voiceNames[i]);
|
||||
}
|
||||
sendPacketRequestIfNeeded(voicePlayers[i]);
|
||||
handleRelease();
|
||||
if (signalSendCallbackIn != null && serverSupport) {
|
||||
voiceClient = new VoiceClientInstance(proto, signalSendCallbackIn);
|
||||
voiceClient.initialize(lastVoiceChannel);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleRelease() {
|
||||
if (voiceClient != null) {
|
||||
voiceClient.release();
|
||||
voiceClient = null;
|
||||
}
|
||||
speakingSet.clear();
|
||||
activateVoice(false);
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeGlobalNew(Collection<SPacketVoiceSignalGlobalEAG.UserData> voicePlayers) {
|
||||
boolean isGlobal = voiceChannel == EnumVoiceChannelType.GLOBAL;
|
||||
uuidToNameLookup.clear();
|
||||
for (SPacketVoiceSignalGlobalEAG.UserData player : voicePlayers) {
|
||||
EaglercraftUUID uuid = new EaglercraftUUID(player.uuidMost, player.uuidLeast);
|
||||
if(player.username != null) {
|
||||
uuidToNameLookup.put(uuid, player.username);
|
||||
}
|
||||
if (isGlobal) {
|
||||
sendPacketRequestIfNeeded(uuid);
|
||||
}
|
||||
if (voiceClient != null) {
|
||||
voiceClient.handleVoiceSignalPacketTypeGlobal(voicePlayers);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleServerDisconnect() {
|
||||
if(!isClientSupported()) return;
|
||||
if (!isClientSupported()) return;
|
||||
serverSupport = false;
|
||||
uuidToNameLookup.clear();
|
||||
for (EaglercraftUUID uuid : nearbyPlayers) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
for (EaglercraftUUID uuid : recentlyNearbyPlayers) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
nearbyPlayers.clear();
|
||||
recentlyNearbyPlayers.clear();
|
||||
Set<EaglercraftUUID> antiConcurrentModificationUUIDs = new HashSet<>(listeningSet);
|
||||
for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
activateVoice(false);
|
||||
packetSendCallback = null;
|
||||
protocolVersion = -1;
|
||||
handleRelease();
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeAllowed(boolean voiceAvailableStat, String[] servs) {
|
||||
serverSupport = voiceAvailableStat;
|
||||
PlatformVoiceClient.setICEServers(servs);
|
||||
if(isSupported()) {
|
||||
EnumVoiceChannelType ch = getVoiceChannel();
|
||||
setVoiceChannel(EnumVoiceChannelType.NONE);
|
||||
setVoiceChannel(ch);
|
||||
if(packetSendCallback != null) {
|
||||
handleRelease();
|
||||
PlatformVoiceClient.setICEServers(servs);
|
||||
if (serverSupport != voiceAvailableStat) {
|
||||
serverSupport = voiceAvailableStat;
|
||||
if (voiceAvailableStat) {
|
||||
voiceClient = new VoiceClientInstance(protocolVersion, packetSendCallback);
|
||||
voiceClient.initialize(lastVoiceChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalConnect(user, offer);
|
||||
if (voiceClient != null) {
|
||||
voiceClient.handleVoiceSignalPacketTypeConnect(user, offer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE && (voiceChannel == EnumVoiceChannelType.GLOBAL || listeningSet.contains(user))) sendPacketRequest(user);
|
||||
if (voiceClient != null) {
|
||||
voiceClient.handleVoiceSignalPacketTypeConnectAnnounce(user);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalDisconnect(user, true);
|
||||
if (voiceClient != null) {
|
||||
voiceClient.handleVoiceSignalPacketTypeDisconnect(user);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalICECandidate(user, ice);
|
||||
if (voiceClient != null) {
|
||||
voiceClient.handleVoiceSignalPacketTypeICECandidate(user, ice);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) PlatformVoiceClient.signalDescription(user, desc);
|
||||
if (voiceClient != null) {
|
||||
voiceClient.handleVoiceSignalPacketTypeDescription(user, desc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void tickVoiceClient(Minecraft mc) {
|
||||
if(!isClientSupported()) return;
|
||||
recentlyNearbyPlayers.checkForExpirations();
|
||||
speakingSet.clear();
|
||||
PlatformVoiceClient.tickVoiceClient();
|
||||
|
||||
if (getVoiceChannel() != EnumVoiceChannelType.NONE && (getVoiceStatus() == EnumVoiceChannelStatus.CONNECTING || getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED)) {
|
||||
activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey));
|
||||
|
||||
if(mc.isSingleplayer() && !LANServerController.isHostingLAN()) {
|
||||
setVoiceChannel(EnumVoiceChannelType.NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mc.theWorld != null && mc.thePlayer != null) {
|
||||
HashSet<EaglercraftUUID> seenPlayers = new HashSet<>();
|
||||
for (EntityPlayer player : mc.theWorld.playerEntities) {
|
||||
if (player == mc.thePlayer) continue;
|
||||
if (getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) updateVoicePosition(player.getUniqueID(), player.posX, player.posY + player.getEyeHeight(), player.posZ);
|
||||
int prox = 22;
|
||||
// cube
|
||||
if (Math.abs(mc.thePlayer.posX - player.posX) <= prox && Math.abs(mc.thePlayer.posY - player.posY) <= prox && Math.abs(mc.thePlayer.posZ - player.posZ) <= prox) {
|
||||
if (!uuidToNameLookup.containsKey(player.getUniqueID())) {
|
||||
uuidToNameLookup.put(player.getUniqueID(), player.getName());
|
||||
}
|
||||
if (addNearbyPlayer(player.getUniqueID())) {
|
||||
seenPlayers.add(player.getUniqueID());
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanupNearbyPlayers(seenPlayers);
|
||||
public static void tickVoiceClient() {
|
||||
if (voiceClient != null) {
|
||||
voiceClient.tickVoiceClient();
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (voiceClient.getVoiceChannel() != EnumVoiceChannelType.NONE) {
|
||||
activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey())
|
||||
&& Keyboard.isKeyDown(mc.gameSettings.voicePTTKey));
|
||||
} else {
|
||||
activateVoice(false);
|
||||
}
|
||||
speakingSet.clear();
|
||||
PlatformVoiceClient.tickVoiceClient();
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean addNearbyPlayer(EaglercraftUUID uuid) {
|
||||
recentlyNearbyPlayers.remove(uuid);
|
||||
if (nearbyPlayers.add(uuid)) {
|
||||
sendPacketRequestIfNeeded(uuid);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final void removeNearbyPlayer(EaglercraftUUID uuid) {
|
||||
if (nearbyPlayers.remove(uuid)) {
|
||||
if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return;
|
||||
if (voiceChannel == EnumVoiceChannelType.PROXIMITY) recentlyNearbyPlayers.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public static final void cleanupNearbyPlayers(HashSet<EaglercraftUUID> existingPlayers) {
|
||||
nearbyPlayers.stream().filter(ud -> !existingPlayers.contains(ud)).collect(Collectors.toSet()).forEach(VoiceClientController::removeNearbyPlayer);
|
||||
}
|
||||
|
||||
public static final void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) {
|
||||
PlatformVoiceClient.updateVoicePosition(uuid, x, y, z);
|
||||
}
|
||||
|
||||
public static void setVoiceChannel(EnumVoiceChannelType channel) {
|
||||
if (voiceChannel == channel) return;
|
||||
if (channel != EnumVoiceChannelType.NONE) PlatformVoiceClient.initializeDevices();
|
||||
if (channel == EnumVoiceChannelType.NONE) {
|
||||
for (EaglercraftUUID uuid : nearbyPlayers) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
for (EaglercraftUUID uuid : recentlyNearbyPlayers) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
nearbyPlayers.clear();
|
||||
recentlyNearbyPlayers.clear();
|
||||
Set<EaglercraftUUID> antiConcurrentModificationUUIDs = new HashSet<>(listeningSet);
|
||||
for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
sendPacketDisconnect();
|
||||
activateVoice(false);
|
||||
} else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) {
|
||||
for (EaglercraftUUID uuid : nearbyPlayers) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
for (EaglercraftUUID uuid : recentlyNearbyPlayers) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
nearbyPlayers.clear();
|
||||
recentlyNearbyPlayers.clear();
|
||||
sendPacketDisconnect();
|
||||
} else if(voiceChannel == EnumVoiceChannelType.GLOBAL) {
|
||||
Set<EaglercraftUUID> antiConcurrentModificationUUIDs = new HashSet<>(listeningSet);
|
||||
antiConcurrentModificationUUIDs.removeAll(nearbyPlayers);
|
||||
antiConcurrentModificationUUIDs.removeAll(recentlyNearbyPlayers);
|
||||
for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) {
|
||||
PlatformVoiceClient.signalDisconnect(uuid, false);
|
||||
}
|
||||
sendPacketDisconnect();
|
||||
}
|
||||
voiceChannel = channel;
|
||||
if (channel != EnumVoiceChannelType.NONE) {
|
||||
sendInitialVoice();
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendInitialVoice() {
|
||||
sendPacketConnect();
|
||||
for (EaglercraftUUID uuid : nearbyPlayers) {
|
||||
sendPacketRequest(uuid);
|
||||
if (voiceClient != null) {
|
||||
voiceClient.setVoiceChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public static EnumVoiceChannelType getVoiceChannel() {
|
||||
return voiceChannel;
|
||||
if (voiceClient != null) {
|
||||
return voiceClient.getVoiceChannel();
|
||||
} else {
|
||||
return EnumVoiceChannelType.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public static EnumVoiceChannelStatus getVoiceStatus() {
|
||||
return (!isClientSupported() || !isServerSupported()) ? EnumVoiceChannelStatus.UNAVAILABLE :
|
||||
(PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED ?
|
||||
EnumVoiceChannelStatus.CONNECTING : EnumVoiceChannelStatus.CONNECTED);
|
||||
if (voiceClient != null) {
|
||||
return voiceClient.getVoiceStatus();
|
||||
} else {
|
||||
return EnumVoiceChannelStatus.UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean talkStatus = false;
|
||||
@ -350,60 +252,29 @@ public class VoiceClientController {
|
||||
}
|
||||
|
||||
public static String getVoiceUsername(EaglercraftUUID uuid) {
|
||||
if(uuid == null) {
|
||||
return "null";
|
||||
if (voiceClient != null) {
|
||||
return voiceClient.getVoiceUsername(uuid);
|
||||
} else {
|
||||
return uuid.toString();
|
||||
}
|
||||
String ret = uuidToNameLookup.get(uuid);
|
||||
return ret == null ? uuid.toString() : ret;
|
||||
}
|
||||
|
||||
public static void sendPacketICE(EaglercraftUUID peerId, String candidate) {
|
||||
if(packetSendCallback != null) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalICEEAG(peerId.msb, peerId.lsb, candidate));
|
||||
if (voiceClient != null) {
|
||||
voiceClient.sendPacketICE(peerId, candidate);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacketDesc(EaglercraftUUID peerId, String desc) {
|
||||
if(packetSendCallback != null) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDescEAG(peerId.msb, peerId.lsb, desc));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacketDisconnect() {
|
||||
if(packetSendCallback != null) {
|
||||
if(protocolVersion <= 3) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG());
|
||||
}else {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectV4EAG());
|
||||
}
|
||||
if (voiceClient != null) {
|
||||
voiceClient.sendPacketDesc(peerId, desc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacketDisconnectPeer(EaglercraftUUID peerId) {
|
||||
if(packetSendCallback != null) {
|
||||
if(protocolVersion <= 3) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG(true, peerId.msb, peerId.lsb));
|
||||
}else {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectPeerV4EAG(peerId.msb, peerId.lsb));
|
||||
}
|
||||
if (voiceClient != null) {
|
||||
voiceClient.sendPacketDisconnectPeer(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacketConnect() {
|
||||
if(packetSendCallback != null) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalConnectEAG());
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacketRequest(EaglercraftUUID peerId) {
|
||||
if(packetSendCallback != null) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalRequestEAG(peerId.msb, peerId.lsb));
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendPacketRequestIfNeeded(EaglercraftUUID uuid) {
|
||||
if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return;
|
||||
if(uuid.equals(EaglerProfile.getPlayerUUID())) return;
|
||||
if (!getVoiceListening().contains(uuid)) sendPacketRequest(uuid);
|
||||
}
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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.voice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformVoiceClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalConnectEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDescEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDisconnectPeerV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDisconnectV3EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalDisconnectV4EAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalICEEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketVoiceSignalRequestEAG;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketVoiceSignalGlobalEAG;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
public class VoiceClientInstance {
|
||||
|
||||
private final int protocolVers;
|
||||
private final Consumer<GameMessagePacket> packetSendCallback;
|
||||
private final Map<EaglercraftUUID, VoicePlayerState> voicePlayers = new HashMap<>(32);
|
||||
private EnumVoiceChannelType voiceChannel = EnumVoiceChannelType.NONE;
|
||||
private long lastUpdate = 0l;
|
||||
|
||||
protected VoiceClientInstance(int protocolVers, Consumer<GameMessagePacket> packetSendCallback) {
|
||||
this.protocolVers = protocolVers;
|
||||
this.packetSendCallback = packetSendCallback;
|
||||
}
|
||||
|
||||
public void initialize(EnumVoiceChannelType voiceChannel) {
|
||||
setVoiceChannel(voiceChannel);
|
||||
}
|
||||
|
||||
public EnumVoiceChannelType getVoiceChannel() {
|
||||
return voiceChannel;
|
||||
}
|
||||
|
||||
public void setVoiceChannel(EnumVoiceChannelType channel) {
|
||||
VoiceClientController.lastVoiceChannel = channel;
|
||||
if (voiceChannel == channel) return;
|
||||
if (channel != EnumVoiceChannelType.NONE) {
|
||||
PlatformVoiceClient.initializeDevices();
|
||||
}
|
||||
if (channel == EnumVoiceChannelType.NONE) {
|
||||
release();
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectV4EAG());
|
||||
} else {
|
||||
if (voiceChannel == EnumVoiceChannelType.PROXIMITY && channel == EnumVoiceChannelType.GLOBAL) {
|
||||
for (VoicePlayerState state : voicePlayers.values()) {
|
||||
if(!state.nearby) {
|
||||
state.tryRequest(EagRuntime.steadyTimeMillis());
|
||||
}
|
||||
PlatformVoiceClient.makePeerGlobal(state.uuid);
|
||||
}
|
||||
} else if (voiceChannel == EnumVoiceChannelType.GLOBAL && channel == EnumVoiceChannelType.PROXIMITY) {
|
||||
for (VoicePlayerState state : new ArrayList<>(voicePlayers.values())) {
|
||||
recheckNearby(state);
|
||||
if(!state.nearby) {
|
||||
PlatformVoiceClient.signalDisconnect(state.uuid, false);
|
||||
}
|
||||
PlatformVoiceClient.makePeerProximity(state.uuid);
|
||||
}
|
||||
} else if (voiceChannel == EnumVoiceChannelType.NONE) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalConnectEAG());
|
||||
}
|
||||
}
|
||||
voiceChannel = channel;
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeGlobal(Collection<SPacketVoiceSignalGlobalEAG.UserData> voicePlayers) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
EaglercraftUUID self = Minecraft.getMinecraft().getSession().getProfile().getId();
|
||||
Set<EaglercraftUUID> playersLost = new HashSet<>(this.voicePlayers.keySet());
|
||||
for (SPacketVoiceSignalGlobalEAG.UserData userData : voicePlayers) {
|
||||
EaglercraftUUID uuid = new EaglercraftUUID(userData.uuidMost, userData.uuidLeast);
|
||||
if (!uuid.equals(self)) {
|
||||
if (!playersLost.remove(uuid)) {
|
||||
announcePlayer(uuid);
|
||||
}
|
||||
this.voicePlayers.get(uuid).name = userData.username;
|
||||
}
|
||||
}
|
||||
if (!playersLost.isEmpty()) {
|
||||
for (EaglercraftUUID uuid : playersLost) {
|
||||
dropPlayer(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void announcePlayer(EaglercraftUUID uuid) {
|
||||
VoicePlayerState voiceState = new VoicePlayerState(this, uuid);
|
||||
voicePlayers.put(uuid, voiceState);
|
||||
if (voiceChannel == EnumVoiceChannelType.GLOBAL) {
|
||||
voiceState.tryRequest(EagRuntime.steadyTimeMillis());
|
||||
} else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) {
|
||||
recheckNearby(voiceState);
|
||||
if(voiceState.nearby) {
|
||||
voiceState.tryRequest(EagRuntime.steadyTimeMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void recheckNearby(VoicePlayerState voiceState) {
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if(mc.theWorld != null) {
|
||||
EntityPlayer player = mc.theWorld.getPlayerEntityByUUID(voiceState.uuid);
|
||||
if(player != null) {
|
||||
voiceState.nearby = goddamnManhattanDistance(mc, player);
|
||||
if(voiceState.nearby) {
|
||||
PlatformVoiceClient.updateVoicePosition(voiceState.uuid, player.posX, player.posY, player.posZ);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
voiceState.nearby = false;
|
||||
}
|
||||
|
||||
// Must perform these ham-fisted manhattan distance calculations to work with old clients
|
||||
private boolean goddamnManhattanDistance(Minecraft mc, Entity player) {
|
||||
final int prox = 22;
|
||||
return Math.abs(mc.thePlayer.posX - player.posX) <= prox && Math.abs(mc.thePlayer.posY - player.posY) <= prox
|
||||
&& Math.abs(mc.thePlayer.posZ - player.posZ) <= prox;
|
||||
}
|
||||
|
||||
private void dropPlayer(EaglercraftUUID uuid) {
|
||||
voicePlayers.remove(uuid);
|
||||
PlatformVoiceClient.signalDisconnect(uuid, true);
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
if (!voicePlayers.containsKey(user)) {
|
||||
// Backwards compat with old servers :(
|
||||
announcePlayer(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
PlatformVoiceClient.signalConnect(user, offer);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
PlatformVoiceClient.signalICECandidate(user, ice);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
PlatformVoiceClient.signalDescription(user, desc);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
VoicePlayerState state = voicePlayers.get(user);
|
||||
if (state != null) {
|
||||
state.handleDisconnect();
|
||||
}
|
||||
PlatformVoiceClient.signalDisconnect(user, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketRequest(VoicePlayerState voicePlayerState) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalRequestEAG(voicePlayerState.uuid.msb, voicePlayerState.uuid.lsb));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketDesc(EaglercraftUUID peerId, String desc) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDescEAG(peerId.msb, peerId.lsb, desc));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketICE(EaglercraftUUID peerId, String candidate) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalICEEAG(peerId.msb, peerId.lsb, candidate));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketDisconnectPeer(EaglercraftUUID peerId) {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
VoicePlayerState state = voicePlayers.get(peerId);
|
||||
if (state != null) {
|
||||
state.handleDisconnect();
|
||||
}
|
||||
if (protocolVers >= 4) {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectPeerV4EAG(peerId.msb, peerId.lsb));
|
||||
} else {
|
||||
packetSendCallback.accept(new CPacketVoiceSignalDisconnectV3EAG(true, peerId.msb, peerId.lsb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getVoiceUsername(EaglercraftUUID uuid) {
|
||||
VoicePlayerState state = voicePlayers.get(uuid);
|
||||
if (state != null) {
|
||||
return state.name;
|
||||
} else {
|
||||
return uuid.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public EnumVoiceChannelStatus getVoiceStatus() {
|
||||
if (voiceChannel != EnumVoiceChannelType.NONE) {
|
||||
return PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED ?
|
||||
EnumVoiceChannelStatus.CONNECTING : EnumVoiceChannelStatus.CONNECTED;
|
||||
} else {
|
||||
return EnumVoiceChannelStatus.DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
public void tickVoiceClient() {
|
||||
if (voiceChannel == EnumVoiceChannelType.GLOBAL) {
|
||||
long millis = EagRuntime.steadyTimeMillis();
|
||||
if (millis - lastUpdate > 1500l) {
|
||||
lastUpdate = millis;
|
||||
for (VoicePlayerState state : voicePlayers.values()) {
|
||||
state.tryRequest(millis);
|
||||
}
|
||||
}
|
||||
} else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) {
|
||||
long millis = EagRuntime.steadyTimeMillis();
|
||||
if (millis - lastUpdate > 100l) {
|
||||
lastUpdate = millis;
|
||||
for (VoicePlayerState state : voicePlayers.values()) {
|
||||
boolean old = state.nearby;
|
||||
recheckNearby(state);
|
||||
if (state.nearby) {
|
||||
state.tryRequest(millis);
|
||||
} else if (old) {
|
||||
PlatformVoiceClient.signalDisconnect(state.uuid, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
for (VoicePlayerState state : new ArrayList<>(voicePlayers.values())) {
|
||||
dropPlayer(state.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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.voice;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
|
||||
public class VoicePlayerState {
|
||||
|
||||
public final VoiceClientInstance client;
|
||||
public final EaglercraftUUID uuid;
|
||||
public long lastRequest = -15000l;
|
||||
public String name = null;
|
||||
public boolean nearby = false;
|
||||
|
||||
public VoicePlayerState(VoiceClientInstance client, EaglercraftUUID uuid) {
|
||||
this.client = client;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public void tryRequest(long millis) {
|
||||
if (!VoiceClientController.getVoiceListening().contains(uuid)) {
|
||||
if (millis - lastRequest > 4000l) {
|
||||
lastRequest = millis;
|
||||
client.sendPacketRequest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDisconnect() {
|
||||
lastRequest = 0l;
|
||||
}
|
||||
|
||||
public boolean isListening() {
|
||||
return VoiceClientController.getVoiceListening().contains(uuid);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user