1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-25 08:26:54 +00:00

Simplify our networking multi-platform code

Historically we used Forge's SimpleChannel methods (and
PacketDistributor) to send the packets to the client. However, we don't
need to do that - it is sufficient to convert it to a vanilla packet,
and send the packet ourselves.

Given we need to do this on Fabric, it makes sense to do this on Forge
as well. This allows us to unify (and thus simplify) a lot of how packet
sending works.

At the same time, we also remove the handling of speaker audio during
decoding. We originally did this to avoid the additional copy of audio
data. However, this doesn't work on 1.20.4 (as packets aren't
encoded/decoded on singleplayer), so it makes sense to do this
Correctly(TM).

This also allows us to get rid of ClientNetworkContext.get(). We do
still need to service load this class (as Forge's networking isn't split
up in the same way Fabric's is), but we'll be able to drop that in
1.20.4.

Finally, we move the record playing code from ClientNetworkContext to
ClientPlatformHelper. This means the network context no longer needs to
be platform-specific!
This commit is contained in:
Jonathan Coates 2024-01-14 22:52:25 +00:00
parent be4512d1c3
commit 30dc4cb38c
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
32 changed files with 309 additions and 329 deletions

View File

@ -7,7 +7,7 @@ package dan200.computercraft.client.gui;
import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.DynamicImageButton; import dan200.computercraft.client.gui.widgets.DynamicImageButton;
import dan200.computercraft.client.gui.widgets.TerminalWidget; 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.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.InputHandler; import dan200.computercraft.shared.computer.core.InputHandler;
@ -207,7 +207,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
return; 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) { public void uploadResult(UploadResult result, @Nullable Component message) {

View File

@ -4,7 +4,7 @@
package dan200.computercraft.client.gui; 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.core.InputHandler;
import dan200.computercraft.shared.computer.menu.ComputerMenu; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.server.ComputerActionServerMessage; import dan200.computercraft.shared.network.server.ComputerActionServerMessage;
@ -29,51 +29,51 @@ public final class ClientInputHandler implements InputHandler {
@Override @Override
public void turnOn() { public void turnOn() {
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON)); ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
} }
@Override @Override
public void shutdown() { public void shutdown() {
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN)); ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.SHUTDOWN));
} }
@Override @Override
public void reboot() { public void reboot() {
ClientPlatformHelper.get().sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT)); ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
} }
@Override @Override
public void queueEvent(String event, @Nullable Object[] arguments) { public void queueEvent(String event, @Nullable Object[] arguments) {
ClientPlatformHelper.get().sendToServer(new QueueEventServerMessage(menu, event, arguments)); ClientNetworking.sendToServer(new QueueEventServerMessage(menu, event, arguments));
} }
@Override @Override
public void keyDown(int key, boolean repeat) { 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 @Override
public void keyUp(int key) { 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 @Override
public void mouseClick(int button, int x, int y) { 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 @Override
public void mouseUp(int button, int x, int y) { 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 @Override
public void mouseDrag(int button, int x, int y) { 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 @Override
public void mouseScroll(int direction, int x, int y) { 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));
} }
} }

View File

@ -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<ServerNetworkContext> message) {
var connection = Minecraft.getInstance().getConnection();
if (connection != null) connection.send(ClientPlatformHelper.get().createPacket(message));
}
}

View File

@ -4,6 +4,7 @@
package dan200.computercraft.client.platform; package dan200.computercraft.client.platform;
import com.google.auto.service.AutoService;
import dan200.computercraft.client.ClientTableFormatter; import dan200.computercraft.client.ClientTableFormatter;
import dan200.computercraft.client.gui.AbstractComputerScreen; import dan200.computercraft.client.gui.AbstractComputerScreen;
import dan200.computercraft.client.gui.OptionScreen; 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.network.client.ClientNetworkContext;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity; import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.player.Player; 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;
/** /**
* The base implementation of {@link ClientNetworkContext}. * The client-side implementation of {@link ClientNetworkContext}.
* <p>
* This should be extended by mod loader specific modules with the remaining abstract methods.
*/ */
public abstract class AbstractClientNetworkContext implements ClientNetworkContext { @AutoService(ClientNetworkContext.class)
public final class ClientNetworkContextImpl implements ClientNetworkContext {
@Override @Override
public final void handleChatTable(TableBuilder table) { public void handleChatTable(TableBuilder table) {
ClientTableFormatter.INSTANCE.display(table); ClientTableFormatter.INSTANCE.display(table);
} }
@Override @Override
public final void handleComputerTerminal(int containerId, TerminalState terminal) { public void handleComputerTerminal(int containerId, TerminalState terminal) {
Player player = Minecraft.getInstance().player; Player player = Minecraft.getInstance().player;
if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) { if (player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu menu) {
menu.updateTerminal(terminal); menu.updateTerminal(terminal);
@ -48,7 +49,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
} }
@Override @Override
public final void handleMonitorData(BlockPos pos, TerminalState terminal) { public void handleMonitorData(BlockPos pos, TerminalState terminal) {
var player = Minecraft.getInstance().player; var player = Minecraft.getInstance().player;
if (player == null) return; if (player == null) return;
@ -59,44 +60,46 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
} }
@Override @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); var computer = ClientPocketComputers.get(instanceId, terminal.colour);
computer.setState(state, lightState); computer.setState(state, lightState);
if (terminal.hasTerminal()) computer.setTerminal(terminal); if (terminal.hasTerminal()) computer.setTerminal(terminal);
} }
@Override @Override
public final void handlePocketComputerDeleted(int instanceId) { public void handlePocketComputerDeleted(int instanceId) {
ClientPocketComputers.remove(instanceId); ClientPocketComputers.remove(instanceId);
} }
@Override @Override
public final void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume) { public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume); SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
} }
@Override @Override
public final void handleSpeakerAudioPush(UUID source, ByteBuf buffer) { public void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
SpeakerManager.getSound(source).pushAudio(buffer);
}
@Override
public final void handleSpeakerMove(UUID source, SpeakerPosition.Message position) {
SpeakerManager.moveSound(source, reifyPosition(position)); SpeakerManager.moveSound(source, reifyPosition(position));
} }
@Override @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); SpeakerManager.getSound(source).playSound(reifyPosition(position), sound, volume, pitch);
} }
@Override @Override
public final void handleSpeakerStop(UUID source) { public void handleSpeakerStop(UUID source) {
SpeakerManager.stopSound(source); SpeakerManager.stopSound(source);
} }
@Override @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 minecraft = Minecraft.getInstance();
var screen = OptionScreen.unwrap(minecraft.screen); var screen = OptionScreen.unwrap(minecraft.screen);

View File

@ -9,6 +9,10 @@ import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.network.server.ServerNetworkContext;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.resources.model.BakedModel; 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; 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<ServerNetworkContext> message); Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
/** /**
* Render a {@link BakedModel}, using any loader-specific hooks. * 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. * @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); 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);
} }

View File

@ -6,7 +6,7 @@ package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel; import com.mojang.blaze3d.audio.Channel;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral; 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.AudioStream;
import net.minecraft.client.sounds.SoundEngine; import net.minecraft.client.sounds.SoundEngine;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
@ -36,7 +36,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#pushAudio(ByteBuf) * @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
*/ */
@Nullable @Nullable
Channel channel; Channel channel;
@ -44,7 +44,7 @@ class DfpwmStream implements AudioStream {
/** /**
* The underlying {@link SoundEngine} executor. * The underlying {@link SoundEngine} executor.
* *
* @see SpeakerInstance#pushAudio(ByteBuf) * @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
* @see SoundEngine#executor * @see SoundEngine#executor
*/ */
@Nullable @Nullable
@ -58,12 +58,12 @@ class DfpwmStream implements AudioStream {
DfpwmStream() { DfpwmStream() {
} }
void push(ByteBuf input) { void push(ByteBuffer input) {
var readable = input.readableBytes(); var readable = input.remaining();
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder()); var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
for (var i = 0; i < readable; i++) { for (var i = 0; i < readable; i++) {
var inputByte = input.readByte(); var inputByte = input.get();
for (var j = 0; j < 8; j++) { for (var j = 0; j < 8; j++) {
var currentBit = (inputByte & 1) != 0; var currentBit = (inputByte & 1) != 0;
var target = currentBit ? 127 : -128; var target = currentBit ? 127 : -128;

View File

@ -7,11 +7,11 @@ 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.SpeakerPosition; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import io.netty.buffer.ByteBuf;
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() {
} }
public synchronized void pushAudio(ByteBuf buffer) { private void pushAudio(ByteBuffer buffer) {
var sound = this.sound; var sound = this.sound;
var stream = currentStream; 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(); var soundManager = Minecraft.getInstance().getSoundManager();
if (sound != null && sound.stream != currentStream) { if (sound != null && sound.stream != currentStream) {

View File

@ -7,7 +7,7 @@ package dan200.computercraft.shared.command.text;
import dan200.computercraft.core.util.Nullability; import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.command.CommandUtils; import dan200.computercraft.shared.command.CommandUtils;
import dan200.computercraft.shared.network.client.ChatTableClientMessage; 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.commands.CommandSourceStack;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
@ -105,7 +105,7 @@ public class TableBuilder {
if (CommandUtils.isPlayer(source)) { if (CommandUtils.isPlayer(source)) {
trim(18); trim(18);
var player = (ServerPlayer) Nullability.assertNonNull(source.getEntity()); var player = (ServerPlayer) Nullability.assertNonNull(source.getEntity());
PlatformHelper.get().sendToPlayer(new ChatTableClientMessage(this), player); ServerNetworking.sendToPlayer(new ChatTableClientMessage(this), player);
} else { } else {
trim(100); trim(100);
new ServerTableFormatter(source).display(this); new ServerTableFormatter(source).display(this);

View File

@ -20,7 +20,7 @@ import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; 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.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
@ -142,7 +142,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
for (var player : server.getPlayerList().getPlayers()) { for (var player : server.getPlayerList().getPlayers()) {
if (player.containerMenu instanceof ComputerMenu && ((ComputerMenu) player.containerMenu).getComputer() == this) { 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);
} }
} }
} }

View File

@ -11,7 +11,7 @@ import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult; import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.client.UploadResultMessage; 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.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@ -138,7 +138,7 @@ public class ServerInputState<T extends AbstractContainerMenu & ComputerMenu> im
return; return;
} }
PlatformHelper.get().sendToPlayer(finishUpload(uploader), uploader); ServerNetworking.sendToPlayer(finishUpload(uploader), uploader);
} }
private UploadResultMessage finishUpload(ServerPlayer player) { private UploadResultMessage finishUpload(ServerPlayer player) {
@ -159,7 +159,7 @@ public class ServerInputState<T extends AbstractContainerMenu & ComputerMenu> im
toUpload.stream().map(x -> new TransferredFile(x.getName(), new ByteBufferChannel(x.getBytes()))).toList(), toUpload.stream().map(x -> new TransferredFile(x.getName(), new ByteBufferChannel(x.getBytes()))).toList(),
() -> { () -> {
if (player.isAlive() && player.containerMenu == owner) { if (player.isAlive() && player.containerMenu == owner) {
PlatformHelper.get().sendToPlayer(UploadResultMessage.consumed(owner), player); ServerNetworking.sendToPlayer(UploadResultMessage.consumed(owner), player);
} }
}), }),
}); });

View File

@ -4,30 +4,24 @@
package dan200.computercraft.shared.network.client; package dan200.computercraft.shared.network.client;
import dan200.computercraft.impl.Services;
import dan200.computercraft.shared.command.text.TableBuilder; 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.SpeakerPosition; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; 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;
/** /**
* The context under which clientbound packets are evaluated. * The context under which clientbound packets are evaluated.
*/ */
public interface ClientNetworkContext { 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 handleChatTable(TableBuilder table);
void handleComputerTerminal(int containerId, TerminalState terminal); void handleComputerTerminal(int containerId, TerminalState terminal);
@ -40,9 +34,7 @@ public interface ClientNetworkContext {
void handlePocketComputerDeleted(int instanceId); void handlePocketComputerDeleted(int instanceId);
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume); void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer audio);
void handleSpeakerAudioPush(UUID source, ByteBuf buffer);
void handleSpeakerMove(UUID source, SpeakerPosition.Message position); void handleSpeakerMove(UUID source, SpeakerPosition.Message position);
@ -51,18 +43,4 @@ public interface ClientNetworkContext {
void handleSpeakerStop(UUID source); void handleSpeakerStop(UUID source);
void handleUploadResult(int containerId, UploadResult result, @Nullable Component errorMessage); 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() {
}
}
} }

View File

@ -11,12 +11,9 @@ 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 javax.annotation.Nullable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.UUID; import java.util.UUID;
import static dan200.computercraft.core.util.Nullability.assertNonNull;
/** /**
* Starts a sound on the client. * Starts a sound on the client.
* <p> * <p>
@ -27,7 +24,7 @@ import static dan200.computercraft.core.util.Nullability.assertNonNull;
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 @Nullable ByteBuffer content; private final ByteBuffer 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, ByteBuffer content) {
@ -42,10 +39,9 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
pos = SpeakerPosition.Message.read(buf); pos = SpeakerPosition.Message.read(buf);
volume = buf.readFloat(); volume = buf.readFloat();
// TODO: Remove this, so we no longer need a getter for ClientNetworkContext. However, doing so without var bytes = new byte[buf.readableBytes()];
// leaking or redundantly copying the buffer is hard. buf.readBytes(bytes);
ClientNetworkContext.get().handleSpeakerAudioPush(source, buf); content = ByteBuffer.wrap(bytes);
content = null;
} }
@Override @Override
@ -53,12 +49,12 @@ 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(assertNonNull(content).duplicate()); buf.writeBytes(content.duplicate());
} }
@Override @Override
public void handle(ClientNetworkContext context) { public void handle(ClientNetworkContext context) {
context.handleSpeakerAudio(source, pos, volume); context.handleSpeakerAudio(source, pos, volume, content);
} }
@Override @Override

View File

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.platform.PlatformHelper;
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.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.Vec3;
import java.util.Collection;
/**
* Methods for sending network messages from the server to clients.
*/
public final class ServerNetworking {
private ServerNetworking() {
}
/**
* Send a message to a specific player.
*
* @param message The message to send.
* @param player The player to send it to.
*/
public static void sendToPlayer(NetworkMessage<ClientNetworkContext> 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<ClientNetworkContext> message, Collection<ServerPlayer> 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<ClientNetworkContext> 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<ClientNetworkContext> 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<ClientNetworkContext> 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);
}
}
}

View File

@ -11,7 +11,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.common.AbstractContainerBlockEntity; import dan200.computercraft.shared.common.AbstractContainerBlockEntity;
import dan200.computercraft.shared.network.client.PlayRecordClientMessage; 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 dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -315,7 +315,7 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
} }
private void sendMessage(PlayRecordClientMessage message) { 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 @Override

View File

@ -7,7 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor;
import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.client.MonitorClientMessage; 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.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
@ -40,7 +40,7 @@ public final class MonitorWatcher {
if (serverMonitor == null || monitor.enqueued) continue; if (serverMonitor == null || monitor.enqueued) continue;
var state = getState(monitor, serverMonitor); 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); var state = getState(tile, monitor);
PlatformHelper.get().sendToAllTracking(new MonitorClientMessage(pos, state), chunk); ServerNetworking.sendToAllTracking(new MonitorClientMessage(pos, state), chunk);
limit -= state.size(); limit -= state.size();
} }

View File

@ -7,7 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.util.Nullability; import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; 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.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
@ -32,7 +32,7 @@ public class SpeakerBlockEntity extends BlockEntity {
public void setRemoved() { public void setRemoved() {
super.setRemoved(); super.setRemoved();
if (level != null && !level.isClientSide) { 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()));
} }
} }

View File

@ -17,7 +17,7 @@ import dan200.computercraft.shared.network.client.SpeakerAudioClientMessage;
import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage; import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage; import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; 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 dan200.computercraft.shared.util.PauseAwareTimer;
import net.minecraft.ResourceLocationException; import net.minecraft.ResourceLocationException;
import net.minecraft.core.BlockPos; 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. // Stop the speaker and nuke the position, so we don't update it again.
if (shouldStop && lastPosition != null) { if (shouldStop && lastPosition != null) {
lastPosition = null; lastPosition = null;
PlatformHelper.get().sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server); ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server);
return; return;
} }
var now = PauseAwareTimer.getTime(); var now = PauseAwareTimer.getTime();
if (sound != null) { if (sound != null) {
lastPlayTime = clock; lastPlayTime = clock;
PlatformHelper.get().sendToAllAround( ServerNetworking.sendToAllAround(
new SpeakerPlayClientMessage(getSource(), position, sound.sound, sound.volume, sound.pitch), new SpeakerPlayClientMessage(getSource(), position, sound.sound, sound.volume, sound.pitch),
(ServerLevel) level, pos, sound.volume * 16 (ServerLevel) level, pos, sound.volume * 16
); );
@ -131,7 +131,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
} else if (dfpwmState != null && dfpwmState.shouldSendPending(now)) { } 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 // If clients need to receive another batch of audio, send it and then notify computers our internal buffer is
// free again. // free again.
PlatformHelper.get().sendToAllTracking( ServerNetworking.sendToAllTracking(
new SpeakerAudioClientMessage(getSource(), position, dfpwmState.getVolume(), dfpwmState.pullPending(now)), new SpeakerAudioClientMessage(getSource(), position, dfpwmState.getVolume(), dfpwmState.pullPending(now)),
level.getChunkAt(BlockPos.containing(pos)) level.getChunkAt(BlockPos.containing(pos))
); );
@ -150,7 +150,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
// in the last second. // in the last second.
if (lastPosition != null && (clock - lastPositionTime) >= 20 && !lastPosition.withinDistance(position, 0.1)) { 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. // 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), new SpeakerMoveClientMessage(getSource(), position),
level.getChunkAt(BlockPos.containing(pos)) level.getChunkAt(BlockPos.containing(pos))
); );

View File

@ -6,7 +6,7 @@ package dan200.computercraft.shared.peripheral.speaker;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; 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(); var server = level.getServer();
if (server == null || server.isStopped()) return; if (server == null || server.isStopped()) return;
PlatformHelper.get().sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server); ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server);
} }
} }

View File

@ -20,9 +20,10 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf; 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.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode; 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.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; 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.BlockHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -178,46 +177,12 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
<T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader); <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> 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 message The messsge to convert.
* @param player The player to send it to. * @return The converted message.
*/ */
void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player); Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message);
/**
* 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<ClientNetworkContext> message, Collection<ServerPlayer> players);
/**
* Send a message to all players.
*
* @param message The message to send.
* @param server The current server.
*/
void sendToAllPlayers(NetworkMessage<ClientNetworkContext> 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<ClientNetworkContext> 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<ClientNetworkContext> message, LevelChunk chunk);
/** /**
* Create a {@link ComponentAccess} for surrounding peripherals. * Create a {@link ComponentAccess} for surrounding peripherals.

View File

@ -15,7 +15,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.client.PocketComputerDataMessage; import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage; 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 dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@ -161,7 +161,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
if (sendState) { if (sendState) {
// Broadcast the state to all players // Broadcast the state to all players
tracking.addAll(getLevel().players()); tracking.addAll(getLevel().players());
PlatformHelper.get().sendToPlayers(new PocketComputerDataMessage(this, false), tracking); ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), tracking);
} else { } else {
// Broadcast the state to new players. // Broadcast the state to new players.
List<ServerPlayer> added = new ArrayList<>(); List<ServerPlayer> added = new ArrayList<>();
@ -169,7 +169,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
if (tracking.add(player)) added.add(player); if (tracking.add(player)) added.add(player);
} }
if (!added.isEmpty()) { 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()) { if (entity instanceof ServerPlayer player && entity.isAlive()) {
// Broadcast the terminal to the current player. // Broadcast the terminal to the current player.
PlatformHelper.get().sendToPlayer(new PocketComputerDataMessage(this, true), player); ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), player);
} }
} }
@Override @Override
protected void onRemoved() { protected void onRemoved() {
super.onRemoved(); super.onRemoved();
PlatformHelper.get().sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceID()), getLevel().getServer()); ServerNetworking.sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceID()), getLevel().getServer());
} }
} }

View File

@ -18,15 +18,18 @@ import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData; import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.platform.*; import dan200.computercraft.shared.platform.*;
import io.netty.buffer.Unpooled;
import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf; 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.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey; 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.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; 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.BlockHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -129,31 +130,6 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
throw new UnsupportedOperationException("Cannot register ArgumentTypeInfo inside tests"); throw new UnsupportedOperationException("Cannot register ArgumentTypeInfo inside tests");
} }
@Override
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToPlayers(NetworkMessage<ClientNetworkContext> message, Collection<ServerPlayer> players) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToAllPlayers(NetworkMessage<ClientNetworkContext> message, MinecraftServer server) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToAllAround(NetworkMessage<ClientNetworkContext> message, ServerLevel level, Vec3 pos, float distance) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToAllTracking(NetworkMessage<ClientNetworkContext> message, LevelChunk chunk) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override @Override
public List<TagKey<Item>> getDyeTags() { public List<TagKey<Item>> getDyeTags() {
throw new UnsupportedOperationException("Cannot query tags inside tests"); 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"); throw new UnsupportedOperationException("Cannot open menu inside tests");
} }
record TypeImpl<T extends NetworkMessage<?>>(
ResourceLocation id, Function<FriendlyByteBuf, T> reader
) implements MessageType<T> {
}
@Override @Override
public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) { public <T extends NetworkMessage<?>> MessageType<T> createMessageType(int id, ResourceLocation channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
record TypeImpl<T extends NetworkMessage<?>>(Function<FriendlyByteBuf, T> reader) implements MessageType<T> { return new TypeImpl<>(channel, reader);
} }
return new TypeImpl<>(reader);
@Override
public Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
var buf = new FriendlyByteBuf(Unpooled.buffer());
message.write(buf);
return new ClientboundCustomPayloadPacket(((TypeImpl<?>) message.type()).id(), buf);
} }
@Override @Override

View File

@ -4,9 +4,10 @@
package dan200.computercraft.client.sound; package dan200.computercraft.client.sound;
import io.netty.buffer.ByteBufAllocator;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.nio.ByteBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class DfpwmStreamTest { public class DfpwmStreamTest {
@ -14,8 +15,7 @@ public class DfpwmStreamTest {
public void testDecodesBytes() { public void testDecodesBytes() {
var stream = new DfpwmStream(); var stream = new DfpwmStream();
var input = ByteBufAllocator.DEFAULT.buffer(); 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 });
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 });
stream.push(input); stream.push(input);
var buffer = stream.read(1024 + 1); var buffer = stream.read(1024 + 1);

View File

@ -6,6 +6,7 @@ package dan200.computercraft.client;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.model.CustomModelLoader; import dan200.computercraft.client.model.CustomModelLoader;
import dan200.computercraft.impl.Services;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.network.NetworkMessages; import dan200.computercraft.shared.network.NetworkMessages;
@ -31,9 +32,10 @@ import static dan200.computercraft.core.util.Nullability.assertNonNull;
public class ComputerCraftClient { public class ComputerCraftClient {
public static void init() { public static void init() {
var clientNetwork = Services.load(ClientNetworkContext.class);
for (var type : NetworkMessages.getClientbound()) { for (var type : NetworkMessages.getClientbound()) {
ClientPlayNetworking.registerGlobalReceiver( ClientPlayNetworking.registerGlobalReceiver(
FabricMessageType.toFabricType(type), (packet, player, responseSender) -> packet.payload().handle(ClientNetworkContext.get()) FabricMessageType.toFabricType(type), (packet, player, responseSender) -> packet.payload().handle(clientNetwork)
); );
} }

View File

@ -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));
}
}

View File

@ -12,13 +12,19 @@ import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.network.server.ServerNetworkContext;
import dan200.computercraft.shared.platform.FabricMessageType; import dan200.computercraft.shared.platform.FabricMessageType;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 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.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.Sheets; import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager; 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.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -28,8 +34,10 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper {
private static final RandomSource random = RandomSource.create(0); private static final RandomSource random = RandomSource.create(0);
@Override @Override
public void sendToServer(NetworkMessage<ServerNetworkContext> message) { public Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message) {
ClientPlayNetworking.send(FabricMessageType.toFabricPacket(message)); var buf = PacketByteBufs.create();
message.write(buf);
return ClientPlayNetworking.createC2SPacket(FabricMessageType.toFabricType(message.type()).getId(), buf);
} }
@Override @Override
@ -55,4 +63,9 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper {
ModelRenderer.renderQuads(transform, buffer, model.getQuads(null, face, random), lightmapCoord, overlayLight, tints); 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);
}
} }

View File

@ -15,6 +15,7 @@ import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.details.FluidDetails; import dan200.computercraft.shared.details.FluidDetails;
import dan200.computercraft.shared.network.NetworkMessages; import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; 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.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity; 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.peripheral.modem.wireless.WirelessModemBlockEntity;
import dan200.computercraft.shared.platform.FabricConfigFile; import dan200.computercraft.shared.platform.FabricConfigFile;
import dan200.computercraft.shared.platform.FabricMessageType; 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.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@ -92,7 +92,7 @@ public class ComputerCraft {
CommonHooks.onServerStopped(); CommonHooks.onServerStopped();
((FabricConfigFile) ConfigSpec.serverSpec).unload(); ((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(CommonHooks::onServerTickStart);
ServerTickEvents.START_SERVER_TICK.register(s -> CommonHooks.onServerTickEnd()); ServerTickEvents.START_SERVER_TICK.register(s -> CommonHooks.onServerTickEnd());

View File

@ -51,8 +51,6 @@ import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; 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.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.ItemTags; 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.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; 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.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable; 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.*; import java.util.function.*;
@AutoService(dan200.computercraft.impl.PlatformHelper.class) @AutoService(dan200.computercraft.impl.PlatformHelper.class)
@ -177,42 +177,13 @@ public class PlatformHelperImpl implements PlatformHelper {
return new FabricMessageType<>(channel, reader); return new FabricMessageType<>(channel, reader);
} }
private Packet<ClientGamePacketListener> encodeClientbound(NetworkMessage<ClientNetworkContext> message) { @Override
public Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
var buf = PacketByteBufs.create(); var buf = PacketByteBufs.create();
message.write(buf); message.write(buf);
return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricType(message.type()).getId(), buf); return ServerPlayNetworking.createS2CPacket(FabricMessageType.toFabricType(message.type()).getId(), buf);
} }
@Override
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
player.connection.send(encodeClientbound(message));
}
@Override
public void sendToPlayers(NetworkMessage<ClientNetworkContext> message, Collection<ServerPlayer> players) {
if (players.isEmpty()) return;
var packet = encodeClientbound(message);
for (var player : players) player.connection.send(packet);
}
@Override
public void sendToAllPlayers(NetworkMessage<ClientNetworkContext> message, MinecraftServer server) {
server.getPlayerList().broadcastAll(encodeClientbound(message));
}
@Override
public void sendToAllAround(NetworkMessage<ClientNetworkContext> 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<ClientNetworkContext> message, LevelChunk chunk) {
var packet = encodeClientbound(message);
for (var player : ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false)) {
player.connection.send(packet);
}
}
@Override @Override
public ComponentAccess<IPeripheral> createPeripheralAccess(BlockEntity owner, Consumer<Direction> invalidate) { public ComponentAccess<IPeripheral> createPeripheralAccess(BlockEntity owner, Consumer<Direction> invalidate) {
return new PeripheralAccessImpl(owner, invalidate); return new PeripheralAccessImpl(owner, invalidate);

View File

@ -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));
}
}

View File

@ -11,11 +11,16 @@ import dan200.computercraft.client.render.ModelRenderer;
import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.network.server.ServerNetworkContext;
import dan200.computercraft.shared.platform.NetworkHandler; import dan200.computercraft.shared.platform.NetworkHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager; import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; 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.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.client.model.data.ModelData;
@ -39,8 +44,8 @@ public class ClientPlatformHelperImpl implements ClientPlatformHelper {
} }
@Override @Override
public void sendToServer(NetworkMessage<ServerNetworkContext> message) { public Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message) {
NetworkHandler.sendToServer(message); return NetworkHandler.createServerboundPacket(message);
} }
@Override @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);
}
} }

View File

@ -9,6 +9,7 @@ import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity; import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; 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.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity; import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity; 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.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity; import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity; import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.CapabilityProvider; import dan200.computercraft.shared.util.CapabilityProvider;
import dan200.computercraft.shared.util.SidedCapabilityProvider; import dan200.computercraft.shared.util.SidedCapabilityProvider;
@ -81,9 +81,9 @@ public class ForgeCommonHooks {
public static void onDatapackSync(OnDatapackSyncEvent event) { public static void onDatapackSync(OnDatapackSyncEvent event) {
var packet = new UpgradesLoadedMessage(); var packet = new UpgradesLoadedMessage();
if (event.getPlayer() == null) { if (event.getPlayer() == null) {
PlatformHelper.get().sendToAllPlayers(packet, event.getPlayerList().getServer()); ServerNetworking.sendToAllPlayers(packet, event.getPlayerList().getServer());
} else { } else {
PlatformHelper.get().sendToPlayer(packet, event.getPlayer()); ServerNetworking.sendToPlayer(packet, event.getPlayer());
} }
} }

View File

@ -5,26 +5,25 @@
package dan200.computercraft.shared.platform; package dan200.computercraft.shared.platform;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.impl.Services;
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.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.network.server.ServerNetworkContext;
import net.minecraft.network.FriendlyByteBuf; 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.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.NetworkDirection;
import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.simple.SimpleChannel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Collection; import javax.annotation.Nullable;
import java.util.function.Function; import java.util.function.Function;
import static dan200.computercraft.core.util.Nullability.assertNonNull; import static dan200.computercraft.core.util.Nullability.assertNonNull;
@ -53,36 +52,18 @@ public final class NetworkHandler {
for (var type : NetworkMessages.getClientbound()) { for (var type : NetworkMessages.getClientbound()) {
var forgeType = (MessageTypeImpl<? extends NetworkMessage<ClientNetworkContext>>) type; var forgeType = (MessageTypeImpl<? extends NetworkMessage<ClientNetworkContext>>) type;
registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientNetworkContext.get()); registerMainThread(forgeType, NetworkDirection.PLAY_TO_CLIENT, x -> ClientHolder.get());
} }
} }
static void sendToPlayer(NetworkMessage<ClientNetworkContext> packet, ServerPlayer player) { @SuppressWarnings("unchecked")
network.sendTo(packet, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); public static Packet<ClientGamePacketListener> createClientboundPacket(NetworkMessage<ClientNetworkContext> packet) {
return (Packet<ClientGamePacketListener>) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_CLIENT);
} }
static void sendToPlayers(NetworkMessage<ClientNetworkContext> packet, Collection<ServerPlayer> players) { @SuppressWarnings("unchecked")
if (players.isEmpty()) return; public static Packet<ServerGamePacketListener> createServerboundPacket(NetworkMessage<ServerNetworkContext> packet) {
return (Packet<ServerGamePacketListener>) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_SERVER);
var vanillaPacket = network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_CLIENT);
for (var player : players) player.connection.send(vanillaPacket);
}
static void sendToAllPlayers(NetworkMessage<ClientNetworkContext> packet) {
network.send(PacketDistributor.ALL.noArg(), packet);
}
static void sendToAllAround(NetworkMessage<ClientNetworkContext> 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<ClientNetworkContext> packet, LevelChunk chunk) {
network.send(PacketDistributor.TRACKING_CHUNK.with(() -> chunk), packet);
}
public static void sendToServer(NetworkMessage<ServerNetworkContext> packet) {
network.sendToServer(packet);
} }
/** /**
@ -115,4 +96,24 @@ public final class NetworkHandler {
int id, Class<T> klass, Function<FriendlyByteBuf, T> reader int id, Class<T> klass, Function<FriendlyByteBuf, T> reader
) implements MessageType<T> { ) implements MessageType<T> {
} }
/**
* 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;
}
}
} }

View File

@ -28,9 +28,10 @@ import net.minecraft.core.Direction;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf; 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.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey; 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.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; 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.BlockHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.ForgeHooks;
@ -164,28 +164,8 @@ public class PlatformHelperImpl implements PlatformHelper {
} }
@Override @Override
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) { public Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
NetworkHandler.sendToPlayer(message, player); return NetworkHandler.createClientboundPacket(message);
}
@Override
public void sendToPlayers(NetworkMessage<ClientNetworkContext> message, Collection<ServerPlayer> players) {
NetworkHandler.sendToPlayers(message, players);
}
@Override
public void sendToAllPlayers(NetworkMessage<ClientNetworkContext> message, MinecraftServer server) {
NetworkHandler.sendToAllPlayers(message);
}
@Override
public void sendToAllAround(NetworkMessage<ClientNetworkContext> message, ServerLevel level, Vec3 pos, float distance) {
NetworkHandler.sendToAllAround(message, level, pos, distance);
}
@Override
public void sendToAllTracking(NetworkMessage<ClientNetworkContext> message, LevelChunk chunk) {
NetworkHandler.sendToAllTracking(message, chunk);
} }
@Override @Override