VoiceModule class
Пакет: com.hypixel.hytale.server.core.modules.voice
Файл: com/hypixel/hytale/server/core/modules/voice/VoiceModule.java
extends: JavaPlugin
Поля (81)
| Модификаторы | Тип | Имя |
|---|---|---|
final |
long |
POSITION_CACHE_UPDATE_INTERVAL_MS |
|
|
instance |
|
HashMap |
var1 |
|
List |
var1 |
|
|
var1 |
|
Iterator |
var1 |
|
boolean |
var10 |
|
int |
var10 |
|
Ref |
var10 |
|
int |
var11 |
|
UUID |
var11 |
|
Store |
var11 |
|
boolean |
var12 |
|
VoiceModule.PositionSnapshot |
var12 |
|
ChunkColumn |
var12 |
|
UUID |
var13 |
|
TransformComponent |
var13 |
|
Ref |
var14 |
|
UUID |
var14 |
|
World |
var15 |
|
World |
var16 |
|
Store |
var2 |
|
PlayerRef |
var2 |
|
UUID |
var2 |
|
PlayerRef |
var2 |
|
EntityVoiceSpeaker |
var2 |
|
Ref |
var2 |
|
ClipPlayback |
var2 |
|
boolean |
var2 |
|
float |
var2 |
|
float |
var2 |
|
boolean |
var2 |
|
boolean |
var2 |
|
NetworkId |
var2 |
|
int |
var2 |
|
PlayerRef |
var3 |
|
EntityStore |
var3 |
|
Ref |
var3 |
|
VoicePlayerState |
var3 |
|
Ref |
var3 |
|
PlayerRef |
var3 |
|
VoicePlayerState |
var3 |
|
VoiceModule.RegisteredInterceptor |
var3 |
|
ArrayList |
var3 |
|
World |
var3 |
|
UUID |
var3 |
|
ClipPlayback |
var3 |
|
boolean |
var3 |
|
|
var3 |
|
|
var3 |
|
PlayerRef |
var3 |
|
UUID |
var4 |
|
Ref |
var4 |
|
Store |
var4 |
|
Store |
var4 |
|
String |
var4 |
|
PlayerVoiceInterceptor |
var4 |
|
boolean |
var4 |
|
|
var4 |
|
int |
var4 |
|
PlayerRef |
var5 |
|
Store |
var5 |
|
EntityStore |
var5 |
|
EntityStore |
var5 |
|
ArrayList |
var5 |
|
int |
var5 |
|
World |
var6 |
|
EntityStore |
var6 |
|
World |
var6 |
|
World |
var6 |
|
int |
var6 |
|
Vector3d |
var6 |
|
HashMap |
var7 |
|
World |
var7 |
|
UUID |
var7 |
|
ChunkStore |
var7 |
|
TransformComponent |
var8 |
|
float |
var8 |
|
long |
var8 |
|
float |
var9 |
|
boolean |
var9 |
Методы (94)
| Модификаторы | Возврат | Сигнатура |
|---|---|---|
public |
record |
PositionSnapshotrecord PositionSnapshot(@Nonnull Vector3dc position, boolean isUnderwater, @Nonnull UUID worldId, int networkId, boolean isDead) |
private |
record |
RegisteredInterceptorrecord RegisteredInterceptor(int priority, @Nonnull PlayerVoiceInterceptor interceptor) |
|
static VoiceModule.PositionSnapshot |
atEyeHeightstatic VoiceModule.PositionSnapshot atEyeHeight(@Nonnull Vector3dc var0, float var1, boolean var2, @Nonnull UUID var3, int var4, boolean var5) |
private |
void |
broadcastConfigToAllPlayersvoid broadcastConfigToAllPlayers() |
private |
void |
broadcastMuteUpdatevoid broadcastMuteUpdate(@Nonnull UUID var1, boolean var2) |
static |
int |
entityNetworkIdint entityNetworkId(@Nonnull Ref<EntityStore> var0, @Nonnull ComponentAccessor<EntityStore> var1) |
private |
void |
executeOnWorldOrSkipvoid executeOnWorldOrSkip(@Nonnull World var1, @Nonnull String var2, @Nonnull Runnable var3) |
|
|
for for(int var2 = 0; var2 < 4; var2++) |
|
|
for for(UUID var5 : var2) |
|
|
for for(VoiceSpeaker var10 : var3) |
|
|
for for(ClipPlayback var3 : var1) |
|
|
for for(ExecutorService var5 : this.voiceExecutors) |
|
|
for for(ExecutorService var20 : this.voiceExecutors) |
|
|
for for(ExecutorService var6 : this.voiceExecutors) |
|
|
for for(VoiceModule.RegisteredInterceptor var3 : this.playerVoiceInterceptors) |
static |
VoiceModule |
getVoiceModule get() |
public |
int |
getBurstCapacityint getBurstCapacity() |
public |
Set<UUID> |
getGloballyMutedPlayersSet<UUID> getGloballyMutedPlayers() |
public |
float |
getMaxHearingDistancefloat getMaxHearingDistance() |
public |
int |
getMaxPacketSizeint getMaxPacketSize() |
public |
int |
getMaxPacketsPerSecondint getMaxPacketsPerSecond() |
public |
Map<UUID, VoicePlayerState> |
getPlayerStatesMap<UUID, VoicePlayerState> getPlayerStates() |
public |
float |
getReferenceDistancefloat getReferenceDistance() |
public |
ExecutorService |
getVoiceExecutorExecutorService getVoiceExecutor(@Nonnull UUID var1) |
public |
VoiceRouter |
getVoiceRouterVoiceRouter getVoiceRouter() |
|
boolean |
hasPlayerVoiceInterceptorsboolean hasPlayerVoiceInterceptors() |
|
|
if if(Constants.SINGLEPLAYER) |
|
|
if if(Constants.SINGLEPLAYER) |
|
|
if if(!this.isShutdown) |
|
|
if if(var5 != null) |
|
|
if if(var6 != null) |
|
|
if if(var14 != null) |
|
|
if if(var16 == null) |
|
|
if if(var2 != null) |
|
|
if if(var3 != null) |
|
|
if if(var3 == null) |
|
|
if if(var5 == null) |
|
|
if if(var6 == null) |
|
|
if if(var7 != var1) |
|
|
if if(var8 == null) |
|
|
if if(var1 == null) |
|
|
if if(var2 == null) |
|
|
if if(var4 == null) |
|
|
if if(var6 != null && var6 == var2) |
|
|
if if(var13 == null) |
|
|
if if(this.positionUpdateTask != null) |
|
|
if if(this.clipPumpTask != null) |
|
|
if if(var3 != null) |
|
|
if if(var4 != null) |
|
|
if if(var5 != null) |
|
|
if if(var6 != null) |
|
|
if if(var3 != null) |
|
|
if if(var2 != null) |
|
|
if if(var3 != null) |
|
|
if if(!this.isShutdown) |
|
|
if if(this.clipPumpTask == null) |
|
|
if if(!var4) |
|
|
if if(!var3) |
|
|
if if(var2 != var1) |
|
|
if if(var2 != var1) |
|
|
if if(var2 != var1) |
|
|
if if(var3 != null) |
|
|
if if(var2) |
|
|
if if(var2) |
public |
boolean |
isDeadPlayersCanHearboolean isDeadPlayersCanHear() |
private |
boolean |
isEyeInFluidboolean isEyeInFluid(@Nonnull Vector3d var1, float var2, @Nonnull World var3) |
public |
boolean |
isPlayerMutedboolean isPlayerMuted(@Nonnull UUID var1) |
public |
boolean |
isShutdownboolean isShutdown() |
public |
boolean |
isVoiceEnabledboolean isVoiceEnabled() |
public |
boolean |
mutePlayerboolean mutePlayer(@Nonnull UUID var1) |
static |
UUID |
newVoiceStreamIdUUID newVoiceStreamId() |
private |
void |
onPlayerConnectvoid onPlayerConnect(@Nonnull PlayerConnectEvent var1) |
private |
void |
onPlayerDisconnectvoid onPlayerDisconnect(@Nonnull PlayerDisconnectEvent var1) |
private |
void |
onServerAccessChangedvoid onServerAccessChanged(@Nonnull SingleplayerRequestAccessEvent var1) |
private |
void |
pumpClipsvoid pumpClips() |
|
void |
removeSpeakervoid removeSpeaker(@Nonnull UUID var1) |
|
void |
runPlayerVoiceInterceptorsvoid runPlayerVoiceInterceptors(@Nonnull PlayerVoiceFrame var1) |
private |
void |
scheduleImmediateEntitySpeakerUpdatevoid scheduleImmediateEntitySpeakerUpdate(@Nonnull VoiceSpeaker var1) |
public |
void |
scheduleImmediatePositionUpdatevoid scheduleImmediatePositionUpdate(@Nonnull PlayerRef var1) |
private |
void |
scheduleWorldSamplevoid scheduleWorldSample(@Nonnull World var1, @Nullable List<UUID> var2, @Nullable List<VoiceSpeaker> var3) |
public |
void |
setMaxHearingDistancevoid setMaxHearingDistance(float var1) |
public |
void |
setPlayerVoiceChannelvoid setPlayerVoiceChannel(@Nonnull UUID var1, @Nullable String var2) |
public |
void |
setReferenceDistancevoid setReferenceDistance(float var1) |
public |
void |
setVoiceEnabledvoid setVoiceEnabled(boolean var1) |
abstract |
|
super super(var1) |
|
|
synchronized synchronized(this.clipPumpLock) |
|
|
synchronized synchronized(this.playerVoiceInterceptorLock) |
|
|
synchronized synchronized(this.playerVoiceInterceptorLock) |
|
|
synchronized synchronized(this.clipPumpLock) |
|
|
synchronized synchronized(this.clipPumpLock) |
public |
boolean |
unmutePlayerboolean unmutePlayer(@Nonnull UUID var1) |
private |
void |
updateAllPlayerPositionsvoid updateAllPlayerPositions() |
private |
void |
updateEntitySpeakerPositionCachevoid updateEntitySpeakerPositionCache(@Nonnull VoiceSpeaker var1, @Nonnull World var2) |
|
void |
updatePositionCachevoid updatePositionCache(@Nonnull UUID var1, @Nonnull VoiceModule.PositionSnapshot var2) |
Исходный код
Показать/скрыть
class="kw">package com.hypixel.hytale.server.core.modules.voice;
class="kw">import com.hypixel.hytale.common.plugin.PluginManifest;
class="kw">import com.hypixel.hytale.component.ComponentAccessor;
class="kw">import com.hypixel.hytale.component.Ref;
class="kw">import com.hypixel.hytale.component.Store;
class="kw">import com.hypixel.hytale.event.EventPriority;
class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.math.util.ChunkUtil;
class="kw">import com.hypixel.hytale.math.util.MathUtil;
class="kw">import com.hypixel.hytale.protocol.packets.serveraccess.Access;
class="kw">import com.hypixel.hytale.protocol.packets.stream.StreamType;
class="kw">import com.hypixel.hytale.registry.Registration;
class="kw">import com.hypixel.hytale.server.core.Constants;
class="kw">import com.hypixel.hytale.server.core.HytaleServer;
class="kw">import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
class="kw">import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
class="kw">import com.hypixel.hytale.server.core.io.ServerManager;
class="kw">import com.hypixel.hytale.server.core.io.stream.StreamManager;
class="kw">import com.hypixel.hytale.server.core.modules.collision.WorldUtil;
class="kw">import com.hypixel.hytale.server.core.modules.entity.EntityModule;
class="kw">import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
class="kw">import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
class="kw">import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
class="kw">import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
class="kw">import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerRequestAccessEvent;
class="kw">import com.hypixel.hytale.server.core.modules.voice.commands.VoiceCommand;
class="kw">import com.hypixel.hytale.server.core.plugin.JavaPlugin;
class="kw">import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
class="kw">import com.hypixel.hytale.server.core.universe.PlayerRef;
class="kw">import com.hypixel.hytale.server.core.universe.Universe;
class="kw">import com.hypixel.hytale.server.core.universe.world.World;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
class="kw">import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
class="kw">import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
class="kw">import com.hypixel.hytale.server.core.util.Config;
class="kw">import com.hypixel.hytale.server.core.util.UUIDUtil;
class="kw">import com.hypixel.hytale.server.core.util.concurrent.ThreadUtil;
class="kw">import java.util.ArrayList;
class="kw">import java.util.Collection;
class="kw">import java.util.Comparator;
class="kw">import java.util.HashMap;
class="kw">import java.util.Iterator;
class="kw">import java.util.List;
class="kw">import java.util.Map;
class="kw">import java.util.Set;
class="kw">import java.util.UUID;
class="kw">import java.util.Map.Entry;
class="kw">import java.util.concurrent.ConcurrentHashMap;
class="kw">import java.util.concurrent.ExecutorService;
class="kw">import java.util.concurrent.Executors;
class="kw">import java.util.concurrent.ScheduledFuture;
class="kw">import java.util.concurrent.TimeUnit;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;
class="kw">import org.joml.Vector3d;
class="kw">import org.joml.Vector3dc;
class="kw">public class VoiceModule class="kw">extends JavaPlugin {
class="kw">private class="kw">static class="kw">final long POSITION_CACHE_UPDATE_INTERVAL_MS = 100L;
class="kw">static class="kw">final long CLIP_FRAME_INTERVAL_MS = 20L;
class="kw">public class="kw">static class="kw">final PluginManifest MANIFEST = PluginManifest.corePlugin(VoiceModule.class).depends(Universe.class).depends(EntityModule.class).build();
class="kw">private class="kw">static VoiceModule instance;
@Nonnull
class="kw">private class="kw">final Config<VoiceModuleConfig> config = this.withConfig("VoiceModule", VoiceModuleConfig.CODEC);
class="kw">private class="kw">static class="kw">final int MAX_PACKETS_PER_SECOND = 60;
class="kw">private class="kw">static class="kw">final int BURST_CAPACITY = 25;
class="kw">private class="kw">static class="kw">final int MAX_PACKET_SIZE = 1024;
class="kw">private class="kw">final Map<UUID, VoicePlayerState> playerStates = new ConcurrentHashMap<>();
class="kw">private class="kw">final Map<UUID, VoiceSpeaker> customSpeakers = new ConcurrentHashMap<>();
class="kw">private class="kw">final VoiceChannels voiceChannels = new VoiceChannels();
class="kw">private class="kw">final Object playerVoiceInterceptorLock = new Object();
class="kw">private class="kw">volatile List<VoiceModule.RegisteredInterceptor> playerVoiceInterceptors = List.of();
class="kw">private VoiceRouter voiceRouter;
class="kw">private class="kw">static class="kw">final int VOICE_THREAD_POOL_SIZE = 4;
class="kw">private class="kw">final ExecutorService[] voiceExecutors;
class="kw">private class="kw">volatile boolean isShutdown = false;
class="kw">private class="kw">final ConcurrentHashMap<UUID, VoiceModule.PositionSnapshot> positionCache = new ConcurrentHashMap<>();
class="kw">private ScheduledFuture<?> positionUpdateTask;
class="kw">private class="kw">final Set<ClipPlayback> activeClips = ConcurrentHashMap.newKeySet();
class="kw">private class="kw">final Object clipPumpLock = new Object();
class="kw">private ScheduledFuture<?> clipPumpTask;
class="kw">public class="kw">static VoiceModule get() {
class="kw">return instance;
}
class="kw">public VoiceModule(@Nonnull JavaPluginInit var1) {
super(var1);
instance = this;
this.voiceExecutors = new ExecutorService[4];
for (int var2 = 0; var2 < 4; var2++) {
this.voiceExecutors[var2] = Executors.newSingleThreadExecutor(ThreadUtil.daemon("VoiceRouter-" + var2));
}
}
@Override
class="kw">protected void setup() {
if (Constants.SINGLEPLAYER) {
this.config.get().setVoiceEnabled(false);
}
this.voiceRouter = new VoiceRouter(this);
this.getCommandRegistry().registerCommand(new VoiceCommand());
ServerManager.get().registerSubPacketHandlers(VoicePacketHandler::new);
StreamManager.getInstance().registerHandler(StreamType.Voice, VoiceStreamHandler::new);
this.getLogger().at(Level.INFO).log("[Voice] Registered voice stream handler");
this.getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerConnect);
this.getEventRegistry().register(PlayerDisconnectEvent.class, this::onPlayerDisconnect);
if (Constants.SINGLEPLAYER) {
this.getEventRegistry().register(SingleplayerRequestAccessEvent.class, this::onServerAccessChanged);
}
this.getLogger()
.at(Level.INFO)
.log(
"[Voice] VoiceModule initialized (maxDistance=%.1f, refDistance=%.1f, enabled=%s, voiceStreamSupported=true)",
this.getMaxHearingDistance(),
this.getReferenceDistance(),
this.isVoiceEnabled()
);
}
@Override
class="kw">protected void start() {
this.positionUpdateTask = HytaleServer.SCHEDULED_EXECUTOR.scheduleAtFixedRate(this::updateAllPlayerPositions, 100L, 100L, TimeUnit.MILLISECONDS);
this.getLogger().at(Level.INFO).log("[Voice] Started position cache update task (%dms interval)", 100L);
}
class="kw">private void updateAllPlayerPositions() {
if (!this.isShutdown) {
HashMap var1 = new HashMap<>();
for (Entry var3 : this.playerStates.entrySet()) {
UUID var4 = var3.getKey();
PlayerRef var5 = Universe.get().getPlayer(var4);
if (var5 != null) {
World var6 = this.resolveWorld(var5.getReference());
if (var6 != null) {
var1.computeIfAbsent(var6, var0 -> new ArrayList<>()).add(var4);
}
}
}
HashMap var7 = new HashMap<>();
for (VoiceSpeaker var11 : this.customSpeakers.values()) {
if (var11.isOpen()) {
Ref var14 = var11.entityRef();
if (var14 != null) {
World var16 = this.resolveWorld(var14);
if (var16 == null) {
if (!var14.isValid()) {
var11.close();
} else {
this.positionCache.remove(var11.id());
}
} else {
var7.computeIfAbsent(var16, var0 -> new ArrayList<>()).add(var11);
}
}
}
}
for (Entry var12 : var1.entrySet()) {
World var15 = var12.getKey();
this.scheduleWorldSample(var15, var12.getValue(), var7.remove(var15));
}
for (Entry var13 : var7.entrySet()) {
this.scheduleWorldSample(var13.getKey(), null, var13.getValue());
}
}
}
class="kw">private void executeOnWorldOrSkip(@Nonnull World var1, @Nonnull String var2, @Nonnull Runnable var3) {
try {
var1.execute(var3);
} catch (RuntimeException var5) {
((HytaleLogger.Api)this.getLogger().at(Level.FINE).withCause(var5)).log("[Voice] Skipped %s for an unavailable world", var2);
}
}
class="kw">private void scheduleWorldSample(@Nonnull World var1, @Nullable List<UUID> var2, @Nullable List<VoiceSpeaker> var3) {
this.executeOnWorldOrSkip(
var1,
"voice position sample",
() -> {
if (var2 != null) {
for (UUID var5 : var2) {
try {
this.samplePlayerPositionOnWorldThread(var1, var5);
} catch (RuntimeException var8) {
((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var8)).log("[Voice] Failed to sample voice position for player %s", var5);
}
}
}
if (var3 != null) {
for (VoiceSpeaker var10 : var3) {
try {
this.updateEntitySpeakerPositionCache(var10, var1);
} catch (RuntimeException var7) {
((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var7))
.log("[Voice] Failed to sample voice position for speaker %s", var10.id());
}
}
}
}
);
}
@Nullable
class="kw">private PlayerRef samplePlayerPositionOnWorldThread(@Nonnull World var1, @Nonnull UUID var2) {
PlayerRef var3 = Universe.get().getPlayer(var2);
if (var3 == null) {
class="kw">return null;
}
Ref var4 = var3.getReference();
if (var4 != null && var4.isValid()) {
Store var5 = var4.getStore();
if (var5 == null) {
class="kw">return null;
}
EntityStore var6 = var5.getExternalData();
if (var6 == null) {
class="kw">return null;
}
World var7 = var6.getWorld();
if (var7 != var1) {
UUID var14 = var1.getWorldConfig().getUuid();
this.positionCache.computeIfPresent(var2, (var1x, var2x) -> (VoiceModule.PositionSnapshot)(var2x.worldId().equals(var14) ? null : var2x));
this.voiceRouter.removePlayerFromWorldSet(var2, var14);
class="kw">return null;
}
TransformComponent var8 = var5.getComponent(var4, EntityModule.get().getTransformComponentType());
if (var8 == null) {
class="kw">return null;
}
float var9 = ModelComponent.getEyeHeight(var4, var5);
boolean var10 = this.isEyeInFluid(var8.getPosition(), var9, var7);
int var11 = entityNetworkId(var4, var5);
boolean var12 = var5.getComponent(var4, DeathComponent.getComponentType()) != null;
UUID var13 = var7.getWorldConfig().getUuid();
this.voiceRouter.updateSpeakerPositionCache(var3, var8.getPosition(), var9, var10, var13, var11, var12);
class="kw">return var3;
} else {
class="kw">return null;
}
}
@Nullable
class="kw">private World resolveWorld(@Nullable Ref<EntityStore> var1) {
if (var1 == null) {
class="kw">return null;
}
Store var2 = var1.getStore();
if (var2 == null) {
class="kw">return null;
}
EntityStore var3 = var2.getExternalData();
class="kw">return var3 != null ? var3.getWorld() : null;
}
class="kw">private void updateEntitySpeakerPositionCache(@Nonnull VoiceSpeaker var1, @Nonnull World var2) {
if (var1.isOpen()) {
Ref var3 = var1.entityRef();
if (var3 != null && var3.isValid()) {
Store var4 = var3.getStore();
if (var4 == null) {
var1.close();
} else {
EntityStore var5 = var4.getExternalData();
World var6 = var5 != null ? var5.getWorld() : null;
if (var6 != null && var6 == var2) {
TransformComponent var13 = var4.getComponent(var3, EntityModule.get().getTransformComponentType());
if (var13 == null) {
this.positionCache.remove(var1.id());
} else {
float var8 = ModelComponent.getEyeHeight(var3, var4);
boolean var9 = this.isEyeInFluid(var13.getPosition(), var8, var6);
int var10 = entityNetworkId(var3, var4);
UUID var11 = var6.getWorldConfig().getUuid();
VoiceModule.PositionSnapshot var12 = VoiceModule.PositionSnapshot.atEyeHeight(var13.getPosition(), var8, var9, var11, var10, false);
this.positionCache.compute(var1.id(), (var2x, var3x) -> var1.isOpen() ? var12 : null);
}
} else {
UUID var7 = var2.getWorldConfig().getUuid();
this.positionCache.computeIfPresent(var1.id(), (var1x, var2x) -> (VoiceModule.PositionSnapshot)(var2x.worldId().equals(var7) ? null : var2x));
}
}
} else {
var1.close();
}
}
}
@Override
class="kw">protected void shutdown() {
this.isShutdown = true;
if (this.positionUpdateTask != null) {
this.positionUpdateTask.cancel(false);
}
List var1;
class="kw">synchronized (this.clipPumpLock) {
if (this.clipPumpTask != null) {
this.clipPumpTask.cancel(false);
this.clipPumpTask = null;
}
var1 = new ArrayList<>(this.activeClips);
this.activeClips.clear();
}
for (ClipPlayback var3 : var1) {
var3.cancel();
}
for (ExecutorService var5 : this.voiceExecutors) {
var5.shutdown();
}
try {
for (ExecutorService var20 : this.voiceExecutors) {
if (!var20.awaitTermination(5L, TimeUnit.SECONDS)) {
this.getLogger().at(Level.WARNING).log("[Voice] VoiceExecutor did not terminate in time, forcing shutdown");
var20.shutdownNow();
}
}
} catch (InterruptedException var7) {
this.getLogger().at(Level.WARNING).log("[Voice] VoiceExecutor shutdown interrupted");
for (ExecutorService var6 : this.voiceExecutors) {
var6.shutdownNow();
}
Thread.currentThread().interrupt();
}
for (VoiceSpeaker var16 : new ArrayList<>(this.customSpeakers.values())) {
var16.close();
}
this.playerStates.clear();
this.positionCache.clear();
this.voiceChannels.clear();
this.getLogger().at(Level.INFO).log("[Voice] VoiceModule shutting down");
}
class="kw">private void onServerAccessChanged(@Nonnull SingleplayerRequestAccessEvent var1) {
if (var1.getAccess() != Access.Private && !this.isVoiceEnabled()) {
this.setVoiceEnabled(true);
this.getLogger().at(Level.INFO).log("[Voice] Auto-enabled voice for %s play", var1.getAccess());
} else if (var1.getAccess() == Access.Private && this.isVoiceEnabled()) {
this.setVoiceEnabled(false);
this.getLogger().at(Level.INFO).log("[Voice] Auto-disabled voice after returning to singleplayer");
}
}
class="kw">private void onPlayerConnect(@Nonnull PlayerConnectEvent var1) {
PlayerRef var2 = var1.getPlayerRef();
VoicePlayerState var3 = new VoicePlayerState(var2.getUuid(), this.getLogger(), 25);
this.playerStates.put(var2.getUuid(), var3);
this.getLogger().at(Level.FINE).log("[Voice] Player connected: %s (%s), totalPlayers=%d", var2.getUsername(), var2.getUuid(), this.playerStates.size());
this.voiceRouter.sendVoiceConfig(var2);
this.scheduleImmediatePositionUpdate(var2);
}
class="kw">public void scheduleImmediatePositionUpdate(@Nonnull PlayerRef var1) {
UUID var2 = var1.getUuid();
Ref var3 = var1.getReference();
if (var3 != null) {
Store var4 = var3.getStore();
if (var4 != null) {
EntityStore var5 = var4.getExternalData();
if (var5 != null) {
World var6 = var5.getWorld();
if (var6 != null) {
this.executeOnWorldOrSkip(var6, "immediate player voice sample", () -> {
PlayerRef var3 = this.samplePlayerPositionOnWorldThread(var6, var2);
if (var3 != null) {
this.getLogger().at(Level.FINE).log("[Voice] Immediate position cache populated for %s", var3.getUsername());
}
});
}
}
}
}
}
class="kw">private void onPlayerDisconnect(@Nonnull PlayerDisconnectEvent var1) {
PlayerRef var2 = var1.getPlayerRef();
VoicePlayerState var3 = this.playerStates.remove(var2.getUuid());
this.positionCache.remove(var2.getUuid());
this.voiceRouter.removePlayerFromWorldSets(var2.getUuid());
this.setPlayerVoiceChannel(var2.getUuid(), null);
String var4 = var3 != null ? var3.getStatsString() : "no state";
this.getLogger()
.at(Level.FINE)
.log("[Voice] Player disconnected: %s (%s), stats=[%s], remainingPlayers=%d", var2.getUsername(), var2.getUuid(), var4, this.playerStates.size());
}
@Nullable
class="kw">public VoicePlayerState getPlayerState(@Nonnull UUID var1) {
class="kw">return this.playerStates.get(var1);
}
class="kw">public void setPlayerVoiceChannel(@Nonnull UUID var1, @Nullable String var2) {
this.voiceChannels.setChannel(var1, var2);
}
@Nonnull
VoiceChannels getVoiceChannels() {
class="kw">return this.voiceChannels;
}
class="kw">public VoiceRouter getVoiceRouter() {
class="kw">return this.voiceRouter;
}
@Nonnull
class="kw">public Registration addPlayerVoiceInterceptor(@Nonnull PlayerVoiceInterceptor var1) {
class="kw">return this.addPlayerVoiceInterceptor(EventPriority.NORMAL, var1);
}
@Nonnull
class="kw">public Registration addPlayerVoiceInterceptor(@Nonnull EventPriority var1, @Nonnull PlayerVoiceInterceptor var2) {
VoiceModule.RegisteredInterceptor var3 = new VoiceModule.RegisteredInterceptor(var1.getValue(), var2);
class="kw">synchronized (this.playerVoiceInterceptorLock) {
ArrayList var5 = new ArrayList<>(this.playerVoiceInterceptors);
var5.add(var3);
var5.sort(Comparator.comparingInt(VoiceModule.RegisteredInterceptor::priority));
this.playerVoiceInterceptors = var5;
}
class="kw">return new Registration(() -> true, () -> {
class="kw">synchronized (this.playerVoiceInterceptorLock) {
ArrayList var3 = new ArrayList<>(this.playerVoiceInterceptors);
var3.remove(var3);
this.playerVoiceInterceptors = var3;
}
});
}
boolean hasPlayerVoiceInterceptors() {
class="kw">return !this.playerVoiceInterceptors.isEmpty();
}
void runPlayerVoiceInterceptors(@Nonnull PlayerVoiceFrame var1) {
for (VoiceModule.RegisteredInterceptor var3 : this.playerVoiceInterceptors) {
PlayerVoiceInterceptor var4 = var3.interceptor();
try {
var4.intercept(var1);
} catch (RuntimeException var6) {
((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var6))
.log("[Voice] Player voice interceptor %s threw and was skipped for this frame", var4.getClass().getName());
}
}
}
@Nonnull
class="kw">public VoiceSpeaker openEntityVoice(@Nonnull Ref<EntityStore> var1) {
EntityVoiceSpeaker var2 = this.registerSpeaker(new EntityVoiceSpeaker(this, newVoiceStreamId(), var1));
this.scheduleImmediateEntitySpeakerUpdate(var2);
class="kw">return var2;
}
class="kw">private void scheduleImmediateEntitySpeakerUpdate(@Nonnull VoiceSpeaker var1) {
Ref var2 = var1.entityRef();
if (var2 != null) {
World var3 = this.resolveWorld(var2);
if (var3 != null) {
this.executeOnWorldOrSkip(var3, "immediate entity voice sample", () -> this.updateEntitySpeakerPositionCache(var1, var3));
}
}
}
@Nonnull
class="kw">public PositionalVoiceSpeaker openPositionalVoice(@Nonnull World var1, @Nonnull Vector3d var2) {
UUID var3 = var1.getWorldConfig().getUuid();
class="kw">return this.registerSpeaker(new PositionalVoiceSpeaker(this, newVoiceStreamId(), var3, var2));
}
@Nonnull
class="kw">public VoiceSpeaker openDirectVoice(@Nonnull Collection<UUID> var1) {
class="kw">return this.registerSpeaker(new DirectVoiceSpeaker(this, newVoiceStreamId(), var1));
}
@Nonnull
class="kw">private <T class="kw">extends VoiceSpeaker> T registerSpeaker(@Nonnull T var1) {
this.customSpeakers.put(var1.id(), var1);
this.getLogger().at(Level.FINE).log("[Voice] Opened custom voice speaker %s", var1.id());
class="kw">return var1;
}
class="kw">private class="kw">static UUID newVoiceStreamId() {
class="kw">return UUIDUtil.generateVersion8UUID();
}
void removeSpeaker(@Nonnull UUID var1) {
this.customSpeakers.remove(var1);
this.positionCache.remove(var1);
}
@Nonnull
ClipPlayback startClip(@Nonnull VoiceSpeaker var1, @Nonnull List<byte[]> var2) {
ClipPlayback var3 = new ClipPlayback(var1, var2);
boolean var4 = false;
class="kw">synchronized (this.clipPumpLock) {
if (!this.isShutdown) {
this.activeClips.add(var3);
if (this.clipPumpTask == null) {
this.clipPumpTask = HytaleServer.SCHEDULED_EXECUTOR.scheduleAtFixedRate(this::pumpClips, 0L, 20L, TimeUnit.MILLISECONDS);
}
var4 = true;
}
}
if (!var4) {
var3.cancel();
}
class="kw">return var3;
}
class="kw">private void pumpClips() {
Iterator var1 = this.activeClips.iterator();
while (var1.hasNext()) {
ClipPlayback var2 = var1.next();
boolean var3;
try {
var3 = var2.advance();
} catch (RuntimeException var7) {
((HytaleLogger.Api)this.getLogger().at(Level.WARNING).withCause(var7)).log("[Voice] Clip pump error, dropping clip");
var2.fail(var7);
var3 = false;
}
if (!var3) {
var1.remove();
}
}
class="kw">synchronized (this.clipPumpLock) {
if (this.activeClips.isEmpty() && this.clipPumpTask != null) {
this.clipPumpTask.cancel(false);
this.clipPumpTask = null;
}
}
}
class="kw">public boolean isVoiceEnabled() {
class="kw">return this.config.get().isVoiceEnabled();
}
class="kw">public void setVoiceEnabled(boolean var1) {
boolean var2 = this.config.get().isVoiceEnabled();
this.config.get().setVoiceEnabled(var1);
if (var2 != var1) {
this.getLogger().at(Level.INFO).log("[Voice] Voice enabled changed: %s -> %s", var2, var1);
this.broadcastConfigToAllPlayers();
this.config.save();
}
}
class="kw">public boolean isDeadPlayersCanHear() {
class="kw">return this.config.get().isDeadPlayersCanHear();
}
class="kw">public float getMaxHearingDistance() {
class="kw">return this.config.get().getMaxHearingDistance();
}
class="kw">public void setMaxHearingDistance(float var1) {
float var2 = this.config.get().getMaxHearingDistance();
this.config.get().setMaxHearingDistance(var1);
if (var2 != var1) {
this.getLogger().at(Level.INFO).log("[Voice] Max hearing distance changed: %.1f -> %.1f", var2, var1);
this.broadcastConfigToAllPlayers();
this.config.save();
}
}
class="kw">public float getReferenceDistance() {
class="kw">return this.config.get().getFullVolumeDistance();
}
class="kw">public void setReferenceDistance(float var1) {
float var2 = this.config.get().getFullVolumeDistance();
this.config.get().setFullVolumeDistance(var1);
if (var2 != var1) {
this.getLogger().at(Level.INFO).log("[Voice] Full volume distance changed: %.1f -> %.1f", var2, var1);
this.broadcastConfigToAllPlayers();
this.config.save();
}
}
class="kw">private void broadcastConfigToAllPlayers() {
for (Entry var2 : this.playerStates.entrySet()) {
PlayerRef var3 = Universe.get().getPlayer(var2.getKey());
if (var3 != null) {
this.voiceRouter.sendVoiceConfig(var3);
}
}
}
class="kw">private void broadcastMuteUpdate(@Nonnull UUID var1, boolean var2) {
this.broadcastConfigToAllPlayers();
this.getLogger().at(Level.INFO).log("[Voice] Broadcast config update for mute change: playerId=%s, isMuted=%s", var1, var2);
}
class="kw">public boolean isPlayerMuted(@Nonnull UUID var1) {
class="kw">return this.config.get().isPlayerMuted(var1);
}
class="kw">public boolean mutePlayer(@Nonnull UUID var1) {
boolean var2 = this.config.get().mutePlayer(var1);
if (var2) {
this.getLogger().at(Level.INFO).log("[Voice] Player globally muted: %s", var1);
this.broadcastMuteUpdate(var1, true);
this.config.save();
}
class="kw">return var2;
}
class="kw">public boolean unmutePlayer(@Nonnull UUID var1) {
boolean var2 = this.config.get().unmutePlayer(var1);
if (var2) {
this.getLogger().at(Level.INFO).log("[Voice] Player globally unmuted: %s", var1);
this.broadcastMuteUpdate(var1, false);
this.config.save();
}
class="kw">return var2;
}
class="kw">public Set<UUID> getGloballyMutedPlayers() {
class="kw">return this.config.get().getMutedPlayers();
}
class="kw">public int getMaxPacketsPerSecond() {
class="kw">return 60;
}
class="kw">public int getBurstCapacity() {
class="kw">return 25;
}
class="kw">public int getMaxPacketSize() {
class="kw">return 1024;
}
class="kw">private class="kw">static int entityNetworkId(@Nonnull Ref<EntityStore> var0, @Nonnull ComponentAccessor<EntityStore> var1) {
NetworkId var2 = var1.getComponent(var0, NetworkId.getComponentType());
class="kw">return var2 != null ? var2.getId() : 0;
}
class="kw">private boolean isEyeInFluid(@Nonnull Vector3d var1, float var2, @Nonnull World var3) {
int var4 = MathUtil.floor(var1.x());
int var5 = MathUtil.floor(var1.y() + var2);
int var6 = MathUtil.floor(var1.z());
ChunkStore var7 = var3.getChunkStore();
long var8 = ChunkUtil.indexChunkFromBlock(var4, var6);
Ref var10 = var7.getChunkReference(var8);
if (var10 != null && var10.isValid()) {
Store var11 = var7.getStore();
ChunkColumn var12 = var11.getComponent(var10, ChunkColumn.getComponentType());
class="kw">return var12 == null ? false : WorldUtil.getFluidIdAtPosition(var11, var12, var4, var5, var6) != 0;
} else {
class="kw">return false;
}
}
void updatePositionCache(@Nonnull UUID var1, @Nonnull VoiceModule.PositionSnapshot var2) {
this.positionCache.put(var1, var2);
}
@Nullable
class="kw">public VoiceModule.PositionSnapshot getCachedPosition(@Nonnull UUID var1) {
class="kw">return this.positionCache.get(var1);
}
class="kw">public ExecutorService getVoiceExecutor(@Nonnull UUID var1) {
int var2 = Math.floorMod(var1.hashCode(), 4);
class="kw">return this.voiceExecutors[var2];
}
class="kw">public boolean isShutdown() {
class="kw">return this.isShutdown;
}
class="kw">public Map<UUID, VoicePlayerState> getPlayerStates() {
class="kw">return this.playerStates;
}
class="kw">public record PositionSnapshot(@Nonnull Vector3dc position, boolean isUnderwater, @Nonnull UUID worldId, int networkId, boolean isDead) {
class="kw">public PositionSnapshot {
}
class="kw">static VoiceModule.PositionSnapshot atEyeHeight(@Nonnull Vector3dc var0, float var1, boolean var2, @Nonnull UUID var3, int var4, boolean var5) {
Vector3d var6 = new Vector3d(var0.x(), var0.y() + var1, var0.z());
class="kw">return new VoiceModule.PositionSnapshot(var6, var2, var3, var4, var5);
}
}
class="kw">private record RegisteredInterceptor(int priority, @Nonnull PlayerVoiceInterceptor interceptor) {
class="kw">private RegisteredInterceptor {
}
}
}