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.jspecify.annotations.Nullable; @Environment(EnvType.CLIENT) public class ChannelAccess { private final Set channels = Sets.newIdentityHashSet(); private final Library library; private final Executor executor; public ChannelAccess(final Library library, final Executor executor) { this.library = library; this.executor = executor; } public CompletableFuture createHandle(final Library.Pool pool) { CompletableFuture result = new CompletableFuture(); this.executor.execute(() -> { Channel channel = this.library.acquireChannel(pool); if (channel != null) { ChannelAccess.ChannelHandle handle = new ChannelAccess.ChannelHandle(channel); this.channels.add(handle); result.complete(handle); } else { result.complete(null); } }); return result; } public void executeOnChannels(final Consumer> action) { this.executor.execute(() -> action.accept(this.channels.stream().map(channelHandle -> channelHandle.channel).filter(Objects::nonNull))); } public void scheduleTick() { this.executor.execute(() -> { Iterator it = this.channels.iterator(); while (it.hasNext()) { ChannelAccess.ChannelHandle handle = (ChannelAccess.ChannelHandle)it.next(); handle.channel.updateStream(); if (handle.channel.stopped()) { handle.release(); it.remove(); } } }); } public void clear() { this.channels.forEach(ChannelAccess.ChannelHandle::release); this.channels.clear(); } @Environment(EnvType.CLIENT) public class ChannelHandle { @Nullable private Channel channel; private boolean stopped; public boolean isStopped() { return this.stopped; } public ChannelHandle(final Channel channel) { Objects.requireNonNull(ChannelAccess.this); super(); this.channel = channel; } public void execute(final Consumer action) { ChannelAccess.this.executor.execute(() -> { if (this.channel != null) { action.accept(this.channel); } }); } public void release() { this.stopped = true; ChannelAccess.this.library.releaseChannel(this.channel); this.channel = null; } } }