minecraft-src/net/minecraft/client/sounds/ChannelAccess.java
2025-07-04 01:41:11 +03:00

114 lines
3.4 KiB
Java

package net.minecraft.client.sounds;
import com.google.common.collect.Sets;
import com.mojang.blaze3d.audio.Channel;
import com.mojang.blaze3d.audio.Library;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Stream;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import org.jetbrains.annotations.Nullable;
/**
* The ChannelAccess class provides access to channels for playing audio data using a given library and executor.
*/
@Environment(EnvType.CLIENT)
public class ChannelAccess {
private final Set<ChannelAccess.ChannelHandle> channels = Sets.newIdentityHashSet();
final Library library;
final Executor executor;
public ChannelAccess(Library library, Executor executor) {
this.library = library;
this.executor = executor;
}
/**
* Creates a new channel handle for the specified system mode and returns a CompletableFuture that completes with the handle when it is created.
* <p>
* @return a CompletableFuture that completes with the channel handle when it is created, or null if it cannot be created
*
* @param systemMode systemMode the system mode to create the channel handle for
*/
public CompletableFuture<ChannelAccess.ChannelHandle> createHandle(Library.Pool systemMode) {
CompletableFuture<ChannelAccess.ChannelHandle> completableFuture = new CompletableFuture();
this.executor.execute(() -> {
Channel channel = this.library.acquireChannel(systemMode);
if (channel != null) {
ChannelAccess.ChannelHandle channelHandle = new ChannelAccess.ChannelHandle(channel);
this.channels.add(channelHandle);
completableFuture.complete(channelHandle);
} else {
completableFuture.complete(null);
}
});
return completableFuture;
}
/**
* @param sourceStreamConsumer the consumer to execute on the stream of channels
*/
public void executeOnChannels(Consumer<Stream<Channel>> sourceStreamConsumer) {
this.executor.execute(() -> sourceStreamConsumer.accept(this.channels.stream().map(channelHandle -> channelHandle.channel).filter(Objects::nonNull)));
}
public void scheduleTick() {
this.executor.execute(() -> {
Iterator<ChannelAccess.ChannelHandle> iterator = this.channels.iterator();
while (iterator.hasNext()) {
ChannelAccess.ChannelHandle channelHandle = (ChannelAccess.ChannelHandle)iterator.next();
channelHandle.channel.updateStream();
if (channelHandle.channel.stopped()) {
channelHandle.release();
iterator.remove();
}
}
});
}
public void clear() {
this.channels.forEach(ChannelAccess.ChannelHandle::release);
this.channels.clear();
}
/**
* Represents a handle to a channel.
*/
@Environment(EnvType.CLIENT)
public class ChannelHandle {
@Nullable
Channel channel;
private boolean stopped;
/**
* {@return {@code true} if the channel has been stopped, {@code false} otherwise}
*/
public boolean isStopped() {
return this.stopped;
}
public ChannelHandle(final Channel channel) {
this.channel = channel;
}
public void execute(Consumer<Channel> soundConsumer) {
ChannelAccess.this.executor.execute(() -> {
if (this.channel != null) {
soundConsumer.accept(this.channel);
}
});
}
public void release() {
this.stopped = true;
ChannelAccess.this.library.releaseChannel(this.channel);
this.channel = null;
}
}
}