mirror of
https://github.com/FabricMC/fabric.git
synced 2025-09-06 19:36:26 +00:00
[1.20.2] Support common registration packets. Add configuration task API (#3244)
* Config networking refactor :) * Add some unit tests for common packets. * write FabricPacket on network thread. Split ServerConfigurationConnectionEvents into two. * Fixes * Rename event * Add a testmod + ssome docs * Improve registry sync fixing deadlock in a number of cases. * Cleanup channel events. * Review feedback and fixes.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.networking.v1;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
|
||||
/**
|
||||
* Offers access to events related to the indication of a connected server's ability to receive packets in certain channels.
|
||||
*/
|
||||
public final class C2SConfigurationChannelEvents {
|
||||
/**
|
||||
* An event for the client configuration network handler receiving an update indicating the connected server's ability to receive packets in certain channels.
|
||||
* This event may be invoked at any time after login and up to disconnection.
|
||||
*/
|
||||
public static final Event<Register> REGISTER = EventFactory.createArrayBacked(Register.class, callbacks -> (handler, sender, client, channels) -> {
|
||||
for (Register callback : callbacks) {
|
||||
callback.onChannelRegister(handler, sender, client, channels);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the client configuration network handler receiving an update indicating the connected server's lack of ability to receive packets in certain channels.
|
||||
* This event may be invoked at any time after login and up to disconnection.
|
||||
*/
|
||||
public static final Event<Unregister> UNREGISTER = EventFactory.createArrayBacked(Unregister.class, callbacks -> (handler, sender, client, channels) -> {
|
||||
for (Unregister callback : callbacks) {
|
||||
callback.onChannelUnregister(handler, sender, client, channels);
|
||||
}
|
||||
});
|
||||
|
||||
private C2SConfigurationChannelEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see C2SConfigurationChannelEvents#REGISTER
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Register {
|
||||
void onChannelRegister(ClientConfigurationNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see C2SConfigurationChannelEvents#UNREGISTER
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Unregister {
|
||||
void onChannelUnregister(ClientConfigurationNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
}
|
@@ -18,10 +18,7 @@ package net.fabricmc.fabric.api.client.networking.v1;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
@@ -53,28 +50,6 @@ public final class C2SPlayChannelEvents {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the client configuration network handler receiving an update indicating the connected server's ability to receive packets in certain channels.
|
||||
* This event may be invoked at any time after login and up to disconnection.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static final Event<RegisterConfiguration> REGISTER_CONFIGURATION = EventFactory.createArrayBacked(RegisterConfiguration.class, callbacks -> (handler, sender, client, channels) -> {
|
||||
for (RegisterConfiguration callback : callbacks) {
|
||||
callback.onChannelRegister(handler, sender, client, channels);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the client configuration network handler receiving an update indicating the connected server's lack of ability to receive packets in certain channels.
|
||||
* This event may be invoked at any time after login and up to disconnection.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static final Event<UnregisterConfiguration> UNREGISTER_CONFIGURATION = EventFactory.createArrayBacked(UnregisterConfiguration.class, callbacks -> (handler, sender, client, channels) -> {
|
||||
for (UnregisterConfiguration callback : callbacks) {
|
||||
callback.onChannelUnregister(handler, sender, client, channels);
|
||||
}
|
||||
});
|
||||
|
||||
private C2SPlayChannelEvents() {
|
||||
}
|
||||
|
||||
@@ -93,22 +68,4 @@ public final class C2SPlayChannelEvents {
|
||||
public interface Unregister {
|
||||
void onChannelUnregister(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see C2SPlayChannelEvents#REGISTER_CONFIGURATION
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@ApiStatus.Experimental
|
||||
public interface RegisterConfiguration {
|
||||
void onChannelRegister(ClientConfigurationNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see C2SPlayChannelEvents#UNREGISTER_CONFIGURATION
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@ApiStatus.Experimental
|
||||
public interface UnregisterConfiguration {
|
||||
void onChannelUnregister(ClientConfigurationNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,6 @@ import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
@@ -384,9 +383,14 @@ public final class ClientConfigurationNetworking {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
send(packet.getType().getId(), buf);
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
addon.sendPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot send packet while not configuring!");
|
||||
}
|
||||
|
||||
private ClientConfigurationNetworking() {
|
||||
|
@@ -31,7 +31,6 @@ import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
@@ -339,6 +338,16 @@ public final class ClientPlayNetworking {
|
||||
return ClientNetworkingImpl.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to the connected server.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static <T extends FabricPacket> Packet<ServerCommonPacketListener> createC2SPacket(T packet) {
|
||||
return ClientNetworkingImpl.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected server.
|
||||
*
|
||||
@@ -381,9 +390,13 @@ public final class ClientPlayNetworking {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
send(packet.getType().getId(), buf);
|
||||
// You cant send without a client player, so this is fine
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
MinecraftClient.getInstance().getNetworkHandler().sendPacket(createC2SPacket(packet));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot send packets when not in game!");
|
||||
}
|
||||
|
||||
private ClientPlayNetworking() {
|
||||
|
@@ -27,16 +27,18 @@ import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.C2SPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.C2SConfigurationChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractChanneledNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufPayload;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientCommonNetworkHandlerAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientConfigurationNetworkHandlerAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientLoginNetworkHandlerAccessor;
|
||||
|
||||
public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetworkAddon<ClientConfigurationNetworking.ConfigurationChannelHandler> {
|
||||
private final ClientConfigurationNetworkHandler handler;
|
||||
@@ -65,8 +67,17 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
}
|
||||
|
||||
public void onServerReady() {
|
||||
this.sendInitialChannelRegistrationPacket();
|
||||
this.sentInitialRegisterPacket = true;
|
||||
// Do nothing for now
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void receiveRegistration(boolean register, PacketByteBuf buf) {
|
||||
super.receiveRegistration(register, buf);
|
||||
|
||||
if (register && !this.sentInitialRegisterPacket) {
|
||||
this.sendInitialChannelRegistrationPacket();
|
||||
this.sentInitialRegisterPacket = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,14 +111,19 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
return ClientPlayNetworking.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
return ClientPlayNetworking.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeRegisterEvent(List<Identifier> ids) {
|
||||
C2SPlayChannelEvents.REGISTER_CONFIGURATION.invoker().onChannelRegister(this.handler, this, this.client, ids);
|
||||
C2SConfigurationChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.client, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeUnregisterEvent(List<Identifier> ids) {
|
||||
C2SPlayChannelEvents.UNREGISTER_CONFIGURATION.invoker().onChannelUnregister(this.handler, this, this.client, ids);
|
||||
C2SConfigurationChannelEvents.UNREGISTER.invoker().onChannelUnregister(this.handler, this, this.client, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,6 +163,10 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return NetworkingImpl.isReservedPlayChannel(channelName);
|
||||
return NetworkingImpl.isReservedCommonChannel(channelName);
|
||||
}
|
||||
|
||||
public ChannelInfoHolder getChannelInfoHolder() {
|
||||
return (ChannelInfoHolder) ((ClientLoginNetworkHandlerAccessor) handler).getConnection();
|
||||
}
|
||||
}
|
||||
|
@@ -16,15 +16,13 @@
|
||||
|
||||
package net.fabricmc.fabric.impl.networking.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.ConnectScreen;
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
@@ -40,20 +38,24 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworkin
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.impl.networking.CommonPacketsImpl;
|
||||
import net.fabricmc.fabric.impl.networking.CommonRegisterPayload;
|
||||
import net.fabricmc.fabric.impl.networking.CommonVersionPayload;
|
||||
import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.FabricPacketPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufPayload;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientLoginNetworkHandlerAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ConnectScreenAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.MinecraftClientAccessor;
|
||||
|
||||
public final class ClientNetworkingImpl {
|
||||
public static final GlobalReceiverRegistry<ClientLoginNetworking.LoginQueryRequestHandler> LOGIN = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ClientConfigurationNetworking.ConfigurationChannelHandler> CONFIGURATION = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ClientPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ClientLoginNetworking.LoginQueryRequestHandler> LOGIN = new GlobalReceiverRegistry<>(NetworkState.LOGIN);
|
||||
public static final GlobalReceiverRegistry<ClientConfigurationNetworking.ConfigurationChannelHandler> CONFIGURATION = new GlobalReceiverRegistry<>(NetworkState.CONFIGURATION);
|
||||
public static final GlobalReceiverRegistry<ClientPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>(NetworkState.PLAY);
|
||||
private static ClientPlayNetworkAddon currentPlayAddon;
|
||||
private static ClientConfigurationNetworkAddon currentConfigurationAddon;
|
||||
|
||||
@@ -61,6 +63,10 @@ public final class ClientNetworkingImpl {
|
||||
return (ClientPlayNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static ClientConfigurationNetworkAddon getAddon(ClientConfigurationNetworkHandler handler) {
|
||||
return (ClientConfigurationNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static ClientLoginNetworkAddon getAddon(ClientLoginNetworkHandler handler) {
|
||||
return (ClientLoginNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
@@ -69,6 +75,19 @@ public final class ClientNetworkingImpl {
|
||||
return new CustomPayloadC2SPacket(new PacketByteBufPayload(channelName, buf));
|
||||
}
|
||||
|
||||
public static Packet<ServerCommonPacketListener> createC2SPacket(FabricPacket packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
if (NetworkingImpl.WRITE_FABRIC_PACKET_CALLING_THREAD) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
return createC2SPacket(packet.getType().getId(), buf);
|
||||
}
|
||||
|
||||
return new CustomPayloadC2SPacket(new FabricPacketPayload(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way logging into a integrated or remote dedicated server will differ, we need to obtain the login client connection differently.
|
||||
*/
|
||||
@@ -134,29 +153,44 @@ public final class ClientNetworkingImpl {
|
||||
currentConfigurationAddon = null;
|
||||
});
|
||||
|
||||
// Register a login query handler for early channel registration.
|
||||
ClientLoginNetworking.registerGlobalReceiver(NetworkingImpl.EARLY_REGISTRATION_CHANNEL, (client, handler, buf, listenerAdder) -> {
|
||||
int n = buf.readVarInt();
|
||||
List<Identifier> ids = new ArrayList<>(n);
|
||||
// Version packet
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(CommonVersionPayload.PACKET_ID, (client, handler, buf, responseSender) -> {
|
||||
var payload = new CommonVersionPayload(buf);
|
||||
int negotiatedVersion = handleVersionPacket(payload, responseSender);
|
||||
ClientNetworkingImpl.getAddon(handler).onCommonVersionPacket(negotiatedVersion);
|
||||
});
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
ids.add(buf.readIdentifier());
|
||||
// Register packet
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(CommonRegisterPayload.PACKET_ID, (client, handler, buf, responseSender) -> {
|
||||
var payload = new CommonRegisterPayload(buf);
|
||||
ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getAddon(handler);
|
||||
|
||||
if (CommonRegisterPayload.PLAY_PHASE.equals(payload.phase())) {
|
||||
if (payload.version() != addon.getNegotiatedVersion()) {
|
||||
throw new IllegalStateException("Negotiated common packet version: %d but received packet with version: %d".formatted(addon.getNegotiatedVersion(), payload.version()));
|
||||
}
|
||||
|
||||
addon.getChannelInfoHolder().getPendingChannelsNames(NetworkState.PLAY).addAll(payload.channels());
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the server");
|
||||
responseSender.sendPacket(new CommonRegisterPayload(addon.getNegotiatedVersion(), CommonRegisterPayload.PLAY_PHASE, ClientPlayNetworking.getGlobalReceivers()));
|
||||
} else {
|
||||
addon.onCommonRegisterPacket(payload);
|
||||
responseSender.sendPacket(addon.createRegisterPayload());
|
||||
}
|
||||
|
||||
ClientConnection connection = ((ClientLoginNetworkHandlerAccessor) handler).getConnection();
|
||||
((ChannelInfoHolder) connection).getPendingChannelsNames(NetworkState.PLAY).addAll(ids);
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the server");
|
||||
|
||||
PacketByteBuf response = PacketByteBufs.create();
|
||||
Collection<Identifier> channels = ClientPlayNetworking.getGlobalReceivers();
|
||||
response.writeVarInt(channels.size());
|
||||
|
||||
for (Identifier id : channels) {
|
||||
response.writeIdentifier(id);
|
||||
}
|
||||
|
||||
NetworkingImpl.LOGGER.debug("Sent accepted channels to the server");
|
||||
return CompletableFuture.completedFuture(response);
|
||||
});
|
||||
}
|
||||
|
||||
// Disconnect if there are no commonly supported versions.
|
||||
// Client responds with the intersection of supported versions.
|
||||
// Return the highest supported version
|
||||
private static int handleVersionPacket(CommonVersionPayload payload, PacketSender packetSender) {
|
||||
int version = CommonPacketsImpl.getHighestCommonVersion(payload.versions(), CommonPacketsImpl.SUPPORTED_COMMON_PACKET_VERSIONS);
|
||||
|
||||
if (version <= 0) {
|
||||
throw new UnsupportedOperationException("Client does not support any requested versions from server");
|
||||
}
|
||||
|
||||
packetSender.sendPacket(new CommonVersionPayload(new int[]{ version }));
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import net.minecraft.util.Identifier;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.C2SPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractChanneledNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
@@ -109,6 +110,11 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
||||
return ClientPlayNetworking.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
return ClientPlayNetworking.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeRegisterEvent(List<Identifier> ids) {
|
||||
C2SPlayChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.client, ids);
|
||||
@@ -151,6 +157,6 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return NetworkingImpl.isReservedPlayChannel(channelName);
|
||||
return NetworkingImpl.isReservedCommonChannel(channelName);
|
||||
}
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ import net.minecraft.server.network.ServerPlayerEntity;
|
||||
*
|
||||
* @see ServerPlayNetworking#registerGlobalReceiver(PacketType, ServerPlayNetworking.PlayPacketHandler)
|
||||
* @see ServerPlayNetworking#send(ServerPlayerEntity, PacketType, FabricPacket)
|
||||
* @see PacketSender#sendPacket(PacketType, FabricPacket)
|
||||
* @see PacketSender#sendPacket(FabricPacket)
|
||||
*/
|
||||
public interface FabricPacket {
|
||||
/**
|
||||
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.networking.v1;
|
||||
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerConfigurationTask;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Fabric-provided extensions for {@link ServerConfigurationNetworkHandler}.
|
||||
* This interface is automatically implemented via Mixin and interface injection.
|
||||
*/
|
||||
public interface FabricServerConfigurationNetworkHandler {
|
||||
/**
|
||||
* Enqueue a {@link ServerPlayerConfigurationTask} task to be processed.
|
||||
*
|
||||
* <p>Before adding a task use {@link ServerConfigurationNetworking#canSend(ServerConfigurationNetworkHandler, Identifier)}
|
||||
* to ensure that the client can process this task.
|
||||
*
|
||||
* <p>Once the client has handled the task a packet should be sent to the server.
|
||||
* Upon receiving this packet the server should call {@link FabricServerConfigurationNetworkHandler#completeTask(ServerPlayerConfigurationTask.Key)}
|
||||
*
|
||||
* @param task the task
|
||||
*/
|
||||
default void addTask(ServerPlayerConfigurationTask task) {
|
||||
throw new UnsupportedOperationException("Implemented via mixin");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key the key
|
||||
*/
|
||||
default void completeTask(ServerPlayerConfigurationTask.Key key) {
|
||||
throw new UnsupportedOperationException("Implemented via mixin");
|
||||
}
|
||||
}
|
@@ -23,9 +23,10 @@ import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.GenericFutureListenerHolder;
|
||||
@@ -43,6 +44,13 @@ public interface PacketSender {
|
||||
*/
|
||||
Packet<?> createPacket(Identifier channelName, PacketByteBuf buf);
|
||||
|
||||
/**
|
||||
* Makes a packet for a fabric packet.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
*/
|
||||
Packet<?> createPacket(FabricPacket packet);
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
@@ -57,9 +65,17 @@ public interface PacketSender {
|
||||
* @param packet the packet
|
||||
*/
|
||||
default <T extends FabricPacket> void sendPacket(T packet) {
|
||||
sendPacket(createPacket(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
* @param payload the payload
|
||||
*/
|
||||
default void sendPacket(CustomPayload payload) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
sendPacket(packet.getType().getId(), buf);
|
||||
payload.write(buf);
|
||||
sendPacket(payload.id(), buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +93,19 @@ public interface PacketSender {
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
*/
|
||||
default <T extends FabricPacket> void sendPacket(T packet, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
sendPacket(createPacket(packet), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
*/
|
||||
default void sendPacket(CustomPayload payload, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
sendPacket(packet.getType().getId(), buf, callback);
|
||||
payload.write(buf);
|
||||
sendPacket(payload.id(), buf, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,9 +123,19 @@ public interface PacketSender {
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
*/
|
||||
default <T extends FabricPacket> void sendPacket(T packet, @Nullable PacketCallbacks callback) {
|
||||
sendPacket(createPacket(packet), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
*/
|
||||
default void sendPacket(CustomPayload payload, @Nullable PacketCallbacks callback) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
sendPacket(packet.getType().getId(), buf, callback);
|
||||
payload.write(buf);
|
||||
sendPacket(payload.id(), buf, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.networking.v1;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Offers access to events related to the indication of a connected client's ability to receive packets in certain channels.
|
||||
*/
|
||||
public final class S2CConfigurationChannelEvents {
|
||||
/**
|
||||
* An event for the server configuration network handler receiving an update indicating the connected client's ability to receive packets in certain channels.
|
||||
* This event may be invoked at any time after login and up to disconnection.
|
||||
*/
|
||||
public static final Event<Register> REGISTER = EventFactory.createArrayBacked(Register.class, callbacks -> (handler, sender, server, channels) -> {
|
||||
for (Register callback : callbacks) {
|
||||
callback.onChannelRegister(handler, sender, server, channels);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the server configuration network handler receiving an update indicating the connected client's lack of ability to receive packets in certain channels.
|
||||
* This event may be invoked at any time after login and up to disconnection.
|
||||
*/
|
||||
public static final Event<Unregister> UNREGISTER = EventFactory.createArrayBacked(Unregister.class, callbacks -> (handler, sender, server, channels) -> {
|
||||
for (Unregister callback : callbacks) {
|
||||
callback.onChannelUnregister(handler, sender, server, channels);
|
||||
}
|
||||
});
|
||||
|
||||
private S2CConfigurationChannelEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see S2CConfigurationChannelEvents#REGISTER
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Register {
|
||||
void onChannelRegister(ServerConfigurationNetworkHandler handler, PacketSender sender, MinecraftServer server, List<Identifier> channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see S2CConfigurationChannelEvents#UNREGISTER
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Unregister {
|
||||
void onChannelUnregister(ServerConfigurationNetworkHandler handler, PacketSender sender, MinecraftServer server, List<Identifier> channels);
|
||||
}
|
||||
}
|
@@ -30,10 +30,37 @@ import net.fabricmc.fabric.api.event.EventFactory;
|
||||
@ApiStatus.Experimental
|
||||
public class ServerConfigurationConnectionEvents {
|
||||
/**
|
||||
* Event indicating a connection began sending configuration packets.
|
||||
* Event fired before any vanilla configuration has taken place.
|
||||
*
|
||||
* <p>This event is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}
|
||||
*
|
||||
* <p>Task queued during this event will complete before vanilla configuration starts.
|
||||
*/
|
||||
public static final Event<Send> SEND = EventFactory.createArrayBacked(Send.class, callbacks -> (handler, server) -> {
|
||||
for (Send callback : callbacks) {
|
||||
public static final Event<Configure> BEFORE_CONFIGURE = EventFactory.createArrayBacked(Configure.class, callbacks -> (handler, server) -> {
|
||||
for (Configure callback : callbacks) {
|
||||
callback.onSendConfiguration(handler, server);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Event fired during vanilla configuration.
|
||||
*
|
||||
* <p>This event is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}
|
||||
*
|
||||
* <p>An example usage of this:
|
||||
* <pre>{@code
|
||||
* ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
|
||||
* if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.PACKET_TYPE)) {
|
||||
* handler.addTask(new TestConfigurationTask("Example data"));
|
||||
* } else {
|
||||
* // You can opt to disconnect the client if it cannot handle the configuration task
|
||||
* handler.disconnect(Text.literal("Network test configuration not supported by client"));
|
||||
* }
|
||||
* });
|
||||
* }</pre>
|
||||
*/
|
||||
public static final Event<Configure> CONFIGURE = EventFactory.createArrayBacked(Configure.class, callbacks -> (handler, server) -> {
|
||||
for (Configure callback : callbacks) {
|
||||
callback.onSendConfiguration(handler, server);
|
||||
}
|
||||
});
|
||||
@@ -50,7 +77,7 @@ public class ServerConfigurationConnectionEvents {
|
||||
});
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Send {
|
||||
public interface Configure {
|
||||
void onSendConfiguration(ServerConfigurationNetworkHandler handler, MinecraftServer server);
|
||||
}
|
||||
|
||||
|
@@ -96,18 +96,7 @@ public final class ServerConfigurationNetworking {
|
||||
@Override
|
||||
public void receive(MinecraftServer server, ServerConfigurationNetworkHandler networkHandler, PacketByteBuf buf, PacketSender sender) {
|
||||
T packet = type.read(buf);
|
||||
|
||||
if (server.isOnThread()) {
|
||||
// Do not submit to the server thread if we're already running there.
|
||||
// Normally, packets are handled on the network IO thread - though it is
|
||||
// not guaranteed (for example, with 1.19.4 S2C packet bundling)
|
||||
// Since we're handling it right now, connection check is redundant.
|
||||
handler.receive(packet, sender);
|
||||
} else {
|
||||
server.execute(() -> {
|
||||
if (networkHandler.isConnectionOpen()) handler.receive(packet, sender);
|
||||
});
|
||||
}
|
||||
handler.receive(packet, networkHandler, sender);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -160,7 +149,7 @@ public final class ServerConfigurationNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* This method differs from {@link ServerConfigurationNetworking#registerGlobalReceiver(Identifier, ConfigurationChannelHandler)} since
|
||||
* the channel handler will only be applied to the player represented by the {@link ServerConfigurationNetworkHandler}.
|
||||
* the channel handler will only be applied to the client represented by the {@link ServerConfigurationNetworkHandler}.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, the world
|
||||
* must be modified in the server thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
@@ -189,7 +178,7 @@ public final class ServerConfigurationNetworking {
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* This method differs from {@link ServerConfigurationNetworking#registerGlobalReceiver(PacketType, ConfigurationPacketHandler)} since
|
||||
* the channel handler will only be applied to the player represented by the {@link ServerConfigurationNetworkHandler}.
|
||||
* the channel handler will only be applied to the client represented by the {@link ServerConfigurationNetworkHandler}.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ServerLoginNetworking#registerGlobalReceiver(Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}
|
||||
* login response has been received, you should use {@link ServerPlayConnectionEvents#INIT} to register the channel handler.
|
||||
@@ -213,18 +202,7 @@ public final class ServerConfigurationNetworking {
|
||||
@Override
|
||||
public void receive(MinecraftServer server, ServerConfigurationNetworkHandler networkHandler2, PacketByteBuf buf, PacketSender sender) {
|
||||
T packet = type.read(buf);
|
||||
|
||||
if (server.isOnThread()) {
|
||||
// Do not submit to the server thread if we're already running there.
|
||||
// Normally, packets are handled on the network IO thread - though it is
|
||||
// not guaranteed (for example, with 1.19.4 S2C packet bundling)
|
||||
// Since we're handling it right now, connection check is redundant.
|
||||
handler.receive(packet, sender);
|
||||
} else {
|
||||
server.execute(() -> {
|
||||
if (networkHandler2.isConnectionOpen()) handler.receive(packet, sender);
|
||||
});
|
||||
}
|
||||
handler.receive(packet, networkHandler2, sender);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -326,6 +304,19 @@ public final class ServerConfigurationNetworking {
|
||||
return ServerNetworkingImpl.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a connected client.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static <T extends FabricPacket> Packet<ClientCommonPacketListener> createS2CPacket(T packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
return ServerNetworkingImpl.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected client.
|
||||
*
|
||||
@@ -364,9 +355,7 @@ public final class ServerConfigurationNetworking {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
handler.sendPacket(createS2CPacket(packet.getType().getId(), buf));
|
||||
handler.sendPacket(createS2CPacket(packet));
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
@@ -391,7 +380,7 @@ public final class ServerConfigurationNetworking {
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft server instance.
|
||||
* Modification to the game should be {@linkplain ThreadExecutor#submit(Runnable) scheduled} using the server instance from {@link ServerConfigurationNetworking#getServer(ServerConfigurationNetworkHandler)}.
|
||||
*
|
||||
* <p>An example usage of this is:
|
||||
* <pre>{@code
|
||||
@@ -405,7 +394,7 @@ public final class ServerConfigurationNetworking {
|
||||
* });
|
||||
* }</pre>
|
||||
* @param server the server
|
||||
* @param handler the network handler that received this packet, representing the player/client who sent the packet
|
||||
* @param handler the network handler that received this packet, representing the client who sent the packet
|
||||
* @param buf the payload of the packet
|
||||
* @param responseSender the packet sender
|
||||
*/
|
||||
@@ -427,7 +416,10 @@ public final class ServerConfigurationNetworking {
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationPacketHandler<T extends FabricPacket> {
|
||||
/**
|
||||
* Handles the incoming packet. This is called on the server thread.
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* <p>Unlike {@link ServerPlayNetworking.PlayPacketHandler} this method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain ThreadExecutor#submit(Runnable) scheduled} using the Minecraft server instance from {@link ServerConfigurationNetworking#getServer(ServerConfigurationNetworkHandler)}.
|
||||
*
|
||||
* <p>An example usage of this:
|
||||
* <pre>{@code
|
||||
@@ -439,9 +431,10 @@ public final class ServerConfigurationNetworking {
|
||||
*
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param networkHandler the network handler
|
||||
* @param responseSender the packet sender
|
||||
* @see FabricPacket
|
||||
*/
|
||||
void receive(T packet, PacketSender responseSender);
|
||||
void receive(T packet, ServerConfigurationNetworkHandler networkHandler, PacketSender responseSender);
|
||||
}
|
||||
}
|
||||
|
@@ -389,6 +389,16 @@ public final class ServerPlayNetworking {
|
||||
return ServerNetworkingImpl.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a connected client.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static <T extends FabricPacket> Packet<ClientCommonPacketListener> createS2CPacket(T packet) {
|
||||
return ServerNetworkingImpl.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected client.
|
||||
*
|
||||
@@ -439,9 +449,7 @@ public final class ServerPlayNetworking {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
player.networkHandler.sendPacket(createS2CPacket(packet.getType().getId(), buf));
|
||||
player.networkHandler.sendPacket(createS2CPacket(packet));
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
@@ -46,22 +46,18 @@ import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
*
|
||||
* @param <H> the channel handler type
|
||||
*/
|
||||
public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAddon<H> implements PacketSender {
|
||||
public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAddon<H> implements PacketSender, CommonPacketHandler {
|
||||
protected final ClientConnection connection;
|
||||
protected final GlobalReceiverRegistry<H> receiver;
|
||||
protected final Set<Identifier> sendableChannels;
|
||||
protected final Set<Identifier> sendableChannelsView;
|
||||
|
||||
protected int commonVersion = -1;
|
||||
|
||||
protected AbstractChanneledNetworkAddon(GlobalReceiverRegistry<H> receiver, ClientConnection connection, String description) {
|
||||
this(receiver, connection, new HashSet<>(), description);
|
||||
}
|
||||
|
||||
protected AbstractChanneledNetworkAddon(GlobalReceiverRegistry<H> receiver, ClientConnection connection, Set<Identifier> sendableChannels, String description) {
|
||||
super(receiver, description);
|
||||
this.connection = connection;
|
||||
this.receiver = receiver;
|
||||
this.sendableChannels = sendableChannels;
|
||||
this.sendableChannelsView = Collections.unmodifiableSet(sendableChannels);
|
||||
this.sendableChannels = Collections.synchronizedSet(new HashSet<>());
|
||||
}
|
||||
|
||||
public abstract void lateInit();
|
||||
@@ -157,17 +153,22 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
||||
}
|
||||
|
||||
this.addId(ids, active);
|
||||
this.schedule(register ? () -> register(ids) : () -> unregister(ids));
|
||||
|
||||
if (register) {
|
||||
register(ids);
|
||||
} else {
|
||||
unregister(ids);
|
||||
}
|
||||
}
|
||||
|
||||
void register(List<Identifier> ids) {
|
||||
this.sendableChannels.addAll(ids);
|
||||
this.invokeRegisterEvent(ids);
|
||||
schedule(() -> this.invokeRegisterEvent(ids));
|
||||
}
|
||||
|
||||
void unregister(List<Identifier> ids) {
|
||||
this.sendableChannels.removeAll(ids);
|
||||
this.invokeUnregisterEvent(ids);
|
||||
schedule(() -> this.invokeUnregisterEvent(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,6 +203,62 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
||||
}
|
||||
|
||||
public Set<Identifier> getSendableChannels() {
|
||||
return this.sendableChannelsView;
|
||||
return Collections.unmodifiableSet(this.sendableChannels);
|
||||
}
|
||||
|
||||
// Common packet handlers
|
||||
|
||||
@Override
|
||||
public void onCommonVersionPacket(int negotiatedVersion) {
|
||||
assert negotiatedVersion == 1; // We only support version 1 for now
|
||||
|
||||
commonVersion = negotiatedVersion;
|
||||
this.logger.info("Negotiated common packet version {}", commonVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommonRegisterPacket(CommonRegisterPayload payload) {
|
||||
if (payload.version() != getNegotiatedVersion()) {
|
||||
throw new IllegalStateException("Negotiated common packet version: %d but received packet with version: %d".formatted(commonVersion, payload.version()));
|
||||
}
|
||||
|
||||
final String currentPhase = getPhase();
|
||||
|
||||
if (currentPhase == null) {
|
||||
// We don't support receiving the register packet during this phase. See getPhase() for supported phases.
|
||||
// The normal case where the play channels are sent during configuration is handled in the client/common configuration packet handlers.
|
||||
logger.warn("Received common register packet for phase {} in network state: {}", payload.phase(), receiver.getState());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!payload.phase().equals(currentPhase)) {
|
||||
// We need to handle receiving the play phase during configuration!
|
||||
throw new IllegalStateException("Register packet received for phase (%s) on handler for phase(%s)".formatted(payload.phase(), currentPhase));
|
||||
}
|
||||
|
||||
register(new ArrayList<>(payload.channels()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonRegisterPayload createRegisterPayload() {
|
||||
return new CommonRegisterPayload(getNegotiatedVersion(), getPhase(), this.getReceivableChannels());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNegotiatedVersion() {
|
||||
if (commonVersion == -1) {
|
||||
throw new IllegalStateException("Not yet negotiated common packet version");
|
||||
}
|
||||
|
||||
return commonVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getPhase() {
|
||||
return switch (receiver.getState()) {
|
||||
case PLAY -> CommonRegisterPayload.PLAY_PHASE;
|
||||
case CONFIGURATION -> CommonRegisterPayload.CONFIGURATION_PHASE;
|
||||
default -> null; // We don't support receiving this packet on any other phase
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
public interface CommonPacketHandler {
|
||||
void onCommonVersionPacket(int negotiatedVersion);
|
||||
|
||||
void onCommonRegisterPacket(CommonRegisterPayload payload);
|
||||
|
||||
CommonRegisterPayload createRegisterPayload();
|
||||
|
||||
int getNegotiatedVersion();
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.network.ServerPlayerConfigurationTask;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
|
||||
public class CommonPacketsImpl {
|
||||
public static final int PACKET_VERSION_1 = 1;
|
||||
public static final int[] SUPPORTED_COMMON_PACKET_VERSIONS = new int[]{ PACKET_VERSION_1 };
|
||||
|
||||
public static void init() {
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(CommonVersionPayload.PACKET_ID, (server, handler, buf, responseSender) -> {
|
||||
var payload = new CommonVersionPayload(buf);
|
||||
ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(handler);
|
||||
addon.onCommonVersionPacket(getNegotiatedVersion(payload));
|
||||
handler.completeTask(CommonVersionConfigurationTask.KEY);
|
||||
});
|
||||
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(CommonRegisterPayload.PACKET_ID, (server, handler, buf, responseSender) -> {
|
||||
var payload = new CommonRegisterPayload(buf);
|
||||
ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(handler);
|
||||
|
||||
if (CommonRegisterPayload.PLAY_PHASE.equals(payload.phase())) {
|
||||
if (payload.version() != addon.getNegotiatedVersion()) {
|
||||
throw new IllegalStateException("Negotiated common packet version: %d but received packet with version: %d".formatted(addon.getNegotiatedVersion(), payload.version()));
|
||||
}
|
||||
|
||||
// Play phase hasnt started yet, add them to the pending names.
|
||||
addon.getChannelInfoHolder().getPendingChannelsNames(NetworkState.PLAY).addAll(payload.channels());
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the client for play phase");
|
||||
} else {
|
||||
addon.onCommonRegisterPacket(payload);
|
||||
}
|
||||
|
||||
handler.completeTask(CommonRegisterConfigurationTask.KEY);
|
||||
});
|
||||
|
||||
// Create a configuration task to send and receive the common packets
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
|
||||
final ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(handler);
|
||||
|
||||
if (ServerConfigurationNetworking.canSend(handler, CommonVersionPayload.PACKET_ID)) {
|
||||
// Tasks are processed in order.
|
||||
handler.addTask(new CommonVersionConfigurationTask(addon));
|
||||
|
||||
if (ServerConfigurationNetworking.canSend(handler, CommonRegisterPayload.PACKET_ID)) {
|
||||
handler.addTask(new CommonRegisterConfigurationTask(addon));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// A configuration phase task to send and receive the version packets.
|
||||
private record CommonVersionConfigurationTask(ServerConfigurationNetworkAddon addon) implements ServerPlayerConfigurationTask {
|
||||
public static final Key KEY = new Key(CommonVersionPayload.PACKET_ID.toString());
|
||||
|
||||
@Override
|
||||
public void sendPacket(Consumer<Packet<?>> sender) {
|
||||
addon.sendPacket(new CommonVersionPayload(SUPPORTED_COMMON_PACKET_VERSIONS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getKey() {
|
||||
return KEY;
|
||||
}
|
||||
}
|
||||
|
||||
// A configuration phase task to send and receive the registration packets.
|
||||
private record CommonRegisterConfigurationTask(ServerConfigurationNetworkAddon addon) implements ServerPlayerConfigurationTask {
|
||||
public static final Key KEY = new Key(CommonRegisterPayload.PACKET_ID.toString());
|
||||
|
||||
@Override
|
||||
public void sendPacket(Consumer<Packet<?>> sender) {
|
||||
addon.sendPacket(addon.createRegisterPayload());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getKey() {
|
||||
return KEY;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNegotiatedVersion(CommonVersionPayload payload) {
|
||||
int version = getHighestCommonVersion(payload.versions(), SUPPORTED_COMMON_PACKET_VERSIONS);
|
||||
|
||||
if (version <= 0) {
|
||||
throw new UnsupportedOperationException("server does not support any requested versions from client");
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
public static int getHighestCommonVersion(int[] a, int[] b) {
|
||||
int[] as = a.clone();
|
||||
int[] bs = b.clone();
|
||||
|
||||
Arrays.sort(as);
|
||||
Arrays.sort(bs);
|
||||
|
||||
int ap = as.length - 1;
|
||||
int bp = bs.length - 1;
|
||||
|
||||
while (ap >= 0 && bp >= 0) {
|
||||
if (as[ap] == bs[bp]) {
|
||||
return as[ap];
|
||||
}
|
||||
|
||||
if (as[ap] > bs[bp]) {
|
||||
ap--;
|
||||
} else {
|
||||
bp--;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public record CommonRegisterPayload(int version, String phase, Set<Identifier> channels) implements CustomPayload {
|
||||
public static final Identifier PACKET_ID = new Identifier("c", "register");
|
||||
|
||||
public static final String PLAY_PHASE = "play";
|
||||
public static final String CONFIGURATION_PHASE = "configuration";
|
||||
|
||||
public CommonRegisterPayload(PacketByteBuf buf) {
|
||||
this(
|
||||
buf.readVarInt(),
|
||||
buf.readString(),
|
||||
buf.readCollection(HashSet::new, PacketByteBuf::readIdentifier)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeVarInt(version);
|
||||
buf.writeString(phase);
|
||||
buf.writeCollection(channels, PacketByteBuf::writeIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return PACKET_ID;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public record CommonVersionPayload(int[] versions) implements CustomPayload {
|
||||
public static final Identifier PACKET_ID = new Identifier("c", "version");
|
||||
|
||||
public CommonVersionPayload(PacketByteBuf buf) {
|
||||
this(buf.readIntArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeIntArray(versions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return PACKET_ID;
|
||||
}
|
||||
}
|
@@ -27,18 +27,22 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public final class GlobalReceiverRegistry<H> {
|
||||
private final NetworkState state;
|
||||
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Map<Identifier, H> handlers;
|
||||
private final Set<AbstractNetworkAddon<H>> trackedAddons = new HashSet<>();
|
||||
|
||||
public GlobalReceiverRegistry() {
|
||||
this(new HashMap<>()); // sync map should be fine as there is little read write competitions
|
||||
public GlobalReceiverRegistry(NetworkState state) {
|
||||
this(state, new HashMap<>()); // sync map should be fine as there is little read write competitions
|
||||
}
|
||||
|
||||
public GlobalReceiverRegistry(Map<Identifier, H> map) {
|
||||
public GlobalReceiverRegistry(NetworkState state, Map<Identifier, H> map) {
|
||||
this.state = state;
|
||||
this.handlers = map;
|
||||
}
|
||||
|
||||
@@ -58,7 +62,7 @@ public final class GlobalReceiverRegistry<H> {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(handler, "Channel handler cannot be null");
|
||||
|
||||
if (NetworkingImpl.isReservedPlayChannel(channelName)) {
|
||||
if (NetworkingImpl.isReservedCommonChannel(channelName)) {
|
||||
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
@@ -81,7 +85,7 @@ public final class GlobalReceiverRegistry<H> {
|
||||
public H unregisterGlobalReceiver(Identifier channelName) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
|
||||
if (NetworkingImpl.isReservedPlayChannel(channelName)) {
|
||||
if (NetworkingImpl.isReservedCommonChannel(channelName)) {
|
||||
throw new IllegalArgumentException(String.format("Cannot unregister packet handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
@@ -172,4 +176,8 @@ public final class GlobalReceiverRegistry<H> {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkState getState() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
@@ -16,77 +16,32 @@
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
|
||||
|
||||
public final class NetworkingImpl {
|
||||
public static final String MOD_ID = "fabric-networking-api-v1";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
/**
|
||||
* When enabled the fabric packet is written to the {@link net.minecraft.network.PacketByteBuf} on the calling thread.
|
||||
* This is not enabled by default as it currently causes issues in single player.
|
||||
*/
|
||||
public static final boolean WRITE_FABRIC_PACKET_CALLING_THREAD = Boolean.parseBoolean(System.getProperty("fabric-api.networking.write-fabric-packet-calling-thread", "true"));
|
||||
|
||||
/**
|
||||
* Id of packet used to register supported channels.
|
||||
*/
|
||||
public static final Identifier REGISTER_CHANNEL = new Identifier("minecraft", "register");
|
||||
|
||||
/**
|
||||
* Id of packet used to unregister supported channels.
|
||||
*/
|
||||
public static final Identifier UNREGISTER_CHANNEL = new Identifier("minecraft", "unregister");
|
||||
/**
|
||||
* Id of the packet used to declare all currently supported channels.
|
||||
* Dynamic registration of supported channels is still allowed using {@link NetworkingImpl#REGISTER_CHANNEL} and {@link NetworkingImpl#UNREGISTER_CHANNEL}.
|
||||
*/
|
||||
public static final Identifier EARLY_REGISTRATION_CHANNEL = new Identifier(MOD_ID, "early_registration");
|
||||
|
||||
public static void init() {
|
||||
// Login setup
|
||||
ServerLoginConnectionEvents.QUERY_START.register((handler, server, sender, synchronizer) -> {
|
||||
// Send early registration packet
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
Collection<Identifier> channelsNames = ServerPlayNetworking.getGlobalReceivers();
|
||||
buf.writeVarInt(channelsNames.size());
|
||||
|
||||
for (Identifier id : channelsNames) {
|
||||
buf.writeIdentifier(id);
|
||||
}
|
||||
|
||||
sender.sendPacket(EARLY_REGISTRATION_CHANNEL, buf);
|
||||
NetworkingImpl.LOGGER.debug("Sent accepted channels to the client for \"{}\"", handler.getConnectionInfo());
|
||||
});
|
||||
|
||||
ServerLoginNetworking.registerGlobalReceiver(EARLY_REGISTRATION_CHANNEL, (server, handler, understood, buf, synchronizer, sender) -> {
|
||||
if (!understood) {
|
||||
// The client is likely a vanilla client.
|
||||
return;
|
||||
}
|
||||
|
||||
int n = buf.readVarInt();
|
||||
List<Identifier> ids = new ArrayList<>(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
ids.add(buf.readIdentifier());
|
||||
}
|
||||
|
||||
ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection();
|
||||
((ChannelInfoHolder) connection).getPendingChannelsNames(NetworkState.PLAY).addAll(ids);
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the client for \"{}\"", handler.getConnectionInfo());
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isReservedPlayChannel(Identifier channelName) {
|
||||
public static boolean isReservedCommonChannel(Identifier channelName) {
|
||||
return channelName.equals(REGISTER_CHANNEL) || channelName.equals(UNREGISTER_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking.payload;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
|
||||
public record FabricPacketLoginQueryRequestPayload(FabricPacket fabricPacket) implements LoginQueryRequestPayload {
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
fabricPacket.write(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return fabricPacket.getType().getId();
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking.payload;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
|
||||
public record FabricPacketPayload(FabricPacket fabricPacket) implements CustomPayload {
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
fabricPacket.write(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return fabricPacket.getType().getId();
|
||||
}
|
||||
}
|
@@ -24,10 +24,13 @@ import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.common.PlayPingS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.S2CConfigurationChannelEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
@@ -36,11 +39,12 @@ import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufPayload;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerCommonNetworkHandlerAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
|
||||
|
||||
public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetworkAddon<ServerConfigurationNetworking.ConfigurationChannelHandler> {
|
||||
private final ServerConfigurationNetworkHandler handler;
|
||||
private final MinecraftServer server;
|
||||
private boolean sentInitialRegisterPacket;
|
||||
private RegisterState registerState = RegisterState.NOT_SENT;
|
||||
|
||||
public ServerConfigurationNetworkAddon(ServerConfigurationNetworkHandler handler, MinecraftServer server) {
|
||||
super(ServerNetworkingImpl.CONFIGURATION, ((ServerCommonNetworkHandlerAccessor) handler).getConnection(), "ServerConfigurationNetworkAddon for " + handler.getDebugProfile().getName());
|
||||
@@ -61,13 +65,48 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
}
|
||||
}
|
||||
|
||||
public void sendConfiguration() {
|
||||
ServerConfigurationConnectionEvents.SEND.invoker().onSendConfiguration(handler, server);
|
||||
public void preConfiguration() {
|
||||
ServerConfigurationConnectionEvents.BEFORE_CONFIGURE.invoker().onSendConfiguration(handler, server);
|
||||
}
|
||||
|
||||
public void onClientReady() {
|
||||
this.sendInitialChannelRegistrationPacket();
|
||||
this.sentInitialRegisterPacket = true;
|
||||
public void configuration() {
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.invoker().onSendConfiguration(handler, server);
|
||||
}
|
||||
|
||||
public boolean startConfiguration() {
|
||||
if (this.registerState == RegisterState.NOT_SENT) {
|
||||
// Send the registration packet, followed by a ping
|
||||
this.sendInitialChannelRegistrationPacket();
|
||||
this.sendPacket(new PlayPingS2CPacket(0xFAB71C));
|
||||
|
||||
this.registerState = RegisterState.SENT;
|
||||
|
||||
// Cancel the configuration for now, the response from the ping or registration packet will continue.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We should have received a response
|
||||
assert registerState == RegisterState.RECEIVED || registerState == RegisterState.NOT_RECEIVED;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void receiveRegistration(boolean register, PacketByteBuf buf) {
|
||||
super.receiveRegistration(register, buf);
|
||||
|
||||
if (register && registerState == RegisterState.SENT) {
|
||||
// We received the registration packet, thus we know this is a modded client, continue with configuration.
|
||||
registerState = RegisterState.RECEIVED;
|
||||
handler.sendConfigurations();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPong(int parameter) {
|
||||
if (registerState == RegisterState.SENT) {
|
||||
// We did not receive the registration packet, thus we think this is a vanilla client, continue with configuration.
|
||||
registerState = RegisterState.NOT_RECEIVED;
|
||||
handler.sendConfigurations();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +128,7 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
|
||||
@Override
|
||||
protected void schedule(Runnable task) {
|
||||
this.server.execute(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,18 +136,25 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
return ServerPlayNetworking.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
return ServerPlayNetworking.createS2CPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeRegisterEvent(List<Identifier> ids) {
|
||||
S2CConfigurationChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.server, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeUnregisterEvent(List<Identifier> ids) {
|
||||
S2CConfigurationChannelEvents.UNREGISTER.invoker().onChannelUnregister(this.handler, this, this.server, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRegistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the register packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
if (this.registerState != RegisterState.NOT_SENT) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
@@ -119,7 +166,7 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
@Override
|
||||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
if (this.registerState != RegisterState.NOT_SENT) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
@@ -136,7 +183,7 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return NetworkingImpl.isReservedPlayChannel(channelName);
|
||||
return NetworkingImpl.isReservedCommonChannel(channelName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,4 +191,15 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
||||
// Ensure we flush the packet.
|
||||
handler.send(packet, callback, true);
|
||||
}
|
||||
|
||||
private enum RegisterState {
|
||||
NOT_SENT,
|
||||
SENT,
|
||||
RECEIVED,
|
||||
NOT_RECEIVED
|
||||
}
|
||||
|
||||
public ChannelInfoHolder getChannelInfoHolder() {
|
||||
return (ChannelInfoHolder) ((ServerLoginNetworkHandlerAccessor) handler).getConnection();
|
||||
}
|
||||
}
|
||||
|
@@ -40,12 +40,14 @@ import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.GenericFutureListenerHolder;
|
||||
import net.fabricmc.fabric.impl.networking.payload.FabricPacketLoginQueryRequestPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufLoginQueryRequestPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufLoginQueryResponse;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
|
||||
@@ -164,9 +166,13 @@ public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLo
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
int queryId = this.queryIdFactory.nextId();
|
||||
return new LoginQueryRequestS2CPacket(queryId, new PacketByteBufLoginQueryRequestPayload(channelName, buf));
|
||||
}
|
||||
|
||||
LoginQueryRequestS2CPacket ret = new LoginQueryRequestS2CPacket(queryId, new PacketByteBufLoginQueryRequestPayload(channelName, buf));
|
||||
return ret;
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
int queryId = this.queryIdFactory.nextId();
|
||||
return new LoginQueryRequestS2CPacket(queryId, new FabricPacketLoginQueryRequestPayload(packet));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -16,6 +16,9 @@
|
||||
|
||||
package net.fabricmc.fabric.impl.networking.server;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.ClientCommonPacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
@@ -25,17 +28,21 @@ import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.FabricPacketPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufPayload;
|
||||
|
||||
public final class ServerNetworkingImpl {
|
||||
public static final GlobalReceiverRegistry<ServerLoginNetworking.LoginQueryResponseHandler> LOGIN = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ServerConfigurationNetworking.ConfigurationChannelHandler> CONFIGURATION = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ServerPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ServerLoginNetworking.LoginQueryResponseHandler> LOGIN = new GlobalReceiverRegistry<>(NetworkState.LOGIN);
|
||||
public static final GlobalReceiverRegistry<ServerConfigurationNetworking.ConfigurationChannelHandler> CONFIGURATION = new GlobalReceiverRegistry<>(NetworkState.CONFIGURATION);
|
||||
public static final GlobalReceiverRegistry<ServerPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>(NetworkState.PLAY);
|
||||
|
||||
public static ServerPlayNetworkAddon getAddon(ServerPlayNetworkHandler handler) {
|
||||
return (ServerPlayNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
@@ -52,4 +59,17 @@ public final class ServerNetworkingImpl {
|
||||
public static Packet<ClientCommonPacketListener> createC2SPacket(Identifier channel, PacketByteBuf buf) {
|
||||
return new CustomPayloadS2CPacket(new PacketByteBufPayload(channel, buf));
|
||||
}
|
||||
|
||||
public static Packet<ClientCommonPacketListener> createC2SPacket(FabricPacket packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
if (NetworkingImpl.WRITE_FABRIC_PACKET_CALLING_THREAD) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
packet.write(buf);
|
||||
return createC2SPacket(packet.getType().getId(), buf);
|
||||
}
|
||||
|
||||
return new CustomPayloadS2CPacket(new FabricPacketPayload(packet));
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.S2CPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
@@ -96,6 +97,11 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
||||
return ServerPlayNetworking.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
return ServerPlayNetworking.createS2CPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeRegisterEvent(List<Identifier> ids) {
|
||||
S2CPlayChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.server, ids);
|
||||
@@ -138,6 +144,6 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return NetworkingImpl.isReservedPlayChannel(channelName);
|
||||
return NetworkingImpl.isReservedCommonChannel(channelName);
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.common.PlayPongC2SPacket;
|
||||
import net.minecraft.server.network.ServerCommonNetworkHandler;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
@@ -51,4 +52,11 @@ public abstract class ServerCommonNetworkHandlerMixin implements NetworkHandlerE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onPlayPong", at = @At("HEAD"))
|
||||
private void onPlayPong(PlayPongC2SPacket packet, CallbackInfo ci) {
|
||||
if (getAddon() instanceof ServerConfigurationNetworkAddon addon) {
|
||||
addon.onPong(packet.getParameter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,12 @@
|
||||
|
||||
package net.fabricmc.fabric.mixin.networking;
|
||||
|
||||
import java.util.Queue;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -28,17 +33,42 @@ import net.minecraft.network.packet.s2c.common.DisconnectS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerCommonNetworkHandler;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerConfigurationTask;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricServerConfigurationNetworkHandler;
|
||||
import net.fabricmc.fabric.impl.networking.DisconnectPacketSource;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
|
||||
// We want to apply a bit earlier than other mods which may not use us in order to prevent refCount issues
|
||||
@Mixin(value = ServerConfigurationNetworkHandler.class, priority = 999)
|
||||
public abstract class ServerConfigurationNetworkHandlerMixin extends ServerCommonNetworkHandler implements NetworkHandlerExtensions, DisconnectPacketSource {
|
||||
@Mixin(value = ServerConfigurationNetworkHandler.class, priority = 900)
|
||||
public abstract class ServerConfigurationNetworkHandlerMixin extends ServerCommonNetworkHandler implements NetworkHandlerExtensions, DisconnectPacketSource, FabricServerConfigurationNetworkHandler {
|
||||
@Shadow
|
||||
@Nullable
|
||||
private ServerPlayerConfigurationTask currentTask;
|
||||
|
||||
@Shadow
|
||||
protected abstract void onTaskFinished(ServerPlayerConfigurationTask.Key key);
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Queue<ServerPlayerConfigurationTask> tasks;
|
||||
|
||||
@Shadow
|
||||
public abstract boolean isConnectionOpen();
|
||||
|
||||
@Shadow
|
||||
public abstract void sendConfigurations();
|
||||
|
||||
@Unique
|
||||
ServerConfigurationNetworkAddon addon;
|
||||
private ServerConfigurationNetworkAddon addon;
|
||||
|
||||
@Unique
|
||||
private boolean sentConfiguration;
|
||||
|
||||
@Unique
|
||||
private boolean earlyTaskExecution;
|
||||
|
||||
public ServerConfigurationNetworkHandlerMixin(MinecraftServer server, ClientConnection connection, int keepAliveId) {
|
||||
super(server, connection, keepAliveId);
|
||||
@@ -51,14 +81,63 @@ public abstract class ServerConfigurationNetworkHandlerMixin extends ServerCommo
|
||||
this.addon.lateInit();
|
||||
}
|
||||
|
||||
@Inject(method = "sendConfigurations", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getCombinedDynamicRegistries()Lnet/minecraft/registry/CombinedDynamicRegistries;"))
|
||||
@Inject(method = "sendConfigurations", at = @At("HEAD"), cancellable = true)
|
||||
private void onClientReady(CallbackInfo ci) {
|
||||
this.addon.onClientReady();
|
||||
// Send the initial channel registration packet
|
||||
if (this.addon.startConfiguration()) {
|
||||
assert currentTask == null;
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ready to start sending packets
|
||||
if (!sentConfiguration) {
|
||||
this.addon.preConfiguration();
|
||||
sentConfiguration = true;
|
||||
earlyTaskExecution = true;
|
||||
}
|
||||
|
||||
// Run the early tasks
|
||||
if (earlyTaskExecution) {
|
||||
if (pollEarlyTasks()) {
|
||||
ci.cancel();
|
||||
return;
|
||||
} else {
|
||||
earlyTaskExecution = false;
|
||||
}
|
||||
}
|
||||
|
||||
// All early tasks should have been completed
|
||||
assert currentTask == null;
|
||||
assert tasks.isEmpty();
|
||||
|
||||
// Run the vanilla tasks.
|
||||
this.addon.configuration();
|
||||
}
|
||||
|
||||
@Inject(method = "sendConfigurations", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerConfigurationNetworkHandler;queueSendResourcePackTask()V"))
|
||||
private void sendConfigurations(CallbackInfo ci) {
|
||||
this.addon.sendConfiguration();
|
||||
@Unique
|
||||
private boolean pollEarlyTasks() {
|
||||
if (!earlyTaskExecution) {
|
||||
throw new IllegalStateException("Early task execution has finished");
|
||||
}
|
||||
|
||||
if (this.currentTask != null) {
|
||||
throw new IllegalStateException("Task " + this.currentTask.getKey().id() + " has not finished yet");
|
||||
}
|
||||
|
||||
if (!this.isConnectionOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerPlayerConfigurationTask task = this.tasks.poll();
|
||||
|
||||
if (task != null) {
|
||||
this.currentTask = task;
|
||||
task.sendPacket(this::sendPacket);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
@@ -75,4 +154,26 @@ public abstract class ServerConfigurationNetworkHandlerMixin extends ServerCommo
|
||||
public Packet<?> createDisconnectPacket(Text message) {
|
||||
return new DisconnectS2CPacket(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTask(ServerPlayerConfigurationTask task) {
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeTask(ServerPlayerConfigurationTask.Key key) {
|
||||
if (!earlyTaskExecution) {
|
||||
onTaskFinished(key);
|
||||
return;
|
||||
}
|
||||
|
||||
final ServerPlayerConfigurationTask.Key currentKey = this.currentTask != null ? this.currentTask.getKey() : null;
|
||||
|
||||
if (!key.equals(currentKey)) {
|
||||
throw new IllegalStateException("Unexpected request for task finish, current task: " + currentKey + ", requested: " + key);
|
||||
}
|
||||
|
||||
this.currentTask = null;
|
||||
sendConfigurations();
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
],
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.impl.networking.NetworkingImpl::init"
|
||||
"net.fabricmc.fabric.impl.networking.CommonPacketsImpl::init"
|
||||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl::clientInit"
|
||||
@@ -37,6 +37,9 @@
|
||||
}
|
||||
],
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable"
|
||||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_8610": [ "net/fabricmc/fabric/api/networking/v1/FabricServerConfigurationNetworkHandler" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.networking.unit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.CommonPacketHandler;
|
||||
import net.fabricmc.fabric.impl.networking.CommonPacketsImpl;
|
||||
import net.fabricmc.fabric.impl.networking.CommonRegisterPayload;
|
||||
import net.fabricmc.fabric.impl.networking.CommonVersionPayload;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
|
||||
public class CommonPacketTests {
|
||||
private PacketSender packetSender;
|
||||
private ChannelInfoHolder channelInfoHolder;
|
||||
|
||||
private ClientConfigurationNetworkHandler clientNetworkHandler;
|
||||
private ClientConfigurationNetworkAddon clientAddon;
|
||||
|
||||
private ServerConfigurationNetworkHandler serverNetworkHandler;
|
||||
private ServerConfigurationNetworkAddon serverAddon;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
CommonPacketsImpl.init();
|
||||
ClientNetworkingImpl.clientInit();
|
||||
|
||||
// Register a receiver to send in the play registry response
|
||||
ClientPlayNetworking.registerGlobalReceiver(new Identifier("fabric", "global_client"), (client, handler, buf, responseSender) -> {
|
||||
});
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
packetSender = mock(PacketSender.class);
|
||||
channelInfoHolder = new MockChannelInfoHolder();
|
||||
|
||||
clientNetworkHandler = mock(ClientConfigurationNetworkHandler.class);
|
||||
clientAddon = mock(ClientConfigurationNetworkAddon.class);
|
||||
when(ClientNetworkingImpl.getAddon(clientNetworkHandler)).thenReturn(clientAddon);
|
||||
when(clientAddon.getChannelInfoHolder()).thenReturn(channelInfoHolder);
|
||||
|
||||
serverNetworkHandler = mock(ServerConfigurationNetworkHandler.class);
|
||||
serverAddon = mock(ServerConfigurationNetworkAddon.class);
|
||||
when(ServerNetworkingImpl.getAddon(serverNetworkHandler)).thenReturn(serverAddon);
|
||||
when(serverAddon.getChannelInfoHolder()).thenReturn(channelInfoHolder);
|
||||
}
|
||||
|
||||
// Test handling the version packet on the client
|
||||
@Test
|
||||
void handleVersionPacketClient() {
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the server
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIntArray(new int[]{1, 2, 3});
|
||||
|
||||
packetHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
|
||||
// Check the response we are sending back to the server
|
||||
PacketByteBuf response = readResponse(packetSender);
|
||||
assertArrayEquals(new int[]{1}, response.readIntArray());
|
||||
assertEquals(0, response.readableBytes());
|
||||
|
||||
assertEquals(1, getNegotiatedVersion(clientAddon));
|
||||
}
|
||||
|
||||
// Test handling the version packet on the client, when the server sends unsupported versions
|
||||
@Test
|
||||
void handleVersionPacketClientUnsupported() {
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the server
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIntArray(new int[]{2, 3}); // We only support version 1
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> {
|
||||
packetHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
});
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
}
|
||||
|
||||
// Test handling the version packet on the server
|
||||
@Test
|
||||
void handleVersionPacketServer() {
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the client
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIntArray(new int[]{1, 2, 3});
|
||||
|
||||
packetHandler.receive(null, serverNetworkHandler, buf, null);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
assertEquals(1, getNegotiatedVersion(serverAddon));
|
||||
}
|
||||
|
||||
// Test handling the version packet on the server unsupported version
|
||||
@Test
|
||||
void handleVersionPacketServerUnsupported() {
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the client
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIntArray(new int[]{3}); // Server only supports version 1
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> {
|
||||
packetHandler.receive(null, serverNetworkHandler, buf, packetSender);
|
||||
});
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
}
|
||||
|
||||
// Test handing the play registry packet on the client configuration handler
|
||||
@Test
|
||||
void handlePlayRegistryClient() {
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(clientAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
||||
// Receive a packet from the server
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeVarInt(1); // Version
|
||||
buf.writeString("play"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
packetHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "test")), channelInfoHolder.getPendingChannelsNames(NetworkState.PLAY));
|
||||
|
||||
// Check the response we are sending back to the server
|
||||
PacketByteBuf response = readResponse(packetSender);
|
||||
assertEquals(1, response.readVarInt());
|
||||
assertEquals("play", response.readString());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "global_client")), response.readCollection(HashSet::new, PacketByteBuf::readIdentifier));
|
||||
assertEquals(0, response.readableBytes());
|
||||
}
|
||||
|
||||
// Test handling the configuration registry packet on the client configuration handler
|
||||
@Test
|
||||
void handleConfigurationRegistryClient() {
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(clientAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
when(clientAddon.createRegisterPayload()).thenAnswer(i -> new CommonRegisterPayload(1, "configuration", Set.of(new Identifier("fabric", "global_configuration_client"))));
|
||||
|
||||
// Receive a packet from the server
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeVarInt(1); // Version
|
||||
buf.writeString("configuration"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
packetHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
verify(clientAddon, times(1)).onCommonRegisterPacket(any());
|
||||
|
||||
// Check the response we are sending back to the server
|
||||
PacketByteBuf response = readResponse(packetSender);
|
||||
assertEquals(1, response.readVarInt());
|
||||
assertEquals("configuration", response.readString());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "global_configuration_client")), response.readCollection(HashSet::new, PacketByteBuf::readIdentifier));
|
||||
assertEquals(0, response.readableBytes());
|
||||
}
|
||||
|
||||
// Test handing the play registry packet on the server configuration handler
|
||||
@Test
|
||||
void handlePlayRegistryServer() {
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(serverAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
||||
// Receive a packet from the client
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeVarInt(1); // Version
|
||||
buf.writeString("play"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
packetHandler.receive(null, serverNetworkHandler, buf, packetSender);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "test")), channelInfoHolder.getPendingChannelsNames(NetworkState.PLAY));
|
||||
}
|
||||
|
||||
// Test handing the configuration registry packet on the server configuration handler
|
||||
@Test
|
||||
void handleConfigurationRegistryServer() {
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(serverAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
||||
// Receive a packet from the client
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeVarInt(1); // Version
|
||||
buf.writeString("configuration"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
packetHandler.receive(null, serverNetworkHandler, buf, packetSender);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
verify(serverAddon, times(1)).onCommonRegisterPacket(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighestCommonVersionWithCommonElement() {
|
||||
int[] a = {1, 2, 3};
|
||||
int[] b = {1, 2};
|
||||
assertEquals(2, CommonPacketsImpl.getHighestCommonVersion(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighestCommonVersionWithoutCommonElement() {
|
||||
int[] a = {1, 3, 5};
|
||||
int[] b = {2, 4, 6};
|
||||
assertEquals(-1, CommonPacketsImpl.getHighestCommonVersion(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighestCommonVersionWithOneEmptyArray() {
|
||||
int[] a = {1, 3, 5};
|
||||
int[] b = {};
|
||||
assertEquals(-1, CommonPacketsImpl.getHighestCommonVersion(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighestCommonVersionWithBothEmptyArrays() {
|
||||
int[] a = {};
|
||||
int[] b = {};
|
||||
assertEquals(-1, CommonPacketsImpl.getHighestCommonVersion(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighestCommonVersionWithIdenticalArrays() {
|
||||
int[] a = {1, 2, 3};
|
||||
int[] b = {1, 2, 3};
|
||||
assertEquals(3, CommonPacketsImpl.getHighestCommonVersion(a, b));
|
||||
}
|
||||
|
||||
private static PacketByteBuf readResponse(PacketSender packetSender) {
|
||||
ArgumentCaptor<CustomPayload> responseCaptor = ArgumentCaptor.forClass(CustomPayload.class);
|
||||
verify(packetSender, times(1)).sendPacket(responseCaptor.capture());
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
responseCaptor.getValue().write(buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static int getNegotiatedVersion(CommonPacketHandler packetHandler) {
|
||||
ArgumentCaptor<Integer> responseCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(packetHandler, times(1)).onCommonVersionPacket(responseCaptor.capture());
|
||||
return responseCaptor.getValue();
|
||||
}
|
||||
|
||||
private static class MockChannelInfoHolder implements ChannelInfoHolder {
|
||||
private final Map<NetworkState, Collection<Identifier>> playChannels = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getPendingChannelsNames(NetworkState state) {
|
||||
return this.playChannels.computeIfAbsent(state, (key) -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.networking.configuration;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.network.ServerPlayerConfigurationTask;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
|
||||
/**
|
||||
* Also see NetworkingConfigurationClientTest.
|
||||
*/
|
||||
public class NetworkingConfigurationTest implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
|
||||
// You must check to see if the client can handle your config task
|
||||
if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.PACKET_TYPE)) {
|
||||
handler.addTask(new TestConfigurationTask("Example data"));
|
||||
} else {
|
||||
// You can opt to disconnect the client if it cannot handle the configuration task
|
||||
handler.disconnect(Text.literal("Network test configuration not supported by client"));
|
||||
}
|
||||
});
|
||||
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(ConfigurationCompletePacket.PACKET_TYPE, (packet, networkHandler, responseSender) -> {
|
||||
networkHandler.completeTask(TestConfigurationTask.KEY);
|
||||
});
|
||||
}
|
||||
|
||||
public record TestConfigurationTask(String data) implements ServerPlayerConfigurationTask {
|
||||
public static final Key KEY = new Key(new Identifier(NetworkingTestmods.ID, "configure").toString());
|
||||
|
||||
@Override
|
||||
public void sendPacket(Consumer<Packet<?>> sender) {
|
||||
var packet = new ConfigurationPacket(data);
|
||||
sender.accept(ServerConfigurationNetworking.createS2CPacket(packet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getKey() {
|
||||
return KEY;
|
||||
}
|
||||
}
|
||||
|
||||
public record ConfigurationPacket(String data) implements FabricPacket {
|
||||
public static final PacketType<ConfigurationPacket> PACKET_TYPE = PacketType.create(new Identifier(NetworkingTestmods.ID, "configure"), ConfigurationPacket::new);
|
||||
|
||||
public ConfigurationPacket(PacketByteBuf buf) {
|
||||
this(buf.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeString(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record ConfigurationCompletePacket() implements FabricPacket {
|
||||
public static final PacketType<ConfigurationCompletePacket> PACKET_TYPE = PacketType.create(new Identifier(NetworkingTestmods.ID, "configure_complete"), ConfigurationCompletePacket::new);
|
||||
|
||||
public ConfigurationCompletePacket(PacketByteBuf buf) {
|
||||
this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,12 +11,14 @@
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.networking.channeltest.NetworkingChannelTest",
|
||||
"net.fabricmc.fabric.test.networking.configuration.NetworkingConfigurationTest",
|
||||
"net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindPacketTest",
|
||||
"net.fabricmc.fabric.test.networking.login.NetworkingLoginQueryTest",
|
||||
"net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketTest"
|
||||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.networking.client.channeltest.NetworkingChannelClientTest",
|
||||
"net.fabricmc.fabric.test.networking.client.configuration.NetworkingConfigurationClientTest",
|
||||
"net.fabricmc.fabric.test.networking.client.DisconnectScreenTest",
|
||||
"net.fabricmc.fabric.test.networking.client.keybindreciever.NetworkingKeybindClientPacketTest",
|
||||
"net.fabricmc.fabric.test.networking.client.login.NetworkingLoginQueryClientTest",
|
||||
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.networking.client.configuration;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
|
||||
import net.fabricmc.fabric.test.networking.configuration.NetworkingConfigurationTest;
|
||||
|
||||
public class NetworkingConfigurationClientTest implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(NetworkingConfigurationTest.ConfigurationPacket.PACKET_TYPE, (packet, responseSender) -> {
|
||||
// Handle stuff here
|
||||
|
||||
// Respond back to the server that the task is complete
|
||||
responseSender.sendPacket(new NetworkingConfigurationTest.ConfigurationCompletePacket());
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user