1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-10 01:10:30 +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.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<T extends AbstractComputerMenu> 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) {

View File

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

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;
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}.
* <p>
* 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);

View File

@ -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<ServerNetworkContext> message);
Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> 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);
}

View File

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

View File

@ -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) {

View File

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

View File

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

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.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<T extends AbstractContainerMenu & ComputerMenu> 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<T extends AbstractContainerMenu & ComputerMenu> 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);
}
}),
});

View File

@ -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() {
}
}
}

View File

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

View File

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

View File

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

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.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))
);

View File

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

View File

@ -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
<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 player The player to send it to.
* @param message The messsge to convert.
* @return The converted message.
*/
void sendToPlayer(NetworkMessage<ClientNetworkContext> 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<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);
Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message);
/**
* 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.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<ServerPlayer> 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());
}
}

View File

@ -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<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
public List<TagKey<Item>> 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<T extends NetworkMessage<?>>(
ResourceLocation id, Function<FriendlyByteBuf, T> reader
) implements MessageType<T> {
}
@Override
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

View File

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

View File

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

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.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<ServerNetworkContext> message) {
ClientPlayNetworking.send(FabricMessageType.toFabricPacket(message));
public Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> 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);
}
}

View File

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

View File

@ -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<ClientGamePacketListener> encodeClientbound(NetworkMessage<ClientNetworkContext> message) {
@Override
public Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
var buf = PacketByteBufs.create();
message.write(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
public ComponentAccess<IPeripheral> createPeripheralAccess(BlockEntity owner, Consumer<Direction> 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.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<ServerNetworkContext> message) {
NetworkHandler.sendToServer(message);
public Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> 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);
}
}

View File

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

View File

@ -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<? 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) {
network.sendTo(packet, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
@SuppressWarnings("unchecked")
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) {
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<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);
@SuppressWarnings("unchecked")
public static Packet<ServerGamePacketListener> createServerboundPacket(NetworkMessage<ServerNetworkContext> packet) {
return (Packet<ServerGamePacketListener>) network.toVanillaPacket(packet, NetworkDirection.PLAY_TO_SERVER);
}
/**
@ -115,4 +96,24 @@ public final class NetworkHandler {
int id, Class<T> klass, Function<FriendlyByteBuf, T> reader
) 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.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<ClientNetworkContext> message, ServerPlayer player) {
NetworkHandler.sendToPlayer(message, player);
}
@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);
public Packet<ClientGamePacketListener> createPacket(NetworkMessage<ClientNetworkContext> message) {
return NetworkHandler.createClientboundPacket(message);
}
@Override