mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-10 09:20:28 +00:00
Send entire DFPWM encoder state to the client
This ensures the client decoder is in sync with the server. Well, mostly - we don't handle the anti-jerk, but that should correct itself within a few samples. Fixes #1748
This commit is contained in:
parent
ffb62dfa02
commit
61f9b1d0c6
@ -17,6 +17,7 @@ import dan200.computercraft.shared.computer.terminal.TerminalState;
|
|||||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@ -27,7 +28,6 @@ import net.minecraft.world.entity.player.Player;
|
|||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,7 +79,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
|
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio buffer) {
|
||||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package dan200.computercraft.client.sound;
|
package dan200.computercraft.client.sound;
|
||||||
|
|
||||||
import com.mojang.blaze3d.audio.Channel;
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.client.sounds.AudioStream;
|
import net.minecraft.client.sounds.AudioStream;
|
||||||
@ -36,7 +37,7 @@ class DfpwmStream implements AudioStream {
|
|||||||
/**
|
/**
|
||||||
* The {@link Channel} which this sound is playing on.
|
* The {@link Channel} which this sound is playing on.
|
||||||
*
|
*
|
||||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Channel channel;
|
Channel channel;
|
||||||
@ -44,21 +45,23 @@ class DfpwmStream implements AudioStream {
|
|||||||
/**
|
/**
|
||||||
* The underlying {@link SoundEngine} executor.
|
* The underlying {@link SoundEngine} executor.
|
||||||
*
|
*
|
||||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||||
* @see SoundEngine#executor
|
* @see SoundEngine#executor
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Executor executor;
|
Executor executor;
|
||||||
|
|
||||||
private int charge = 0; // q
|
|
||||||
private int strength = 0; // s
|
|
||||||
private int lowPassCharge;
|
private int lowPassCharge;
|
||||||
private boolean previousBit = false;
|
|
||||||
|
|
||||||
DfpwmStream() {
|
DfpwmStream() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(ByteBuffer input) {
|
void push(EncodedAudio audio) {
|
||||||
|
var charge = audio.charge();
|
||||||
|
var strength = audio.strength();
|
||||||
|
var previousBit = audio.previousBit();
|
||||||
|
var input = audio.audio();
|
||||||
|
|
||||||
var readable = input.remaining();
|
var readable = input.remaining();
|
||||||
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@ package dan200.computercraft.client.sound;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.core.util.Nullability;
|
import dan200.computercraft.core.util.Nullability;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
||||||
@ -25,7 +25,7 @@ public class SpeakerInstance {
|
|||||||
SpeakerInstance() {
|
SpeakerInstance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushAudio(ByteBuffer buffer) {
|
private void pushAudio(EncodedAudio buffer) {
|
||||||
var sound = this.sound;
|
var sound = this.sound;
|
||||||
|
|
||||||
var stream = currentStream;
|
var stream = currentStream;
|
||||||
@ -43,7 +43,7 @@ public class SpeakerInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
|
public void playAudio(SpeakerPosition position, float volume, EncodedAudio buffer) {
|
||||||
pushAudio(buffer);
|
pushAudio(buffer);
|
||||||
|
|
||||||
var soundManager = Minecraft.getInstance().getSoundManager();
|
var soundManager = Minecraft.getInstance().getSoundManager();
|
||||||
|
@ -8,6 +8,7 @@ import dan200.computercraft.shared.command.text.TableBuilder;
|
|||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@ -15,7 +16,6 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,7 +34,7 @@ public interface ClientNetworkContext {
|
|||||||
|
|
||||||
void handlePocketComputerDeleted(int instanceId);
|
void handlePocketComputerDeleted(int instanceId);
|
||||||
|
|
||||||
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer audio);
|
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio audio);
|
||||||
|
|
||||||
void handleSpeakerMove(UUID source, SpeakerPosition.Message position);
|
void handleSpeakerMove(UUID source, SpeakerPosition.Message position);
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ package dan200.computercraft.shared.network.client;
|
|||||||
import dan200.computercraft.shared.network.MessageType;
|
import dan200.computercraft.shared.network.MessageType;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.NetworkMessages;
|
import dan200.computercraft.shared.network.NetworkMessages;
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,10 +24,10 @@ import java.util.UUID;
|
|||||||
public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkContext> {
|
public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkContext> {
|
||||||
private final UUID source;
|
private final UUID source;
|
||||||
private final SpeakerPosition.Message pos;
|
private final SpeakerPosition.Message pos;
|
||||||
private final ByteBuffer content;
|
private final EncodedAudio content;
|
||||||
private final float volume;
|
private final float volume;
|
||||||
|
|
||||||
public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, ByteBuffer content) {
|
public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, EncodedAudio content) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.pos = pos.asMessage();
|
this.pos = pos.asMessage();
|
||||||
this.content = content;
|
this.content = content;
|
||||||
@ -38,10 +38,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
|||||||
source = buf.readUUID();
|
source = buf.readUUID();
|
||||||
pos = SpeakerPosition.Message.read(buf);
|
pos = SpeakerPosition.Message.read(buf);
|
||||||
volume = buf.readFloat();
|
volume = buf.readFloat();
|
||||||
|
content = EncodedAudio.read(buf);
|
||||||
var bytes = new byte[buf.readableBytes()];
|
|
||||||
buf.readBytes(bytes);
|
|
||||||
content = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,7 +46,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
|||||||
buf.writeUUID(source);
|
buf.writeUUID(source);
|
||||||
pos.write(buf);
|
pos.write(buf);
|
||||||
buf.writeFloat(volume);
|
buf.writeFloat(volume);
|
||||||
buf.writeBytes(content.duplicate());
|
content.write(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,7 +37,7 @@ class DfpwmState {
|
|||||||
private boolean unplayed = true;
|
private boolean unplayed = true;
|
||||||
private long clientEndTime = PauseAwareTimer.getTime();
|
private long clientEndTime = PauseAwareTimer.getTime();
|
||||||
private float pendingVolume = 1.0f;
|
private float pendingVolume = 1.0f;
|
||||||
private @Nullable ByteBuffer pendingAudio;
|
private @Nullable EncodedAudio pendingAudio;
|
||||||
|
|
||||||
synchronized boolean pushBuffer(LuaTable<?, ?> table, int size, Optional<Double> volume) throws LuaException {
|
synchronized boolean pushBuffer(LuaTable<?, ?> table, int size, Optional<Double> volume) throws LuaException {
|
||||||
if (pendingAudio != null) return false;
|
if (pendingAudio != null) return false;
|
||||||
@ -45,6 +45,10 @@ class DfpwmState {
|
|||||||
var outSize = size / 8;
|
var outSize = size / 8;
|
||||||
var buffer = ByteBuffer.allocate(outSize);
|
var buffer = ByteBuffer.allocate(outSize);
|
||||||
|
|
||||||
|
var initialCharge = charge;
|
||||||
|
var initialStrength = strength;
|
||||||
|
var initialPreviousBit = previousBit;
|
||||||
|
|
||||||
for (var i = 0; i < outSize; i++) {
|
for (var i = 0; i < outSize; i++) {
|
||||||
var thisByte = 0;
|
var thisByte = 0;
|
||||||
for (var j = 1; j <= 8; j++) {
|
for (var j = 1; j <= 8; j++) {
|
||||||
@ -80,7 +84,7 @@ class DfpwmState {
|
|||||||
|
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
|
|
||||||
pendingAudio = buffer;
|
pendingAudio = new EncodedAudio(initialCharge, initialStrength, initialPreviousBit, buffer);
|
||||||
pendingVolume = (float) clampVolume(volume.orElse((double) pendingVolume));
|
pendingVolume = (float) clampVolume(volume.orElse((double) pendingVolume));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -89,12 +93,12 @@ class DfpwmState {
|
|||||||
return pendingAudio != null && now >= clientEndTime - CLIENT_BUFFER;
|
return pendingAudio != null && now >= clientEndTime - CLIENT_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer pullPending(long now) {
|
EncodedAudio pullPending(long now) {
|
||||||
var audio = pendingAudio;
|
var audio = pendingAudio;
|
||||||
if (audio == null) throw new IllegalStateException("Should not pull pending audio yet");
|
if (audio == null) throw new IllegalStateException("Should not pull pending audio yet");
|
||||||
pendingAudio = null;
|
pendingAudio = null;
|
||||||
// Compute when we should consider sending the next packet.
|
// Compute when we should consider sending the next packet.
|
||||||
clientEndTime = Math.max(now, clientEndTime) + (audio.remaining() * SECOND * 8 / SAMPLE_RATE);
|
clientEndTime = Math.max(now, clientEndTime) + (audio.audio().remaining() * SECOND * 8 / SAMPLE_RATE);
|
||||||
unplayed = false;
|
unplayed = false;
|
||||||
return audio;
|
return audio;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.peripheral.speaker;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chunk of encoded audio, along with the state required for the decoder to reproduce the original audio samples.
|
||||||
|
*
|
||||||
|
* @param charge The DFPWM charge.
|
||||||
|
* @param strength The DFPWM strength.
|
||||||
|
* @param previousBit The previous bit.
|
||||||
|
* @param audio The block of encoded audio.
|
||||||
|
*/
|
||||||
|
public record EncodedAudio(int charge, int strength, boolean previousBit, ByteBuffer audio) {
|
||||||
|
public void write(FriendlyByteBuf buf) {
|
||||||
|
buf.writeVarInt(charge());
|
||||||
|
buf.writeVarInt(strength());
|
||||||
|
buf.writeBoolean(previousBit());
|
||||||
|
buf.writeBytes(audio().duplicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EncodedAudio read(FriendlyByteBuf buf) {
|
||||||
|
var charge = buf.readVarInt();
|
||||||
|
var strength = buf.readVarInt();
|
||||||
|
var previousBit = buf.readBoolean();
|
||||||
|
|
||||||
|
var bytes = new byte[buf.readableBytes()];
|
||||||
|
buf.readBytes(bytes);
|
||||||
|
|
||||||
|
return new EncodedAudio(charge, strength, previousBit, ByteBuffer.wrap(bytes));
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.sound;
|
package dan200.computercraft.client.sound;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -16,7 +17,7 @@ public class DfpwmStreamTest {
|
|||||||
var stream = new DfpwmStream();
|
var stream = new DfpwmStream();
|
||||||
|
|
||||||
var input = ByteBuffer.wrap(new byte[]{ 43, -31, 33, 44, 30, -16, -85, 23, -3, -55, 46, -70, 68, -67, 74, -96, -68, 16, 94, -87, -5, 87, 11, -16, 19, 92, 85, -71, 126, 5, -84, 64, 17, -6, 85, -11, -1, -87, -12, 1, 85, -56, 33, -80, 82, 104, -93, 17, 126, 23, 91, -30, 37, -32, 117, -72, -58, 11, -76, 19, -108, 86, -65, -10, -1, -68, -25, 10, -46, 85, 124, -54, 15, -24, 43, -94, 117, 63, -36, 15, -6, 88, 87, -26, -83, 106, 41, 13, -28, -113, -10, -66, 119, -87, -113, 68, -55, 40, -107, 62, 20, 72, 3, -96, 114, -87, -2, 39, -104, 30, 20, 42, 84, 24, 47, 64, 43, 61, -35, 95, -65, 42, 61, 42, -50, 4, -9, 81 });
|
var input = ByteBuffer.wrap(new byte[]{ 43, -31, 33, 44, 30, -16, -85, 23, -3, -55, 46, -70, 68, -67, 74, -96, -68, 16, 94, -87, -5, 87, 11, -16, 19, 92, 85, -71, 126, 5, -84, 64, 17, -6, 85, -11, -1, -87, -12, 1, 85, -56, 33, -80, 82, 104, -93, 17, 126, 23, 91, -30, 37, -32, 117, -72, -58, 11, -76, 19, -108, 86, -65, -10, -1, -68, -25, 10, -46, 85, 124, -54, 15, -24, 43, -94, 117, 63, -36, 15, -6, 88, 87, -26, -83, 106, 41, 13, -28, -113, -10, -66, 119, -87, -113, 68, -55, 40, -107, 62, 20, 72, 3, -96, 114, -87, -2, 39, -104, 30, 20, 42, 84, 24, 47, 64, 43, 61, -35, 95, -65, 42, 61, 42, -50, 4, -9, 81 });
|
||||||
stream.push(input);
|
stream.push(new EncodedAudio(0, 0, false, input));
|
||||||
|
|
||||||
var buffer = stream.read(1024 + 1);
|
var buffer = stream.read(1024 + 1);
|
||||||
assertEquals(1024, buffer.remaining(), "Must have read 1024 bytes");
|
assertEquals(1024, buffer.remaining(), "Must have read 1024 bytes");
|
||||||
|
@ -23,7 +23,7 @@ class DfpwmStateTest {
|
|||||||
|
|
||||||
var state = new DfpwmState();
|
var state = new DfpwmState();
|
||||||
state.pushBuffer(new ObjectLuaTable(inputTbl), input.length, Optional.empty());
|
state.pushBuffer(new ObjectLuaTable(inputTbl), input.length, Optional.empty());
|
||||||
var result = state.pullPending(0);
|
var result = state.pullPending(0).audio();
|
||||||
var contents = new byte[result.remaining()];
|
var contents = new byte[result.remaining()];
|
||||||
result.get(contents);
|
result.get(contents);
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.peripheral.speaker;
|
||||||
|
|
||||||
|
import dan200.computercraft.test.core.ArbitraryByteBuffer;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.jqwik.api.*;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class EncodedAudioTest {
|
||||||
|
/**
|
||||||
|
* Sends the audio on a roundtrip, ensuring that its contents are reassembled on the other end.
|
||||||
|
*
|
||||||
|
* @param audio The message to send.
|
||||||
|
*/
|
||||||
|
@Property
|
||||||
|
public void testRoundTrip(@ForAll("audio") EncodedAudio audio) {
|
||||||
|
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
|
||||||
|
audio.write(buffer);
|
||||||
|
|
||||||
|
var converted = EncodedAudio.read(buffer);
|
||||||
|
assertEquals(buffer.readableBytes(), 0, "Whole packet was read");
|
||||||
|
|
||||||
|
assertThat("Messages are equal", converted, equalTo(converted));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provide
|
||||||
|
Arbitrary<EncodedAudio> audio() {
|
||||||
|
return Combinators.combine(
|
||||||
|
Arbitraries.integers(),
|
||||||
|
Arbitraries.integers(),
|
||||||
|
Arbitraries.of(true, false),
|
||||||
|
ArbitraryByteBuffer.bytes().ofMaxSize(1000)
|
||||||
|
).as(EncodedAudio::new);
|
||||||
|
}
|
||||||
|
}
|
@ -105,7 +105,6 @@ internal class SideProvider {
|
|||||||
companion object {
|
companion object {
|
||||||
private val notClientPackages = listOf(
|
private val notClientPackages = listOf(
|
||||||
// Ugly! But we do what we must.
|
// Ugly! But we do what we must.
|
||||||
"net.fabricmc.fabric.api.client.itemgroup",
|
|
||||||
"dan200.computercraft.shared.network.client",
|
"dan200.computercraft.shared.network.client",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user