mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-27 01:55:41 -05:00
Fabric Networking API V1 (#1081)
* Networking api v1 Some final docs? Licenses and testmod Fix a bunch o imports and make things work for v1 (v0 is bork) Make the testmod pass checkstyle and work Docs for v1 * Deprecate v0 and implement using v1 * Drop files down one package due to package check error * Fix issue with channel registration, add another testmod * jaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaavadoc * Make javadoc use `code`, move impl interface to package access * this things * Rename a few internal methods * Mark all client side stuff client only, move client mixins * Add null checks around the place, clarify some javadoc and method names * Make FutureListeners uninstantiable * Some internal nullable annotations * An impl class I forgot to rename * Some comments and clarify some client login handler javadoc * Add a missing FunctionalInterface annotation * Split play and login, move client stuff to right package * No interface left behind * Inline channel registries in api * Login and play subpackages not needed * Add helper method to create play custom packets * hasGlobalChannel -> hasGlobalReceiver * Just rename the collection method for now * Inline PlayPacketSender into static methods * Start on testmod idea for verifying dynamic registration * Add client login events * You don't say hello when talking to yourself. Also more testmod stuff * Make event names present tense * Some javadoc and impl interface rename * Change the test keybinding * Begin working on dynamic reg * Dynamic reg works, just need a lot of cleanup and reimpling global * A few renames, readd global methods * Try to reduce the amount of duplicate registration logic * Reimplement dynamic accessors * More impl * Start reimplementing global receivers. Still very hacky solution. * Reimplement some server global reciever stuff * Add login init event for server login. * Implement client login query start event * Move event invocations into addon, don't dual register global recievers * Finally reimplement global recievers for all networking phases * A revelation: Send packets properly This also finds the issue with screen getting the proper S2C channels, current on TODO list. * Disconnect event does not need a packet sender * Clarify, add methods to get channels net handler can recieve on client * Unregister actually works now * Bunch of null checks, add simpler login delay test for vanilla clients * Add some debug logging entries, fix unregister on client's session reg * Play channel event javadoc and rename login query handlers * More channel -> channelName * thisening * Introduce the basics infrastructure for tracking global receivers * Add more substantial javadoc to login connection events * Javadoc, reimplement unreg methods on v0, 1 impl fix * Implement tracking for global recievers * Dont forget to start tracked sessions in 3/4 cases * Global receiver docs and move methods in classes * Complete null checks * big boi javadoc part 1 * Finish the main javadoc, usage javadoc is left * Set so has method is not needed * Rename receiveable and sendable methods * Add the two missing private ctors * buildscript update to upstream * Split out player finding stuff to networking player tracking API v1 Signed-off-by: liach <liach@users.noreply.github.com> Forward v0 PlayerStream to new module, add entity track events Rename module to player tracking Well javadoc can make sense Decide on tracking for the name Update fabric-player-tracking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/player/tracking/v1/package-info.java Co-authored-by: Erlend Åmdal <erlend@aamdal.com> Remove exceptions from javadoc that are not thrown javadoc fix again Handle a case where the player manager happens to be null rename player tracking to player lookup Yeet * Cherrypick #1092 * Remove some redundant client networking methods, rename `(un)register` to `(un)registerReceiver` * Simplify access to dynamic reg on client * Param shifting, let users get sender. * Warning about time and distance units * Make sure these are client only * Fix control flow in ClientPlayNetworking#send * Correct example code javadoc * javadoc correction in server login * Put login delay tests behind system property Also remove unnecessary junk added by old module that was merged together. * Fix ordering so channel registrations during `PHASE`_INIT work * Fix prod bug and an oversight * Fix login when connecting to dedicated server * Update registry sync to v0 to prevent issue with reg sync hanging client * this is done
This commit is contained in:
parent
ffb68a877b
commit
3775d21f76
93 changed files with 5307 additions and 678 deletions
|
@ -32,7 +32,6 @@ import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
|
|||
import net.fabricmc.fabric.api.container.ContainerFactory;
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
|
||||
import net.fabricmc.fabric.impl.networking.PacketTypes;
|
||||
|
||||
public class ScreenProviderRegistryImpl implements ScreenProviderRegistry {
|
||||
/**
|
||||
|
@ -68,7 +67,7 @@ public class ScreenProviderRegistryImpl implements ScreenProviderRegistry {
|
|||
}
|
||||
|
||||
public static void init() {
|
||||
ClientSidePacketRegistry.INSTANCE.register(PacketTypes.OPEN_CONTAINER, (packetContext, packetByteBuf) -> {
|
||||
ClientSidePacketRegistry.INSTANCE.register(ContainerProviderImpl.OPEN_CONTAINER, (packetContext, packetByteBuf) -> {
|
||||
Identifier identifier = packetByteBuf.readIdentifier();
|
||||
int syncId = packetByteBuf.readUnsignedByte();
|
||||
packetByteBuf.retain();
|
||||
|
|
|
@ -33,10 +33,10 @@ import net.minecraft.network.PacketByteBuf;
|
|||
|
||||
import net.fabricmc.fabric.api.container.ContainerFactory;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.PacketTypes;
|
||||
import net.fabricmc.fabric.mixin.container.ServerPlayerEntityAccessor;
|
||||
|
||||
public class ContainerProviderImpl implements ContainerProviderRegistry {
|
||||
public static final Identifier OPEN_CONTAINER = new Identifier("fabric", "container/open");
|
||||
/**
|
||||
* Use the instance provided by ContainerProviderRegistry.
|
||||
*/
|
||||
|
@ -91,7 +91,7 @@ public class ContainerProviderImpl implements ContainerProviderRegistry {
|
|||
buf.writeByte(syncId);
|
||||
|
||||
writer.accept(buf);
|
||||
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(PacketTypes.OPEN_CONTAINER, buf));
|
||||
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(OPEN_CONTAINER, buf));
|
||||
|
||||
PacketByteBuf clonedBuf = new PacketByteBuf(buf.duplicate());
|
||||
clonedBuf.readIdentifier();
|
||||
|
|
12
fabric-networking-api-v1/build.gradle
Normal file
12
fabric-networking-api-v1/build.gradle
Normal file
|
@ -0,0 +1,12 @@
|
|||
archivesBaseName = "fabric-networking-api-v1"
|
||||
version = getSubprojectVersion(project, "1.0.0")
|
||||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base'
|
||||
])
|
||||
|
||||
dependencies {
|
||||
testmodCompile project(path: ':fabric-command-api-v1', configuration: 'dev')
|
||||
testmodCompile project(path: ':fabric-lifecycle-events-v1', configuration: 'dev')
|
||||
testmodCompile project(path: ':fabric-key-binding-api-v1', configuration: 'dev')
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Offers access to events related to the connection to a server on the client while the server is processing the client's login request.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientLoginConnectionEvents {
|
||||
/**
|
||||
* An event for when the client's login process has begun.
|
||||
* This event may be used by mods to prepare their client side state.
|
||||
* This event does not guarantee that a login attempt will be successful.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.
|
||||
*/
|
||||
public static final Event<LoginInit> LOGIN_INIT = EventFactory.createArrayBacked(LoginInit.class, callbacks -> (handler, client) -> {
|
||||
for (LoginInit callback : callbacks) {
|
||||
callback.onLoginStart(handler, client);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for when the client has started receiving login queries.
|
||||
* A client can only start receiving login queries when a server has sent the first login query.
|
||||
* Vanilla servers will typically never make the client enter this login phase, but it is not a guarantee that the
|
||||
* connected server is a vanilla server since a modded server or proxy may have no login queries to send to the client
|
||||
* and therefore bypass the login query phase.
|
||||
* If this event is fired then it is a sign that a server is not a vanilla server or the server is behind a proxy which
|
||||
* is capable of handling login queries.
|
||||
*
|
||||
* <p>This event may be used to {@link ClientLoginNetworking.LoginQueryRequestHandler register login query handlers}
|
||||
* which may be used to send a response to a server.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.
|
||||
*/
|
||||
public static final Event<LoginQueryStart> LOGIN_QUERY_START = EventFactory.createArrayBacked(LoginQueryStart.class, callbacks -> (handler, client) -> {
|
||||
for (LoginQueryStart callback : callbacks) {
|
||||
callback.onLoginQueryStart(handler, client);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for when the client's login process has ended due to disconnection.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.
|
||||
*/
|
||||
public static final Event<LoginDisconnect> LOGIN_DISCONNECT = EventFactory.createArrayBacked(LoginDisconnect.class, callbacks -> (handler, client) -> {
|
||||
for (LoginDisconnect callback : callbacks) {
|
||||
callback.onLoginDisconnect(handler, client);
|
||||
}
|
||||
});
|
||||
|
||||
private ClientLoginConnectionEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClientLoginConnectionEvents#LOGIN_INIT
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface LoginInit {
|
||||
void onLoginStart(ClientLoginNetworkHandler handler, MinecraftClient client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClientLoginConnectionEvents#LOGIN_QUERY_START
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface LoginQueryStart {
|
||||
void onLoginQueryStart(ClientLoginNetworkHandler handler, MinecraftClient client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClientLoginConnectionEvents#LOGIN_DISCONNECT
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface LoginDisconnect {
|
||||
void onLoginDisconnect(ClientLoginNetworkHandler handler, MinecraftClient client);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl;
|
||||
|
||||
/**
|
||||
* Offers access to login stage client-side networking functionalities.
|
||||
*
|
||||
* <p>The Minecraft login protocol only allows the client to respond to a server's request, but not initiate one of its own.
|
||||
*
|
||||
* @see ClientPlayNetworking
|
||||
* @see ServerLoginNetworking
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientLoginNetworking {
|
||||
/**
|
||||
* Registers a handler to a query request channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterGlobalReceiver(Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param queryHandler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ClientLoginNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ClientLoginNetworking#registerReceiver(Identifier, LoginQueryRequestHandler)
|
||||
*/
|
||||
public static boolean registerGlobalReceiver(Identifier channelName, LoginQueryRequestHandler queryHandler) {
|
||||
return ClientNetworkingImpl.LOGIN.registerGlobalReceiver(channelName, queryHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a query request channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code channel} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @see ClientLoginNetworking#registerGlobalReceiver(Identifier, LoginQueryRequestHandler)
|
||||
* @see ClientLoginNetworking#unregisterReceiver(Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static ClientLoginNetworking.LoginQueryRequestHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return ClientNetworkingImpl.LOGIN.unregisterGlobalReceiver(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all query request channel names which global receivers are registered for.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* @return all channel names which global receivers are registered for.
|
||||
*/
|
||||
public static Set<Identifier> getGlobalReceivers() {
|
||||
return ClientNetworkingImpl.LOGIN.getChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a query request channel.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channelName}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param queryHandler the handler
|
||||
* @return false if a handler is already registered to the channel name
|
||||
* @throws IllegalStateException if the client is not logging in
|
||||
*/
|
||||
public static boolean registerReceiver(Identifier channelName, LoginQueryRequestHandler queryHandler) throws IllegalStateException {
|
||||
final ClientConnection connection = ClientNetworkingImpl.getLoginConnection();
|
||||
|
||||
if (connection != null) {
|
||||
final PacketListener packetListener = connection.getPacketListener();
|
||||
|
||||
if (packetListener instanceof ClientLoginNetworkHandler) {
|
||||
return ClientNetworkingImpl.getAddon(((ClientLoginNetworkHandler) packetListener)).registerChannel(channelName, queryHandler);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot register receiver while client is not logging in!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a query request channel.
|
||||
*
|
||||
* <p>The {@code channelName} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel name
|
||||
* @throws IllegalStateException if the client is not logging in
|
||||
*/
|
||||
@Nullable
|
||||
public static LoginQueryRequestHandler unregisterReceiver(Identifier channelName) throws IllegalStateException {
|
||||
final ClientConnection connection = ClientNetworkingImpl.getLoginConnection();
|
||||
|
||||
if (connection != null) {
|
||||
final PacketListener packetListener = connection.getPacketListener();
|
||||
|
||||
if (packetListener instanceof ClientLoginNetworkHandler) {
|
||||
return ClientNetworkingImpl.getAddon(((ClientLoginNetworkHandler) packetListener)).unregisterChannel(channelName);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot unregister receiver while client is not logging in!");
|
||||
}
|
||||
|
||||
private ClientLoginNetworking() {
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface LoginQueryRequestHandler {
|
||||
/**
|
||||
* Handles an incoming query request from a server.
|
||||
*
|
||||
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance.
|
||||
*
|
||||
* <p>The return value of this method is a completable future that may be used to delay the login process to the server until a task {@link CompletableFuture#isDone() is done}.
|
||||
* The future should complete in reasonably time to prevent disconnection by the server.
|
||||
* If your request processes instantly, you may use {@link CompletableFuture#completedFuture(Object)} to wrap your response for immediate sending.
|
||||
*
|
||||
* @param client the client
|
||||
* @param handler the network handler that received this packet
|
||||
* @param buf the payload of the packet
|
||||
* @param listenerAdder listeners to be called when the response packet is sent to the server
|
||||
* @return a completable future which contains the payload to respond to the server with.
|
||||
* If the future contains {@code null}, then the server will be notified that the client did not understand the query.
|
||||
*/
|
||||
CompletableFuture<@Nullable PacketByteBuf> receive(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer<GenericFutureListener<? extends Future<? super Void>>> listenerAdder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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 net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
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 connection to a server on a logical client.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientPlayConnectionEvents {
|
||||
/**
|
||||
* An event for the initialization of the client play network handler.
|
||||
*
|
||||
* <p>At this stage, the network handler is ready to send packets to the server.
|
||||
*/
|
||||
public static final Event<PlayInit> PLAY_INIT = EventFactory.createArrayBacked(PlayInit.class, callbacks -> (handler, sender, client) -> {
|
||||
for (PlayInit callback : callbacks) {
|
||||
callback.onPlayInit(handler, sender, client);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the disconnection of the client play network handler.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.
|
||||
*/
|
||||
public static final Event<PlayDisconnect> PLAY_DISCONNECT = EventFactory.createArrayBacked(PlayDisconnect.class, callbacks -> (handler, client) -> {
|
||||
for (PlayDisconnect callback : callbacks) {
|
||||
callback.onPlayDisconnect(handler, client);
|
||||
}
|
||||
});
|
||||
|
||||
private ClientPlayConnectionEvents() {
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface PlayInit {
|
||||
void onPlayInit(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface PlayDisconnect {
|
||||
void onPlayDisconnect(ClientPlayNetworkHandler handler, MinecraftClient client);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* 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.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl;
|
||||
|
||||
/**
|
||||
* Offers access to play stage client-side networking functionalities.
|
||||
*
|
||||
* <p>Client-side networking functionalities include receiving clientbound packets,
|
||||
* sending serverbound packets, and events related to client-side network handlers.
|
||||
*
|
||||
* <p>This class should be only used on the physical client and for the logical client.
|
||||
*
|
||||
* @see ClientLoginNetworking
|
||||
* @see ServerPlayNetworking
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientPlayNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterGlobalReceiver(Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param channelHandler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ClientPlayNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ClientPlayNetworking#registerReceiver(Identifier, PlayChannelHandler)
|
||||
*/
|
||||
public static boolean registerGlobalReceiver(Identifier channelName, PlayChannelHandler channelHandler) {
|
||||
return ClientNetworkingImpl.PLAY.registerGlobalReceiver(channelName, channelHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code channel} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @see ClientPlayNetworking#registerGlobalReceiver(Identifier, PlayChannelHandler)
|
||||
* @see ClientPlayNetworking#unregisterReceiver(Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return ClientNetworkingImpl.PLAY.unregisterGlobalReceiver(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all channel names which global receivers are registered for.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* @return all channel names which global receivers are registered for.
|
||||
*/
|
||||
public static Set<Identifier> getGlobalReceivers() {
|
||||
return ClientNetworkingImpl.PLAY.getChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static boolean registerReceiver(Identifier channelName, PlayChannelHandler channelHandler) {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler()).registerChannel(channelName, channelHandler);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot register receiver while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
*
|
||||
* <p>The {@code channel} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterReceiver(Identifier channelName) throws IllegalStateException {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler()).unregisterChannel(channelName);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot unregister receiver while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the channel names that the client can receive packets on.
|
||||
*
|
||||
* @return All the channel names that the client can receive packets on
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static Set<Identifier> getReceived() throws IllegalStateException {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler()).getReceivableChannels();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot get a list of channels the client can receive packets on while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all channel names that the connected server declared the ability to receive a packets on.
|
||||
*
|
||||
* @return All the channel names the connected server declared the ability to receive a packets on
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static Set<Identifier> getSendable() throws IllegalStateException {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler()).getSendableChannels();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot get a list of channels the server can receive packets on while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the connected server declared the ability to receive a packet on a specified channel name.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @return True if the connected server has declared the ability to receive a packet on the specified channel
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static boolean canSend(Identifier channelName) throws IllegalArgumentException {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler()).getSendableChannels().contains(channelName);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot check whether the server can receive a packet while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a the connected server.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @param buf the packet byte buf which represents the payload of the packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static Packet<?> createC2SPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(buf, "Buf cannot be null");
|
||||
|
||||
return ClientNetworkingImpl.createPlayC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected server.
|
||||
*
|
||||
* @return the client's packet sender
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static PacketSender getSender() throws IllegalStateException {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler());
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot get packet sender when not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to the connected server.
|
||||
*
|
||||
* @param channelName the channel of the packet
|
||||
* @param buf the payload of the packet
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static void send(Identifier channelName, PacketByteBuf buf) throws IllegalStateException {
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
MinecraftClient.getInstance().getNetworkHandler().sendPacket(createC2SPacket(channelName, buf));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot send packets when not in game!");
|
||||
}
|
||||
|
||||
private ClientPlayNetworking() {
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface PlayChannelHandler {
|
||||
/**
|
||||
* 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 net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance.
|
||||
*
|
||||
* <p>An example usage of this is to display an overlay message:
|
||||
* <pre>{@code
|
||||
* ClientPlayNetworking.registerReceiver(new Identifier("mymod", "overlay"), (client, handler, buf, responseSender) -&rt; {
|
||||
* String message = buf.readString(32767);
|
||||
*
|
||||
* // All operations on the server or world must be executed on the server thread
|
||||
* client.execute(() -&rt; {
|
||||
* client.inGameHud.setOverlayMessage(message, true);
|
||||
* });
|
||||
* });
|
||||
* }</pre>
|
||||
* @param client the client
|
||||
* @param handler the network handler that received this packet
|
||||
* @param buf the payload of the packet
|
||||
* @param responseSender the packet sender
|
||||
*/
|
||||
void receive(MinecraftClient client, ClientPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.ClientPlayNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
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.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class S2CPlayChannelEvents {
|
||||
/**
|
||||
* An event for the client play 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 play 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 S2CPlayChannelEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see S2CPlayChannelEvents#REGISTER
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface Register {
|
||||
void onChannelRegister(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see S2CPlayChannelEvents#UNREGISTER
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface Unregister {
|
||||
void onChannelUnregister(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client, List<Identifier> channels);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Networking API (client side), version 1.
|
||||
*
|
||||
* <p>For login stage networking see {@link net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking}.
|
||||
* For play stage networking see {@link net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking}.
|
||||
*
|
||||
* <p>For events related to connection to a server see {@link net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents} for login stage
|
||||
* or {@link net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents} for play stage.
|
||||
*
|
||||
* <p>For events related to the ability of a server to receive packets on a channel of a specific name see {@link net.fabricmc.fabric.api.client.networking.v1.S2CPlayChannelEvents}.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.networking.v1;
|
|
@ -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.ServerPlayNetworkHandler;
|
||||
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 C2SPlayChannelEvents {
|
||||
/**
|
||||
* An event for the server play 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 play 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 C2SPlayChannelEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see C2SPlayChannelEvents#REGISTER
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Register {
|
||||
void onChannelRegister(ServerPlayNetworkHandler handler, PacketSender sender, MinecraftServer server, List<Identifier> channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see C2SPlayChannelEvents#UNREGISTER
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Unregister {
|
||||
void onChannelUnregister(ServerPlayNetworkHandler handler, PacketSender sender, MinecraftServer server, List<Identifier> channels);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.entity.Entity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Events related to a tracking entities within a player's view distance.
|
||||
*/
|
||||
public final class EntityTrackingEvents {
|
||||
/**
|
||||
* An event that is called before player starts tracking an entity.
|
||||
* Typically this occurs when an entity enters a client's view distance.
|
||||
* This event is called before the player's client is sent the entity's {@link Entity#createSpawnPacket() spawn packet}.
|
||||
*/
|
||||
public static final Event<StartTracking> START_TRACKING = EventFactory.createArrayBacked(StartTracking.class, callbacks -> (trackedEntity, player) -> {
|
||||
for (StartTracking callback : callbacks) {
|
||||
callback.onStartTracking(trackedEntity, player);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event that is called after a player has stopped tracking an entity.
|
||||
* The client at this point was sent a packet to {@link net.minecraft.network.packet.s2c.play.EntitiesDestroyS2CPacket destroy} the entity on the client.
|
||||
* The entity still exists on the server.
|
||||
*/
|
||||
public static final Event<StopTracking> STOP_TRACKING = EventFactory.createArrayBacked(StopTracking.class, callbacks -> (trackedEntity, player) -> {
|
||||
for (StopTracking callback : callbacks) {
|
||||
callback.onStopTracking(trackedEntity, player);
|
||||
}
|
||||
});
|
||||
|
||||
@FunctionalInterface
|
||||
public interface StartTracking {
|
||||
/**
|
||||
* Called before an entity starts getting tracked by a player.
|
||||
*
|
||||
* @param trackedEntity the entity that will be tracked
|
||||
* @param player the player that will track the entity
|
||||
*/
|
||||
void onStartTracking(Entity trackedEntity, ServerPlayerEntity player);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface StopTracking {
|
||||
/**
|
||||
* Called after an entity stops getting tracked by a player.
|
||||
*
|
||||
* @param trackedEntity the entity that is no longer being tracked
|
||||
* @param player the player that is no longer tracking the entity
|
||||
*/
|
||||
void onStopTracking(Entity trackedEntity, ServerPlayerEntity player);
|
||||
}
|
||||
|
||||
private EntityTrackingEvents() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.Objects;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.local.LocalChannel;
|
||||
import io.netty.channel.local.LocalServerChannel;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.FutureListener;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
/**
|
||||
* Utilities for working with netty's future listeners.
|
||||
* @see FutureListener
|
||||
* @see ChannelFutureListener
|
||||
*/
|
||||
public final class FutureListeners {
|
||||
/**
|
||||
* Returns a future listener that releases a packet byte buf when the buffer has been sent to a remote connection.
|
||||
*
|
||||
* @param buf the buffer
|
||||
* @return the future listener
|
||||
*/
|
||||
public static ChannelFutureListener free(PacketByteBuf buf) {
|
||||
Objects.requireNonNull(buf, "PacketByteBuf cannot be null");
|
||||
|
||||
return (future) -> {
|
||||
if (!isLocalChannel(future.channel())) {
|
||||
buf.release();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a netty channel performs local transportation, or if the message objects in the channel are directly passed than written to and read from a byte buf.
|
||||
*
|
||||
* @param channel the channel to check
|
||||
* @return whether the channel is local
|
||||
*/
|
||||
public static boolean isLocalChannel(Channel channel) {
|
||||
return channel instanceof LocalServerChannel || channel instanceof LocalChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines two future listeners.
|
||||
*
|
||||
* @param first the first future listener
|
||||
* @param second the second future listener
|
||||
* @param <A> the future type of the first listener, used for casting
|
||||
* @param <B> the future type of the second listener, used for casting
|
||||
* @return the combined future listener.
|
||||
*/
|
||||
// A, B exist just to allow casting
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <A extends Future<? super Void>, B extends Future<? super Void>> GenericFutureListener<? extends Future<? super Void>> union(GenericFutureListener<A> first, GenericFutureListener<B> second) {
|
||||
// Return an empty future listener in the case of both parameters somehow being null
|
||||
if (first == null && second == null) {
|
||||
return future -> { };
|
||||
}
|
||||
|
||||
if (first == null) {
|
||||
return second;
|
||||
}
|
||||
|
||||
if (second == null) {
|
||||
return first;
|
||||
}
|
||||
|
||||
return future -> {
|
||||
first.operationComplete((A) future);
|
||||
second.operationComplete((B) future);
|
||||
};
|
||||
}
|
||||
|
||||
private FutureListeners() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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.Objects;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
/**
|
||||
* Helper methods for working with and creating {@link PacketByteBuf}s.
|
||||
*/
|
||||
public final class PacketByteBufs {
|
||||
private static final PacketByteBuf EMPTY_PACKET_BYTE_BUF = new PacketByteBuf(Unpooled.EMPTY_BUFFER);
|
||||
|
||||
/**
|
||||
* Returns an empty instance of packet byte buf.
|
||||
*
|
||||
* @return an empty buf
|
||||
*/
|
||||
public static PacketByteBuf empty() {
|
||||
return EMPTY_PACKET_BYTE_BUF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new heap memory-backed instance of packet byte buf.
|
||||
*
|
||||
* @return a new buf
|
||||
*/
|
||||
public static PacketByteBuf create() {
|
||||
return new PacketByteBuf(Unpooled.buffer());
|
||||
}
|
||||
|
||||
// Convenience methods for byte buf methods that return a new byte buf
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.readBytes} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @param length the number of bytes to transfer
|
||||
* @return the transferred bytes
|
||||
* @see ByteBuf#readBytes(int)
|
||||
*/
|
||||
public static PacketByteBuf readBytes(ByteBuf buf, int length) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.readBytes(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.readSlice} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @param length the size of the new slice
|
||||
* @return the newly created slice
|
||||
* @see ByteBuf#readSlice(int)
|
||||
*/
|
||||
public static PacketByteBuf readSlice(ByteBuf buf, int length) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.readSlice(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.readRetainedSlice} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @param length the size of the new slice
|
||||
* @return the newly created slice
|
||||
* @see ByteBuf#readRetainedSlice(int)
|
||||
*/
|
||||
public static PacketByteBuf readRetainedSlice(ByteBuf buf, int length) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.readRetainedSlice(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.copy} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @return a copy of the buf
|
||||
* @see ByteBuf#copy()
|
||||
*/
|
||||
public static PacketByteBuf copy(ByteBuf buf) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.copy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.copy} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @param index the starting index
|
||||
* @param length the size of the copy
|
||||
* @return a copy of the buf
|
||||
* @see ByteBuf#copy(int, int)
|
||||
*/
|
||||
public static PacketByteBuf copy(ByteBuf buf, int index, int length) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.copy(index, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.slice} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @return a slice of the buf
|
||||
* @see ByteBuf#slice()
|
||||
*/
|
||||
public static PacketByteBuf slice(ByteBuf buf) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.slice());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.retainedSlice} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @return a slice of the buf
|
||||
* @see ByteBuf#retainedSlice()
|
||||
*/
|
||||
public static PacketByteBuf retainedSlice(ByteBuf buf) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.retainedSlice());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.slice} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @param index the starting index
|
||||
* @param length the size of the copy
|
||||
* @return a slice of the buf
|
||||
* @see ByteBuf#slice(int, int)
|
||||
*/
|
||||
public static PacketByteBuf slice(ByteBuf buf, int index, int length) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.slice(index, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.retainedSlice} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @param index the starting index
|
||||
* @param length the size of the copy
|
||||
* @return a slice of the buf
|
||||
* @see ByteBuf#retainedSlice(int, int)
|
||||
*/
|
||||
public static PacketByteBuf retainedSlice(ByteBuf buf, int index, int length) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.retainedSlice(index, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.duplicate} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @return a duplicate of the buf
|
||||
* @see ByteBuf#duplicate()
|
||||
*/
|
||||
public static PacketByteBuf duplicate(ByteBuf buf) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.duplicate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the newly created buf from {@code buf.retainedDuplicate} in a packet byte buf.
|
||||
*
|
||||
* @param buf the original buf
|
||||
* @return a duplicate of the buf
|
||||
* @see ByteBuf#retainedDuplicate()
|
||||
*/
|
||||
public static PacketByteBuf retainedDuplicate(ByteBuf buf) {
|
||||
Objects.requireNonNull(buf, "ByteBuf cannot be null");
|
||||
|
||||
return new PacketByteBuf(buf.retainedDuplicate());
|
||||
}
|
||||
|
||||
private PacketByteBufs() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.Objects;
|
||||
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Represents something that supports sending packets to channels.
|
||||
* @see PacketByteBufs
|
||||
*/
|
||||
public interface PacketSender {
|
||||
/**
|
||||
* Makes a packet for a channel.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param buf the content of the packet
|
||||
*/
|
||||
Packet<?> createPacket(Identifier channelName, PacketByteBuf buf);
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param packet the packet
|
||||
*/
|
||||
void sendPacket(Packet<?> packet);
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
*/
|
||||
void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> callback);
|
||||
|
||||
/**
|
||||
* Sends a packet to a channel.
|
||||
*
|
||||
* @param channel the id of the channel
|
||||
* @param buf the content of the packet
|
||||
*/
|
||||
default void sendPacket(Identifier channel, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channel, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Payload cannot be null");
|
||||
|
||||
this.sendPacket(this.createPacket(channel, buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to a channel.
|
||||
*
|
||||
* @param channel the id of the channel
|
||||
* @param buf the content of the packet
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}
|
||||
*/
|
||||
// the generic future listener can accept ChannelFutureListener
|
||||
default void sendPacket(Identifier channel, PacketByteBuf buf, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
Objects.requireNonNull(channel, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Payload cannot be null");
|
||||
|
||||
this.sendPacket(this.createPacket(channel, buf), callback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import net.minecraft.world.chunk.ChunkManager;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.ThreadedAnvilChunkStorageTrackingExtensions;
|
||||
|
||||
/**
|
||||
* For example, a block entity may use the methods in this class to send a packet to all clients which can see the block entity in order notify clients about a change.
|
||||
*
|
||||
* <p>The word "tracking" means that an entity/chunk on the server is known to a player's client (within in view distance) and the (block) entity should notify tracking clients of changes.
|
||||
*
|
||||
* <p>These methods should only be called on the server thread and only be used on logical a server.
|
||||
*/
|
||||
public final class PlayerLookup {
|
||||
/**
|
||||
* Gets all the players on the minecraft server.
|
||||
*
|
||||
* <p>The returned collection is immutable.
|
||||
*
|
||||
* @param server the server
|
||||
* @return all players on the server
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> all(MinecraftServer server) {
|
||||
Objects.requireNonNull(server, "The server cannot be null");
|
||||
|
||||
// return an immutable collection to guard against accidental removals.
|
||||
if (server.getPlayerManager() != null) {
|
||||
return Collections.unmodifiableCollection(server.getPlayerManager().getPlayerList());
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the players in a server world.
|
||||
*
|
||||
* <p>The returned collection is immutable.
|
||||
*
|
||||
* @param world the server world
|
||||
* @return the players in the server world
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> world(ServerWorld world) {
|
||||
Objects.requireNonNull(world, "The world cannot be null");
|
||||
|
||||
// return an immutable collection to guard against accidental removals.
|
||||
return Collections.unmodifiableCollection(world.getPlayers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all players tracking a chunk in a server world.
|
||||
*
|
||||
* @param world the server world
|
||||
* @param pos the chunk in question
|
||||
* @return the players tracking the chunk
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> tracking(ServerWorld world, ChunkPos pos) {
|
||||
Objects.requireNonNull(world, "The world cannot be null");
|
||||
Objects.requireNonNull(pos, "The chunk pos cannot be null");
|
||||
|
||||
return world.getChunkManager().threadedAnvilChunkStorage.getPlayersWatchingChunk(pos, false).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all players tracking an entity in a server world.
|
||||
*
|
||||
* <p>The returned collection is immutable.
|
||||
*
|
||||
* <p><b>Warning</b>: If the provided entity is a player, it is not
|
||||
* guaranteed by the contract that said player is included in the
|
||||
* resulting stream.
|
||||
*
|
||||
* @param entity the entity being tracked
|
||||
* @return the players tracking the entity
|
||||
* @throws IllegalArgumentException if the entity is not in a server world
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> tracking(Entity entity) {
|
||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||
ChunkManager manager = entity.world.getChunkManager();
|
||||
|
||||
if (manager instanceof ServerChunkManager) {
|
||||
ThreadedAnvilChunkStorage storage = ((ServerChunkManager) manager).threadedAnvilChunkStorage;
|
||||
|
||||
// return an immutable collection to guard against accidental removals.
|
||||
return Collections.unmodifiableCollection(((ThreadedAnvilChunkStorageTrackingExtensions) storage).fabric_getTrackingPlayers(entity));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Only supported on server worlds!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all players tracking a block entity in a server world.
|
||||
*
|
||||
* @param blockEntity the block entity
|
||||
* @return the players tracking the block position
|
||||
* @throws IllegalArgumentException if the block entity is not in a server world
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> tracking(BlockEntity blockEntity) {
|
||||
Objects.requireNonNull(blockEntity, "BlockEntity cannot be null");
|
||||
|
||||
//noinspection ConstantConditions - IJ intrinsics don't know hasWorld == true will result in no null
|
||||
if (!blockEntity.hasWorld() || blockEntity.getWorld().isClient()) {
|
||||
throw new IllegalArgumentException("Only supported on server worlds!");
|
||||
}
|
||||
|
||||
return tracking((ServerWorld) blockEntity.getWorld(), blockEntity.getPos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all players tracking a block position in a server world.
|
||||
*
|
||||
* @param world the server world
|
||||
* @param pos the block position
|
||||
* @return the players tracking the block position
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> tracking(ServerWorld world, BlockPos pos) {
|
||||
Objects.requireNonNull(pos, "BlockPos cannot be null");
|
||||
|
||||
return tracking(world, new ChunkPos(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all players around a position in a world.
|
||||
*
|
||||
* <p>The distance check is done in the three-dimensional space instead of in the horizontal plane.
|
||||
*
|
||||
* @param world the world
|
||||
* @param pos the position
|
||||
* @param radius the maximum distance from the position in blocks
|
||||
* @return the players around the position
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> around(ServerWorld world, Vec3d pos, double radius) {
|
||||
double radiusSq = radius * radius;
|
||||
|
||||
return world(world)
|
||||
.stream()
|
||||
.filter((p) -> p.squaredDistanceTo(pos) <= radiusSq)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all players around a position in a world.
|
||||
*
|
||||
* <p>The distance check is done in the three-dimensional space instead of in the horizontal plane.
|
||||
*
|
||||
* @param world the world
|
||||
* @param pos the position (can be a block pos)
|
||||
* @param radius the maximum distance from the position in blocks
|
||||
* @return the players around the position
|
||||
*/
|
||||
public static Collection<ServerPlayerEntity> around(ServerWorld world, Vec3i pos, double radius) {
|
||||
double radiusSq = radius * radius;
|
||||
|
||||
return world(world)
|
||||
.stream()
|
||||
.filter((p) -> p.squaredDistanceTo(pos.getX(), pos.getY(), pos.getZ()) <= radiusSq)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private PlayerLookup() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
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 connection to a client on a logical server while a client is logging in.
|
||||
*/
|
||||
public final class ServerLoginConnectionEvents {
|
||||
/**
|
||||
* An event for the initialization of the server login network handler.
|
||||
* This event may be used to register {@link ServerLoginNetworking.LoginQueryResponseHandler login query response handlers}
|
||||
* using {@link ServerLoginNetworking#registerReceiver(ServerLoginNetworkHandler, Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.
|
||||
*/
|
||||
public static final Event<LoginInit> LOGIN_INIT = EventFactory.createArrayBacked(LoginInit.class, callbacks -> (handler, server) -> {
|
||||
for (LoginInit callback : callbacks) {
|
||||
callback.onLoginInit(handler, server);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the start of login queries of the server login network handler.
|
||||
* This event may be used to register {@link ServerLoginNetworking.LoginQueryResponseHandler login query response handlers}
|
||||
* using {@link ServerLoginNetworking#registerReceiver(ServerLoginNetworkHandler, Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}
|
||||
* since this event is fired just before the first login query response is processed.
|
||||
*
|
||||
* <p>You may send login queries to the connected client using the provided {@link PacketSender}.
|
||||
*/
|
||||
public static final Event<LoginQueryStart> LOGIN_QUERY_START = EventFactory.createArrayBacked(LoginQueryStart.class, callbacks -> (handler, server, sender, synchronizer) -> {
|
||||
for (LoginQueryStart callback : callbacks) {
|
||||
callback.onLoginStart(handler, server, sender, synchronizer);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the disconnection of the server login network handler.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.
|
||||
*/
|
||||
public static final Event<LoginDisconnect> LOGIN_DISCONNECT = EventFactory.createArrayBacked(LoginDisconnect.class, callbacks -> (handler, server) -> {
|
||||
for (LoginDisconnect callback : callbacks) {
|
||||
callback.onLoginDisconnect(handler, server);
|
||||
}
|
||||
});
|
||||
|
||||
private ServerLoginConnectionEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ServerLoginConnectionEvents#LOGIN_INIT
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoginInit {
|
||||
void onLoginInit(ServerLoginNetworkHandler handler, MinecraftServer server);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ServerLoginConnectionEvents#LOGIN_QUERY_START
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoginQueryStart {
|
||||
void onLoginStart(ServerLoginNetworkHandler handler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ServerLoginConnectionEvents#LOGIN_DISCONNECT
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoginDisconnect {
|
||||
void onLoginDisconnect(ServerLoginNetworkHandler handler, MinecraftServer server);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerLoginNetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
|
||||
|
||||
/**
|
||||
* Offers access to login stage server-side networking functionalities.
|
||||
*
|
||||
* <p>Server-side networking functionalities include receiving serverbound query responses and sending clientbound query requests.
|
||||
*
|
||||
* @see ServerPlayNetworking
|
||||
* @see ClientLoginNetworking
|
||||
*/
|
||||
public final class ServerLoginNetworking {
|
||||
/**
|
||||
* Registers a handler to a query response channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterGlobalReceiver(Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param channelHandler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ServerLoginNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ServerLoginNetworking#registerReceiver(ServerLoginNetworkHandler, Identifier, LoginQueryResponseHandler)
|
||||
*/
|
||||
public static boolean registerGlobalReceiver(Identifier channelName, LoginQueryResponseHandler channelHandler) {
|
||||
return ServerNetworkingImpl.LOGIN.registerGlobalReceiver(channelName, channelHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a query response channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code channel} is guaranteed not to have a handler after this call.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @see ServerLoginNetworking#registerGlobalReceiver(Identifier, LoginQueryResponseHandler)
|
||||
* @see ServerLoginNetworking#unregisterReceiver(ServerLoginNetworkHandler, Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static ServerLoginNetworking.LoginQueryResponseHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return ServerNetworkingImpl.LOGIN.unregisterGlobalReceiver(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all channel names which global receivers are registered for.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* @return all channel names which global receivers are registered for.
|
||||
*/
|
||||
public static Set<Identifier> getGlobalReceivers() {
|
||||
return ServerNetworkingImpl.LOGIN.getChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a query response channel.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channelName}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerLoginNetworkHandler, Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param networkHandler the handler
|
||||
* @param channelName the id of the channel
|
||||
* @param responseHandler the handler
|
||||
* @return false if a handler is already registered to the channel name
|
||||
*/
|
||||
public static boolean registerReceiver(ServerLoginNetworkHandler networkHandler, Identifier channelName, LoginQueryResponseHandler responseHandler) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return ((ServerLoginNetworkHandlerExtensions) networkHandler).getAddon().registerChannel(channelName, responseHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a query response channel.
|
||||
*
|
||||
* <p>The {@code channelName} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel name
|
||||
*/
|
||||
@Nullable
|
||||
public static ServerLoginNetworking.LoginQueryResponseHandler unregisterReceiver(ServerLoginNetworkHandler networkHandler, Identifier channelName) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return ((ServerLoginNetworkHandlerExtensions) networkHandler).getAddon().unregisterChannel(channelName);
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
/**
|
||||
* Returns the <i>Minecraft</i> Server of a server login network handler.
|
||||
*
|
||||
* @param handler the server login network handler
|
||||
*/
|
||||
public static MinecraftServer getServer(ServerLoginNetworkHandler handler) {
|
||||
Objects.requireNonNull(handler, "Network handler cannot be null");
|
||||
|
||||
return ((ServerLoginNetworkHandlerAccessor) handler).getServer();
|
||||
}
|
||||
|
||||
private ServerLoginNetworking() {
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface LoginQueryResponseHandler {
|
||||
/**
|
||||
* Handles an incoming query response from a client.
|
||||
*
|
||||
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance.
|
||||
*
|
||||
* <p><b>Whether the client understood the query should be checked before reading from the payload of the packet.</b>
|
||||
* @param server the server
|
||||
* @param handler the network handler that received this packet, representing the player/client who sent the response
|
||||
* @param understood whether the client understood the packet
|
||||
* @param buf the payload of the packet
|
||||
* @param synchronizer the synchronizer which may be used to delay log-in till a {@link Future} is completed.
|
||||
* @param responseSender the packet sender
|
||||
*/
|
||||
void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows blocking client log-in until all all futures passed into {@link LoginSynchronizer#waitFor(Future)} are completed.
|
||||
*
|
||||
* @apiNote this interface is not intended to be implemented by users of api.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoginSynchronizer {
|
||||
/**
|
||||
* Allows blocking client log-in until the {@code future} is {@link Future#isDone() done}.
|
||||
*
|
||||
* <p>Since packet reception happens on netty's event loops, this allows handlers to
|
||||
* perform logic on the Server Thread, etc. For instance, a handler can prepare an
|
||||
* upcoming query request or check necessary login data on the server thread.</p>
|
||||
*
|
||||
* <p>Here is an example where the player log-in is blocked so that a credential check and
|
||||
* building of a followup query request can be performed properly on the logical server
|
||||
* thread before the player successfully logs in:
|
||||
* <pre>{@code
|
||||
* ServerLoginNetworking.registerGlobalReceiver(CHECK_CHANNEL, (server, handler, understood, buf, synchronizer, responseSender) -> {
|
||||
* if (!understood) {
|
||||
* handler.disconnect(new LiteralText("Only accept clients that can check!"));
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* String checkMessage = buf.readString(32767);
|
||||
*
|
||||
* // Just send the CompletableFuture returned by the server's submit method
|
||||
* synchronizer.waitFor(server.submit(() -> {
|
||||
* LoginInfoChecker checker = LoginInfoChecker.get(server);
|
||||
*
|
||||
* if (!checker.check(handler.getConnectionInfo(), checkMessage)) {
|
||||
* handler.disconnect(new LiteralText("Invalid credentials!"));
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* responseSender.send(UPCOMING_CHECK, checker.buildSecondQueryPacket(handler, checkMessage));
|
||||
* }));
|
||||
* });
|
||||
* }</pre>
|
||||
* Usually it is enough to pass the return value for {@link net.minecraft.util.thread.ThreadExecutor#submit(Runnable)} for {@code future}.</p>
|
||||
*
|
||||
* @param future the future that must be done before the player can log in
|
||||
*/
|
||||
void waitFor(Future<?> future);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Offers access to events related to the connection to a client on a logical server while a client is in game.
|
||||
*/
|
||||
public final class ServerPlayConnectionEvents {
|
||||
/**
|
||||
* An event for the initialization of the server play network handler.
|
||||
*
|
||||
* <p>At this stage, the network handler is ready to send packets to the client.
|
||||
*/
|
||||
public static final Event<PlayInit> PLAY_INIT = EventFactory.createArrayBacked(PlayInit.class, callbacks -> (handler, sender, server) -> {
|
||||
for (PlayInit callback : callbacks) {
|
||||
callback.onPlayInit(handler, sender, server);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event for the disconnection of the server play network handler.
|
||||
*
|
||||
* <p>No packets should be sent when this event is invoked.</p>
|
||||
*/
|
||||
public static final Event<PlayDisconnect> PLAY_DISCONNECT = EventFactory.createArrayBacked(PlayDisconnect.class, callbacks -> (handler, server) -> {
|
||||
for (PlayDisconnect callback : callbacks) {
|
||||
callback.onPlayDisconnect(handler, server);
|
||||
}
|
||||
});
|
||||
|
||||
private ServerPlayConnectionEvents() {
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayInit {
|
||||
void onPlayInit(ServerPlayNetworkHandler handler, PacketSender sender, MinecraftServer server);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayDisconnect {
|
||||
void onPlayDisconnect(ServerPlayNetworkHandler handler, MinecraftServer server);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* 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.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkHandlerExtensions;
|
||||
|
||||
/**
|
||||
* Offers access to play stage server-side networking functionalities.
|
||||
*
|
||||
* <p>Server-side networking functionalities include receiving serverbound packets, sending clientbound packets, and events related to server-side network handlers.
|
||||
*
|
||||
* <p>This class should be only used for the logical server.
|
||||
*
|
||||
* @see ServerLoginNetworking
|
||||
* @see ClientPlayNetworking
|
||||
*/
|
||||
public final class ServerPlayNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerPlayNetworkHandler, Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param channelHandler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ServerPlayNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ServerPlayNetworking#registerReceiver(ServerPlayNetworkHandler, Identifier, PlayChannelHandler)
|
||||
*/
|
||||
public static boolean registerGlobalReceiver(Identifier channelName, PlayChannelHandler channelHandler) {
|
||||
return ServerNetworkingImpl.PLAY.registerGlobalReceiver(channelName, channelHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code channel} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @see ServerPlayNetworking#registerGlobalReceiver(Identifier, PlayChannelHandler)
|
||||
* @see ServerPlayNetworking#unregisterReceiver(ServerPlayNetworkHandler, Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return ServerNetworkingImpl.PLAY.unregisterGlobalReceiver(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all channel names which global receivers are registered for.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* @return all channel names which global receivers are registered for.
|
||||
*/
|
||||
public static Set<Identifier> getGlobalReceivers() {
|
||||
return ServerNetworkingImpl.PLAY.getChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channelName}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerPlayNetworkHandler, Identifier)} to unregister the existing handler.</p>
|
||||
*
|
||||
* @param networkHandler the handler
|
||||
* @param channelName the id of the channel
|
||||
* @param channelHandler the handler
|
||||
* @return false if a handler is already registered to the channel name
|
||||
*/
|
||||
public static boolean registerReceiver(ServerPlayNetworkHandler networkHandler, Identifier channelName, PlayChannelHandler channelHandler) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return ((ServerPlayNetworkHandlerExtensions) networkHandler).getAddon().registerChannel(channelName, channelHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
*
|
||||
* <p>The {@code channelName} is guaranteed not to have a handler after this call.</p>
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel name
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterReceiver(ServerPlayNetworkHandler networkHandler, Identifier channelName) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the channel names that the server can receive packets on.
|
||||
*
|
||||
* @param player the player
|
||||
* @return All the channel names that the server can receive packets on
|
||||
*/
|
||||
public static Set<Identifier> getReceived(ServerPlayerEntity player) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
|
||||
return getReceived(player.networkHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the channel names that the server can receive packets on.
|
||||
*
|
||||
* @param handler the network handler
|
||||
* @return All the channel names that the server can receive packets on
|
||||
*/
|
||||
public static Set<Identifier> getReceived(ServerPlayNetworkHandler handler) {
|
||||
Objects.requireNonNull(handler, "Server play network handler cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(handler).getReceivableChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all channel names that the connected client declared the ability to receive a packets on.
|
||||
*
|
||||
* @param player the player
|
||||
* @return All the channel names the connected client declared the ability to receive a packets on
|
||||
*/
|
||||
public static Set<Identifier> getSendable(ServerPlayerEntity player) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
|
||||
return getSendable(player.networkHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all channel names that a the connected client declared the ability to receive a packets on.
|
||||
*
|
||||
* @param handler the network handler
|
||||
* @return True if the connected client has declared the ability to receive a packet on the specified channel
|
||||
*/
|
||||
public static Set<Identifier> getSendable(ServerPlayNetworkHandler handler) {
|
||||
Objects.requireNonNull(handler, "Server play network handler cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(handler).getSendableChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the connected client declared the ability to receive a packet on a specified channel name.
|
||||
*
|
||||
* @param player the player
|
||||
* @param channelName the channel name
|
||||
* @return True if the connected client has declared the ability to receive a packet on the specified channel
|
||||
*/
|
||||
public static boolean canSend(ServerPlayerEntity player, Identifier channelName) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
|
||||
return canSend(player.networkHandler, channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the connected client declared the ability to receive a packet on a specified channel name.
|
||||
*
|
||||
* @param handler the network handler
|
||||
* @param channelName the channel name
|
||||
* @return True if the connected client has declared the ability to receive a packet on the specified channel
|
||||
*/
|
||||
public static boolean canSend(ServerPlayNetworkHandler handler, Identifier channelName) {
|
||||
Objects.requireNonNull(handler, "Server play network handler cannot be null");
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(handler).getSendableChannels().contains(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a the connected client.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @param buf the packet byte buf which represents the payload of the packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static Packet<?> createS2CPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channelName, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Buf cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.createPlayC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected client.
|
||||
*
|
||||
* @param player the player
|
||||
* @return the packet sender
|
||||
*/
|
||||
public static PacketSender getSender(ServerPlayerEntity player) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
|
||||
return getSender(player.networkHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected client.
|
||||
*
|
||||
* @param handler the network handler, representing the connection to the player/client
|
||||
* @return the packet sender
|
||||
*/
|
||||
public static PacketSender getSender(ServerPlayNetworkHandler handler) {
|
||||
Objects.requireNonNull(handler, "Server play network handler cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to a player.
|
||||
*
|
||||
* @param player the player to send the packet to
|
||||
* @param channelName the channel of the packet
|
||||
* @param buf the payload of the packet.
|
||||
*/
|
||||
public static void send(ServerPlayerEntity player, Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(buf, "Packet byte buf cannot be null");
|
||||
|
||||
player.networkHandler.sendPacket(createS2CPacket(channelName, buf));
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
/**
|
||||
* Returns the <i>Minecraft</i> Server of a server play network handler.
|
||||
*
|
||||
* @param handler the server play network handler
|
||||
*/
|
||||
public static MinecraftServer getServer(ServerPlayNetworkHandler handler) {
|
||||
Objects.requireNonNull(handler, "Network handler cannot be null");
|
||||
|
||||
return handler.player.server;
|
||||
}
|
||||
|
||||
private ServerPlayNetworking() {
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayChannelHandler {
|
||||
/**
|
||||
* 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 net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft server instance.
|
||||
*
|
||||
* <p>An example usage of this is to create an explosion where the player is looking:
|
||||
* <pre>{@code
|
||||
* ServerPlayNetworking.registerReceiver(new Identifier("mymod", "boom"), (server, player, handler, buf, responseSender) -&rt; {
|
||||
* boolean fire = buf.readBoolean();
|
||||
*
|
||||
* // All operations on the server or world must be executed on the server thread
|
||||
* server.execute(() -&rt; {
|
||||
* ModPacketHandler.createExplosion(player, fire);
|
||||
* });
|
||||
* });
|
||||
* }</pre>
|
||||
* @param server the server
|
||||
* @param player the player
|
||||
* @param handler the network handler that received this packet, representing the player/client who sent the packet
|
||||
* @param buf the payload of the packet
|
||||
* @param responseSender the packet sender
|
||||
*/
|
||||
void receive(MinecraftServer server, ServerPlayerEntity player, ServerPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Networking API, version 1.
|
||||
*
|
||||
* <p>For login stage networking see {@link net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking}.
|
||||
* For play stage networking see {@link net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking}.
|
||||
*
|
||||
* <p>For events related to the connection to a client see {@link net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents} for login stage
|
||||
* or {@link net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents} for play stage.
|
||||
*
|
||||
* <p>For events related to the ability of a client to receive packets on a channel of a specific name see {@link net.fabricmc.fabric.api.networking.v1.C2SPlayChannelEvents}.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.networking.v1;
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* 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.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
|
||||
/**
|
||||
* A network addon which is aware of the channels the other side may receive.
|
||||
*
|
||||
* @param <H> the channel handler type
|
||||
*/
|
||||
public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAddon<H> implements PacketSender {
|
||||
protected final ClientConnection connection;
|
||||
protected final GlobalReceiverRegistry<H> receiver;
|
||||
protected final Set<Identifier> sendableChannels;
|
||||
protected final Set<Identifier> sendableChannelsView;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected void registerPendingChannels(ChannelInfoHolder holder) {
|
||||
final Collection<Identifier> pending = holder.getPendingChannelsNames();
|
||||
|
||||
if (!pending.isEmpty()) {
|
||||
register(new ArrayList<>(pending));
|
||||
pending.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// always supposed to handle async!
|
||||
protected boolean handle(Identifier channelName, PacketByteBuf originalBuf) {
|
||||
this.logger.debug("Handling inbound packet from channel with name \"{}\"", channelName);
|
||||
|
||||
// Handle reserved packets
|
||||
if (NetworkingImpl.REGISTER_CHANNEL.equals(channelName)) {
|
||||
this.receiveRegistration(true, PacketByteBufs.slice(originalBuf));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NetworkingImpl.UNREGISTER_CHANNEL.equals(channelName)) {
|
||||
this.receiveRegistration(false, PacketByteBufs.slice(originalBuf));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable H handler = this.getHandler(channelName);
|
||||
|
||||
if (handler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.slice(originalBuf);
|
||||
|
||||
try {
|
||||
this.receive(handler, buf);
|
||||
} catch (Throwable ex) {
|
||||
this.logger.error("Encountered exception while handling in channel with name \"{}\"", channelName, ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void receive(H handler, PacketByteBuf buf);
|
||||
|
||||
public void sendChannelRegistrationPacket() {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(this.receiver.getChannels());
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected PacketByteBuf createRegistrationPacket(Collection<Identifier> channels) {
|
||||
if (channels.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
boolean first = true;
|
||||
|
||||
for (Identifier channel : channels) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
buf.writeByte(0);
|
||||
}
|
||||
|
||||
buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII));
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
// wrap in try with res (buf)
|
||||
protected void receiveRegistration(boolean register, PacketByteBuf buf) {
|
||||
List<Identifier> ids = new ArrayList<>();
|
||||
StringBuilder active = new StringBuilder();
|
||||
|
||||
while (buf.isReadable()) {
|
||||
byte b = buf.readByte();
|
||||
|
||||
if (b != 0) {
|
||||
active.append(AsciiString.b2c(b));
|
||||
} else {
|
||||
this.addId(ids, active);
|
||||
active = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
this.addId(ids, active);
|
||||
this.schedule(register ? () -> register(ids) : () -> unregister(ids));
|
||||
}
|
||||
|
||||
public void register(List<Identifier> ids) {
|
||||
this.sendableChannels.addAll(ids);
|
||||
this.invokeRegisterEvent(ids);
|
||||
}
|
||||
|
||||
public void unregister(List<Identifier> ids) {
|
||||
this.sendableChannels.removeAll(ids);
|
||||
this.invokeUnregisterEvent(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
|
||||
this.connection.send(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
|
||||
this.connection.send(packet, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to run on the main thread.
|
||||
*/
|
||||
protected abstract void schedule(Runnable task);
|
||||
|
||||
protected abstract void invokeRegisterEvent(List<Identifier> ids);
|
||||
|
||||
protected abstract void invokeUnregisterEvent(List<Identifier> ids);
|
||||
|
||||
private void addId(List<Identifier> ids, StringBuilder sb) {
|
||||
String literal = sb.toString();
|
||||
|
||||
try {
|
||||
ids.add(new Identifier(literal));
|
||||
} catch (InvalidIdentifierException ex) {
|
||||
this.logger.warn("Received invalid channel identifier \"{}\" from connection {}", literal, this.connection, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Identifier> getSendableChannels() {
|
||||
return this.sendableChannelsView;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* A network addon is a simple abstraction to hold information about a player's registered channels.
|
||||
*
|
||||
* @param <H> the channel handler type
|
||||
*/
|
||||
public abstract class AbstractNetworkAddon<H> {
|
||||
protected final GlobalReceiverRegistry<H> receiver;
|
||||
protected final Logger logger;
|
||||
// A lock is used due to possible access on netty's event loops and game thread at same times such as during dynamic registration
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
// Sync map should be fine as there is little read write competition
|
||||
// All access to this map is guarded by the lock
|
||||
private final Map<Identifier, H> handlers = new HashMap<>();
|
||||
|
||||
protected AbstractNetworkAddon(GlobalReceiverRegistry<H> receiver, String description) {
|
||||
this.receiver = receiver;
|
||||
this.logger = LogManager.getLogger(description);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public H getHandler(Identifier channel) {
|
||||
Lock lock = this.lock.readLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
return this.handlers.get(channel);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean registerChannel(Identifier channelName, H handler) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(handler, "Packet handler cannot be null");
|
||||
|
||||
if (this.isReservedChannel(channelName)) {
|
||||
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;
|
||||
|
||||
if (replaced) {
|
||||
this.handleRegistration(channelName);
|
||||
}
|
||||
|
||||
return replaced;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public H unregisterChannel(Identifier channelName) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
|
||||
if (this.isReservedChannel(channelName)) {
|
||||
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
final H removed = this.handlers.remove(channelName);
|
||||
|
||||
if (removed != null) {
|
||||
this.handleUnregistration(channelName);
|
||||
}
|
||||
|
||||
return removed;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Identifier> getReceivableChannels() {
|
||||
Lock lock = this.lock.readLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
return new HashSet<>(this.handlers.keySet());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handleRegistration(Identifier channelName);
|
||||
|
||||
protected abstract void handleUnregistration(Identifier channelName);
|
||||
|
||||
public abstract void invokeDisconnectEvent();
|
||||
|
||||
/**
|
||||
* Checks if a channel is considered a "reserved" channel.
|
||||
* A reserved channel such as "minecraft:(un)register" has special handling and should not have any channel handlers registered for it.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @return whether the channel is reserved
|
||||
*/
|
||||
protected abstract boolean isReservedChannel(Identifier channelName);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.Collection;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public interface ChannelInfoHolder {
|
||||
/**
|
||||
* @return Channels which are declared as receivable by the other side but have not been declared yet.
|
||||
*/
|
||||
Collection<Identifier> getPendingChannelsNames();
|
||||
}
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
public final class PacketDebugOptions {
|
||||
public static final boolean DISABLE_BUFFER_RELEASES = System.getProperty("fabric.networking.broken.disableBufferReleases", "false").equalsIgnoreCase("true");
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
private PacketDebugOptions() { }
|
||||
public interface DisconnectPacketSource {
|
||||
Packet<?> createDisconnectPacket(Text message);
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public final class GlobalReceiverRegistry<H> {
|
||||
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(Map<Identifier, H> map) {
|
||||
this.handlers = map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public H getHandler(Identifier channelName) {
|
||||
Lock lock = this.lock.readLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
return this.handlers.get(channelName);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean registerGlobalReceiver(Identifier channelName, H handler) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(handler, "Channel handler cannot be null");
|
||||
|
||||
if (NetworkingImpl.isReservedPlayChannel(channelName)) {
|
||||
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;
|
||||
|
||||
if (!replaced) {
|
||||
this.handleRegistration(channelName, handler);
|
||||
}
|
||||
|
||||
return replaced;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public H unregisterGlobalReceiver(Identifier channelName) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
|
||||
if (NetworkingImpl.isReservedPlayChannel(channelName)) {
|
||||
throw new IllegalArgumentException(String.format("Cannot unregister packet handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
final H removed = this.handlers.remove(channelName);
|
||||
|
||||
if (removed != null) {
|
||||
this.handleUnregistration(channelName);
|
||||
}
|
||||
|
||||
return removed;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Identifier, H> getHandlers() {
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
return new HashMap<>(this.handlers);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Identifier> getChannels() {
|
||||
Lock lock = this.lock.readLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
return new HashSet<>(this.handlers.keySet());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// State tracking methods
|
||||
|
||||
public void startSession(AbstractNetworkAddon<H> addon) {
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
this.trackedAddons.add(addon);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void endSession(AbstractNetworkAddon<H> addon) {
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
this.trackedAddons.remove(addon);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRegistration(Identifier channelName, H handler) {
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
for (AbstractNetworkAddon<H> addon : this.trackedAddons) {
|
||||
addon.registerChannel(channelName, handler);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnregistration(Identifier channelName) {
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
for (AbstractNetworkAddon<H> addon : this.trackedAddons) {
|
||||
addon.unregisterChannel(channelName);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
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;
|
||||
|
||||
public final class NetworkingImpl {
|
||||
public static final String MOD_ID = "fabric-networking-api-v1";
|
||||
public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
|
||||
/**
|
||||
* 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.LOGIN_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());
|
||||
}
|
||||
|
||||
((ChannelInfoHolder) handler.getConnection()).getPendingChannelsNames().addAll(ids);
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the client for \"{}\"", handler.getConnectionInfo());
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isReservedPlayChannel(Identifier channelName) {
|
||||
return channelName.equals(REGISTER_CHANNEL) || channelName.equals(UNREGISTER_CHANNEL);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.Packet;
|
||||
|
||||
public interface PacketCallbackListener {
|
||||
/**
|
||||
* Called after a packet has been sent.
|
||||
*
|
||||
* @param packet the packet
|
||||
*/
|
||||
void sent(Packet<?> packet);
|
||||
}
|
|
@ -14,13 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking.server;
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public interface EntityTrackerStorageAccessor {
|
||||
Stream<ServerPlayerEntity> fabric_getTrackingPlayers(Entity entity);
|
||||
public interface ThreadedAnvilChunkStorageTrackingExtensions {
|
||||
Collection<ServerPlayerEntity> fabric_getTrackingPlayers(Entity entity);
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FutureListeners;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractNetworkAddon;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.LoginQueryRequestS2CPacketAccessor;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientLoginNetworkAddon extends AbstractNetworkAddon<ClientLoginNetworking.LoginQueryRequestHandler> {
|
||||
private final ClientLoginNetworkHandler handler;
|
||||
private final MinecraftClient client;
|
||||
private boolean firstResponse = true;
|
||||
|
||||
public ClientLoginNetworkAddon(ClientLoginNetworkHandler handler, MinecraftClient client) {
|
||||
super(ClientNetworkingImpl.LOGIN, "ClientLoginNetworkAddon for Client");
|
||||
this.handler = handler;
|
||||
this.client = client;
|
||||
|
||||
ClientLoginConnectionEvents.LOGIN_INIT.invoker().onLoginStart(this.handler, this.client);
|
||||
this.receiver.startSession(this);
|
||||
}
|
||||
|
||||
public boolean handlePacket(LoginQueryRequestS2CPacket packet) {
|
||||
LoginQueryRequestS2CPacketAccessor access = (LoginQueryRequestS2CPacketAccessor) packet;
|
||||
return handlePacket(packet.getQueryId(), access.getChannel(), access.getPayload());
|
||||
}
|
||||
|
||||
private boolean handlePacket(int queryId, Identifier channelName, PacketByteBuf originalBuf) {
|
||||
this.logger.debug("Handling inbound login response with id {} and channel with name {}", queryId, channelName);
|
||||
|
||||
if (this.firstResponse) {
|
||||
// Register global handlers
|
||||
for (Map.Entry<Identifier, ClientLoginNetworking.LoginQueryRequestHandler> entry : ClientNetworkingImpl.LOGIN.getHandlers().entrySet()) {
|
||||
ClientLoginNetworking.registerReceiver(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
ClientLoginConnectionEvents.LOGIN_QUERY_START.invoker().onLoginQueryStart(this.handler, this.client);
|
||||
this.firstResponse = false;
|
||||
}
|
||||
|
||||
@Nullable ClientLoginNetworking.LoginQueryRequestHandler handler = this.getHandler(channelName);
|
||||
|
||||
if (handler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.slice(originalBuf);
|
||||
List<GenericFutureListener<? extends Future<? super Void>>> futureListeners = new ArrayList<>();
|
||||
|
||||
try {
|
||||
CompletableFuture<@Nullable PacketByteBuf> future = handler.receive(this.client, this.handler, buf, futureListeners::add);
|
||||
future.thenAccept(result -> {
|
||||
LoginQueryResponseC2SPacket packet = new LoginQueryResponseC2SPacket(queryId, result);
|
||||
GenericFutureListener<? extends Future<? super Void>> listener = null;
|
||||
|
||||
for (GenericFutureListener<? extends Future<? super Void>> each : futureListeners) {
|
||||
listener = FutureListeners.union(listener, each);
|
||||
}
|
||||
|
||||
this.handler.getConnection().send(packet, listener);
|
||||
});
|
||||
} catch (Throwable ex) {
|
||||
this.logger.error("Encountered exception while handling in channel with name \"{}\"", channelName, ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRegistration(Identifier channelName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUnregistration(Identifier channelName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDisconnectEvent() {
|
||||
ClientLoginConnectionEvents.LOGIN_DISCONNECT.invoker().onLoginDisconnect(this.handler, this.client);
|
||||
this.receiver.endSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public interface ClientLoginNetworkHandlerExtensions {
|
||||
ClientLoginNetworkAddon getAddon();
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.ConnectScreen;
|
||||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ConnectScreenAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.MinecraftClientAccessor;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientNetworkingImpl {
|
||||
public static final GlobalReceiverRegistry<ClientLoginNetworking.LoginQueryRequestHandler> LOGIN = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ClientPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>();
|
||||
|
||||
public static ClientPlayNetworkAddon getAddon(ClientPlayNetworkHandler handler) {
|
||||
return ((ClientPlayNetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static ClientLoginNetworkAddon getAddon(ClientLoginNetworkHandler handler) {
|
||||
return ((ClientLoginNetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static Packet<?> createPlayC2SPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return new CustomPayloadC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way logging into a integrated or remote dedicated server will differ, we need to obtain the login client connection differently.
|
||||
*/
|
||||
@Nullable
|
||||
public static ClientConnection getLoginConnection() {
|
||||
final ClientConnection connection = ((MinecraftClientAccessor) MinecraftClient.getInstance()).getConnection();
|
||||
|
||||
// Check if we are connecting to an integrated server. This will set the field on MinecraftClient
|
||||
if (connection != null) {
|
||||
return connection;
|
||||
} else {
|
||||
// We are probably connecting to a remote server.
|
||||
// Check if the ConnectScreen is the currentScreen to determine that:
|
||||
if (MinecraftClient.getInstance().currentScreen instanceof ConnectScreen) {
|
||||
return ((ConnectScreenAccessor) MinecraftClient.getInstance().currentScreen).getConnection();
|
||||
}
|
||||
}
|
||||
|
||||
// We are not connected to a server at all.
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void clientInit() {
|
||||
// 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);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
ids.add(buf.readIdentifier());
|
||||
}
|
||||
|
||||
((ChannelInfoHolder) handler.getConnection()).getPendingChannelsNames().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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.S2CPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractChanneledNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<ClientPlayNetworking.PlayChannelHandler> {
|
||||
private final ClientPlayNetworkHandler handler;
|
||||
private final MinecraftClient client;
|
||||
private boolean canSendPackets;
|
||||
|
||||
public ClientPlayNetworkAddon(ClientPlayNetworkHandler handler, MinecraftClient client) {
|
||||
super(ClientNetworkingImpl.PLAY, handler.getConnection(), "ClientPlayNetworkAddon for " + handler.getProfile().getName());
|
||||
this.handler = handler;
|
||||
this.client = client;
|
||||
|
||||
// Must register pending channels via lateinit
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection);
|
||||
}
|
||||
|
||||
// also expose sendRegistration
|
||||
|
||||
public void onServerReady() {
|
||||
// Register global receivers
|
||||
for (Map.Entry<Identifier, ClientPlayNetworking.PlayChannelHandler> entry : ClientNetworkingImpl.PLAY.getHandlers().entrySet()) {
|
||||
this.registerChannel(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
this.sendChannelRegistrationPacket();
|
||||
this.canSendPackets = true;
|
||||
|
||||
ClientPlayConnectionEvents.PLAY_INIT.invoker().onPlayInit(this.handler, this, this.client);
|
||||
this.receiver.startSession(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* @param packet the packet to handle
|
||||
* @return true if the packet has been handled
|
||||
*/
|
||||
public boolean handle(CustomPayloadS2CPacket packet) {
|
||||
PacketByteBuf buf = packet.getData();
|
||||
|
||||
try {
|
||||
return this.handle(packet.getChannel(), buf);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void receive(ClientPlayNetworking.PlayChannelHandler handler, PacketByteBuf buf) {
|
||||
handler.receive(this.client, this.handler, buf, this);
|
||||
}
|
||||
|
||||
// impl details
|
||||
|
||||
@Override
|
||||
protected void schedule(Runnable task) {
|
||||
MinecraftClient.getInstance().execute(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return ClientPlayNetworking.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeRegisterEvent(List<Identifier> ids) {
|
||||
S2CPlayChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.client, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeUnregisterEvent(List<Identifier> ids) {
|
||||
S2CPlayChannelEvents.UNREGISTER.invoker().onChannelUnregister(this.handler, this, this.client, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRegistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the register packet for this channel
|
||||
if (this.canSendPackets) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.canSendPackets) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDisconnectEvent() {
|
||||
ClientPlayConnectionEvents.PLAY_DISCONNECT.invoker().onPlayDisconnect(this.handler, this.client);
|
||||
this.receiver.endSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return NetworkingImpl.isReservedPlayChannel(channelName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public interface ClientPlayNetworkHandlerExtensions {
|
||||
ClientPlayNetworkAddon getAddon();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.server;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Tracks the current query id used for login query responses.
|
||||
*/
|
||||
interface QueryIdFactory {
|
||||
static QueryIdFactory create() {
|
||||
return new QueryIdFactory() {
|
||||
private final AtomicInteger currentId = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public int nextId() {
|
||||
return this.currentId.getAndIncrement();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// called async prob.
|
||||
int nextId();
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* 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.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginCompressionS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractNetworkAddon;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.LoginQueryRequestS2CPacketAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.LoginQueryResponseC2SPacketAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
|
||||
|
||||
public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLoginNetworking.LoginQueryResponseHandler> implements PacketSender {
|
||||
private final ClientConnection connection;
|
||||
private final ServerLoginNetworkHandler handler;
|
||||
private final MinecraftServer server;
|
||||
private final QueryIdFactory queryIdFactory;
|
||||
private final Collection<Future<?>> waits = new ConcurrentLinkedQueue<>();
|
||||
private final Map<Integer, Identifier> channels = new ConcurrentHashMap<>();
|
||||
private boolean firstQueryTick = true;
|
||||
|
||||
public ServerLoginNetworkAddon(ServerLoginNetworkHandler handler) {
|
||||
super(ServerNetworkingImpl.LOGIN, "ServerLoginNetworkAddon for " + handler.getConnectionInfo());
|
||||
this.connection = handler.connection;
|
||||
this.handler = handler;
|
||||
this.server = ((ServerLoginNetworkHandlerAccessor) handler).getServer();
|
||||
this.queryIdFactory = QueryIdFactory.create();
|
||||
|
||||
ServerLoginConnectionEvents.LOGIN_INIT.invoker().onLoginInit(handler, this.server);
|
||||
this.receiver.startSession(this);
|
||||
}
|
||||
|
||||
// return true if no longer ticks query
|
||||
public boolean queryTick() {
|
||||
if (this.firstQueryTick) {
|
||||
// Send the compression packet now so clients receive compressed login queries
|
||||
this.sendCompressionPacket();
|
||||
|
||||
// Register global receivers.
|
||||
for (Map.Entry<Identifier, ServerLoginNetworking.LoginQueryResponseHandler> entry : ServerNetworkingImpl.LOGIN.getHandlers().entrySet()) {
|
||||
ServerLoginNetworking.registerReceiver(this.handler, entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
ServerLoginConnectionEvents.LOGIN_QUERY_START.invoker().onLoginStart(this.handler, this.server, this, this.waits::add);
|
||||
this.firstQueryTick = false;
|
||||
}
|
||||
|
||||
AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
this.waits.removeIf(future -> {
|
||||
if (!future.isDone()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
future.get();
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable caught = ex.getCause();
|
||||
error.getAndUpdate(oldEx -> {
|
||||
if (oldEx == null) {
|
||||
return caught;
|
||||
}
|
||||
|
||||
oldEx.addSuppressed(caught);
|
||||
return oldEx;
|
||||
});
|
||||
} catch (InterruptedException | CancellationException ignored) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return this.channels.isEmpty() && this.waits.isEmpty();
|
||||
}
|
||||
|
||||
private void sendCompressionPacket() {
|
||||
// Compression is not needed for local transport
|
||||
if (this.server.getNetworkCompressionThreshold() >= 0 && !this.connection.isLocal()) {
|
||||
this.connection.send(new LoginCompressionS2CPacket(this.server.getNetworkCompressionThreshold()), (channelFuture) ->
|
||||
this.connection.setCompressionThreshold(this.server.getNetworkCompressionThreshold())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming query response during login.
|
||||
*
|
||||
* @param packet the packet to handle
|
||||
* @return true if the packet was handled
|
||||
*/
|
||||
public boolean handle(LoginQueryResponseC2SPacket packet) {
|
||||
LoginQueryResponseC2SPacketAccessor access = (LoginQueryResponseC2SPacketAccessor) packet;
|
||||
return handle(access.getQueryId(), access.getResponse());
|
||||
}
|
||||
|
||||
private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) {
|
||||
this.logger.debug("Handling inbound login query with id {}", queryId);
|
||||
Identifier channel = this.channels.remove(queryId);
|
||||
|
||||
if (channel == null) {
|
||||
this.logger.warn("Query ID {} was received but no query has been associated in {}!", queryId, this.connection);
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean understood = originalBuf != null;
|
||||
@Nullable ServerLoginNetworking.LoginQueryResponseHandler handler = ServerNetworkingImpl.LOGIN.getHandler(channel);
|
||||
|
||||
if (handler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketByteBuf buf = understood ? PacketByteBufs.slice(originalBuf) : PacketByteBufs.empty();
|
||||
|
||||
try {
|
||||
handler.receive(this.server, this.handler, understood, buf, this.waits::add, this);
|
||||
} catch (Throwable ex) {
|
||||
this.logger.error("Encountered exception while handling in channel \"{}\"", channel, ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
int queryId = this.queryIdFactory.nextId();
|
||||
LoginQueryRequestS2CPacket ret = new LoginQueryRequestS2CPacket();
|
||||
// The constructor for creating a non-empty response was removed by proguard
|
||||
LoginQueryRequestS2CPacketAccessor access = (LoginQueryRequestS2CPacketAccessor) ret;
|
||||
access.setQueryId(queryId);
|
||||
access.setChannel(channelName);
|
||||
access.setPayload(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
|
||||
this.connection.send(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet, GenericFutureListener<? extends io.netty.util.concurrent.Future<? super Void>> callback) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
|
||||
this.connection.send(packet, callback);
|
||||
}
|
||||
|
||||
public void registerOutgoingPacket(LoginQueryRequestS2CPacket packet) {
|
||||
LoginQueryRequestS2CPacketAccessor access = (LoginQueryRequestS2CPacketAccessor) packet;
|
||||
this.channels.put(access.getServerQueryId(), access.getChannel());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRegistration(Identifier channelName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUnregistration(Identifier channelName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDisconnectEvent() {
|
||||
ServerLoginConnectionEvents.LOGIN_DISCONNECT.invoker().onLoginDisconnect(this.handler, this.server);
|
||||
this.receiver.endSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -16,10 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.networking.server;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public interface EntityTrackerStreamAccessor {
|
||||
Stream<ServerPlayerEntity> fabric_getTrackingPlayers();
|
||||
public interface ServerLoginNetworkHandlerExtensions {
|
||||
ServerLoginNetworkAddon getAddon();
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.server;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
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.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry;
|
||||
|
||||
public final class ServerNetworkingImpl {
|
||||
public static final GlobalReceiverRegistry<ServerLoginNetworking.LoginQueryResponseHandler> LOGIN = new GlobalReceiverRegistry<>();
|
||||
public static final GlobalReceiverRegistry<ServerPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>();
|
||||
|
||||
public static ServerPlayNetworkAddon getAddon(ServerPlayNetworkHandler handler) {
|
||||
return ((ServerPlayNetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static Packet<?> createPlayC2SPacket(Identifier channel, PacketByteBuf buf) {
|
||||
return new CustomPayloadS2CPacket(channel, buf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.server;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.C2SPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
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.mixin.networking.accessor.CustomPayloadC2SPacketAccessor;
|
||||
|
||||
public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<ServerPlayNetworking.PlayChannelHandler> {
|
||||
private final ServerPlayNetworkHandler handler;
|
||||
private final MinecraftServer server;
|
||||
private boolean canSendPackets = false;
|
||||
|
||||
public ServerPlayNetworkAddon(ServerPlayNetworkHandler handler, MinecraftServer server) {
|
||||
super(ServerNetworkingImpl.PLAY, handler.getConnection(), "ServerPlayNetworkAddon for " + handler.player.getEntityName());
|
||||
this.handler = handler;
|
||||
this.server = server;
|
||||
|
||||
// Must register pending channels via lateinit
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection);
|
||||
}
|
||||
|
||||
public void onClientReady() {
|
||||
// Register global receivers
|
||||
for (Map.Entry<Identifier, ServerPlayNetworking.PlayChannelHandler> entry : ServerNetworkingImpl.PLAY.getHandlers().entrySet()) {
|
||||
this.registerChannel(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
this.sendChannelRegistrationPacket();
|
||||
this.canSendPackets = true;
|
||||
|
||||
ServerPlayConnectionEvents.PLAY_INIT.invoker().onPlayInit(this.handler, this, this.server);
|
||||
this.receiver.startSession(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* @param packet the packet to handle
|
||||
* @return true if the packet has been handled
|
||||
*/
|
||||
public boolean handle(CustomPayloadC2SPacket packet) {
|
||||
CustomPayloadC2SPacketAccessor access = (CustomPayloadC2SPacketAccessor) packet;
|
||||
return this.handle(access.getChannel(), access.getData());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void receive(ServerPlayNetworking.PlayChannelHandler handler, PacketByteBuf buf) {
|
||||
handler.receive(this.server, this.handler.player, this.handler, buf, this);
|
||||
}
|
||||
|
||||
// impl details
|
||||
|
||||
@Override
|
||||
protected void schedule(Runnable task) {
|
||||
this.handler.player.server.execute(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return ServerPlayNetworking.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeRegisterEvent(List<Identifier> ids) {
|
||||
C2SPlayChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.server, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeUnregisterEvent(List<Identifier> ids) {
|
||||
C2SPlayChannelEvents.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.canSendPackets) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.canSendPackets) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDisconnectEvent() {
|
||||
ServerPlayConnectionEvents.PLAY_DISCONNECT.invoker().onPlayDisconnect(this.handler, this.server);
|
||||
this.receiver.endSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReservedChannel(Identifier channelName) {
|
||||
return NetworkingImpl.isReservedPlayChannel(channelName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.server;
|
||||
|
||||
public interface ServerPlayNetworkHandlerExtensions {
|
||||
ServerPlayNetworkAddon getAddon();
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.mixin.networking;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
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;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.DisconnectPacketSource;
|
||||
import net.fabricmc.fabric.impl.networking.PacketCallbackListener;
|
||||
|
||||
@Mixin(ClientConnection.class)
|
||||
abstract class ClientConnectionMixin implements ChannelInfoHolder {
|
||||
@Shadow
|
||||
private PacketListener packetListener;
|
||||
|
||||
@Shadow
|
||||
public abstract void send(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> callback);
|
||||
|
||||
@Shadow
|
||||
public abstract void disconnect(Text disconnectReason);
|
||||
|
||||
@Unique
|
||||
private Collection<Identifier> playChannels;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initAddedFields(NetworkSide side, CallbackInfo ci) {
|
||||
this.playChannels = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
// Must be fully qualified due to mixin not working in production without it
|
||||
@SuppressWarnings("UnnecessaryQualifiedMemberReference")
|
||||
@Redirect(method = "Lnet/minecraft/network/ClientConnection;exceptionCaught(Lio/netty/channel/ChannelHandlerContext;Ljava/lang/Throwable;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;send(Lnet/minecraft/network/Packet;Lio/netty/util/concurrent/GenericFutureListener;)V"))
|
||||
private void resendOnExceptionCaught(ClientConnection self, Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> listener) {
|
||||
PacketListener handler = this.packetListener;
|
||||
|
||||
if (handler instanceof DisconnectPacketSource) {
|
||||
this.send(((DisconnectPacketSource) handler).createDisconnectPacket(new TranslatableText("disconnect.genericReason")), listener);
|
||||
} else {
|
||||
this.disconnect(new TranslatableText("disconnect.genericReason")); // Don't send packet if we cannot send proper packets
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "sendImmediately", at = @At(value = "FIELD", target = "Lnet/minecraft/network/ClientConnection;packetsSentCounter:I"))
|
||||
private void checkPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> callback, CallbackInfo ci) {
|
||||
if (this.packetListener instanceof PacketCallbackListener) {
|
||||
((PacketCallbackListener) this.packetListener).sent(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getPendingChannelsNames() {
|
||||
return this.playChannels;
|
||||
}
|
||||
}
|
|
@ -16,25 +16,32 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.networking;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.EntityTrackerEntry;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.server.EntityTrackerStreamAccessor;
|
||||
import net.fabricmc.fabric.api.networking.v1.EntityTrackingEvents;
|
||||
|
||||
@Mixin(targets = "net.minecraft.server.world.ThreadedAnvilChunkStorage$EntityTracker")
|
||||
public class MixinEntityTracker implements EntityTrackerStreamAccessor {
|
||||
@Mixin(EntityTrackerEntry.class)
|
||||
abstract class EntityTrackerEntryMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private Set<ServerPlayerEntity> playersTracking;
|
||||
private Entity entity;
|
||||
|
||||
@Override
|
||||
public Stream<ServerPlayerEntity> fabric_getTrackingPlayers() {
|
||||
return playersTracking.stream();
|
||||
@Inject(method = "startTracking", at = @At("HEAD"))
|
||||
private void onStartTracking(ServerPlayerEntity player, CallbackInfo ci) {
|
||||
EntityTrackingEvents.START_TRACKING.invoker().onStartTracking(this.entity, player);
|
||||
}
|
||||
|
||||
@Inject(method = "stopTracking", at = @At("TAIL"))
|
||||
private void onStopTracking(ServerPlayerEntity player, CallbackInfo ci) {
|
||||
EntityTrackingEvents.STOP_TRACKING.invoker().onStopTracking(this.entity, player);
|
||||
}
|
||||
}
|
|
@ -21,15 +21,16 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public class MixinMinecraftClient {
|
||||
@Inject(at = @At("RETURN"), method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V")
|
||||
public void disconnectAfter(Screen screen_1, CallbackInfo info) {
|
||||
ClientSidePacketRegistryImpl.invalidateRegisteredIdList();
|
||||
@Mixin(PlayerManager.class)
|
||||
abstract class PlayerManagerMixin {
|
||||
@Inject(method = "onPlayerConnect", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/CustomPayloadS2CPacket;<init>(Lnet/minecraft/util/Identifier;Lnet/minecraft/network/PacketByteBuf;)V"))
|
||||
private void handlePlayerConnection(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) {
|
||||
ServerNetworkingImpl.getAddon(player.networkHandler).onClientReady();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.mixin.networking;
|
||||
|
||||
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;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginDisconnectS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.DisconnectPacketSource;
|
||||
import net.fabricmc.fabric.impl.networking.PacketCallbackListener;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerLoginNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerLoginNetworkHandlerExtensions;
|
||||
|
||||
@Mixin(ServerLoginNetworkHandler.class)
|
||||
abstract class ServerLoginNetworkHandlerMixin implements ServerLoginNetworkHandlerExtensions, DisconnectPacketSource, PacketCallbackListener {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftServer server;
|
||||
|
||||
@Shadow
|
||||
public abstract void acceptPlayer();
|
||||
|
||||
@Unique
|
||||
private ServerLoginNetworkAddon addon;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initAddon(CallbackInfo ci) {
|
||||
this.addon = new ServerLoginNetworkAddon((ServerLoginNetworkHandler) (Object) this);
|
||||
}
|
||||
|
||||
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerLoginNetworkHandler;acceptPlayer()V"))
|
||||
private void handlePlayerJoin(ServerLoginNetworkHandler handler) {
|
||||
// Do not accept the player, thereby moving into play stage until all login futures being waited on are completed
|
||||
if (this.addon.queryTick()) {
|
||||
this.acceptPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onQueryResponse", at = @At("HEAD"), cancellable = true)
|
||||
private void handleCustomPayloadReceivedAsync(LoginQueryResponseC2SPacket packet, CallbackInfo ci) {
|
||||
// Handle queries
|
||||
if (this.addon.handle(packet)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "acceptPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getNetworkCompressionThreshold()I", ordinal = 0))
|
||||
private int removeLateCompressionPacketSending(MinecraftServer server) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
private void handleDisconnection(Text reason, CallbackInfo ci) {
|
||||
this.addon.invokeDisconnectEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sent(Packet<?> packet) {
|
||||
if (packet instanceof LoginQueryRequestS2CPacket) {
|
||||
this.addon.registerOutgoingPacket((LoginQueryRequestS2CPacket) packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLoginNetworkAddon getAddon() {
|
||||
return this.addon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createDisconnectPacket(Text message) {
|
||||
return new LoginDisconnectS2CPacket(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.mixin.networking;
|
||||
|
||||
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;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.play.DisconnectS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.DisconnectPacketSource;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkHandlerExtensions;
|
||||
|
||||
// We want to apply a bit earlier than other mods which may not use us in order to prevent refCount issues
|
||||
@Mixin(value = ServerPlayNetworkHandler.class, priority = 999)
|
||||
abstract class ServerPlayNetworkHandlerMixin implements ServerPlayNetworkHandlerExtensions, DisconnectPacketSource {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftServer server;
|
||||
@Shadow
|
||||
@Final
|
||||
public ClientConnection connection;
|
||||
|
||||
@Unique
|
||||
private ServerPlayNetworkAddon addon;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initAddon(CallbackInfo ci) {
|
||||
this.addon = new ServerPlayNetworkAddon((ServerPlayNetworkHandler) (Object) this, this.server);
|
||||
}
|
||||
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
private void handleCustomPayloadReceivedAsync(CustomPayloadC2SPacket packet, CallbackInfo ci) {
|
||||
if (this.addon.handle(packet)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
private void handleDisconnection(Text reason, CallbackInfo ci) {
|
||||
this.addon.invokeDisconnectEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPlayNetworkAddon getAddon() {
|
||||
return this.addon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createDisconnectPacket(Text message) {
|
||||
return new DisconnectS2CPacket(message);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.networking;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
|
@ -27,18 +28,25 @@ import net.minecraft.entity.Entity;
|
|||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.server.EntityTrackerStorageAccessor;
|
||||
import net.fabricmc.fabric.impl.networking.server.EntityTrackerStreamAccessor;
|
||||
import net.fabricmc.fabric.impl.networking.ThreadedAnvilChunkStorageTrackingExtensions;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.EntityTrackerAccessor;
|
||||
|
||||
@Mixin(ThreadedAnvilChunkStorage.class)
|
||||
public class MixinThreadedAnvilChunkStorage implements EntityTrackerStorageAccessor {
|
||||
abstract class ThreadedAnvilChunkStorageMixin implements ThreadedAnvilChunkStorageTrackingExtensions {
|
||||
@Shadow
|
||||
@Final
|
||||
private Int2ObjectMap<EntityTrackerStreamAccessor> entityTrackers;
|
||||
// We can abuse type erasure here and just get the type in the map as the accessor.
|
||||
// This allows us to avoid an access widener for the package-private `EntityTracker` subclass.
|
||||
private Int2ObjectMap<EntityTrackerAccessor> entityTrackers;
|
||||
|
||||
@Override
|
||||
public Stream<ServerPlayerEntity> fabric_getTrackingPlayers(Entity entity) {
|
||||
EntityTrackerStreamAccessor accessor = entityTrackers.get(entity.getEntityId());
|
||||
return accessor != null ? accessor.fabric_getTrackingPlayers() : Stream.empty();
|
||||
public Collection<ServerPlayerEntity> fabric_getTrackingPlayers(Entity entity) {
|
||||
EntityTrackerAccessor accessor = this.entityTrackers.get(entity.getEntityId());
|
||||
|
||||
if (accessor != null) {
|
||||
return accessor.getPlayersTracking();
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
|
@ -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.mixin.networking.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.client.gui.screen.ConnectScreen;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(ConnectScreen.class)
|
||||
public interface ConnectScreenAccessor {
|
||||
@Accessor
|
||||
ClientConnection getConnection();
|
||||
}
|
|
@ -14,17 +14,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
package net.fabricmc.fabric.mixin.networking.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Helper interface containing getters for CustomPayloadC2SPacket
|
||||
* which were omitted from the compiled game.
|
||||
*/
|
||||
@Mixin(CustomPayloadC2SPacket.class)
|
||||
public interface CustomPayloadC2SPacketAccessor {
|
||||
@Accessor
|
||||
Identifier getChannel();
|
||||
|
||||
@Accessor
|
||||
PacketByteBuf getData();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.mixin.networking.accessor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
@Mixin(targets = "net/minecraft/server/world/ThreadedAnvilChunkStorage$EntityTracker")
|
||||
public interface EntityTrackerAccessor {
|
||||
@Accessor
|
||||
Set<ServerPlayerEntity> getPlayersTracking();
|
||||
}
|
|
@ -14,31 +14,32 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.networking;
|
||||
package net.fabricmc.fabric.mixin.networking.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.CustomPayloadC2SPacketAccessor;
|
||||
@Mixin(LoginQueryRequestS2CPacket.class)
|
||||
public interface LoginQueryRequestS2CPacketAccessor {
|
||||
@Accessor("queryId")
|
||||
int getServerQueryId();
|
||||
|
||||
@Mixin(CustomPayloadC2SPacket.class)
|
||||
public class MixinCustomPayloadC2SPacket implements CustomPayloadC2SPacketAccessor {
|
||||
@Shadow
|
||||
private Identifier channel;
|
||||
@Shadow
|
||||
private PacketByteBuf data;
|
||||
@Accessor
|
||||
Identifier getChannel();
|
||||
|
||||
@Override
|
||||
public Identifier getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketByteBuf getData() {
|
||||
return new PacketByteBuf(this.data.copy());
|
||||
}
|
||||
@Accessor
|
||||
void setChannel(Identifier channel);
|
||||
|
||||
@Accessor
|
||||
PacketByteBuf getPayload();
|
||||
|
||||
@Accessor
|
||||
void setPayload(PacketByteBuf payload);
|
||||
|
||||
@Accessor
|
||||
void setQueryId(int queryId);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.mixin.networking.accessor;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket;
|
||||
|
||||
@Mixin(LoginQueryResponseC2SPacket.class)
|
||||
public interface LoginQueryResponseC2SPacketAccessor {
|
||||
@Accessor
|
||||
int getQueryId();
|
||||
|
||||
@Nullable
|
||||
@Accessor
|
||||
PacketByteBuf getResponse();
|
||||
}
|
|
@ -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.mixin.networking.accessor;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(MinecraftClient.class)
|
||||
public interface MinecraftClientAccessor {
|
||||
@Nullable
|
||||
@Accessor
|
||||
ClientConnection getConnection();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.mixin.networking.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
|
||||
@Mixin(ServerLoginNetworkHandler.class)
|
||||
public interface ServerLoginNetworkHandlerAccessor {
|
||||
@Accessor
|
||||
MinecraftServer getServer();
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.mixin.networking.client;
|
||||
|
||||
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;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientLoginNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientLoginNetworkHandlerExtensions;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(ClientLoginNetworkHandler.class)
|
||||
abstract class ClientLoginNetworkHandlerMixin implements ClientLoginNetworkHandlerExtensions {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Unique
|
||||
private ClientLoginNetworkAddon addon;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initAddon(CallbackInfo ci) {
|
||||
this.addon = new ClientLoginNetworkAddon((ClientLoginNetworkHandler) (Object) this, this.client);
|
||||
}
|
||||
|
||||
@Inject(method = "onQueryRequest", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", remap = false, shift = At.Shift.AFTER), cancellable = true)
|
||||
private void handleQueryRequest(LoginQueryRequestS2CPacket packet, CallbackInfo ci) {
|
||||
if (this.addon.handlePacket(packet)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
private void invokeLoginDisconnectEvent(Text reason, CallbackInfo ci) {
|
||||
this.addon.invokeDisconnectEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientLoginNetworkAddon getAddon() {
|
||||
return this.addon;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.mixin.networking.client;
|
||||
|
||||
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;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientPlayNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientPlayNetworkHandlerExtensions;
|
||||
|
||||
// We want to apply a bit earlier than other mods which may not use us in order to prevent refCount issues
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(value = ClientPlayNetworkHandler.class, priority = 999)
|
||||
abstract class ClientPlayNetworkHandlerMixin implements ClientPlayNetworkHandlerExtensions {
|
||||
@Shadow
|
||||
private MinecraftClient client;
|
||||
|
||||
@Unique
|
||||
private ClientPlayNetworkAddon addon;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initAddon(CallbackInfo ci) {
|
||||
this.addon = new ClientPlayNetworkAddon((ClientPlayNetworkHandler) (Object) this, this.client);
|
||||
}
|
||||
|
||||
@Inject(method = "onGameJoin", at = @At("RETURN"))
|
||||
private void handleServerPlayReady(GameJoinS2CPacket packet, CallbackInfo ci) {
|
||||
this.addon.onServerReady();
|
||||
}
|
||||
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
private void handleCustomPayload(CustomPayloadS2CPacket packet, CallbackInfo ci) {
|
||||
if (this.addon.handle(packet)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
private void handleDisconnection(Text reason, CallbackInfo ci) {
|
||||
this.addon.invokeDisconnectEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPlayNetworkAddon getAddon() {
|
||||
return this.addon;
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.networking",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"ClientConnectionMixin",
|
||||
"EntityTrackerEntryMixin",
|
||||
"PlayerManagerMixin",
|
||||
"ServerLoginNetworkHandlerMixin",
|
||||
"ServerPlayNetworkHandlerMixin",
|
||||
"ThreadedAnvilChunkStorageMixin",
|
||||
"accessor.CustomPayloadC2SPacketAccessor",
|
||||
"accessor.EntityTrackerAccessor",
|
||||
"accessor.LoginQueryRequestS2CPacketAccessor",
|
||||
"accessor.LoginQueryResponseC2SPacketAccessor",
|
||||
"accessor.ServerLoginNetworkHandlerAccessor"
|
||||
],
|
||||
"client": [
|
||||
"accessor.ConnectScreenAccessor",
|
||||
"accessor.MinecraftClientAccessor",
|
||||
"client.ClientLoginNetworkHandlerMixin",
|
||||
"client.ClientPlayNetworkHandlerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
34
fabric-networking-api-v1/src/main/resources/fabric.mod.json
Normal file
34
fabric-networking-api-v1/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-networking-api-v1",
|
||||
"name": "Fabric Networking API (v1)",
|
||||
"version": "${version}",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/fabric-networking-api-v1/icon.png",
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net",
|
||||
"irc": "irc://irc.esper.net:6667/fabric",
|
||||
"issues": "https://github.com/FabricMC/fabric/issues",
|
||||
"sources": "https://github.com/FabricMC/fabric"
|
||||
},
|
||||
"authors": [
|
||||
"FabricMC"
|
||||
],
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.impl.networking.NetworkingImpl::init"
|
||||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl::clientInit"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"fabric-api-base": "*"
|
||||
},
|
||||
"description": "Low-level, vanilla protocol oriented networking hooks.",
|
||||
"mixins": [
|
||||
"fabric-networking-api-v1.mixins.json"
|
||||
]
|
||||
}
|
|
@ -14,14 +14,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
package net.fabricmc.fabric.test.networking;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public final class PacketTypes {
|
||||
public static final Identifier BRAND = new Identifier("minecraft:brand");
|
||||
public static final Identifier REGISTER = new Identifier("minecraft:register");
|
||||
public static final Identifier UNREGISTER = new Identifier("minecraft:unregister");
|
||||
public final class NetworkingTestmods {
|
||||
public static final String ID = "fabric-networking-api-v1-testmod";
|
||||
public static final Logger LOGGER = LogManager.getLogger(ID);
|
||||
|
||||
public static final Identifier OPEN_CONTAINER = new Identifier("fabric", "container/open");
|
||||
public static Identifier id(String name) {
|
||||
return new Identifier(ID, name);
|
||||
}
|
||||
|
||||
private NetworkingTestmods() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.channeltest;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.widget.EntryListWidget;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
final class ChannelList extends EntryListWidget<ChannelList.Entry> {
|
||||
ChannelList(MinecraftClient client, int width, int height, int top, int bottom, int itemHeight) {
|
||||
super(client, width, height, top, bottom, itemHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEntry(Entry entry) {
|
||||
return super.addEntry(entry);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
this.clearEntries();
|
||||
}
|
||||
|
||||
class Entry extends EntryListWidget.Entry<Entry> {
|
||||
private final Identifier channel;
|
||||
|
||||
Entry(Identifier channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||
ChannelList.this.client.textRenderer.draw(matrices, new LiteralText(this.channel.toString()), x, y, Formatting.WHITE.getColorValue());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.channeltest;
|
||||
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
|
||||
final class ChannelScreen extends Screen {
|
||||
private final NetworkingChannelClientTest mod;
|
||||
private ButtonWidget s2cButton;
|
||||
private ButtonWidget c2sButton;
|
||||
private ButtonWidget closeButton;
|
||||
private ChannelList channelList;
|
||||
|
||||
ChannelScreen(NetworkingChannelClientTest mod) {
|
||||
super(new LiteralText("TODO"));
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
this.s2cButton = this.addButton(new ButtonWidget(this.width / 2 - 55, 5, 50, 20, new LiteralText("S2C"), this::toS2C, (button, matrices, mouseX, mouseY) -> {
|
||||
this.renderTooltip(matrices, new LiteralText("Packets this client can receive"), mouseX, mouseY);
|
||||
}));
|
||||
this.c2sButton = this.addButton(new ButtonWidget(this.width / 2 + 5, 5, 50, 20, new LiteralText("C2S"), this::toC2S, (button, matrices, mouseX, mouseY) -> {
|
||||
this.renderTooltip(matrices, new LiteralText("Packets the server can receive"), mouseX, mouseY);
|
||||
}));
|
||||
this.closeButton = this.addButton(new ButtonWidget(this.width / 2 - 60, this.height - 25, 120, 20, new LiteralText("Close"), button -> this.onClose()));
|
||||
this.channelList = this.addChild(new ChannelList(this.client, this.width, this.height - 60, 30, this.height - 30, this.textRenderer.fontHeight + 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
this.renderBackgroundTexture(0);
|
||||
this.channelList.render(matrices, mouseX, mouseY, delta);
|
||||
super.render(matrices, mouseX, mouseY, delta);
|
||||
|
||||
if (this.s2cButton.active && this.c2sButton.active) {
|
||||
final Text clickMe = new LiteralText("Click S2C or C2S to view supported channels");
|
||||
|
||||
final int textWidth = this.textRenderer.getWidth(clickMe);
|
||||
//noinspection ConstantConditions
|
||||
this.textRenderer.draw(
|
||||
matrices,
|
||||
clickMe,
|
||||
this.width / 2.0F - (textWidth / 2.0F),
|
||||
60,
|
||||
Formatting.YELLOW.getColorValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
if (!this.c2sButton.active && this.s2cButton.active) {
|
||||
this.toC2S(this.c2sButton);
|
||||
}
|
||||
}
|
||||
|
||||
private void toC2S(ButtonWidget button) {
|
||||
this.s2cButton.active = true;
|
||||
button.active = false;
|
||||
this.channelList.clear();
|
||||
|
||||
for (Identifier receiver : ClientPlayNetworking.getSendable()) {
|
||||
this.channelList.addEntry(this.channelList.new Entry(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
private void toS2C(ButtonWidget button) {
|
||||
this.c2sButton.active = true;
|
||||
button.active = false;
|
||||
this.channelList.clear();
|
||||
|
||||
for (Identifier receiver : ClientPlayNetworking.getReceived()) {
|
||||
this.channelList.addEntry(this.channelList.new Entry(receiver));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.channeltest;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.options.KeyBinding;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.S2CPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
|
||||
public final class NetworkingChannelClientTest implements ClientModInitializer {
|
||||
public static final KeyBinding OPEN = KeyBindingHelper.registerKeyBinding(new KeyBinding("networking-v1-test", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_MENU, "fabric-networking-api-v1-testmod\""));
|
||||
static final Set<Identifier> SUPPORTED_C2S_CHANNELS = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
if (client.player != null) {
|
||||
if (OPEN.wasPressed()) {
|
||||
client.openScreen(new ChannelScreen(this));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
S2CPlayChannelEvents.REGISTER.register((handler, sender, client, channels) -> {
|
||||
SUPPORTED_C2S_CHANNELS.addAll(channels);
|
||||
|
||||
if (MinecraftClient.getInstance().currentScreen instanceof ChannelScreen) {
|
||||
((ChannelScreen) MinecraftClient.getInstance().currentScreen).refresh();
|
||||
}
|
||||
});
|
||||
|
||||
S2CPlayChannelEvents.UNREGISTER.register((handler, sender, client, channels) -> {
|
||||
SUPPORTED_C2S_CHANNELS.removeAll(channels);
|
||||
|
||||
if (MinecraftClient.getInstance().currentScreen instanceof ChannelScreen) {
|
||||
((ChannelScreen) MinecraftClient.getInstance().currentScreen).refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// State destruction on disconnection:
|
||||
ClientLoginConnectionEvents.LOGIN_DISCONNECT.register((handler, client) -> {
|
||||
SUPPORTED_C2S_CHANNELS.clear();
|
||||
});
|
||||
|
||||
ClientPlayConnectionEvents.PLAY_DISCONNECT.register((handler, client) -> {
|
||||
SUPPORTED_C2S_CHANNELS.clear();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.channeltest;
|
||||
|
||||
import static net.minecraft.command.argument.EntityArgumentType.getPlayer;
|
||||
import static net.minecraft.command.argument.EntityArgumentType.player;
|
||||
import static net.minecraft.command.argument.IdentifierArgumentType.getIdentifier;
|
||||
import static net.minecraft.command.argument.IdentifierArgumentType.identifier;
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.command.EntitySelector;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
|
||||
public final class NetworkingChannelTest implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
|
||||
final LiteralCommandNode<ServerCommandSource> channelTestCommand = literal("network_channel_test").build();
|
||||
|
||||
// Info
|
||||
{
|
||||
final LiteralCommandNode<ServerCommandSource> info = literal("info")
|
||||
.executes(context -> infoCommand(context, context.getSource().getPlayer()))
|
||||
.build();
|
||||
|
||||
final ArgumentCommandNode<ServerCommandSource, EntitySelector> player = argument("player", player())
|
||||
.executes(context -> infoCommand(context, getPlayer(context, "player")))
|
||||
.build();
|
||||
|
||||
info.addChild(player);
|
||||
channelTestCommand.addChild(info);
|
||||
}
|
||||
|
||||
// Register
|
||||
{
|
||||
final LiteralCommandNode<ServerCommandSource> register = literal("register")
|
||||
.then(argument("channel", identifier())
|
||||
.executes(context -> registerChannel(context, context.getSource().getPlayer())))
|
||||
.build();
|
||||
|
||||
channelTestCommand.addChild(register);
|
||||
}
|
||||
|
||||
// Unregister
|
||||
{
|
||||
final LiteralCommandNode<ServerCommandSource> unregister = literal("unregister")
|
||||
.then(argument("channel", identifier()).suggests(NetworkingChannelTest::suggestReceivableChannels)
|
||||
.executes(context -> unregisterChannel(context, context.getSource().getPlayer())))
|
||||
.build();
|
||||
|
||||
channelTestCommand.addChild(unregister);
|
||||
}
|
||||
|
||||
dispatcher.getRoot().addChild(channelTestCommand);
|
||||
});
|
||||
}
|
||||
|
||||
private static CompletableFuture<Suggestions> suggestReceivableChannels(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) throws CommandSyntaxException {
|
||||
final ServerPlayerEntity player = context.getSource().getPlayer();
|
||||
|
||||
return CommandSource.suggestIdentifiers(ServerPlayNetworking.getReceived(player), builder);
|
||||
}
|
||||
|
||||
private static int registerChannel(CommandContext<ServerCommandSource> context, ServerPlayerEntity executor) throws CommandSyntaxException {
|
||||
final Identifier channel = getIdentifier(context, "channel");
|
||||
|
||||
if (ServerPlayNetworking.getReceived(executor).contains(channel)) {
|
||||
throw new SimpleCommandExceptionType(new LiteralText(String.format("Cannot register channel %s twice for server player", channel))).create();
|
||||
}
|
||||
|
||||
ServerPlayNetworking.registerReceiver(executor.networkHandler, channel, (server, player, handler, buf, sender) -> {
|
||||
System.out.printf("Received packet on channel %s%n", channel);
|
||||
});
|
||||
|
||||
context.getSource().sendFeedback(new LiteralText(String.format("Registered channel %s for %s", channel, executor.getEntityName())), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int unregisterChannel(CommandContext<ServerCommandSource> context, ServerPlayerEntity player) throws CommandSyntaxException {
|
||||
final Identifier channel = getIdentifier(context, "channel");
|
||||
|
||||
if (!ServerPlayNetworking.getReceived(player).contains(channel)) {
|
||||
throw new SimpleCommandExceptionType(new LiteralText("Cannot unregister channel the server player entity cannot recieve packets on")).create();
|
||||
}
|
||||
|
||||
ServerPlayNetworking.unregisterReceiver(player.networkHandler, channel);
|
||||
context.getSource().sendFeedback(new LiteralText(String.format("Unregistered channel %s for %s", getIdentifier(context, "channel"), player.getEntityName())), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int infoCommand(CommandContext<ServerCommandSource> context, ServerPlayerEntity player) {
|
||||
// TODO:
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -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.test.networking.keybindreciever;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import net.minecraft.client.options.KeyBinding;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
|
||||
// Sends a packet to the server when a keybinding was pressed
|
||||
// The server in response will send a chat message to the client.
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class NetworkingKeybindClientPacketTest implements ClientModInitializer {
|
||||
public static final KeyBinding TEST_BINDING = KeyBindingHelper.registerKeyBinding(new KeyBinding("fabric-networking-api-v1-testmod-keybind", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_RIGHT_BRACKET, "fabric-networking-api-v1-testmod"));
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
// Player must be in game to send packets, i.e. client.player != null
|
||||
if (client.getNetworkHandler() != null) {
|
||||
if (TEST_BINDING.wasPressed()) {
|
||||
// Send an empty payload, server just needs to be told when packet is sent
|
||||
ClientPlayNetworking.send(NetworkingKeybindPacketTest.KEYBINDING_PACKET_ID, PacketByteBufs.empty());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.keybindreciever;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.KeybindText;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
|
||||
// Listens for a packet from the client which is sent to the server when a keybinding is pressed.
|
||||
// In response the server will send a message containing the keybind text letting the client know it pressed that key.
|
||||
public final class NetworkingKeybindPacketTest implements ModInitializer {
|
||||
public static final Identifier KEYBINDING_PACKET_ID = NetworkingTestmods.id("keybind_press_test");
|
||||
|
||||
private static void receive(MinecraftServer server, ServerPlayerEntity player, ServerPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender) {
|
||||
// TODO: Can we send chat off the server thread?
|
||||
server.execute(() -> {
|
||||
player.sendMessage(new LiteralText("So you pressed ").append(new KeybindText("fabric-networking-api-v1-testmod-keybind").styled(style -> style.withFormatting(Formatting.BLUE))), false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ServerPlayConnectionEvents.PLAY_INIT.register((handler, sender, server) -> {
|
||||
ServerPlayNetworking.registerReceiver(handler, KEYBINDING_PACKET_ID, NetworkingKeybindPacketTest::receive);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.login;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketTest;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class NetworkingLoginQueryClientTest implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
// Send a dummy response to the server in return, by registering here we essentially say we understood the server's query
|
||||
ClientLoginNetworking.registerGlobalReceiver(NetworkingPlayPacketTest.TEST_CHANNEL, (client, handler, buf, listenerAdder) -> {
|
||||
return CompletableFuture.completedFuture(PacketByteBufs.empty());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.login;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.util.Util;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
import net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketTest;
|
||||
|
||||
public final class NetworkingLoginQueryTest implements ModInitializer {
|
||||
private static final boolean useLoginDelayTest = System.getProperty("fabric-networking-api-v1.loginDelayTest") != null;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ServerLoginConnectionEvents.LOGIN_QUERY_START.register(this::onLoginStart);
|
||||
ServerLoginConnectionEvents.LOGIN_QUERY_START.register(this::delaySimply);
|
||||
|
||||
// login delaying example
|
||||
ServerLoginNetworking.registerGlobalReceiver(NetworkingPlayPacketTest.TEST_CHANNEL, (server, handler, understood, buf, synchronizer, sender) -> {
|
||||
if (understood) {
|
||||
NetworkingTestmods.LOGGER.info("Understood response from client in {}", NetworkingPlayPacketTest.TEST_CHANNEL);
|
||||
|
||||
if (useLoginDelayTest) {
|
||||
FutureTask<?> future = new FutureTask<>(() -> {
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
Thread.sleep(300);
|
||||
NetworkingTestmods.LOGGER.info("Delayed login for number {} 300 milliseconds", i);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
// Execute the task on a worker thread as not to block the server thread
|
||||
Util.getMainWorkerExecutor().execute(future);
|
||||
synchronizer.waitFor(future);
|
||||
}
|
||||
} else {
|
||||
NetworkingTestmods.LOGGER.info("Client did not understand response query message with channel name {}", NetworkingPlayPacketTest.TEST_CHANNEL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void delaySimply(ServerLoginNetworkHandler handler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer) {
|
||||
if (useLoginDelayTest) {
|
||||
synchronizer.waitFor(CompletableFuture.runAsync(() -> {
|
||||
NetworkingTestmods.LOGGER.info("Starting simple delay task for 3000 milliseconds");
|
||||
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
NetworkingTestmods.LOGGER.info("Simple delay task completed");
|
||||
} catch (InterruptedException e) {
|
||||
NetworkingTestmods.LOGGER.error("Delay task caught exception", e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private void onLoginStart(ServerLoginNetworkHandler networkHandler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer) {
|
||||
// Send a dummy query when the client starts accepting queries.
|
||||
sender.sendPacket(NetworkingPlayPacketTest.TEST_CHANNEL, PacketByteBufs.empty()); // dummy packet
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.play;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
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.PacketSender;
|
||||
|
||||
public final class NetworkingPlayPacketClientTest implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
//ClientPlayNetworking.registerGlobalReceiver(NetworkingPlayPacketTest.TEST_CHANNEL, this::receive);
|
||||
|
||||
ClientPlayConnectionEvents.PLAY_INIT.register((handler, sender, client) -> {
|
||||
ClientPlayNetworking.registerReceiver(NetworkingPlayPacketTest.TEST_CHANNEL, (client1, handler1, buf, sender1) -> receive(handler1, sender1, client1, buf));
|
||||
});
|
||||
}
|
||||
|
||||
private void receive(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client, PacketByteBuf buf) {
|
||||
Text text = buf.readText();
|
||||
client.execute(() -> client.inGameHud.setOverlayMessage(text, true));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.play;
|
||||
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.string;
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
|
||||
public final class NetworkingPlayPacketTest implements ModInitializer {
|
||||
public static final Identifier TEST_CHANNEL = NetworkingTestmods.id("test_channel");
|
||||
|
||||
public static void sendToTestChannel(ServerPlayerEntity player, String stuff) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeText(new LiteralText(stuff));
|
||||
ServerPlayNetworking.send(player, TEST_CHANNEL, buf);
|
||||
NetworkingTestmods.LOGGER.info("Sent custom payload packet in {}", TEST_CHANNEL);
|
||||
}
|
||||
|
||||
public static void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
NetworkingTestmods.LOGGER.info("Registering test command");
|
||||
|
||||
dispatcher.register(literal("networktestcommand").then(argument("stuff", string()).executes(ctx -> {
|
||||
String stuff = StringArgumentType.getString(ctx, "stuff");
|
||||
sendToTestChannel(ctx.getSource().getPlayer(), stuff);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
NetworkingTestmods.LOGGER.info("Hello from networking user!");
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
|
||||
NetworkingPlayPacketTest.registerCommand(dispatcher);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-networking-api-v1-testmod",
|
||||
"name": "Fabric Networking API (v1) Test Mod",
|
||||
"version": "1.0.0",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabric-networking-api-v1": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.networking.channeltest.NetworkingChannelTest",
|
||||
"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.channeltest.NetworkingChannelClientTest",
|
||||
"net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindClientPacketTest",
|
||||
"net.fabricmc.fabric.test.networking.login.NetworkingLoginQueryClientTest",
|
||||
"net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketClientTest"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
archivesBaseName = "fabric-networking-v0"
|
||||
version = getSubprojectVersion(project, "0.1.10")
|
||||
version = getSubprojectVersion(project, "0.2.0")
|
||||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base'
|
||||
'fabric-api-base',
|
||||
'fabric-networking-api-v1'
|
||||
])
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Collection;
|
|||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.S2CPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
|
@ -30,8 +31,15 @@ import net.fabricmc.fabric.api.event.EventFactory;
|
|||
*
|
||||
* <p>Registrations received will be for <em>server -> client</em> packets
|
||||
* that the sending client can understand.
|
||||
*
|
||||
* @deprecated Please migrate to {@link S2CPlayChannelEvents}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface C2SPacketTypeCallback {
|
||||
/**
|
||||
* @deprecated Please migrate to {@link S2CPlayChannelEvents#REGISTER}.
|
||||
*/
|
||||
@Deprecated
|
||||
Event<C2SPacketTypeCallback> REGISTERED = EventFactory.createArrayBacked(
|
||||
C2SPacketTypeCallback.class,
|
||||
(callbacks) -> (client, types) -> {
|
||||
|
@ -41,6 +49,10 @@ public interface C2SPacketTypeCallback {
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated Please migrate to {@link S2CPlayChannelEvents#UNREGISTER}.
|
||||
*/
|
||||
@Deprecated
|
||||
Event<C2SPacketTypeCallback> UNREGISTERED = EventFactory.createArrayBacked(
|
||||
C2SPacketTypeCallback.class,
|
||||
(callbacks) -> (client, types) -> {
|
||||
|
|
|
@ -22,6 +22,7 @@ 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.C2SPlayChannelEvents;
|
||||
|
||||
/**
|
||||
* Event for listening to packet type registration and unregistration notifications
|
||||
|
@ -29,8 +30,15 @@ import net.fabricmc.fabric.api.event.EventFactory;
|
|||
*
|
||||
* <p>Registrations received will be for <em>client -> server</em> packets
|
||||
* that the sending server can understand.
|
||||
*
|
||||
* @deprecated Please migrate to {@link C2SPlayChannelEvents}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface S2CPacketTypeCallback {
|
||||
/**
|
||||
* @deprecated Please migrate to {@link C2SPlayChannelEvents#REGISTER}.
|
||||
*/
|
||||
@Deprecated
|
||||
Event<S2CPacketTypeCallback> REGISTERED = EventFactory.createArrayBacked(
|
||||
S2CPacketTypeCallback.class,
|
||||
(callbacks) -> (types) -> {
|
||||
|
@ -40,6 +48,10 @@ public interface S2CPacketTypeCallback {
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated Please migrate to {@link C2SPlayChannelEvents#UNREGISTER}.
|
||||
*/
|
||||
@Deprecated
|
||||
Event<S2CPacketTypeCallback> UNREGISTERED = EventFactory.createArrayBacked(
|
||||
S2CPacketTypeCallback.class,
|
||||
(callbacks) -> (types) -> {
|
||||
|
|
|
@ -23,6 +23,7 @@ import net.minecraft.network.Packet;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
|
||||
|
||||
/**
|
||||
|
@ -32,7 +33,10 @@ import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
|
|||
*
|
||||
* <ul><li>registering client-side packet receivers (server -> client packets)
|
||||
* <li>sending packets to the server (client -> server packets).</ul>
|
||||
*
|
||||
* @deprecated Please migrate to {@link ClientPlayNetworking}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ClientSidePacketRegistry extends PacketRegistry {
|
||||
ClientSidePacketRegistry INSTANCE = new ClientSidePacketRegistryImpl();
|
||||
|
||||
|
|
|
@ -18,9 +18,15 @@ package net.fabricmc.fabric.api.network;
|
|||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
|
||||
/**
|
||||
* Interface for receiving CustomPayload-based packets.
|
||||
*
|
||||
* @deprecated See the corresponding play packet handler in {@link ClientPlayNetworking} or {@link ServerPlayNetworking}
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface PacketConsumer {
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import net.fabricmc.api.EnvType;
|
|||
* to additional information, such as the source/target of the player, or
|
||||
* the correct task queue to enqueue synchronization-requiring code on.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PacketContext {
|
||||
/**
|
||||
* Get the environment associated with the packet.
|
||||
|
|
|
@ -20,6 +20,7 @@ import net.minecraft.network.Packet;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
@Deprecated
|
||||
public interface PacketRegistry {
|
||||
/**
|
||||
* Turn a (identifier, byte buffer) pair into a "custom payload" packet
|
||||
|
|
|
@ -24,6 +24,7 @@ import net.minecraft.network.Packet;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.api.server.PlayerStream;
|
||||
import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl;
|
||||
|
||||
|
@ -36,7 +37,10 @@ import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl;
|
|||
* <li>sending packets to clients (server -> client packets).</ul>
|
||||
*
|
||||
* <p>For iterating over clients in a server, see {@link PlayerStream}.
|
||||
*
|
||||
* @deprecated Please migrate to {@link ServerPlayNetworking}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ServerSidePacketRegistry extends PacketRegistry {
|
||||
ServerSidePacketRegistry INSTANCE = new ServerSidePacketRegistryImpl();
|
||||
|
||||
|
|
|
@ -23,22 +23,22 @@ import net.minecraft.entity.Entity;
|
|||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.ChunkManager;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.server.EntityTrackerStorageAccessor;
|
||||
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
|
||||
|
||||
/**
|
||||
* Helper streams for looking up players on a server.
|
||||
*
|
||||
* <p>In general, most of these methods will only function with a {@link ServerWorld} instance.
|
||||
*
|
||||
* @deprecated Please use {@link PlayerLookup} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class PlayerStream {
|
||||
private PlayerStream() { }
|
||||
|
||||
|
@ -52,7 +52,7 @@ public final class PlayerStream {
|
|||
|
||||
public static Stream<PlayerEntity> world(World world) {
|
||||
if (world instanceof ServerWorld) {
|
||||
// noinspection unchecked
|
||||
// noinspection unchecked,rawtypes
|
||||
return ((Stream) ((ServerWorld) world).getPlayers().stream());
|
||||
} else {
|
||||
throw new RuntimeException("Only supported on ServerWorld!");
|
||||
|
@ -60,14 +60,12 @@ public final class PlayerStream {
|
|||
}
|
||||
|
||||
public static Stream<PlayerEntity> watching(World world, ChunkPos pos) {
|
||||
ChunkManager manager = world.getChunkManager();
|
||||
|
||||
if (!(manager instanceof ServerChunkManager)) {
|
||||
throw new RuntimeException("Only supported on ServerWorld!");
|
||||
} else {
|
||||
//noinspection unchecked
|
||||
return ((Stream) ((ServerChunkManager) manager).threadedAnvilChunkStorage.getPlayersWatchingChunk(pos, false));
|
||||
if (world instanceof ServerWorld) {
|
||||
//noinspection unchecked,rawtypes
|
||||
return (Stream) PlayerLookup.tracking((ServerWorld) world, pos).stream();
|
||||
}
|
||||
|
||||
throw new RuntimeException("Only supported on ServerWorld!");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,19 +75,8 @@ public final class PlayerStream {
|
|||
*/
|
||||
@SuppressWarnings("JavaDoc")
|
||||
public static Stream<PlayerEntity> watching(Entity entity) {
|
||||
ChunkManager manager = entity.getEntityWorld().getChunkManager();
|
||||
|
||||
if (manager instanceof ServerChunkManager) {
|
||||
ThreadedAnvilChunkStorage storage = ((ServerChunkManager) manager).threadedAnvilChunkStorage;
|
||||
|
||||
if (storage instanceof EntityTrackerStorageAccessor) {
|
||||
//noinspection unchecked
|
||||
return ((Stream) ((EntityTrackerStorageAccessor) storage).fabric_getTrackingPlayers(entity));
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
return watching(entity.getEntityWorld(), new ChunkPos((int) (entity.getX() / 16.0D), (int) (entity.getZ() / 16.0D)));
|
||||
//noinspection unchecked,rawtypes
|
||||
return (Stream) PlayerLookup.tracking(entity).stream();
|
||||
}
|
||||
|
||||
public static Stream<PlayerEntity> watching(BlockEntity entity) {
|
||||
|
|
|
@ -16,87 +16,73 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.event.network.S2CPacketTypeCallback;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
import net.fabricmc.fabric.api.network.PacketConsumer;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.api.network.PacketRegistry;
|
||||
|
||||
public class ClientSidePacketRegistryImpl extends PacketRegistryImpl implements ClientSidePacketRegistry {
|
||||
private final Collection<Identifier> serverPayloadIds = new HashSet<>();
|
||||
|
||||
public static void invalidateRegisteredIdList() {
|
||||
((ClientSidePacketRegistryImpl) ClientSidePacketRegistry.INSTANCE).serverPayloadIds.clear();
|
||||
}
|
||||
|
||||
public class ClientSidePacketRegistryImpl implements ClientSidePacketRegistry, PacketRegistry {
|
||||
@Override
|
||||
public boolean canServerReceive(Identifier id) {
|
||||
return serverPayloadIds.contains(id);
|
||||
return ClientPlayNetworking.getReceived().contains(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToServer(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> completionListener) {
|
||||
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
MinecraftClient.getInstance().getNetworkHandler().getConnection().send(packet, completionListener);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
if (completionListener == null) {
|
||||
// stay closer to the vanilla codepath
|
||||
handler.sendPacket(packet);
|
||||
} else {
|
||||
handler.getConnection().send(packet, completionListener);
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Sending packet " + packet + " to server failed, not connected!");
|
||||
}
|
||||
throw new IllegalStateException("Cannot send packet to server while not in game!"); // TODO: Error message
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> toPacket(Identifier id, PacketByteBuf buf) {
|
||||
return new CustomPayloadC2SPacket(id, buf);
|
||||
return ClientPlayNetworking.createC2SPacket(id, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRegister(Identifier id) {
|
||||
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
|
||||
public void register(Identifier id, PacketConsumer consumer) {
|
||||
// id is checked in client networking
|
||||
Objects.requireNonNull(consumer, "PacketConsumer cannot be null");
|
||||
|
||||
if (handler != null) {
|
||||
createRegisterTypePacket(PacketTypes.REGISTER, Collections.singleton(id)).ifPresent(handler::sendPacket);
|
||||
}
|
||||
ClientPlayNetworking.registerGlobalReceiver(id, (client, handler, buf, sender) -> {
|
||||
consumer.accept(new PacketContext() {
|
||||
@Override
|
||||
public EnvType getPacketEnvironment() {
|
||||
return EnvType.CLIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnregister(Identifier id) {
|
||||
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
|
||||
|
||||
if (handler != null) {
|
||||
createRegisterTypePacket(PacketTypes.UNREGISTER, Collections.singleton(id)).ifPresent(handler::sendPacket);
|
||||
}
|
||||
public PlayerEntity getPlayer() {
|
||||
return client.player;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Identifier> getIdCollectionFor(PacketContext context) {
|
||||
return serverPayloadIds;
|
||||
public ThreadExecutor<?> getTaskQueue() {
|
||||
return client;
|
||||
}
|
||||
}, buf);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceivedRegisterPacket(PacketContext context, Collection<Identifier> ids) {
|
||||
S2CPacketTypeCallback.REGISTERED.invoker().accept(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceivedUnregisterPacket(PacketContext context, Collection<Identifier> ids) {
|
||||
S2CPacketTypeCallback.UNREGISTERED.invoker().accept(ids);
|
||||
public void unregister(Identifier id) {
|
||||
ClientPlayNetworking.unregisterGlobalReceiver(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.event.network.S2CPacketTypeCallback;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.S2CPlayChannelEvents;
|
||||
|
||||
public final class OldClientNetworkingHooks implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
// Must be lambdas below
|
||||
S2CPlayChannelEvents.REGISTER.register((handler, client, sender, channels) -> S2CPacketTypeCallback.REGISTERED.invoker().accept(channels));
|
||||
S2CPlayChannelEvents.UNREGISTER.register((handler, client, sender, channels) -> S2CPacketTypeCallback.UNREGISTERED.invoker().accept(channels));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.network.C2SPacketTypeCallback;
|
||||
import net.fabricmc.fabric.api.networking.v1.C2SPlayChannelEvents;
|
||||
|
||||
public final class OldNetworkingHooks implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
// Must be lambdas below
|
||||
C2SPlayChannelEvents.REGISTER.register((handler, server, sender, channels) -> {
|
||||
C2SPacketTypeCallback.REGISTERED.invoker().accept(handler.player, channels);
|
||||
});
|
||||
C2SPlayChannelEvents.UNREGISTER.register((handler, server, sender, channels) -> {
|
||||
C2SPacketTypeCallback.UNREGISTERED.invoker().accept(handler.player, channels);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* 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.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import net.fabricmc.fabric.api.network.PacketConsumer;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.api.network.PacketRegistry;
|
||||
|
||||
public abstract class PacketRegistryImpl implements PacketRegistry {
|
||||
protected static final Logger LOGGER = LogManager.getLogger();
|
||||
protected final Map<Identifier, PacketConsumer> consumerMap;
|
||||
|
||||
PacketRegistryImpl() {
|
||||
consumerMap = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public static Optional<Packet<?>> createInitialRegisterPacket(PacketRegistry registry) {
|
||||
PacketRegistryImpl impl = (PacketRegistryImpl) registry;
|
||||
return impl.createRegisterTypePacket(PacketTypes.REGISTER, impl.consumerMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Identifier id, PacketConsumer consumer) {
|
||||
boolean isNew = true;
|
||||
|
||||
if (consumerMap.containsKey(id)) {
|
||||
LOGGER.warn("Registered duplicate packet " + id + "!");
|
||||
LOGGER.trace(new Throwable());
|
||||
isNew = false;
|
||||
}
|
||||
|
||||
consumerMap.put(id, consumer);
|
||||
|
||||
if (isNew) {
|
||||
onRegister(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(Identifier id) {
|
||||
if (consumerMap.remove(id) != null) {
|
||||
onUnregister(id);
|
||||
} else {
|
||||
LOGGER.warn("Tried to unregister non-registered packet " + id + "!");
|
||||
LOGGER.trace(new Throwable());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void onRegister(Identifier id);
|
||||
|
||||
protected abstract void onUnregister(Identifier id);
|
||||
|
||||
protected abstract Collection<Identifier> getIdCollectionFor(PacketContext context);
|
||||
|
||||
protected abstract void onReceivedRegisterPacket(PacketContext context, Collection<Identifier> ids);
|
||||
|
||||
protected abstract void onReceivedUnregisterPacket(PacketContext context, Collection<Identifier> ids);
|
||||
|
||||
protected Optional<Packet<?>> createRegisterTypePacket(Identifier id, Collection<Identifier> ids) {
|
||||
if (ids.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
|
||||
boolean first = true;
|
||||
|
||||
for (Identifier a : ids) {
|
||||
if (!first) {
|
||||
buf.writeByte(0);
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
buf.writeBytes(a.toString().getBytes(StandardCharsets.US_ASCII));
|
||||
}
|
||||
|
||||
return Optional.of(toPacket(id, buf));
|
||||
}
|
||||
|
||||
private boolean acceptRegisterType(Identifier id, PacketContext context, Supplier<PacketByteBuf> bufSupplier) {
|
||||
Collection<Identifier> ids = new HashSet<>();
|
||||
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char c;
|
||||
PacketByteBuf buf = bufSupplier.get();
|
||||
|
||||
try {
|
||||
while (buf.readerIndex() < buf.writerIndex()) {
|
||||
c = (char) buf.readByte();
|
||||
|
||||
if (c == 0) {
|
||||
String s = sb.toString();
|
||||
|
||||
if (!s.isEmpty()) {
|
||||
try {
|
||||
ids.add(new Identifier(s));
|
||||
} catch (InvalidIdentifierException e) {
|
||||
LOGGER.warn("Received invalid identifier in " + id + ": " + s + " (" + e.getLocalizedMessage() + ")");
|
||||
LOGGER.trace(e);
|
||||
}
|
||||
}
|
||||
|
||||
sb = new StringBuilder();
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
|
||||
String s = sb.toString();
|
||||
|
||||
if (!s.isEmpty()) {
|
||||
try {
|
||||
ids.add(new Identifier(s));
|
||||
} catch (InvalidIdentifierException e) {
|
||||
LOGGER.warn("Received invalid identifier in " + id + ": " + s + " (" + e.getLocalizedMessage() + ")");
|
||||
LOGGER.trace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Identifier> target = getIdCollectionFor(context);
|
||||
|
||||
if (id.equals(PacketTypes.UNREGISTER)) {
|
||||
target.removeAll(ids);
|
||||
onReceivedUnregisterPacket(context, ids);
|
||||
} else {
|
||||
target.addAll(ids);
|
||||
onReceivedRegisterPacket(context, ids);
|
||||
}
|
||||
|
||||
return false; // continue execution for other mods
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for accepting packets used in Fabric mixins.
|
||||
*
|
||||
* <p>As PacketByteBuf getters in vanilla create a copy (to allow releasing the original packet buffer without
|
||||
* breaking other, potentially delayed accesses), we use a Supplier to generate those copies and release them
|
||||
* when needed.
|
||||
*
|
||||
* @param id The packet Identifier received.
|
||||
* @param context The packet context provided.
|
||||
* @param bufSupplier A supplier creating a new PacketByteBuf.
|
||||
* @return Whether or not the packet was handled by this packet registry.
|
||||
*/
|
||||
public boolean accept(Identifier id, PacketContext context, Supplier<PacketByteBuf> bufSupplier) {
|
||||
if (id.equals(PacketTypes.REGISTER) || id.equals(PacketTypes.UNREGISTER)) {
|
||||
return acceptRegisterType(id, context, bufSupplier);
|
||||
}
|
||||
|
||||
PacketConsumer consumer = consumerMap.get(id);
|
||||
|
||||
if (consumer != null) {
|
||||
PacketByteBuf buf = bufSupplier.get();
|
||||
|
||||
try {
|
||||
consumer.accept(context, buf);
|
||||
} catch (Throwable t) {
|
||||
LOGGER.warn("Failed to handle packet " + id + "!", t);
|
||||
} finally {
|
||||
if (buf.refCnt() > 0 && !PacketDebugOptions.DISABLE_BUFFER_RELEASES) {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,14 +16,7 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
@ -31,59 +24,36 @@ import io.netty.util.concurrent.GenericFutureListener;
|
|||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.event.network.C2SPacketTypeCallback;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.fabric.api.network.PacketConsumer;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.api.network.PacketRegistry;
|
||||
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
|
||||
public class ServerSidePacketRegistryImpl extends PacketRegistryImpl implements ServerSidePacketRegistry {
|
||||
private final WeakHashMap<PlayerEntity, Collection<Identifier>> playerPayloadIds = new WeakHashMap<>();
|
||||
private final Set<WeakReference<ServerPlayNetworkHandler>> handlers = new HashSet<>();
|
||||
|
||||
public void onQueryResponse(LoginQueryResponseC2SPacket packet) {
|
||||
}
|
||||
|
||||
public void addNetworkHandler(ServerPlayNetworkHandler handler) {
|
||||
handlers.add(new WeakReference<>(handler));
|
||||
}
|
||||
|
||||
protected void forEachHandler(Consumer<ServerPlayNetworkHandler> consumer) {
|
||||
Iterator<WeakReference<ServerPlayNetworkHandler>> it = handlers.iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
ServerPlayNetworkHandler server = it.next().get();
|
||||
|
||||
if (server != null) {
|
||||
consumer.accept(server);
|
||||
} else {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerSidePacketRegistryImpl implements ServerSidePacketRegistry, PacketRegistry {
|
||||
@Override
|
||||
public boolean canPlayerReceive(PlayerEntity player, Identifier id) {
|
||||
Collection<Identifier> ids = playerPayloadIds.get(player);
|
||||
|
||||
if (ids != null) {
|
||||
return ids.contains(id);
|
||||
} else {
|
||||
return false;
|
||||
if (player instanceof ServerPlayerEntity) {
|
||||
return ServerPlayNetworking.canSend((ServerPlayerEntity) player, id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToPlayer(PlayerEntity player, Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> completionListener) {
|
||||
if (!(player instanceof ServerPlayerEntity)) {
|
||||
throw new RuntimeException("Can only send to ServerPlayerEntities!");
|
||||
} else {
|
||||
if (player instanceof ServerPlayerEntity) {
|
||||
((ServerPlayerEntity) player).networkHandler.sendPacket(packet, completionListener);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Can only send to ServerPlayerEntities!");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,29 +62,31 @@ public class ServerSidePacketRegistryImpl extends PacketRegistryImpl implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onRegister(Identifier id) {
|
||||
createRegisterTypePacket(PacketTypes.REGISTER, Collections.singleton(id))
|
||||
.ifPresent((packet) -> forEachHandler((n) -> n.sendPacket(packet)));
|
||||
public void register(Identifier id, PacketConsumer consumer) {
|
||||
Objects.requireNonNull(consumer, "PacketConsumer cannot be null");
|
||||
|
||||
ServerPlayNetworking.registerGlobalReceiver(id, (server, player, handler, buf, sender) -> {
|
||||
consumer.accept(new PacketContext() {
|
||||
@Override
|
||||
public EnvType getPacketEnvironment() {
|
||||
return EnvType.SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnregister(Identifier id) {
|
||||
createRegisterTypePacket(PacketTypes.UNREGISTER, Collections.singleton(id))
|
||||
.ifPresent((packet) -> forEachHandler((n) -> n.sendPacket(packet)));
|
||||
public PlayerEntity getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Identifier> getIdCollectionFor(PacketContext context) {
|
||||
return playerPayloadIds.computeIfAbsent(context.getPlayer(), (p) -> new HashSet<>());
|
||||
public ThreadExecutor<?> getTaskQueue() {
|
||||
return server;
|
||||
}
|
||||
}, buf);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceivedRegisterPacket(PacketContext context, Collection<Identifier> ids) {
|
||||
C2SPacketTypeCallback.REGISTERED.invoker().accept(context.getPlayer(), ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceivedUnregisterPacket(PacketContext context, Collection<Identifier> ids) {
|
||||
C2SPacketTypeCallback.UNREGISTERED.invoker().accept(context.getPlayer(), ids);
|
||||
public void unregister(Identifier id) {
|
||||
ServerPlayNetworking.unregisterGlobalReceiver(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* 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.mixin.networking;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
|
||||
import net.fabricmc.fabric.impl.networking.PacketRegistryImpl;
|
||||
import net.fabricmc.fabric.impl.networking.PacketTypes;
|
||||
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public abstract class MixinClientPlayNetworkHandler implements PacketContext {
|
||||
@Shadow
|
||||
private MinecraftClient client;
|
||||
|
||||
@Shadow
|
||||
public abstract void sendPacket(Packet<?> var1);
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "onGameJoin")
|
||||
public void onGameJoin(GameJoinS2CPacket packet, CallbackInfo info) {
|
||||
Optional<Packet<?>> optionalPacket = PacketRegistryImpl.createInitialRegisterPacket(ClientSidePacketRegistry.INSTANCE);
|
||||
|
||||
//noinspection OptionalIsPresent
|
||||
if (optionalPacket.isPresent()) {
|
||||
sendPacket(optionalPacket.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Optional hook: it only removes a warning message.
|
||||
@Inject(method = "onCustomPayload", at = @At(value = "CONSTANT", args = "stringValue=Unknown custom packed identifier: {}"), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT, require = 0)
|
||||
public void onCustomPayloadNotFound(CustomPayloadS2CPacket packet, CallbackInfo info, Identifier id, PacketByteBuf buf) {
|
||||
if (packet.getChannel().equals(PacketTypes.REGISTER) || packet.getChannel().equals(PacketTypes.UNREGISTER)) {
|
||||
if (buf.refCnt() > 0) {
|
||||
buf.release();
|
||||
}
|
||||
|
||||
info.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
public void onCustomPayload(CustomPayloadS2CPacket packet, CallbackInfo info) {
|
||||
if (((ClientSidePacketRegistryImpl) ClientSidePacketRegistry.INSTANCE).accept(packet.getChannel(), this, packet::getData)) {
|
||||
info.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnvType getPacketEnvironment() {
|
||||
return EnvType.CLIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerEntity getPlayer() {
|
||||
return client.player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadExecutor getTaskQueue() {
|
||||
return client;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* 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.mixin.networking;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.PacketRegistryImpl;
|
||||
import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl;
|
||||
|
||||
@Mixin(priority = 500, value = PlayerManager.class)
|
||||
public abstract class MixinPlayerManager {
|
||||
@Inject(method = "onPlayerConnect", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/DifficultyS2CPacket;<init>(Lnet/minecraft/world/Difficulty;Z)V"))
|
||||
public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo info) {
|
||||
Optional<Packet<?>> optionalPacket = PacketRegistryImpl.createInitialRegisterPacket(ServerSidePacketRegistry.INSTANCE);
|
||||
|
||||
//noinspection OptionalIsPresent
|
||||
if (optionalPacket.isPresent()) {
|
||||
player.networkHandler.sendPacket(optionalPacket.get());
|
||||
((ServerSidePacketRegistryImpl) ServerSidePacketRegistry.INSTANCE).addNetworkHandler(player.networkHandler);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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.mixin.networking;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.CustomPayloadC2SPacketAccessor;
|
||||
import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public class MixinServerPlayNetworkHandler implements PacketContext {
|
||||
@Shadow
|
||||
private MinecraftServer server;
|
||||
@Shadow
|
||||
private ServerPlayerEntity player;
|
||||
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
public void onCustomPayload(CustomPayloadC2SPacket packet, CallbackInfo info) {
|
||||
Identifier channel = ((CustomPayloadC2SPacketAccessor) packet).getChannel();
|
||||
|
||||
if (((ServerSidePacketRegistryImpl) ServerSidePacketRegistry.INSTANCE).accept(channel, this, ((CustomPayloadC2SPacketAccessor) packet)::getData)) {
|
||||
info.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnvType getPacketEnvironment() {
|
||||
return EnvType.SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerEntity getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadExecutor getTaskQueue() {
|
||||
return server;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.networking",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"MixinCustomPayloadC2SPacket",
|
||||
"MixinEntityTracker",
|
||||
"MixinPlayerManager",
|
||||
"MixinServerPlayNetworkHandler",
|
||||
"MixinThreadedAnvilChunkStorage"
|
||||
],
|
||||
"client": [
|
||||
"MixinClientPlayNetworkHandler",
|
||||
"MixinMinecraftClient"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -15,12 +15,18 @@
|
|||
"authors": [
|
||||
"FabricMC"
|
||||
],
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.impl.networking.OldNetworkingHooks"
|
||||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.impl.networking.OldClientNetworkingHooks"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"fabric-api-base": "*"
|
||||
"fabric-api-base": "*",
|
||||
"fabric-networking-api-v1": "*"
|
||||
},
|
||||
"description": "Networking packet hooks and registries.",
|
||||
"mixins": [
|
||||
"fabric-networking-v0.mixins.json"
|
||||
]
|
||||
"description": "Legacy Networking packet hooks and registries, superseded by fabric-networking-api-v1."
|
||||
}
|
||||
|
|
|
@ -3,5 +3,5 @@ version = getSubprojectVersion(project, "0.7.1")
|
|||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base',
|
||||
'fabric-networking-v0'
|
||||
'fabric-networking-api-v1'
|
||||
])
|
||||
|
|
|
@ -19,26 +19,23 @@ package net.fabricmc.fabric.impl.registry.sync;
|
|||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
|
||||
public class FabricRegistryClientInit implements ClientModInitializer {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientSidePacketRegistry.INSTANCE.register(RegistrySyncManager.ID, (ctx, buf) -> {
|
||||
ClientPlayNetworking.registerGlobalReceiver(RegistrySyncManager.ID, (client, handler, buf, responseSender) -> {
|
||||
// if not hosting server, apply packet
|
||||
RegistrySyncManager.receivePacket(ctx, buf, RegistrySyncManager.DEBUG || !MinecraftClient.getInstance().isInSingleplayer(), (e) -> {
|
||||
RegistrySyncManager.receivePacket(client, buf, RegistrySyncManager.DEBUG || client.isInSingleplayer(), (e) -> {
|
||||
LOGGER.error("Registry remapping failed!", e);
|
||||
MinecraftClient.getInstance().execute(() -> {
|
||||
((ClientPlayerEntity) ctx.getPlayer()).networkHandler.getConnection().disconnect(
|
||||
new LiteralText("Registry remapping failed: " + e.getMessage())
|
||||
);
|
||||
|
||||
client.execute(() -> {
|
||||
handler.getConnection().disconnect(new LiteralText("Registry remapping failed: " + e.getMessage()));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -42,11 +42,11 @@ import net.minecraft.network.Packet;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryAttribute;
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryAttributeHolder;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
|
||||
public final class RegistrySyncManager {
|
||||
static final boolean DEBUG = System.getProperty("fabric.registry.debug", "false").equalsIgnoreCase("true");
|
||||
|
@ -71,15 +71,15 @@ public final class RegistrySyncManager {
|
|||
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
|
||||
buf.writeCompoundTag(tag);
|
||||
|
||||
return ServerSidePacketRegistry.INSTANCE.toPacket(ID, buf);
|
||||
return ServerPlayNetworking.createS2CPacket(ID, buf);
|
||||
}
|
||||
|
||||
public static void receivePacket(PacketContext context, PacketByteBuf buf, boolean accept, Consumer<Exception> errorHandler) {
|
||||
public static void receivePacket(ThreadExecutor<?> executor, PacketByteBuf buf, boolean accept, Consumer<Exception> errorHandler) {
|
||||
CompoundTag compound = buf.readCompoundTag();
|
||||
|
||||
if (accept) {
|
||||
try {
|
||||
context.getTaskQueue().submit(() -> {
|
||||
executor.submit(() -> {
|
||||
if (compound == null) {
|
||||
errorHandler.accept(new RemapException("Received null compound tag in sync packet!"));
|
||||
return null;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"depends": {
|
||||
"fabricloader": ">=0.9.2",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-networking-v0": "*"
|
||||
"fabric-networking-api-v1": "*"
|
||||
},
|
||||
"description": "Syncs registry mappings.",
|
||||
"mixins": [
|
||||
|
|
|
@ -34,6 +34,7 @@ include 'fabric-loot-tables-v1'
|
|||
include 'fabric-mining-levels-v0'
|
||||
include 'fabric-models-v0'
|
||||
include 'fabric-networking-v0'
|
||||
include 'fabric-networking-api-v1'
|
||||
include 'fabric-networking-blockentity-v0'
|
||||
include 'fabric-object-builder-api-v1'
|
||||
include 'fabric-object-builders-v0'
|
||||
|
|
Loading…
Reference in a new issue