diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java index 6d66a55fd..50f12c15e 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/AbstractComputerScreen.java @@ -7,7 +7,7 @@ package dan200.computercraft.client.gui; import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.DynamicImageButton; import dan200.computercraft.client.gui.widgets.TerminalWidget; -import dan200.computercraft.client.platform.ClientPlatformHelper; +import dan200.computercraft.client.network.ClientNetworking; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.InputHandler; @@ -207,7 +207,7 @@ public abstract class AbstractComputerScreen ext return; } - if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientPlatformHelper.get()::sendToServer); + if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer); } public void uploadResult(UploadResult result, @Nullable Component message) { diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/ClientInputHandler.java b/projects/common/src/client/java/dan200/computercraft/client/gui/ClientInputHandler.java index 0ddc4607e..bb6827616 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/ClientInputHandler.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/ClientInputHandler.java @@ -4,7 +4,7 @@ package dan200.computercraft.client.gui; -import dan200.computercraft.client.platform.ClientPlatformHelper; +import dan200.computercraft.client.network.ClientNetworking; import dan200.computercraft.shared.computer.core.InputHandler; import dan200.computercraft.shared.computer.menu.ComputerMenu; import dan200.computercraft.shared.network.server.ComputerActionServerMessage; @@ -29,51 +29,51 @@ public final class ClientInputHandler implements InputHandler { @Override public void turnOn() { - ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON)); + ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON)); } @Override public void shutdown() { - ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN)); + ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN)); } @Override public void reboot() { - ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT)); + ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT)); } @Override public void queueEvent(String event, @Nullable Object[] arguments) { - ClientPlatformHelper.get().sendToServer(new QueueEventServerMessage(menu, event, arguments)); + ClientNetworking.sendToServer(new QueueEventServerMessage(menu, event, arguments)); } @Override public void keyDown(int key, boolean repeat) { - ClientPlatformHelper.get().sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key)); + ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key)); } @Override public void keyUp(int key) { - ClientPlatformHelper.get().sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key)); + ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key)); } @Override public void mouseClick(int button, int x, int y) { - ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y)); + ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y)); } @Override public void mouseUp(int button, int x, int y) { - ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y)); + ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y)); } @Override public void mouseDrag(int button, int x, int y) { - ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y)); + ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y)); } @Override public void mouseScroll(int direction, int x, int y) { - ClientPlatformHelper.get().sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y)); + ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y)); } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/network/ClientNetworking.java b/projects/common/src/client/java/dan200/computercraft/client/network/ClientNetworking.java new file mode 100644 index 000000000..bf8070fa9 --- /dev/null +++ b/projects/common/src/client/java/dan200/computercraft/client/network/ClientNetworking.java @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.client.network; + +import dan200.computercraft.client.platform.ClientPlatformHelper; +import dan200.computercraft.shared.network.NetworkMessage; +import dan200.computercraft.shared.network.server.ServerNetworkContext; +import net.minecraft.client.Minecraft; + +/** + * Methods for sending packets from clients to the server. + */ +public class ClientNetworking { + /** + * Send a network message to the server. + * + * @param message The message to send. + */ + public static void sendToServer(NetworkMessage message) { + var connection = Minecraft.getInstance().getConnection(); + if (connection != null) connection.send(ClientPlatformHelper.get().createPacket(message)); + } +} diff --git a/projects/common/src/client/java/dan200/computercraft/client/platform/AbstractClientNetworkContext.java b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientNetworkContextImpl.java similarity index 69% rename from projects/common/src/client/java/dan200/computercraft/client/platform/AbstractClientNetworkContext.java rename to projects/common/src/client/java/dan200/computercraft/client/platform/ClientNetworkContextImpl.java index 7c454b47f..941c56c73 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/platform/AbstractClientNetworkContext.java +++ b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientNetworkContextImpl.java @@ -4,6 +4,7 @@ package dan200.computercraft.client.platform; +import com.google.auto.service.AutoService; import dan200.computercraft.client.ClientTableFormatter; import dan200.computercraft.client.gui.AbstractComputerScreen; import dan200.computercraft.client.gui.OptionScreen; @@ -17,30 +18,30 @@ import dan200.computercraft.shared.computer.upload.UploadResult; import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; -import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import javax.annotation.Nullable; +import java.nio.ByteBuffer; import java.util.UUID; /** - * The base implementation of {@link ClientNetworkContext}. - *

- * This should be extended by mod loader specific modules with the remaining abstract methods. + * The client-side implementation of {@link ClientNetworkContext}. */ -public abstract class AbstractClientNetworkContext implements ClientNetworkContext { +@AutoService(ClientNetworkContext.class) +public final class ClientNetworkContextImpl implements ClientNetworkContext { @Override - public final void handleChatTable(TableBuilder table) { + public void handleChatTable(TableBuilder table) { ClientTableFormatter.INSTANCE.display(table); } @Override - public final void handleComputerTerminal(int containerId, TerminalState terminal) { + public void handleComputerTerminal(int containerId, TerminalState terminal) { Player player = Minecraft.getInstance().player; if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) { menu.updateTerminal(terminal); @@ -48,7 +49,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte } @Override - public final void handleMonitorData(BlockPos pos, TerminalState terminal) { + public void handleMonitorData(BlockPos pos, TerminalState terminal) { var player = Minecraft.getInstance().player; if (player == null) return; @@ -59,44 +60,46 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte } @Override - public final void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) { + public void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name) { + var mc = Minecraft.getInstance(); + ClientPlatformHelper.get().playStreamingMusic(pos, sound); + if (name != null) mc.gui.setNowPlaying(Component.literal(name)); + } + + @Override + public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) { var computer = ClientPocketComputers.get(instanceId, terminal.colour); computer.setState(state, lightState); if (terminal.hasTerminal()) computer.setTerminal(terminal); } @Override - public final void handlePocketComputerDeleted(int instanceId) { + public void handlePocketComputerDeleted(int instanceId) { ClientPocketComputers.remove(instanceId); } @Override - public final void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume) { - SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume); + public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) { + SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer); } @Override - public final void handleSpeakerAudioPush(UUID source, ByteBuf buffer) { - SpeakerManager.getSound(source).pushAudio(buffer); - } - - @Override - public final void handleSpeakerMove(UUID source, SpeakerPosition.Message position) { + public void handleSpeakerMove(UUID source, SpeakerPosition.Message position) { SpeakerManager.moveSound(source, reifyPosition(position)); } @Override - public final void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, ResourceLocation sound, float volume, float pitch) { + public void handleSpeakerPlay(UUID source, SpeakerPosition.Message position, ResourceLocation sound, float volume, float pitch) { SpeakerManager.getSound(source).playSound(reifyPosition(position), sound, volume, pitch); } @Override - public final void handleSpeakerStop(UUID source) { + public void handleSpeakerStop(UUID source) { SpeakerManager.stopSound(source); } @Override - public final void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) { + public void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage) { var minecraft = Minecraft.getInstance(); var screen = OptionScreen.unwrap(minecraft.screen); diff --git a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java index 70d5dc327..4200f9545 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java +++ b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java @@ -9,6 +9,10 @@ import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ServerGamePacketListener; +import net.minecraft.sounds.SoundEvent; import javax.annotation.Nullable; @@ -18,11 +22,12 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C } /** - * Send a network message to the server. + * Convert a serverbound {@link NetworkMessage} to a Minecraft {@link Packet}. * - * @param message The message to send. + * @param message The messsge to convert. + * @return The converted message. */ - void sendToServer(NetworkMessage message); + Packet createPacket(NetworkMessage message); /** * Render a {@link BakedModel}, using any loader-specific hooks. @@ -35,4 +40,13 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C * @param tints Block colour tints to apply to the model. */ void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints); + + /** + * Play a record at a particular position. + * + * @param pos The position to play this record. + * @param sound The record to play, or {@code null} to stop it. + * @see net.minecraft.client.renderer.LevelRenderer#playStreamingMusic(SoundEvent, BlockPos) + */ + void playStreamingMusic(BlockPos pos, @Nullable SoundEvent sound); } diff --git a/projects/common/src/client/java/dan200/computercraft/client/sound/DfpwmStream.java b/projects/common/src/client/java/dan200/computercraft/client/sound/DfpwmStream.java index 40739c2ec..6cbca4c3a 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/sound/DfpwmStream.java +++ b/projects/common/src/client/java/dan200/computercraft/client/sound/DfpwmStream.java @@ -6,7 +6,7 @@ package dan200.computercraft.client.sound; import com.mojang.blaze3d.audio.Channel; import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral; -import io.netty.buffer.ByteBuf; +import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; import net.minecraft.client.sounds.AudioStream; import net.minecraft.client.sounds.SoundEngine; import org.lwjgl.BufferUtils; @@ -36,7 +36,7 @@ class DfpwmStream implements AudioStream { /** * The {@link Channel} which this sound is playing on. * - * @see SpeakerInstance#pushAudio(ByteBuf) + * @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer) */ @Nullable Channel channel; @@ -44,7 +44,7 @@ class DfpwmStream implements AudioStream { /** * The underlying {@link SoundEngine} executor. * - * @see SpeakerInstance#pushAudio(ByteBuf) + * @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer) * @see SoundEngine#executor */ @Nullable @@ -58,12 +58,12 @@ class DfpwmStream implements AudioStream { DfpwmStream() { } - void push(ByteBuf input) { - var readable = input.readableBytes(); + void push(ByteBuffer input) { + var readable = input.remaining(); var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder()); for (var i = 0; i < readable; i++) { - var inputByte = input.readByte(); + var inputByte = input.get(); for (var j = 0; j < 8; j++) { var currentBit = (inputByte & 1) != 0; var target = currentBit ? 127 : -128; diff --git a/projects/common/src/client/java/dan200/computercraft/client/sound/SpeakerInstance.java b/projects/common/src/client/java/dan200/computercraft/client/sound/SpeakerInstance.java index ca336b4d1..472bcffb1 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/sound/SpeakerInstance.java +++ b/projects/common/src/client/java/dan200/computercraft/client/sound/SpeakerInstance.java @@ -7,11 +7,11 @@ package dan200.computercraft.client.sound; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.core.util.Nullability; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; -import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; 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. @@ -25,7 +25,7 @@ public class SpeakerInstance { SpeakerInstance() { } - public synchronized void pushAudio(ByteBuf buffer) { + private void pushAudio(ByteBuffer buffer) { var sound = this.sound; var stream = currentStream; @@ -43,7 +43,9 @@ public class SpeakerInstance { } } - public void playAudio(SpeakerPosition position, float volume) { + public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) { + pushAudio(buffer); + var soundManager = Minecraft.getInstance().getSoundManager(); if (sound != null && sound.stream != currentStream) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java b/projects/common/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java index fa683ab1f..2428e5637 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared.command.text; import dan200.computercraft.core.util.Nullability; import dan200.computercraft.shared.command.CommandUtils; import dan200.computercraft.shared.network.client.ChatTableClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -105,7 +105,7 @@ public class TableBuilder { if (CommandUtils.isPlayer(source)) { trim(18); var player = (ServerPlayer) Nullability.assertNonNull(source.getEntity()); - PlatformHelper.get().sendToPlayer(new ChatTableClientMessage(this), player); + ServerNetworking.sendToPlayer(new ChatTableClientMessage(this), player); } else { trim(100); new ServerTableFormatter(source).display(this); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index 23eb06216..72bb03570 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -20,7 +20,7 @@ import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -142,7 +142,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { for (var player : server.getPlayerList().getPlayers()) { if (player.containerMenu instanceof ComputerMenu && ((ComputerMenu) player.containerMenu).getComputer() == this) { - PlatformHelper.get().sendToPlayer(createPacket.apply(player.containerMenu), player); + ServerNetworking.sendToPlayer(createPacket.apply(player.containerMenu), player); } } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java index f9fa8e1ed..21753d281 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java @@ -11,7 +11,7 @@ import dan200.computercraft.shared.computer.upload.FileSlice; import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.computer.upload.UploadResult; import dan200.computercraft.shared.network.client.UploadResultMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import net.minecraft.network.chat.Component; @@ -138,7 +138,7 @@ public class ServerInputState im return; } - PlatformHelper.get().sendToPlayer(finishUpload(uploader), uploader); + ServerNetworking.sendToPlayer(finishUpload(uploader), uploader); } private UploadResultMessage finishUpload(ServerPlayer player) { @@ -159,7 +159,7 @@ public class ServerInputState im toUpload.stream().map(x -> new TransferredFile(x.getName(), new ByteBufferChannel(x.getBytes()))).toList(), () -> { if (player.isAlive() && player.containerMenu == owner) { - PlatformHelper.get().sendToPlayer(UploadResultMessage.consumed(owner), player); + ServerNetworking.sendToPlayer(UploadResultMessage.consumed(owner), player); } }), }); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/client/ClientNetworkContext.java b/projects/common/src/main/java/dan200/computercraft/shared/network/client/ClientNetworkContext.java index a0268454f..d981544d4 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/client/ClientNetworkContext.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/client/ClientNetworkContext.java @@ -4,30 +4,24 @@ package dan200.computercraft.shared.network.client; -import dan200.computercraft.impl.Services; import dan200.computercraft.shared.command.text.TableBuilder; import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.computer.upload.UploadResult; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; -import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import javax.annotation.Nullable; +import java.nio.ByteBuffer; import java.util.UUID; /** * The context under which clientbound packets are evaluated. */ public interface ClientNetworkContext { - static ClientNetworkContext get() { - var instance = Instance.INSTANCE; - return instance == null ? Services.raise(ClientNetworkContext.class, Instance.ERROR) : instance; - } - void handleChatTable(TableBuilder table); void handleComputerTerminal(int containerId, TerminalState terminal); @@ -40,9 +34,7 @@ public interface ClientNetworkContext { void handlePocketComputerDeleted(int instanceId); - void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume); - - void handleSpeakerAudioPush(UUID source, ByteBuf buffer); + void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer audio); void handleSpeakerMove(UUID source, SpeakerPosition.Message position); @@ -51,18 +43,4 @@ public interface ClientNetworkContext { void handleSpeakerStop(UUID source); void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage); - - final class Instance { - static final @Nullable ClientNetworkContext INSTANCE; - static final @Nullable Throwable ERROR; - - static { - var helper = Services.tryLoad(ClientNetworkContext.class); - INSTANCE = helper.instance(); - ERROR = helper.error(); - } - - private Instance() { - } - } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java b/projects/common/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java index c98f4366a..6de8a0b57 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java @@ -11,12 +11,9 @@ import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; import net.minecraft.network.FriendlyByteBuf; -import javax.annotation.Nullable; import java.nio.ByteBuffer; import java.util.UUID; -import static dan200.computercraft.core.util.Nullability.assertNonNull; - /** * Starts a sound on the client. *

@@ -27,7 +24,7 @@ import static dan200.computercraft.core.util.Nullability.assertNonNull; public class SpeakerAudioClientMessage implements NetworkMessage { private final UUID source; private final SpeakerPosition.Message pos; - private final @Nullable ByteBuffer content; + private final ByteBuffer content; private final float volume; public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, ByteBuffer content) { @@ -42,10 +39,9 @@ public class SpeakerAudioClientMessage implements NetworkMessage message, ServerPlayer player) { + player.connection.send(PlatformHelper.get().createPacket(message)); + } + + /** + * Send a message to a set of players. + * + * @param message The message to send. + * @param players The players to send it to. + */ + public static void sendToPlayers(NetworkMessage message, Collection players) { + if (players.isEmpty()) return; + var packet = PlatformHelper.get().createPacket(message); + for (var player : players) player.connection.send(packet); + } + + /** + * Send a message to all players. + * + * @param message The message to send. + * @param server The current server. + */ + public static void sendToAllPlayers(NetworkMessage message, MinecraftServer server) { + server.getPlayerList().broadcastAll(PlatformHelper.get().createPacket(message)); + } + + /** + * Send a message to all players around a point. + * + * @param message The message to send. + * @param level The level the point is in. + * @param pos The centre position. + * @param distance The distance to the centre players must be within. + */ + public static void sendToAllAround(NetworkMessage message, ServerLevel level, Vec3 pos, float distance) { + level.getServer().getPlayerList().broadcast(null, pos.x, pos.y, pos.z, distance, level.dimension(), PlatformHelper.get().createPacket(message)); + } + + /** + * Send a message to all players tracking a chunk. + * + * @param message The message to send. + * @param chunk The chunk players must be tracking. + */ + public static void sendToAllTracking(NetworkMessage message, LevelChunk chunk) { + var packet = PlatformHelper.get().createPacket(message); + for (var player : ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false)) { + player.connection.send(packet); + } + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java index e8d1c75c6..0ea28530a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java @@ -11,7 +11,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.common.AbstractContainerBlockEntity; import dan200.computercraft.shared.network.client.PlayRecordClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -315,7 +315,7 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity { } private void sendMessage(PlayRecordClientMessage message) { - PlatformHelper.get().sendToAllAround(message, (ServerLevel) getLevel(), Vec3.atCenterOf(getBlockPos()), 64); + ServerNetworking.sendToAllAround(message, (ServerLevel) getLevel(), Vec3.atCenterOf(getBlockPos()), 64); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java index bf3f56be5..499c2acf8 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.network.client.MonitorClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.chunk.LevelChunk; @@ -40,7 +40,7 @@ public final class MonitorWatcher { if (serverMonitor == null || monitor.enqueued) continue; var state = getState(monitor, serverMonitor); - PlatformHelper.get().sendToPlayer(new MonitorClientMessage(monitor.getBlockPos(), state), player); + ServerNetworking.sendToPlayer(new MonitorClientMessage(monitor.getBlockPos(), state), player); } } @@ -66,7 +66,7 @@ public final class MonitorWatcher { } var state = getState(tile, monitor); - PlatformHelper.get().sendToAllTracking(new MonitorClientMessage(pos, state), chunk); + ServerNetworking.sendToAllTracking(new MonitorClientMessage(pos, state), chunk); limit -= state.size(); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlockEntity.java index edb3593b1..972f67be5 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerBlockEntity.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.util.Nullability; import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -32,7 +32,7 @@ public class SpeakerBlockEntity extends BlockEntity { public void setRemoved() { super.setRemoved(); if (level != null && !level.isClientSide) { - PlatformHelper.get().sendToAllPlayers(new SpeakerStopClientMessage(peripheral.getSource()), Nullability.assertNonNull(getLevel().getServer())); + ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(peripheral.getSource()), Nullability.assertNonNull(getLevel().getServer())); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index acda7fbe0..9791b9977 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -17,7 +17,7 @@ import dan200.computercraft.shared.network.client.SpeakerAudioClientMessage; import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage; import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage; import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.util.PauseAwareTimer; import net.minecraft.ResourceLocationException; import net.minecraft.core.BlockPos; @@ -116,14 +116,14 @@ public abstract class SpeakerPeripheral implements IPeripheral { // Stop the speaker and nuke the position, so we don't update it again. if (shouldStop && lastPosition != null) { lastPosition = null; - PlatformHelper.get().sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server); + ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server); return; } var now = PauseAwareTimer.getTime(); if (sound != null) { lastPlayTime = clock; - PlatformHelper.get().sendToAllAround( + ServerNetworking.sendToAllAround( new SpeakerPlayClientMessage(getSource(), position, sound.sound, sound.volume, sound.pitch), (ServerLevel) level, pos, sound.volume * 16 ); @@ -131,7 +131,7 @@ public abstract class SpeakerPeripheral implements IPeripheral { } else if (dfpwmState != null && dfpwmState.shouldSendPending(now)) { // If clients need to receive another batch of audio, send it and then notify computers our internal buffer is // free again. - PlatformHelper.get().sendToAllTracking( + ServerNetworking.sendToAllTracking( new SpeakerAudioClientMessage(getSource(), position, dfpwmState.getVolume(), dfpwmState.pullPending(now)), level.getChunkAt(BlockPos.containing(pos)) ); @@ -150,7 +150,7 @@ public abstract class SpeakerPeripheral implements IPeripheral { // in the last second. if (lastPosition != null && (clock - lastPositionTime) >= 20 && !lastPosition.withinDistance(position, 0.1)) { // TODO: What to do when entities move away? How do we notify people left behind that they're gone. - PlatformHelper.get().sendToAllTracking( + ServerNetworking.sendToAllTracking( new SpeakerMoveClientMessage(getSource(), position), level.getChunkAt(BlockPos.containing(pos)) ); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java index 64b861de1..8fb3abb7d 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java @@ -6,7 +6,7 @@ package dan200.computercraft.shared.peripheral.speaker; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; /** @@ -25,6 +25,6 @@ public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral { var server = level.getServer(); if (server == null || server.isStopped()) return; - PlatformHelper.get().sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server); + ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java b/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java index b621a36b3..8ed71f6da 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java @@ -20,9 +20,10 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; @@ -44,12 +45,10 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import javax.annotation.Nullable; -import java.util.Collection; import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -178,46 +177,12 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper > MessageType createMessageType(int id, ResourceLocation channel, Class klass, FriendlyByteBuf.Reader reader); /** - * Send a message to a specific player. + * Convert a clientbound {@link NetworkMessage} to a Minecraft {@link Packet}. * - * @param message The message to send. - * @param player The player to send it to. + * @param message The messsge to convert. + * @return The converted message. */ - void sendToPlayer(NetworkMessage message, ServerPlayer player); - - /** - * Send a message to a set of players. - * - * @param message The message to send. - * @param players The players to send it to. - */ - void sendToPlayers(NetworkMessage message, Collection players); - - /** - * Send a message to all players. - * - * @param message The message to send. - * @param server The current server. - */ - void sendToAllPlayers(NetworkMessage message, MinecraftServer server); - - /** - * Send a message to all players around a point. - * - * @param message The message to send. - * @param level The level the point is in. - * @param pos The centre position. - * @param distance The distance to the centre players must be within. - */ - void sendToAllAround(NetworkMessage message, ServerLevel level, Vec3 pos, float distance); - - /** - * Send a message to all players tracking a chunk. - * - * @param message The message to send. - * @param chunk The chunk players must be tracking. - */ - void sendToAllTracking(NetworkMessage message, LevelChunk chunk); + Packet createPacket(NetworkMessage message); /** * Create a {@link ComponentAccess} for surrounding peripherals. diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java index 35db4abdd..97a7f9c6a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -15,7 +15,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.network.client.PocketComputerDataMessage; import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage; -import dan200.computercraft.shared.platform.PlatformHelper; +import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -161,7 +161,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces if (sendState) { // Broadcast the state to all players tracking.addAll(getLevel().players()); - PlatformHelper.get().sendToPlayers(new PocketComputerDataMessage(this, false), tracking); + ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), tracking); } else { // Broadcast the state to new players. List added = new ArrayList<>(); @@ -169,7 +169,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces if (tracking.add(player)) added.add(player); } if (!added.isEmpty()) { - PlatformHelper.get().sendToPlayers(new PocketComputerDataMessage(this, false), added); + ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), added); } } } @@ -180,13 +180,13 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces if (entity instanceof ServerPlayer player && entity.isAlive()) { // Broadcast the terminal to the current player. - PlatformHelper.get().sendToPlayer(new PocketComputerDataMessage(this, true), player); + ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), player); } } @Override protected void onRemoved() { super.onRemoved(); - PlatformHelper.get().sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceID()), getLevel().getServer()); + ServerNetworking.sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceID()), getLevel().getServer()); } } diff --git a/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java b/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java index dfc7382f5..815c716db 100644 --- a/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java +++ b/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java @@ -18,15 +18,18 @@ import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.container.ContainerData; import dan200.computercraft.shared.platform.*; +import io.netty.buffer.Unpooled; import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.TagKey; @@ -47,12 +50,10 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import javax.annotation.Nullable; -import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.function.BiFunction; @@ -129,31 +130,6 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat throw new UnsupportedOperationException("Cannot register ArgumentTypeInfo inside tests"); } - @Override - public void sendToPlayer(NetworkMessage message, ServerPlayer player) { - throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests"); - } - - @Override - public void sendToPlayers(NetworkMessage message, Collection players) { - throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests"); - } - - @Override - public void sendToAllPlayers(NetworkMessage message, MinecraftServer server) { - throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests"); - } - - @Override - public void sendToAllAround(NetworkMessage message, ServerLevel level, Vec3 pos, float distance) { - throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests"); - } - - @Override - public void sendToAllTracking(NetworkMessage message, LevelChunk chunk) { - throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests"); - } - @Override public List> getDyeTags() { throw new UnsupportedOperationException("Cannot query tags inside tests"); @@ -169,11 +145,21 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat throw new UnsupportedOperationException("Cannot open menu inside tests"); } + record TypeImpl>( + ResourceLocation id, Function reader + ) implements MessageType { + } + @Override public > MessageType createMessageType(int id, ResourceLocation channel, Class klass, FriendlyByteBuf.Reader reader) { - record TypeImpl>(Function reader) implements MessageType { - } - return new TypeImpl<>(reader); + return new TypeImpl<>(channel, reader); + } + + @Override + public Packet createPacket(NetworkMessage message) { + var buf = new FriendlyByteBuf(Unpooled.buffer()); + message.write(buf); + return new ClientboundCustomPayloadPacket(((TypeImpl) message.type()).id(), buf); } @Override diff --git a/projects/common/src/test/java/dan200/computercraft/client/sound/DfpwmStreamTest.java b/projects/common/src/test/java/dan200/computercraft/client/sound/DfpwmStreamTest.java index 2cb2f72b3..de1f89b8e 100644 --- a/projects/common/src/test/java/dan200/computercraft/client/sound/DfpwmStreamTest.java +++ b/projects/common/src/test/java/dan200/computercraft/client/sound/DfpwmStreamTest.java @@ -4,9 +4,10 @@ package dan200.computercraft.client.sound; -import io.netty.buffer.ByteBufAllocator; import org.junit.jupiter.api.Test; +import java.nio.ByteBuffer; + import static org.junit.jupiter.api.Assertions.assertEquals; public class DfpwmStreamTest { @@ -14,8 +15,7 @@ public class DfpwmStreamTest { public void testDecodesBytes() { var stream = new DfpwmStream(); - var input = ByteBufAllocator.DEFAULT.buffer(); - input.writeBytes(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); var buffer = stream.read(1024 + 1); diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java b/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java index d882323cb..eb35b3208 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java @@ -6,6 +6,7 @@ package dan200.computercraft.client; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.client.model.CustomModelLoader; +import dan200.computercraft.impl.Services; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.network.NetworkMessages; @@ -31,9 +32,10 @@ import static dan200.computercraft.core.util.Nullability.assertNonNull; public class ComputerCraftClient { public static void init() { + var clientNetwork = Services.load(ClientNetworkContext.class); for (var type : NetworkMessages.getClientbound()) { ClientPlayNetworking.registerGlobalReceiver( - FabricMessageType.toFabricType(type), (packet, player, responseSender) -> packet.payload().handle(ClientNetworkContext.get()) + FabricMessageType.toFabricType(type), (packet, player, responseSender) -> packet.payload().handle(clientNetwork) ); } diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientNetworkHandlerImpl.java b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientNetworkHandlerImpl.java deleted file mode 100644 index a16a007be..000000000 --- a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientNetworkHandlerImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.client.platform; - -import com.google.auto.service.AutoService; -import dan200.computercraft.shared.network.client.ClientNetworkContext; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvent; - -import javax.annotation.Nullable; - -@AutoService(ClientNetworkContext.class) -public class ClientNetworkHandlerImpl extends AbstractClientNetworkContext { - @Override - public void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name) { - var mc = Minecraft.getInstance(); - mc.levelRenderer.playStreamingMusic(sound, pos); - if (name != null) mc.gui.setNowPlaying(Component.literal(name)); - } -} diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java index ba3af63fa..8452d9e58 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java @@ -12,13 +12,19 @@ import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.platform.FabricMessageType; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.Sheets; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ServerGamePacketListener; import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; import net.minecraft.util.RandomSource; import javax.annotation.Nullable; @@ -28,8 +34,10 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper { private static final RandomSource random = RandomSource.create(0); @Override - public void sendToServer(NetworkMessage message) { - ClientPlayNetworking.send(FabricMessageType.toFabricPacket(message)); + public Packet createPacket(NetworkMessage message) { + var buf = PacketByteBufs.create(); + message.write(buf); + return ClientPlayNetworking.createC2SPacket(FabricMessageType.toFabricType(message.type()).getId(), buf); } @Override @@ -55,4 +63,9 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper { ModelRenderer.renderQuads(transform, buffer, model.getQuads(null, face, random), lightmapCoord, overlayLight, tints); } } + + @Override + public void playStreamingMusic(BlockPos pos, @Nullable SoundEvent sound) { + Minecraft.getInstance().levelRenderer.playStreamingMusic(sound, pos); + } } diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java index 8181742f0..d0e6f2ab4 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java @@ -15,6 +15,7 @@ import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.details.FluidDetails; import dan200.computercraft.shared.network.NetworkMessages; import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; +import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity; @@ -22,7 +23,6 @@ import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEnt import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity; import dan200.computercraft.shared.platform.FabricConfigFile; import dan200.computercraft.shared.platform.FabricMessageType; -import dan200.computercraft.shared.platform.PlatformHelper; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; @@ -92,7 +92,7 @@ public class ComputerCraft { CommonHooks.onServerStopped(); ((FabricConfigFile) ConfigSpec.serverSpec).unload(); }); - ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> PlatformHelper.get().sendToPlayer(new UpgradesLoadedMessage(), player)); + ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> ServerNetworking.sendToPlayer(new UpgradesLoadedMessage(), player)); ServerTickEvents.START_SERVER_TICK.register(CommonHooks::onServerTickStart); ServerTickEvents.START_SERVER_TICK.register(s -> CommonHooks.onServerTickEnd()); diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 06991e325..3ed6f52c0 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -51,8 +51,6 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.ItemTags; @@ -77,13 +75,15 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; import java.util.function.*; @AutoService(dan200.computercraft.impl.PlatformHelper.class) @@ -177,42 +177,13 @@ public class PlatformHelperImpl implements PlatformHelper { return new FabricMessageType<>(channel, reader); } - private Packet encodeClientbound(NetworkMessage message) { + @Override + public Packet createPacket(NetworkMessage message) { var buf = PacketByteBufs.create(); message.write(buf); return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricType(message.type()).getId(), buf); } - @Override - public void sendToPlayer(NetworkMessage message, ServerPlayer player) { - player.connection.send(encodeClientbound(message)); - } - - @Override - public void sendToPlayers(NetworkMessage message, Collection players) { - if (players.isEmpty()) return; - var packet = encodeClientbound(message); - for (var player : players) player.connection.send(packet); - } - - @Override - public void sendToAllPlayers(NetworkMessage message, MinecraftServer server) { - server.getPlayerList().broadcastAll(encodeClientbound(message)); - } - - @Override - public void sendToAllAround(NetworkMessage message, ServerLevel level, Vec3 pos, float distance) { - level.getServer().getPlayerList().broadcast(null, pos.x, pos.y, pos.z, distance, level.dimension(), encodeClientbound(message)); - } - - @Override - public void sendToAllTracking(NetworkMessage message, LevelChunk chunk) { - var packet = encodeClientbound(message); - for (var player : ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false)) { - player.connection.send(packet); - } - } - @Override public ComponentAccess createPeripheralAccess(BlockEntity owner, Consumer invalidate) { return new PeripheralAccessImpl(owner, invalidate); diff --git a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientNetworkContextImpl.java b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientNetworkContextImpl.java deleted file mode 100644 index 79057e24b..000000000 --- a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientNetworkContextImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.client.platform; - -import com.google.auto.service.AutoService; -import dan200.computercraft.shared.network.client.ClientNetworkContext; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvent; - -import javax.annotation.Nullable; - -@AutoService(ClientNetworkContext.class) -public class ClientNetworkContextImpl extends AbstractClientNetworkContext { - @Override - public void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name) { - var mc = Minecraft.getInstance(); - mc.levelRenderer.playStreamingMusic(sound, pos, null); - if (name != null) mc.gui.setNowPlaying(Component.literal(name)); - } -} diff --git a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java index aaa41c4a4..612a79225 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java @@ -11,11 +11,16 @@ import dan200.computercraft.client.render.ModelRenderer; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.platform.NetworkHandler; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ServerGamePacketListener; import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; import net.minecraftforge.client.model.data.ModelData; @@ -39,8 +44,8 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper { } @Override - public void sendToServer(NetworkMessage message) { - NetworkHandler.sendToServer(message); + public Packet createPacket(NetworkMessage message) { + return NetworkHandler.createServerboundPacket(message); } @Override @@ -54,4 +59,9 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper { } } } + + @Override + public void playStreamingMusic(BlockPos pos, @Nullable SoundEvent sound) { + Minecraft.getInstance().levelRenderer.playStreamingMusic(sound, pos, null); + } } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java index d5f1a4ed5..ed36e39c7 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java @@ -9,6 +9,7 @@ import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; +import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral; import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity; import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity; @@ -17,7 +18,6 @@ import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockE import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity; import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity; import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity; -import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; import dan200.computercraft.shared.util.CapabilityProvider; import dan200.computercraft.shared.util.SidedCapabilityProvider; @@ -81,9 +81,9 @@ public class ForgeCommonHooks { public static void onDatapackSync(OnDatapackSyncEvent event) { var packet = new UpgradesLoadedMessage(); if (event.getPlayer() == null) { - PlatformHelper.get().sendToAllPlayers(packet, event.getPlayerList().getServer()); + ServerNetworking.sendToAllPlayers(packet, event.getPlayerList().getServer()); } else { - PlatformHelper.get().sendToPlayer(packet, event.getPlayer()); + ServerNetworking.sendToPlayer(packet, event.getPlayer()); } } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java index c96e19a5e..1c07b3b24 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/NetworkHandler.java @@ -5,26 +5,25 @@ package dan200.computercraft.shared.platform; import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.impl.Services; import dan200.computercraft.shared.network.MessageType; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.NetworkMessages; import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.server.ServerNetworkContext; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ServerGamePacketListener; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.phys.Vec3; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.NetworkRegistry; -import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.simple.SimpleChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collection; +import javax.annotation.Nullable; import java.util.function.Function; import static dan200.computercraft.core.util.Nullability.assertNonNull; @@ -53,36 +52,18 @@ public final class NetworkHandler { for (var type : NetworkMessages.getClientbound()) { var forgeType = (MessageTypeImpl>) type; - registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientNetworkContext.get()); + registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientHolder.get()); } } - static void sendToPlayer(NetworkMessage packet, ServerPlayer player) { - network.sendTo(packet, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + @SuppressWarnings("unchecked") + public static Packet createClientboundPacket(NetworkMessage packet) { + return (Packet) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_CLIENT); } - static void sendToPlayers(NetworkMessage packet, Collection players) { - if (players.isEmpty()) return; - - var vanillaPacket = network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_CLIENT); - for (var player : players) player.connection.send(vanillaPacket); - } - - static void sendToAllPlayers(NetworkMessage packet) { - network.send(PacketDistributor.ALL.noArg(), packet); - } - - static void sendToAllAround(NetworkMessage packet, Level world, Vec3 pos, double range) { - var target = new PacketDistributor.TargetPoint(pos.x, pos.y, pos.z, range, world.dimension()); - network.send(PacketDistributor.NEAR.with(() -> target), packet); - } - - static void sendToAllTracking(NetworkMessage packet, LevelChunk chunk) { - network.send(PacketDistributor.TRACKING_CHUNK.with(() -> chunk), packet); - } - - public static void sendToServer(NetworkMessage packet) { - network.sendToServer(packet); + @SuppressWarnings("unchecked") + public static Packet createServerboundPacket(NetworkMessage packet) { + return (Packet) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_SERVER); } /** @@ -115,4 +96,24 @@ public final class NetworkHandler { int id, Class klass, Function reader ) implements MessageType { } + + /** + * This holds an instance of {@link ClientNetworkContext}. This is a separate class to ensure that the instance is + * lazily created when needed on the client. + */ + private static final class ClientHolder { + private static final @Nullable ClientNetworkContext INSTANCE; + private static final @Nullable Throwable ERROR; + + static { + var helper = Services.tryLoad(ClientNetworkContext.class); + INSTANCE = helper.instance(); + ERROR = helper.error(); + } + + static ClientNetworkContext get() { + var instance = INSTANCE; + return instance == null ? Services.raise(ClientNetworkContext.class, ERROR) : instance; + } + } } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 3ce9832bd..63bc64304 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -28,9 +28,10 @@ import net.minecraft.core.Direction; import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.TagKey; @@ -52,7 +53,6 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.ForgeHooks; @@ -164,28 +164,8 @@ public class PlatformHelperImpl implements PlatformHelper { } @Override - public void sendToPlayer(NetworkMessage message, ServerPlayer player) { - NetworkHandler.sendToPlayer(message, player); - } - - @Override - public void sendToPlayers(NetworkMessage message, Collection players) { - NetworkHandler.sendToPlayers(message, players); - } - - @Override - public void sendToAllPlayers(NetworkMessage message, MinecraftServer server) { - NetworkHandler.sendToAllPlayers(message); - } - - @Override - public void sendToAllAround(NetworkMessage message, ServerLevel level, Vec3 pos, float distance) { - NetworkHandler.sendToAllAround(message, level, pos, distance); - } - - @Override - public void sendToAllTracking(NetworkMessage message, LevelChunk chunk) { - NetworkHandler.sendToAllTracking(message, chunk); + public Packet createPacket(NetworkMessage message) { + return NetworkHandler.createClientboundPacket(message); } @Override