mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-21 10:48:18 -05:00
Port to 24w03b (#3537)
* Deprecate FabricBlockSettings * Deprecate FabricItemSettings * Start on 24w03a * Main menu :) * Update mappings * PayloadTypeRegistry * Networking part 2 of many * Networking part 3 of many * Networking part 4 of many * Recipe api * Port Item API to 1.20.5 * Is this even right? * Port FabricParticleTypes to 1.20.5 * Remove redundant fuel caching logic * Remove fabric-containers-v0, deprecated since 2020 * Regsync work * Adapt screen handler to new networking * Update yarn + more work * More mapping updates * Compile fixes * Checkstyle + small fixes * Single and multiplayer fixes * Handle play packets on main thread. * Update mappings * Even more networking * Networking tests * Fix todo's * Update javadocs * Networking API improvements * Some small regsync refactors * Fix handling of null NBT in NbtIngredient * Update fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java Co-authored-by: ErrorCraft <51973682+ErrorCraft@users.noreply.github.com> * Update fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java Co-authored-by: ErrorCraft <51973682+ErrorCraft@users.noreply.github.com> * Add context objects * ChannelInfoHolder.getPendingChannelsNames -> fabric_getPendingChannelsNames * Fix crash * send `c:register` packet for play phase instead of config (#3544) * Bump version --------- Co-authored-by: ErrorCraft <51973682+ErrorCraft@users.noreply.github.com> Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com> Co-authored-by: Drex <nicknamedrex@gmail.com> Co-authored-by: deirn <deirn@bai.lol>
This commit is contained in:
parent
e89ad72381
commit
7b70ea8a7a
143 changed files with 2272 additions and 3480 deletions
|
@ -1,6 +0,0 @@
|
|||
version = getSubprojectVersion(project)
|
||||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base',
|
||||
'fabric-networking-api-v1'
|
||||
])
|
|
@ -1,29 +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.api.client.screen;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry.Factory} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface ContainerScreenFactory<C extends ScreenHandler> {
|
||||
HandledScreen create(C container);
|
||||
}
|
|
@ -1,50 +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.api.client.screen;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.container.ContainerFactory;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.fabricmc.fabric.impl.client.container.ScreenProviderRegistryImpl;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ScreenProviderRegistry {
|
||||
ScreenProviderRegistry INSTANCE = new ScreenProviderRegistryImpl();
|
||||
|
||||
/**
|
||||
* Register a "Container -> ContainerScreen" factory. This is used only on the client side.
|
||||
*
|
||||
* @param identifier a shared identifier, this identifier should also be used to register a container using {@link ContainerProviderRegistry}
|
||||
* @param containerScreenFactory the supplier that should be used to create the new gui
|
||||
*/
|
||||
<C extends ScreenHandler> void registerFactory(Identifier identifier, ContainerScreenFactory<C> containerScreenFactory);
|
||||
|
||||
/**
|
||||
* Register a "packet -> ContainerScreen" factory. This is used only on the client side, and allows you
|
||||
* to override the default behaviour of re-using the existing "packet -> Container" logic.
|
||||
*
|
||||
* @param identifier a shared identifier, this identifier should also be used to register a container using {@link ContainerProviderRegistry}
|
||||
* @param factory the gui factory, this should return a new {@link HandledScreen}
|
||||
*/
|
||||
void registerFactory(Identifier identifier, ContainerFactory<HandledScreen> factory);
|
||||
}
|
|
@ -1,93 +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.client.container;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.client.screen.ContainerScreenFactory;
|
||||
import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
|
||||
import net.fabricmc.fabric.api.container.ContainerFactory;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
|
||||
|
||||
public class ScreenProviderRegistryImpl implements ScreenProviderRegistry {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ScreenProviderRegistryImpl.class);
|
||||
|
||||
private static final Map<Identifier, ContainerFactory<HandledScreen>> FACTORIES = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void registerFactory(Identifier identifier, ContainerFactory<HandledScreen> factory) {
|
||||
if (FACTORIES.containsKey(identifier)) {
|
||||
throw new RuntimeException("A factory has already been registered as " + identifier + "!");
|
||||
}
|
||||
|
||||
FACTORIES.put(identifier, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <C extends ScreenHandler> void registerFactory(Identifier identifier, ContainerScreenFactory<C> containerScreenFactory) {
|
||||
registerFactory(identifier, (syncId, identifier1, player, buf) -> {
|
||||
C container = ((ContainerProviderImpl) ContainerProviderRegistry.INSTANCE).createContainer(syncId, identifier1, player, buf);
|
||||
|
||||
if (container == null) {
|
||||
LOGGER.error("Could not open container for {} - a null object was created!", identifier1.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return containerScreenFactory.create(container);
|
||||
});
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
ClientPlayNetworking.registerGlobalReceiver(ContainerProviderImpl.OPEN_CONTAINER, (client, handler, buf, responseSender) -> {
|
||||
Identifier identifier = buf.readIdentifier();
|
||||
int syncId = buf.readUnsignedByte();
|
||||
|
||||
// Retain the buf since we must open the screen handler with it's extra modded data on the client thread
|
||||
buf.retain();
|
||||
|
||||
client.execute(() -> {
|
||||
try {
|
||||
ContainerFactory<HandledScreen> factory = FACTORIES.get(identifier);
|
||||
|
||||
if (factory == null) {
|
||||
LOGGER.error("No GUI factory found for {}!", identifier.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
ClientPlayerEntity player = client.player;
|
||||
HandledScreen<?> gui = factory.create(syncId, identifier, player, buf);
|
||||
|
||||
player.currentScreenHandler = gui.getScreenHandler();
|
||||
client.setScreen(gui);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,39 +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.api.container;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry.ExtendedClientHandlerFactory} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface ContainerFactory<T> {
|
||||
/**
|
||||
* Creates the new object.
|
||||
*
|
||||
* @param syncId The container synchronization ID.
|
||||
* @param identifier the Identifier is the name that was used when registering the factory
|
||||
* @param player the player that is opening the gui/container
|
||||
* @param buf the buffer contains the same data that was provided with {@link net.fabricmc.fabric.api.container.ContainerProviderRegistry#openContainer}
|
||||
* @return the new gui or container
|
||||
*/
|
||||
T create(int syncId, Identifier identifier, PlayerEntity player, PacketByteBuf buf);
|
||||
}
|
|
@ -1,61 +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.api.container;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ContainerProviderRegistry {
|
||||
ContainerProviderRegistry INSTANCE = new ContainerProviderImpl();
|
||||
|
||||
/**
|
||||
* Register a "packet buffer -> container" factory. This is used both on the client and server side.
|
||||
*
|
||||
* @param identifier a shared identifier, this identifier should also be used to register a container using {@link net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry}
|
||||
* @param factory the ContainerFactory that should return a new {@link ScreenHandler}
|
||||
*/
|
||||
void registerFactory(Identifier identifier, ContainerFactory<ScreenHandler> factory);
|
||||
|
||||
/**
|
||||
* Open a modded container.
|
||||
*
|
||||
* @param identifier the identifier that was used when registering the container
|
||||
* @param player the player that should open the container
|
||||
* @param writer a PacketByteBuf where data can be written to, this data is then accessible by the container factory when creating the container or the gui
|
||||
*/
|
||||
void openContainer(Identifier identifier, ServerPlayerEntity player, Consumer<PacketByteBuf> writer);
|
||||
|
||||
/**
|
||||
* Open a modded container. This should be called on the server side - it has no effect on the client side.
|
||||
*
|
||||
* @param identifier the identifier that was used when registering the container
|
||||
* @param player the player that should open the container
|
||||
* @param writer a PacketByteBuf where data can be written to, this data is then accessible by the container factory when creating the container or the gui
|
||||
*/
|
||||
void openContainer(Identifier identifier, PlayerEntity player, Consumer<PacketByteBuf> writer);
|
||||
}
|
|
@ -1,117 +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.container;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.container.ContainerFactory;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.mixin.container.ServerPlayerEntityAccessor;
|
||||
|
||||
public class ContainerProviderImpl implements ContainerProviderRegistry {
|
||||
public static final Identifier OPEN_CONTAINER = new Identifier("fabric", "container/open");
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ContainerProviderImpl.class);
|
||||
|
||||
private static final Map<Identifier, ContainerFactory<ScreenHandler>> FACTORIES = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void registerFactory(Identifier identifier, ContainerFactory<ScreenHandler> factory) {
|
||||
if (FACTORIES.containsKey(identifier)) {
|
||||
throw new RuntimeException("A factory has already been registered as " + identifier.toString());
|
||||
}
|
||||
|
||||
FACTORIES.put(identifier, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openContainer(Identifier identifier, PlayerEntity player, Consumer<PacketByteBuf> writer) {
|
||||
if (!(player instanceof ServerPlayerEntity)) {
|
||||
LOGGER.warn("Please only use ContainerProviderRegistry.openContainer() with server-sided player entities!");
|
||||
return;
|
||||
}
|
||||
|
||||
openContainer(identifier, (ServerPlayerEntity) player, writer);
|
||||
}
|
||||
|
||||
private boolean emittedNoSyncHookWarning = false;
|
||||
|
||||
@Override
|
||||
public void openContainer(Identifier identifier, ServerPlayerEntity player, Consumer<PacketByteBuf> writer) {
|
||||
int syncId;
|
||||
|
||||
if (player instanceof ServerPlayerEntitySyncHook) {
|
||||
ServerPlayerEntitySyncHook serverPlayerEntitySyncHook = (ServerPlayerEntitySyncHook) player;
|
||||
syncId = serverPlayerEntitySyncHook.fabric_incrementSyncId();
|
||||
} else if (player instanceof ServerPlayerEntityAccessor) {
|
||||
if (!emittedNoSyncHookWarning) {
|
||||
LOGGER.warn("ServerPlayerEntitySyncHook could not be applied - fabric-containers is using a hack!");
|
||||
emittedNoSyncHookWarning = true;
|
||||
}
|
||||
|
||||
syncId = (((ServerPlayerEntityAccessor) player).getScreenHandlerSyncId() + 1) % 100;
|
||||
((ServerPlayerEntityAccessor) player).setScreenHandlerSyncId(syncId);
|
||||
} else {
|
||||
throw new RuntimeException("Neither ServerPlayerEntitySyncHook nor Accessor present! This should not happen!");
|
||||
}
|
||||
|
||||
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
|
||||
buf.writeIdentifier(identifier);
|
||||
buf.writeByte(syncId);
|
||||
|
||||
writer.accept(buf);
|
||||
player.networkHandler.sendPacket(ServerPlayNetworking.createS2CPacket(OPEN_CONTAINER, buf));
|
||||
|
||||
PacketByteBuf clonedBuf = new PacketByteBuf(buf.duplicate());
|
||||
clonedBuf.readIdentifier();
|
||||
clonedBuf.readUnsignedByte();
|
||||
|
||||
ScreenHandler screenHandler = createContainer(syncId, identifier, player, clonedBuf);
|
||||
|
||||
if (screenHandler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.currentScreenHandler = screenHandler;
|
||||
((ServerPlayerEntityAccessor) player).callOnScreenHandlerOpened(screenHandler);
|
||||
}
|
||||
|
||||
public <C extends ScreenHandler> C createContainer(int syncId, Identifier identifier, PlayerEntity player, PacketByteBuf buf) {
|
||||
ContainerFactory<ScreenHandler> factory = FACTORIES.get(identifier);
|
||||
|
||||
if (factory == null) {
|
||||
LOGGER.error("No container factory found for {}!", identifier.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return (C) factory.create(syncId, identifier, player, buf);
|
||||
}
|
||||
}
|
|
@ -1,35 +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.container;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public interface ServerPlayerEntityAccessor {
|
||||
@Accessor
|
||||
int getScreenHandlerSyncId();
|
||||
@Accessor
|
||||
void setScreenHandlerSyncId(int syncId);
|
||||
|
||||
@Invoker()
|
||||
void callOnScreenHandlerOpened(ScreenHandler screenHandler);
|
||||
}
|
|
@ -1,39 +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.container;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.impl.container.ServerPlayerEntitySyncHook;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public abstract class ServerPlayerEntityMixin implements ServerPlayerEntitySyncHook {
|
||||
@Shadow
|
||||
private int screenHandlerSyncId;
|
||||
|
||||
@Shadow
|
||||
protected abstract void incrementScreenHandlerSyncId();
|
||||
|
||||
@Override
|
||||
public int fabric_incrementSyncId() {
|
||||
incrementScreenHandlerSyncId();
|
||||
return screenHandlerSyncId;
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"required": false,
|
||||
"package": "net.fabricmc.fabric.mixin.container",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"ServerPlayerEntityMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"required": false,
|
||||
"package": "net.fabricmc.fabric.mixin.container",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"ServerPlayerEntityAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-containers-v0",
|
||||
"name": "Fabric Containers (v0)",
|
||||
"version": "${version}",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/fabric-containers-v0/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"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.15.6",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-networking-api-v1": "*"
|
||||
},
|
||||
"description": "Adds hooks for containers.",
|
||||
"mixins": [
|
||||
"fabric-containers-v0.mixins.json",
|
||||
"fabric-containers-v0.accurate.mixins.json"
|
||||
],
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"net.fabricmc.fabric.impl.client.container.ScreenProviderRegistryImpl::init"
|
||||
]
|
||||
},
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "deprecated"
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package net.fabricmc.fabric.test.lookup;
|
|||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
|
@ -28,7 +29,6 @@ import net.minecraft.util.math.Direction;
|
|||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||
import net.fabricmc.fabric.test.lookup.api.ItemApis;
|
||||
import net.fabricmc.fabric.test.lookup.api.ItemInsertable;
|
||||
|
@ -41,17 +41,17 @@ public class FabricApiLookupTest implements ModInitializer {
|
|||
public static final String MOD_ID = "fabric-lookup-api-v1-testmod";
|
||||
// Chute - Block without model that transfers item from the container above to the container below.
|
||||
// It's meant to work with unsided containers: chests, dispensers, droppers and hoppers.
|
||||
public static final ChuteBlock CHUTE_BLOCK = new ChuteBlock(FabricBlockSettings.create());
|
||||
public static final ChuteBlock CHUTE_BLOCK = new ChuteBlock(AbstractBlock.Settings.create());
|
||||
public static final BlockItem CHUTE_ITEM = new BlockItem(CHUTE_BLOCK, new Item.Settings());
|
||||
public static BlockEntityType<ChuteBlockEntity> CHUTE_BLOCK_ENTITY_TYPE;
|
||||
// Cobble gen - Block without model that can generate infinite cobblestone when placed above a chute.
|
||||
// It's meant to test BlockApiLookup#registerSelf.
|
||||
public static final CobbleGenBlock COBBLE_GEN_BLOCK = new CobbleGenBlock(FabricBlockSettings.create());
|
||||
public static final CobbleGenBlock COBBLE_GEN_BLOCK = new CobbleGenBlock(AbstractBlock.Settings.create());
|
||||
public static final BlockItem COBBLE_GEN_ITEM = new BlockItem(COBBLE_GEN_BLOCK, new Item.Settings());
|
||||
public static BlockEntityType<CobbleGenBlockEntity> COBBLE_GEN_BLOCK_ENTITY_TYPE;
|
||||
// Testing for item api lookups is done in the `item` package.
|
||||
|
||||
public static final InspectorBlock INSPECTOR_BLOCK = new InspectorBlock(FabricBlockSettings.create());
|
||||
public static final InspectorBlock INSPECTOR_BLOCK = new InspectorBlock(AbstractBlock.Settings.create());
|
||||
public static final BlockItem INSPECTOR_ITEM = new BlockItem(INSPECTOR_BLOCK, new Item.Settings());
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,12 +18,12 @@ package net.fabricmc.fabric.test.lookup;
|
|||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.class_9062;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.ItemActionResult;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
@ -38,7 +38,7 @@ public class InspectorBlock extends Block {
|
|||
}
|
||||
|
||||
@Override
|
||||
public class_9062 method_55765(ItemStack stack, BlockState blockState, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) {
|
||||
public ItemActionResult onUseWithItem(ItemStack stack, BlockState blockState, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) {
|
||||
Inspectable inspectable = FabricItemApiLookupTest.INSPECTABLE.find(stack, null);
|
||||
|
||||
if (inspectable != null) {
|
||||
|
@ -46,10 +46,10 @@ public class InspectorBlock extends Block {
|
|||
player.sendMessage(inspectable.inspect(), true);
|
||||
}
|
||||
|
||||
return class_9062.method_55644(world.isClient());
|
||||
return ItemActionResult.success(world.isClient());
|
||||
}
|
||||
|
||||
return class_9062.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||
return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.content.registry;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
|
@ -31,7 +30,6 @@ import net.minecraft.registry.Registries;
|
|||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.registry.FuelRegistry;
|
||||
|
||||
// TODO: Clamp values to 32767 (+ add hook for mods which extend the limit to disable the check?)
|
||||
|
@ -39,23 +37,13 @@ public final class FuelRegistryImpl implements FuelRegistry {
|
|||
private static final Logger LOGGER = LoggerFactory.getLogger(FuelRegistryImpl.class);
|
||||
private final Object2IntMap<ItemConvertible> itemCookTimes = new Object2IntLinkedOpenHashMap<>();
|
||||
private final Object2IntMap<TagKey<Item>> tagCookTimes = new Object2IntLinkedOpenHashMap<>();
|
||||
private volatile Map<Item, Integer> fuelTimeCache = null; // thread safe via copy-on-write mechanism
|
||||
|
||||
public FuelRegistryImpl() {
|
||||
// Reset cache after tags change since it depends on tags.
|
||||
CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> {
|
||||
resetCache();
|
||||
});
|
||||
}
|
||||
|
||||
public Map<Item, Integer> getFuelTimes() {
|
||||
Map<Item, Integer> ret = fuelTimeCache;
|
||||
|
||||
if (ret == null) {
|
||||
fuelTimeCache = ret = new IdentityHashMap<>(AbstractFurnaceBlockEntity.createFuelTimeMap()); // IdentityHashMap is faster than vanilla's LinkedHashMap and suitable for Item keys
|
||||
}
|
||||
|
||||
return ret;
|
||||
// Cached by vanilla now
|
||||
return AbstractFurnaceBlockEntity.createFuelTimeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -138,6 +126,7 @@ public final class FuelRegistryImpl implements FuelRegistry {
|
|||
}
|
||||
|
||||
public void resetCache() {
|
||||
fuelTimeCache = null;
|
||||
// Note: tag reload is already handled by vanilla, see DataPackContents#refresh
|
||||
AbstractFurnaceBlockEntity.clearFuelTimes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ public final class ContentRegistryTest implements ModInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ActionResult method_55766(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||
// Emit the test event
|
||||
world.emitGameEvent(player, TEST_EVENT, pos);
|
||||
return ActionResult.SUCCESS;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"FabricMC"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.15.1",
|
||||
"fabricloader": ">=0.15.6",
|
||||
"fabric-entity-events-v1": "*",
|
||||
"fabric-object-builder-api-v1": "*"
|
||||
},
|
||||
|
|
|
@ -56,7 +56,7 @@ public interface FabricElytraItem {
|
|||
|
||||
if (!entity.getWorld().isClient && nextRoll % 10 == 0) {
|
||||
if ((nextRoll / 10) % 2 == 0) {
|
||||
chestStack.damage(1, entity, p -> p.sendEquipmentBreakStatus(EquipmentSlot.CHEST));
|
||||
chestStack.damage(1, entity, EquipmentSlot.CHEST);
|
||||
}
|
||||
|
||||
entity.emitGameEvent(GameEvent.ELYTRA_GLIDE);
|
||||
|
|
|
@ -40,7 +40,7 @@ abstract class LivingEntityMixin extends Entity {
|
|||
* Handle ALLOW and CUSTOM {@link EntityElytraEvents} when an entity is fall flying.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/entity/EquipmentSlot;CHEST:Lnet/minecraft/entity/EquipmentSlot;"), method = "tickFallFlying()V", allow = 1, cancellable = true)
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getEquippedStack(Lnet/minecraft/entity/EquipmentSlot;)Lnet/minecraft/item/ItemStack;"), method = "tickFallFlying()V", allow = 1, cancellable = true)
|
||||
void injectElytraTick(CallbackInfo info) {
|
||||
LivingEntity self = (LivingEntity) (Object) this;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ public class TestBedBlock extends Block {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ActionResult method_55766(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||
if (state.get(OCCUPIED)) {
|
||||
player.sendMessage(Text.translatable("block.minecraft.bed.occupied"), true);
|
||||
return ActionResult.CONSUME;
|
||||
|
|
|
@ -21,7 +21,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.network.ConnectedClientData;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
|
@ -31,7 +30,7 @@ public class FakePlayerNetworkHandler extends ServerPlayNetworkHandler {
|
|||
private static final ClientConnection FAKE_CONNECTION = new FakeClientConnection();
|
||||
|
||||
public FakePlayerNetworkHandler(ServerPlayerEntity player) {
|
||||
super(player.getServer(), FAKE_CONNECTION, player, ConnectedClientData.createDefault(player.getGameProfile()));
|
||||
super(player.getServer(), FAKE_CONNECTION, player, ConnectedClientData.createDefault(player.getGameProfile(), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,9 +40,5 @@ public class FakePlayerNetworkHandler extends ServerPlayNetworkHandler {
|
|||
private FakeClientConnection() {
|
||||
super(NetworkSide.CLIENTBOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPacketListener(PacketListener packetListener) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,13 +32,11 @@ import net.minecraft.resource.ResourceFinder;
|
|||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.TestCommand;
|
||||
import net.minecraft.test.GameTestBatch;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.test.TestFailureLogger;
|
||||
import net.minecraft.test.TestFunction;
|
||||
import net.minecraft.test.TestFunctions;
|
||||
import net.minecraft.test.TestServer;
|
||||
import net.minecraft.test.TestUtil;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
|
@ -80,7 +78,7 @@ public final class FabricGameTestHelper {
|
|||
|
||||
LOGGER.info("Starting test server");
|
||||
MinecraftServer server = TestServer.startServer(thread -> {
|
||||
return TestServer.create(thread, session, resourcePackManager, getBatches(), BlockPos.ORIGIN);
|
||||
return TestServer.create(thread, session, resourcePackManager, getTestFunctions(), BlockPos.ORIGIN);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -128,10 +126,6 @@ public final class FabricGameTestHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private static Collection<GameTestBatch> getBatches() {
|
||||
return TestUtil.createBatches(getTestFunctions());
|
||||
}
|
||||
|
||||
private static Collection<TestFunction> getTestFunctions() {
|
||||
return TestFunctions.getTestFunctions();
|
||||
}
|
||||
|
|
|
@ -16,26 +16,27 @@
|
|||
|
||||
package net.fabricmc.fabric.api.item.v1;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Allows an item to run custom logic when {@link ItemStack#damage(int, LivingEntity, Consumer)} is called.
|
||||
* Allows an item to run custom logic when {@link ItemStack#damage(int, LivingEntity, EquipmentSlot)} is called.
|
||||
* This is useful for items that, for example, may drain durability from some other source before damaging
|
||||
* the stack itself.
|
||||
*
|
||||
* <p>Custom damage handlers can be set with {@link FabricItemSettings#customDamage}.
|
||||
* <p>Custom damage handlers can be set with {@link FabricItem.Settings#customDamage}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CustomDamageHandler {
|
||||
/**
|
||||
* Called to apply damage to the given stack.
|
||||
* This can be used to e.g. drain from a battery before actually damaging the item.
|
||||
* @param amount The amount of damage originally requested
|
||||
* @param breakCallback Callback when the stack reaches zero damage. See {@link ItemStack#damage(int, LivingEntity, Consumer)} and its callsites for more information.
|
||||
* Note that this does not get called if non-entities, such as dispensers, are damaging the item.
|
||||
* Calling {@code breakCallback} breaks the item, bypassing the vanilla logic. The return value is
|
||||
* ignored in this case.
|
||||
* @param amount the amount of damage originally requested
|
||||
* @return The amount of damage to pass to vanilla's logic
|
||||
*/
|
||||
int damage(ItemStack stack, int amount, LivingEntity entity, Consumer<LivingEntity> breakCallback);
|
||||
int damage(ItemStack stack, int amount, LivingEntity entity, EquipmentSlot slot, Runnable breakCallback);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import net.minecraft.item.ItemStack;
|
|||
* <p>The preferred requipment slot of an item stack can be queried using
|
||||
* {@link net.minecraft.entity.LivingEntity#getPreferredEquipmentSlot(ItemStack) LivingEntity.getPreferredEquipmentSlot()}.
|
||||
*
|
||||
* <p>Equipment slot providers can be set with {@link FabricItemSettings#equipmentSlot(EquipmentSlotProvider)}.
|
||||
* <p>Equipment slot providers can be set with {@link FabricItem.Settings#equipmentSlot(EquipmentSlotProvider)}.
|
||||
*
|
||||
* <p>Note that items extending {@link net.minecraft.item.ArmorItem} don't need to use this
|
||||
* as there's {@link net.minecraft.item.ArmorItem#getSlotType()}.
|
||||
|
|
|
@ -30,6 +30,8 @@ import net.minecraft.item.ItemStack;
|
|||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.util.Hand;
|
||||
|
||||
import net.fabricmc.fabric.impl.item.FabricItemInternals;
|
||||
|
||||
/**
|
||||
* General-purpose Fabric-provided extensions for {@link Item} subclasses.
|
||||
*
|
||||
|
@ -136,4 +138,32 @@ public interface FabricItem {
|
|||
default @Nullable FoodComponent getFoodComponent(ItemStack stack) {
|
||||
return ((Item) this).getFoodComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fabric-provided extensions for {@link Item.Settings}.
|
||||
* This interface is automatically implemented on all item settings via Mixin and interface injection.
|
||||
*/
|
||||
interface Settings {
|
||||
/**
|
||||
* Sets the equipment slot provider of the item.
|
||||
*
|
||||
* @param equipmentSlotProvider the equipment slot provider
|
||||
* @return this builder
|
||||
*/
|
||||
default Item.Settings equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
|
||||
FabricItemInternals.computeExtraData((Item.Settings) this).equipmentSlot(equipmentSlotProvider);
|
||||
return (Item.Settings) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom damage handler of the item.
|
||||
* Note that this is only called on an ItemStack if {@link ItemStack#isDamageable()} returns true.
|
||||
*
|
||||
* @see CustomDamageHandler
|
||||
*/
|
||||
default Item.Settings customDamage(CustomDamageHandler handler) {
|
||||
FabricItemInternals.computeExtraData((Item.Settings) this).customDamage(handler);
|
||||
return (Item.Settings) this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,19 +25,18 @@ import net.minecraft.util.Rarity;
|
|||
import net.fabricmc.fabric.impl.item.FabricItemInternals;
|
||||
|
||||
/**
|
||||
* Fabric's version of Item.Settings. Adds additional methods and hooks
|
||||
* not found in the original class.
|
||||
*
|
||||
* <p>To use it, simply replace {@code new Item.Settings()} with
|
||||
* {@code new FabricItemSettings()}.
|
||||
* @deprecated replace with {@link Item.Settings}
|
||||
*/
|
||||
@Deprecated
|
||||
public class FabricItemSettings extends Item.Settings {
|
||||
/**
|
||||
* Sets the equipment slot provider of the item.
|
||||
*
|
||||
* @param equipmentSlotProvider the equipment slot provider
|
||||
* @return this builder
|
||||
* @deprecated replace with {@link FabricItem.Settings#equipmentSlot(EquipmentSlotProvider)}
|
||||
*/
|
||||
@Deprecated
|
||||
public FabricItemSettings equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) {
|
||||
FabricItemInternals.computeExtraData(this).equipmentSlot(equipmentSlotProvider);
|
||||
return this;
|
||||
|
@ -47,8 +46,10 @@ public class FabricItemSettings extends Item.Settings {
|
|||
* Sets the custom damage handler of the item.
|
||||
* Note that this is only called on an ItemStack if {@link ItemStack#isDamageable()} returns true.
|
||||
*
|
||||
* @deprecated replace with {@link FabricItem.Settings#customDamage(CustomDamageHandler)}
|
||||
* @see CustomDamageHandler
|
||||
*/
|
||||
@Deprecated
|
||||
public FabricItemSettings customDamage(CustomDamageHandler handler) {
|
||||
FabricItemInternals.computeExtraData(this).customDamage(handler);
|
||||
return this;
|
||||
|
|
|
@ -14,7 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* API for working with screen handlers on the client.
|
||||
*/
|
||||
package net.fabricmc.fabric.api.client.screenhandler.v1;
|
||||
package net.fabricmc.fabric.mixin.item;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItem;
|
||||
|
||||
@Mixin(Item.Settings.class)
|
||||
public class ItemSettingsMixin implements FabricItem.Settings {
|
||||
}
|
|
@ -16,28 +16,28 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.item;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
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.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.attribute.EntityAttribute;
|
||||
import net.minecraft.entity.attribute.EntityAttributeModifier;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemStack;
|
||||
|
@ -48,33 +48,23 @@ import net.fabricmc.fabric.impl.item.ItemExtensions;
|
|||
public abstract class ItemStackMixin implements FabricItemStack {
|
||||
@Shadow public abstract Item getItem();
|
||||
|
||||
@Unique
|
||||
private LivingEntity fabric_damagingEntity;
|
||||
|
||||
@Unique
|
||||
private Consumer<LivingEntity> fabric_breakCallback;
|
||||
|
||||
@Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At("HEAD"))
|
||||
private void saveDamager(int amount, LivingEntity entity, Consumer<LivingEntity> breakCallback, CallbackInfo ci) {
|
||||
this.fabric_damagingEntity = entity;
|
||||
this.fabric_breakCallback = breakCallback;
|
||||
}
|
||||
|
||||
@ModifyArg(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;damage(ILnet/minecraft/util/math/random/Random;Lnet/minecraft/server/network/ServerPlayerEntity;)Z"), index = 0)
|
||||
private int hookDamage(int amount) {
|
||||
@WrapOperation(method = "damage(ILnet/minecraft/entity/LivingEntity;Lnet/minecraft/entity/EquipmentSlot;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;damage(ILnet/minecraft/util/math/random/Random;Lnet/minecraft/server/network/ServerPlayerEntity;Ljava/lang/Runnable;)V"))
|
||||
private void hookDamage(ItemStack instance, int amount, Random random, ServerPlayerEntity serverPlayerEntity, Runnable runnable, Operation<Void> original, @Local(argsOnly = true) EquipmentSlot slot) {
|
||||
CustomDamageHandler handler = ((ItemExtensions) getItem()).fabric_getCustomDamageHandler();
|
||||
|
||||
if (handler != null) {
|
||||
return handler.damage((ItemStack) (Object) this, amount, fabric_damagingEntity, fabric_breakCallback);
|
||||
// Track whether an item has been broken by custom handler
|
||||
MutableBoolean mut = new MutableBoolean(false);
|
||||
amount = handler.damage((ItemStack) (Object) this, amount, serverPlayerEntity, slot, () -> {
|
||||
mut.setTrue();
|
||||
runnable.run();
|
||||
});
|
||||
|
||||
// If item is broken, there's no reason to call the original.
|
||||
if (mut.booleanValue()) return;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
@Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At("RETURN"))
|
||||
private <T extends LivingEntity> void clearDamage(int amount, T entity, Consumer<T> breakCallback, CallbackInfo ci) {
|
||||
this.fabric_damagingEntity = null;
|
||||
this.fabric_breakCallback = null;
|
||||
original.call(instance, amount, random, serverPlayerEntity, runnable);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"FoxEntityMixin",
|
||||
"HungerManagerMixin",
|
||||
"ItemMixin",
|
||||
"ItemSettingsMixin",
|
||||
"ItemStackMixin",
|
||||
"LivingEntityMixin",
|
||||
"RecipeMixin",
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_1792": ["net/fabricmc/fabric/api/item/v1/FabricItem"],
|
||||
"net/minecraft/class_1792\u0024class_1793": ["net/fabricmc/fabric/api/item/v1/FabricItem\u0024Settings"],
|
||||
"net/minecraft/class_1799": ["net/fabricmc/fabric/api/item/v1/FabricItemStack"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import net.minecraft.util.Identifier;
|
|||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||
import net.fabricmc.fabric.api.registry.FabricBrewingRecipeRegistry;
|
||||
import net.fabricmc.fabric.api.registry.FuelRegistry;
|
||||
|
||||
|
@ -44,7 +43,7 @@ public class CustomDamageTest implements ModInitializer {
|
|||
FabricBrewingRecipeRegistry.registerPotionRecipe(Potions.WATER, Ingredient.ofItems(WEIRD_PICK), Potions.AWKWARD);
|
||||
}
|
||||
|
||||
public static final CustomDamageHandler WEIRD_DAMAGE_HANDLER = (stack, amount, entity, breakCallback) -> {
|
||||
public static final CustomDamageHandler WEIRD_DAMAGE_HANDLER = (stack, amount, entity, slot, breakCallback) -> {
|
||||
// If sneaking, apply all damage to vanilla. Otherwise, increment a tag on the stack by one and don't apply any damage
|
||||
if (entity.isSneaking()) {
|
||||
return amount;
|
||||
|
@ -57,7 +56,7 @@ public class CustomDamageTest implements ModInitializer {
|
|||
|
||||
public static class WeirdPick extends PickaxeItem {
|
||||
protected WeirdPick() {
|
||||
super(ToolMaterials.GOLD, 1, -2.8F, new FabricItemSettings().customDamage(WEIRD_DAMAGE_HANDLER));
|
||||
super(ToolMaterials.GOLD, 1, -2.8F, new Item.Settings().customDamage(WEIRD_DAMAGE_HANDLER));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ public class FabricItemSettingsTests implements ModInitializer {
|
|||
@Override
|
||||
public void onInitialize() {
|
||||
// Registers an item with a custom equipment slot.
|
||||
Item testItem = new Item(new FabricItemSettings().equipmentSlot(stack -> EquipmentSlot.CHEST));
|
||||
Item testItem = new Item(new Item.Settings().equipmentSlot(stack -> EquipmentSlot.CHEST));
|
||||
Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "test_item"), testItem);
|
||||
|
||||
final List<String> missingMethods = new ArrayList<>();
|
||||
|
|
|
@ -26,11 +26,10 @@ import net.minecraft.registry.Registry;
|
|||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||
|
||||
public final class FoodGameInitializer implements ModInitializer {
|
||||
public static final Item DAMAGE = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "damage_food"), new DamageFood(new FabricItemSettings().maxDamage(20)));
|
||||
public static final Item NAME = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "name_food"), new NameFood(new FabricItemSettings()));
|
||||
public static final Item DAMAGE = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "damage_food"), new DamageFood(new Item.Settings().maxDamage(20)));
|
||||
public static final Item NAME = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "name_food"), new NameFood(new Item.Settings()));
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
|
|
|
@ -19,94 +19,55 @@ package net.fabricmc.fabric.api.client.networking.v1;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.ServerCommonPacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientCommonNetworkHandlerAccessor;
|
||||
|
||||
/**
|
||||
* Offers access to configuration 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.
|
||||
* Packets <strong>received</strong> by this class must be registered to {@link
|
||||
* PayloadTypeRegistry#configurationS2C()} on both ends.
|
||||
* Packets <strong>sent</strong> by this class must be registered to {@link
|
||||
* PayloadTypeRegistry#configurationC2S()} on both ends.
|
||||
* Packets must be registered before registering any receivers.
|
||||
*
|
||||
* <p>This class should be only used on the physical client and for the logical client.
|
||||
*
|
||||
* <p>See {@link net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking} for information on how to use the packet
|
||||
* <p>See {@link ServerPlayNetworking} for information on how to use the packet
|
||||
* object-based API.
|
||||
*
|
||||
* @see ServerConfigurationNetworking
|
||||
*/
|
||||
public final class ClientConfigurationNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, access to game state
|
||||
* must be performed in the render thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
*
|
||||
* <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>For new code, {@link #registerGlobalReceiver(PacketType, ConfigurationPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param channelHandler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ClientConfigurationNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ClientConfigurationNetworking#registerReceiver(Identifier, ConfigurationChannelHandler)
|
||||
*/
|
||||
public static boolean registerGlobalReceiver(Identifier channelName, ConfigurationChannelHandler channelHandler) {
|
||||
return ClientNetworkingImpl.CONFIGURATION.registerGlobalReceiver(channelName, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterGlobalReceiver(PacketType)} to unregister the existing handler.
|
||||
* Use {@link #unregisterGlobalReceiver(CustomPayload.Id)} to unregister the existing handler.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param handler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ClientConfigurationNetworking#unregisterGlobalReceiver(PacketType)
|
||||
* @see ClientConfigurationNetworking#registerReceiver(PacketType, ConfigurationPacketHandler)
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#configurationS2C() registered} yet
|
||||
* @see ClientConfigurationNetworking#unregisterGlobalReceiver(CustomPayload.Id)
|
||||
* @see ClientConfigurationNetworking#registerReceiver(CustomPayload.Id, ConfigurationPayloadHandler)
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerGlobalReceiver(PacketType<T> type, ConfigurationPacketHandler<T> handler) {
|
||||
return ClientNetworkingImpl.CONFIGURATION.registerGlobalReceiver(type.getId(), wrapTyped(type, handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @see ClientConfigurationNetworking#registerGlobalReceiver(Identifier, ConfigurationChannelHandler)
|
||||
* @see ClientConfigurationNetworking#unregisterReceiver(Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static ClientConfigurationNetworking.ConfigurationChannelHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return unwrapUntyped(ClientNetworkingImpl.CONFIGURATION.unregisterGlobalReceiver(channelName));
|
||||
public static <T extends CustomPayload> boolean registerGlobalReceiver(CustomPayload.Id<T> type, ConfigurationPayloadHandler<T> handler) {
|
||||
return ClientNetworkingImpl.CONFIGURATION.registerGlobalReceiver(type.id(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,15 +76,15 @@ public final class ClientConfigurationNetworking {
|
|||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param id the packet id
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerGlobalReceiver(PacketType, ConfigurationPacketHandler)}
|
||||
* @see ClientConfigurationNetworking#registerGlobalReceiver(PacketType, ConfigurationPacketHandler)
|
||||
* @see ClientConfigurationNetworking#unregisterReceiver(PacketType)
|
||||
* or it was not registered using {@link #registerGlobalReceiver(CustomPayload.Id, ConfigurationPayloadHandler)}
|
||||
* @see ClientConfigurationNetworking#registerGlobalReceiver(CustomPayload.Id, ConfigurationPayloadHandler)
|
||||
* @see ClientConfigurationNetworking#unregisterReceiver(Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> ClientConfigurationNetworking.ConfigurationPacketHandler<T> unregisterGlobalReceiver(PacketType<T> type) {
|
||||
return unwrapTyped(ClientNetworkingImpl.CONFIGURATION.unregisterGlobalReceiver(type.getId()));
|
||||
public static ClientConfigurationNetworking.ConfigurationPayloadHandler<?> unregisterGlobalReceiver(CustomPayload.Id<?> id) {
|
||||
return ClientNetworkingImpl.CONFIGURATION.unregisterGlobalReceiver(id.id());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,93 +98,47 @@ public final class ClientConfigurationNetworking {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* Registers a handler for a packet type.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ClientLoginNetworking#registerGlobalReceiver(Identifier, ClientLoginNetworking.LoginQueryRequestHandler)}
|
||||
* login query has been received, you should use {@link ClientPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* <p>For new code, {@link #registerReceiver(PacketType, ConfigurationPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @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
|
||||
* @see ClientPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static boolean registerReceiver(Identifier channelName, ConfigurationChannelHandler channelHandler) {
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return addon.registerChannel(channelName, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot register receiver while not configuring!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(PacketType)} to unregister the existing handler.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ClientLoginNetworking#registerGlobalReceiver(Identifier, ClientLoginNetworking.LoginQueryRequestHandler)}
|
||||
* login query has been received, you should use {@link ClientPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param id the payload id
|
||||
* @param handler the handler
|
||||
* @return {@code false} if a handler is already registered for the type
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#configurationS2C() registered} yet
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
* @see ClientPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerReceiver(PacketType<T> type, ConfigurationPacketHandler<T> handler) {
|
||||
public static <T extends CustomPayload> boolean registerReceiver(CustomPayload.Id<T> id, ConfigurationPayloadHandler<T> handler) {
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return addon.registerChannel(type.getId(), wrapTyped(type, handler));
|
||||
return addon.registerChannel(id.id(), handler);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot register receiver while not configuring!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
*
|
||||
* <p>The {@code channelName} 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
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
@Nullable
|
||||
public static ClientConfigurationNetworking.ConfigurationChannelHandler unregisterReceiver(Identifier channelName) throws IllegalStateException {
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return unwrapUntyped(addon.unregisterChannel(channelName));
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot unregister receiver while not configuring!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param id the payload id to unregister
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerReceiver(PacketType, ConfigurationPacketHandler)}
|
||||
* or it was not registered using {@link #registerReceiver(CustomPayload.Id, ConfigurationPayloadHandler)}
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> ClientConfigurationNetworking.ConfigurationPacketHandler<T> unregisterReceiver(PacketType<T> type) {
|
||||
public static ClientConfigurationNetworking.ConfigurationPayloadHandler<?> unregisterReceiver(Identifier id) {
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return unwrapTyped(addon.unregisterChannel(type.getId()));
|
||||
return addon.unregisterChannel(id);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot unregister receiver while not configuring!");
|
||||
|
@ -285,22 +200,8 @@ public final class ClientConfigurationNetworking {
|
|||
* @param type the packet type
|
||||
* @return {@code true} if the connected server has declared the ability to receive a packet on the specified channel
|
||||
*/
|
||||
public static boolean canSend(PacketType<?> type) {
|
||||
return canSend(type.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to 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<ServerCommonPacketListener> createC2SPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(buf, "Buf cannot be null");
|
||||
|
||||
return ClientNetworkingImpl.createC2SPacket(channelName, buf);
|
||||
public static boolean canSend(CustomPayload.Id<?> type) {
|
||||
return canSend(type.id());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -322,35 +223,19 @@ public final class ClientConfigurationNetworking {
|
|||
/**
|
||||
* 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 {
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
addon.sendPacket(createC2SPacket(channelName, buf));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot send packet while not configuring!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to the connected server.
|
||||
* <p>Any packets sent must be {@linkplain PayloadTypeRegistry#configurationC2S() registered}.</p>
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param payload to be sent
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static <T extends FabricPacket> void send(T packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
public static void send(CustomPayload payload) {
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
final ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (addon != null) {
|
||||
addon.sendPacket(packet);
|
||||
addon.sendPacket(payload);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -360,98 +245,37 @@ public final class ClientConfigurationNetworking {
|
|||
private ClientConfigurationNetworking() {
|
||||
}
|
||||
|
||||
private static ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> wrapUntyped(ConfigurationChannelHandler actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(null, actualHandler, (client, handler, payload, responseSender) -> {
|
||||
actualHandler.receive(client, handler, ((UntypedPayload) payload).buffer(), responseSender);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends FabricPacket> ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> wrapTyped(PacketType<T> type, ConfigurationPacketHandler<T> actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(type, actualHandler, (client, handler, payload, responseSender) -> {
|
||||
T packet = (T) ((TypedPayload) payload).packet();
|
||||
|
||||
if (client.isOnThread()) {
|
||||
// Do not submit to the render thread if we're already running there.
|
||||
// Normally, packets are handled on the network IO thread - though it is
|
||||
// not guaranteed (for example, with 1.19.4 S2C packet bundling)
|
||||
// Since we're handling it right now, connection check is redundant.
|
||||
actualHandler.receive(packet, responseSender);
|
||||
} else {
|
||||
client.execute(() -> {
|
||||
if (((ClientCommonNetworkHandlerAccessor) handler).getConnection().isOpen()) {
|
||||
actualHandler.receive(packet, responseSender);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ConfigurationChannelHandler unwrapUntyped(@Nullable ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof ConfigurationChannelHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private static <T extends FabricPacket> ConfigurationPacketHandler<T> unwrapTyped(@Nullable ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof ConfigurationPacketHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationChannelHandler {
|
||||
/**
|
||||
* 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
|
||||
* ClientConfigurationNetworking.registerReceiver(new Identifier("mymod", "overlay"), (client, handler, buf, responseSender) -> {
|
||||
* String message = buf.readString(32767);
|
||||
*
|
||||
* // All operations on the server or world must be executed on the server thread
|
||||
* client.execute(() -> {
|
||||
* 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, ClientConfigurationNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender);
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread-safe packet handler utilizing {@link FabricPacket}.
|
||||
* A packet handler utilizing {@link CustomPayload}.
|
||||
* @param <T> the type of the packet
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationPacketHandler<T extends FabricPacket> {
|
||||
public interface ConfigurationPayloadHandler<T extends CustomPayload> {
|
||||
/**
|
||||
* Handles the incoming packet. This is called on the render thread, and can safely
|
||||
* call client methods.
|
||||
* Handles the incoming packet.
|
||||
*
|
||||
* <p>Unlike {@link ClientPlayNetworking.PlayPayloadHandler} this method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain ThreadExecutor#submit(Runnable) scheduled}.
|
||||
*
|
||||
* <p>An example usage of this is to display an overlay message:
|
||||
* <pre>{@code
|
||||
* // See FabricPacket for creating the packet
|
||||
* ClientConfigurationNetworking.registerReceiver(OVERLAY_PACKET_TYPE, (player, packet, responseSender) -> {
|
||||
* MinecraftClient.getInstance().inGameHud.setOverlayMessage(packet.message(), true);
|
||||
* ClientConfigurationNetworking.registerReceiver(OVERLAY_PACKET_TYPE, (packet, responseSender) -> {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param responseSender the packet sender
|
||||
* @see FabricPacket
|
||||
* @param payload the packet payload
|
||||
* @param context the configuration networking context
|
||||
* @see CustomPayload
|
||||
*/
|
||||
void receive(T packet, PacketSender responseSender);
|
||||
void receive(T payload, Context context);
|
||||
}
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public interface Context {
|
||||
/**
|
||||
* @return The packet sender
|
||||
*/
|
||||
PacketSender responseSender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,14 +20,13 @@ 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.PacketCallbacks;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
|
@ -153,10 +152,10 @@ public final class ClientLoginNetworking {
|
|||
* @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
|
||||
* @param callbacksConsumer 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);
|
||||
CompletableFuture<@Nullable PacketByteBuf> receive(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer<PacketCallbacks> callbacksConsumer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,36 +19,34 @@ package net.fabricmc.fabric.api.client.networking.v1;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.ServerCommonPacketListener;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientPlayNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Packets <strong>received</strong> by this class must be registered to {@link PayloadTypeRegistry#playS2C()} on both ends.
|
||||
* Packets <strong>sent</strong> by this class must be registered to {@link PayloadTypeRegistry#playC2S()} on both ends.
|
||||
* Packets must be registered before registering any receivers.
|
||||
*
|
||||
* <p>This class should be only used on the physical client and for the logical client.
|
||||
*
|
||||
* <p>See {@link ServerPlayNetworking} for information on how to use the packet
|
||||
* <p>See {@link ServerPlayNetworking} for information on how to use the payload
|
||||
* object-based API.
|
||||
*
|
||||
* @see ClientLoginNetworking
|
||||
|
@ -57,76 +55,38 @@ import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
|||
*/
|
||||
public final class ClientPlayNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, access to game state
|
||||
* must be performed in the render thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
*
|
||||
* <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>For new code, {@link #registerGlobalReceiver(PacketType, PlayPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @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, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* Registers a handler for a payload type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterGlobalReceiver(PacketType)} to unregister the existing handler.
|
||||
* Use {@link #unregisterGlobalReceiver(Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param type the payload type
|
||||
* @param handler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ClientPlayNetworking#unregisterGlobalReceiver(PacketType)
|
||||
* @see ClientPlayNetworking#registerReceiver(PacketType, PlayPacketHandler)
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#playS2C() registered} yet
|
||||
* @see ClientPlayNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ClientPlayNetworking#registerReceiver(CustomPayload.Id, PlayPayloadHandler)
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerGlobalReceiver(PacketType<T> type, PlayPacketHandler<T> handler) {
|
||||
return ClientNetworkingImpl.PLAY.registerGlobalReceiver(type.getId(), wrapTyped(type, handler));
|
||||
public static <T extends CustomPayload> boolean registerGlobalReceiver(CustomPayload.Id<T> type, PlayPayloadHandler<T> handler) {
|
||||
return ClientNetworkingImpl.PLAY.registerGlobalReceiver(type.id(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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 unwrapUntyped(ClientNetworkingImpl.PLAY.unregisterGlobalReceiver(channelName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
* Removes the handler for a payload type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param id the payload id
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerGlobalReceiver(PacketType, PlayPacketHandler)}
|
||||
* @see ClientPlayNetworking#registerGlobalReceiver(PacketType, PlayPacketHandler)
|
||||
* @see ClientPlayNetworking#unregisterReceiver(PacketType)
|
||||
* or it was not registered using {@link #registerGlobalReceiver(CustomPayload.Id, PlayPayloadHandler)}
|
||||
* @see ClientPlayNetworking#registerGlobalReceiver(CustomPayload.Id, PlayPayloadHandler)
|
||||
* @see ClientPlayNetworking#unregisterReceiver(Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> PlayPacketHandler<T> unregisterGlobalReceiver(PacketType<T> type) {
|
||||
return unwrapTyped(ClientNetworkingImpl.PLAY.unregisterGlobalReceiver(type.getId()));
|
||||
public static ClientPlayNetworking.PlayPayloadHandler<?> unregisterGlobalReceiver(Identifier id) {
|
||||
return ClientNetworkingImpl.PLAY.unregisterGlobalReceiver(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,93 +100,47 @@ public final class ClientPlayNetworking {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* Registers a handler for a payload type.
|
||||
*
|
||||
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ClientLoginNetworking#registerGlobalReceiver(Identifier, ClientLoginNetworking.LoginQueryRequestHandler)}
|
||||
* login query has been received, you should use {@link ClientPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* <p>For new code, {@link #registerReceiver(PacketType, PlayPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @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
|
||||
* @see ClientPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static boolean registerReceiver(Identifier channelName, PlayChannelHandler channelHandler) {
|
||||
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return addon.registerChannel(channelName, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot register receiver while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(PacketType)} to unregister the existing handler.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ClientLoginNetworking#registerGlobalReceiver(Identifier, ClientLoginNetworking.LoginQueryRequestHandler)}
|
||||
* login query has been received, you should use {@link ClientPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param type the payload type
|
||||
* @param handler the handler
|
||||
* @return {@code false} if a handler is already registered for the type
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#playS2C() registered} yet
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
* @see ClientPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerReceiver(PacketType<T> type, PlayPacketHandler<T> handler) {
|
||||
public static <T extends CustomPayload> boolean registerReceiver(CustomPayload.Id<T> type, PlayPayloadHandler<T> handler) {
|
||||
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return addon.registerChannel(type.getId(), wrapTyped(type, handler));
|
||||
return addon.registerChannel(type.id(), handler);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot register receiver while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
*
|
||||
* <p>The {@code channelName} 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
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterReceiver(Identifier channelName) throws IllegalStateException {
|
||||
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return unwrapUntyped(addon.unregisterChannel(channelName));
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot unregister receiver while not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
* Removes the handler for a payload id.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param id the payload id
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerReceiver(PacketType, PlayPacketHandler)}
|
||||
* or it was not registered using {@link #registerReceiver(CustomPayload.Id, PlayPayloadHandler)}
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> PlayPacketHandler<T> unregisterReceiver(PacketType<T> type) {
|
||||
public static ClientPlayNetworking.PlayPayloadHandler<?> unregisterReceiver(Identifier id) {
|
||||
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
|
||||
|
||||
if (addon != null) {
|
||||
return unwrapTyped(addon.unregisterChannel(type.getId()));
|
||||
return addon.unregisterChannel(id);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot unregister receiver while not in game!");
|
||||
|
@ -265,10 +179,10 @@ public final class ClientPlayNetworking {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if the connected server declared the ability to receive a packet on a specified channel name.
|
||||
* Checks if the connected server declared the ability to receive a payload on a specified channel name.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @return {@code true} if the connected server has declared the ability to receive a packet on the specified channel.
|
||||
* @return {@code true} if the connected server has declared the ability to receive a payload on the specified channel.
|
||||
* False if the client is not in game.
|
||||
*/
|
||||
public static boolean canSend(Identifier channelName) throws IllegalArgumentException {
|
||||
|
@ -281,44 +195,30 @@ public final class ClientPlayNetworking {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if the connected server declared the ability to receive a packet on a specified channel name.
|
||||
* Checks if the connected server declared the ability to receive a payload on a specified channel name.
|
||||
* This returns {@code false} if the client is not in game.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @return {@code true} if the connected server has declared the ability to receive a packet on the specified channel
|
||||
* @param type the payload type
|
||||
* @return {@code true} if the connected server has declared the ability to receive a payload on the specified channel
|
||||
*/
|
||||
public static boolean canSend(PacketType<?> type) {
|
||||
return canSend(type.getId());
|
||||
public static boolean canSend(CustomPayload.Id<?> type) {
|
||||
return canSend(type.id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to the connected server.
|
||||
* Creates a payload which may be sent to 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
|
||||
* @param packet the fabric payload
|
||||
* @return a new payload
|
||||
*/
|
||||
public static Packet<ServerCommonPacketListener> createC2SPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(buf, "Buf cannot be null");
|
||||
|
||||
return ClientNetworkingImpl.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to the connected server.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static <T extends FabricPacket> Packet<ServerCommonPacketListener> createC2SPacket(T packet) {
|
||||
public static <T extends CustomPayload> Packet<ServerCommonPacketListener> createC2SPacket(T packet) {
|
||||
return ClientNetworkingImpl.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet sender which sends packets to the connected server.
|
||||
* Gets the payload sender which sends packets to the connected server.
|
||||
*
|
||||
* @return the client's packet sender
|
||||
* @return the client's payload sender
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static PacketSender getSender() throws IllegalStateException {
|
||||
|
@ -327,39 +227,24 @@ public final class ClientPlayNetworking {
|
|||
return ClientNetworkingImpl.getAddon(MinecraftClient.getInstance().getNetworkHandler());
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot get packet sender when not in game!");
|
||||
throw new IllegalStateException("Cannot get payload sender when not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to the connected server.
|
||||
* Sends a payload to the connected server.
|
||||
*
|
||||
* @param channelName the channel of the packet
|
||||
* @param buf the payload of the packet
|
||||
* <p>Any packets sent must be {@linkplain PayloadTypeRegistry#playC2S() registered}.</p>
|
||||
*
|
||||
* @param payload the payload
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static void send(Identifier channelName, PacketByteBuf buf) throws IllegalStateException {
|
||||
// You cant send without a client player, so this is fine
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
MinecraftClient.getInstance().getNetworkHandler().sendPacket(createC2SPacket(channelName, buf));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot send packets when not in game!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to the connected server.
|
||||
*
|
||||
* @param packet the packet
|
||||
* @throws IllegalStateException if the client is not connected to a server
|
||||
*/
|
||||
public static <T extends FabricPacket> void send(T packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
public static void send(CustomPayload payload) {
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
// You cant send without a client player, so this is fine
|
||||
if (MinecraftClient.getInstance().getNetworkHandler() != null) {
|
||||
MinecraftClient.getInstance().getNetworkHandler().sendPacket(createC2SPacket(packet));
|
||||
MinecraftClient.getInstance().getNetworkHandler().sendPacket(createC2SPacket(payload));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -369,98 +254,48 @@ public final class ClientPlayNetworking {
|
|||
private ClientPlayNetworking() {
|
||||
}
|
||||
|
||||
private static ResolvablePayload.Handler<ClientPlayNetworkAddon.Handler> wrapUntyped(PlayChannelHandler actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(null, actualHandler, (client, handler, payload, responseSender) -> {
|
||||
actualHandler.receive(client, handler, ((UntypedPayload) payload).buffer(), responseSender);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends FabricPacket> ResolvablePayload.Handler<ClientPlayNetworkAddon.Handler> wrapTyped(PacketType<T> type, PlayPacketHandler<T> actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(type, actualHandler, (client, handler, payload, responseSender) -> {
|
||||
T packet = (T) ((TypedPayload) payload).packet();
|
||||
|
||||
if (client.isOnThread()) {
|
||||
// Do not submit to the render thread if we're already running there.
|
||||
// Normally, packets are handled on the network IO thread - though it is
|
||||
// not guaranteed (for example, with 1.19.4 S2C packet bundling)
|
||||
// Since we're handling it right now, connection check is redundant.
|
||||
actualHandler.receive(packet, client.player, responseSender);
|
||||
} else {
|
||||
client.execute(() -> {
|
||||
if (handler.getConnection().isOpen()) actualHandler.receive(packet, client.player, responseSender);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PlayChannelHandler unwrapUntyped(@Nullable ResolvablePayload.Handler<ClientPlayNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof PlayChannelHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private static <T extends FabricPacket> PlayPacketHandler<T> unwrapTyped(@Nullable ResolvablePayload.Handler<ClientPlayNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof PlayPacketHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@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) -> {
|
||||
* String message = buf.readString(32767);
|
||||
*
|
||||
* // All operations on the server or world must be executed on the server thread
|
||||
* client.execute(() -> {
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread-safe packet handler utilizing {@link FabricPacket}.
|
||||
* @param <T> the type of the packet
|
||||
* A thread-safe payload handler utilizing {@link CustomPayload}.
|
||||
* @param <T> the type of the payload
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PlayPacketHandler<T extends FabricPacket> {
|
||||
public interface PlayPayloadHandler<T extends CustomPayload> {
|
||||
/**
|
||||
* Handles the incoming packet. This is called on the render thread, and can safely
|
||||
* Handles the incoming payload. This is called on the render thread, and can safely
|
||||
* call client methods.
|
||||
*
|
||||
* <p>An example usage of this is to display an overlay message:
|
||||
* <pre>{@code
|
||||
* // See FabricPacket for creating the packet
|
||||
* ClientPlayNetworking.registerReceiver(OVERLAY_PACKET_TYPE, (player, packet, responseSender) -> {
|
||||
* MinecraftClient.getInstance().inGameHud.setOverlayMessage(packet.message(), true);
|
||||
* // See FabricPacket for creating the payload
|
||||
* ClientPlayNetworking.registerReceiver(OVERLAY_PACKET_TYPE, (player, payload, responseSender) -> {
|
||||
* MinecraftClient.getInstance().inGameHud.setOverlayMessage(payload.message(), true);
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The network handler can be accessed via {@link ClientPlayerEntity#networkHandler}.
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param player the player that received the packet
|
||||
* @param responseSender the packet sender
|
||||
* @see FabricPacket
|
||||
* @param payload the packet payload
|
||||
* @param context the play networking context
|
||||
* @see CustomPayload
|
||||
*/
|
||||
void receive(T packet, ClientPlayerEntity player, PacketSender responseSender);
|
||||
void receive(T payload, Context context);
|
||||
}
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public interface Context {
|
||||
/**
|
||||
* @return The MinecraftClient instance
|
||||
*/
|
||||
MinecraftClient client();
|
||||
|
||||
/**
|
||||
* @return The player that received the payload
|
||||
*/
|
||||
ClientPlayerEntity player();
|
||||
|
||||
/**
|
||||
* @return The packet sender
|
||||
*/
|
||||
PacketSender responseSender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,36 +21,37 @@ import java.util.List;
|
|||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.C2SConfigurationChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractChanneledNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.RegistrationPayload;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientCommonNetworkHandlerAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientConfigurationNetworkHandlerAccessor;
|
||||
|
||||
public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetworkAddon<ClientConfigurationNetworkAddon.Handler> {
|
||||
public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetworkAddon<ClientConfigurationNetworking.ConfigurationPayloadHandler<?>> {
|
||||
private final ClientConfigurationNetworkHandler handler;
|
||||
private final MinecraftClient client;
|
||||
private final ContextImpl context;
|
||||
private boolean sentInitialRegisterPacket;
|
||||
|
||||
public ClientConfigurationNetworkAddon(ClientConfigurationNetworkHandler handler, MinecraftClient client) {
|
||||
super(ClientNetworkingImpl.CONFIGURATION, ((ClientCommonNetworkHandlerAccessor) handler).getConnection(), "ClientPlayNetworkAddon for " + ((ClientConfigurationNetworkHandlerAccessor) handler).getProfile().getName());
|
||||
this.handler = handler;
|
||||
this.client = client;
|
||||
this.context = new ContextImpl(this);
|
||||
|
||||
// Must register pending channels via lateinit
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkState.CONFIGURATION);
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkPhase.CONFIGURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,8 +64,8 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void receiveRegistration(boolean register, ResolvablePayload resolvable) {
|
||||
super.receiveRegistration(register, resolvable);
|
||||
protected void receiveRegistration(boolean register, RegistrationPayload payload) {
|
||||
super.receiveRegistration(register, payload);
|
||||
|
||||
if (register && !this.sentInitialRegisterPacket) {
|
||||
this.sendInitialChannelRegistrationPacket();
|
||||
|
@ -73,8 +74,8 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void receive(Handler handler, ResolvedPayload payload) {
|
||||
handler.receive(this.client, this.handler, payload, this);
|
||||
protected void receive(ClientConfigurationNetworking.ConfigurationPayloadHandler<?> handler, CustomPayload payload) {
|
||||
((ClientConfigurationNetworking.ConfigurationPayloadHandler) handler).receive(payload, this.context);
|
||||
}
|
||||
|
||||
// impl details
|
||||
|
@ -85,12 +86,7 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return ClientPlayNetworking.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
public Packet<?> createPacket(CustomPayload packet) {
|
||||
return ClientPlayNetworking.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
|
@ -108,10 +104,10 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
protected void handleRegistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the register packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
final RegistrationPayload payload = this.createRegistrationPayload(RegistrationPayload.REGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
if (payload != null) {
|
||||
this.sendPacket(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +116,10 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
final RegistrationPayload payload = this.createRegistrationPayload(RegistrationPayload.UNREGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
|
||||
if (payload != null) {
|
||||
this.sendPacket(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +143,6 @@ public final class ClientConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
return (ChannelInfoHolder) ((ClientCommonNetworkHandlerAccessor) handler).getConnection();
|
||||
}
|
||||
|
||||
public interface Handler {
|
||||
void receive(MinecraftClient client, ClientConfigurationNetworkHandler handler, ResolvedPayload payload, PacketSender responseSender);
|
||||
private record ContextImpl(PacketSender responseSender) implements ClientConfigurationNetworking.Context {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,23 +20,20 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
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.PacketCallbacks;
|
||||
import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
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.impl.networking.GenericFutureListenerHolder;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufLoginQueryRequestPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufLoginQueryResponse;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ClientLoginNetworkHandlerAccessor;
|
||||
|
@ -77,19 +74,18 @@ public final class ClientLoginNetworkAddon extends AbstractNetworkAddon<ClientLo
|
|||
}
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.slice(originalBuf);
|
||||
List<GenericFutureListener<? extends Future<? super Void>>> futureListeners = new ArrayList<>();
|
||||
List<PacketCallbacks> callbacks = new ArrayList<>();
|
||||
|
||||
try {
|
||||
CompletableFuture<@Nullable PacketByteBuf> future = handler.receive(this.client, this.handler, buf, futureListeners::add);
|
||||
CompletableFuture<@Nullable PacketByteBuf> future = handler.receive(this.client, this.handler, buf, callbacks::add);
|
||||
future.thenAccept(result -> {
|
||||
LoginQueryResponseC2SPacket packet = new LoginQueryResponseC2SPacket(queryId, result == null ? null : new PacketByteBufLoginQueryResponse(result));
|
||||
GenericFutureListener<? extends Future<? super Void>> listener = null;
|
||||
|
||||
for (GenericFutureListener<? extends Future<? super Void>> each : futureListeners) {
|
||||
listener = FutureListeners.union(listener, each);
|
||||
}
|
||||
|
||||
((ClientLoginNetworkHandlerAccessor) this.handler).getConnection().send(packet, GenericFutureListenerHolder.create(listener));
|
||||
((ClientLoginNetworkHandlerAccessor) this.handler).getConnection().send(packet, new PacketCallbacks() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
callbacks.forEach(PacketCallbacks::onSuccess);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (Throwable ex) {
|
||||
this.logger.error("Encountered exception while handling in channel with name \"{}\"", channelName, ex);
|
||||
|
|
|
@ -26,19 +26,18 @@ import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
|||
import net.minecraft.client.network.ClientLoginNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.network.listener.ServerCommonPacketListener;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.impl.networking.CommonPacketsImpl;
|
||||
import net.fabricmc.fabric.impl.networking.CommonRegisterPayload;
|
||||
|
@ -46,17 +45,14 @@ import net.fabricmc.fabric.impl.networking.CommonVersionPayload;
|
|||
import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.PayloadTypeRegistryImpl;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.ConnectScreenAccessor;
|
||||
import net.fabricmc.fabric.mixin.networking.client.accessor.MinecraftClientAccessor;
|
||||
|
||||
public final class ClientNetworkingImpl {
|
||||
public static final GlobalReceiverRegistry<ClientLoginNetworking.LoginQueryRequestHandler> LOGIN = new GlobalReceiverRegistry<>(NetworkState.LOGIN);
|
||||
public static final GlobalReceiverRegistry<ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler>> CONFIGURATION = new GlobalReceiverRegistry<>(NetworkState.CONFIGURATION);
|
||||
public static final GlobalReceiverRegistry<ResolvablePayload.Handler<ClientPlayNetworkAddon.Handler>> PLAY = new GlobalReceiverRegistry<>(NetworkState.PLAY);
|
||||
public static final GlobalReceiverRegistry<ClientLoginNetworking.LoginQueryRequestHandler> LOGIN = new GlobalReceiverRegistry<>(NetworkSide.CLIENTBOUND, NetworkPhase.LOGIN, null);
|
||||
public static final GlobalReceiverRegistry<ClientConfigurationNetworking.ConfigurationPayloadHandler<?>> CONFIGURATION = new GlobalReceiverRegistry<>(NetworkSide.CLIENTBOUND, NetworkPhase.CONFIGURATION, PayloadTypeRegistryImpl.CONFIGURATION_S2C);
|
||||
public static final GlobalReceiverRegistry<ClientPlayNetworking.PlayPayloadHandler<?>> PLAY = new GlobalReceiverRegistry<>(NetworkSide.CLIENTBOUND, NetworkPhase.PLAY, PayloadTypeRegistryImpl.PLAY_S2C);
|
||||
|
||||
private static ClientPlayNetworkAddon currentPlayAddon;
|
||||
private static ClientConfigurationNetworkAddon currentConfigurationAddon;
|
||||
|
@ -73,22 +69,15 @@ public final class ClientNetworkingImpl {
|
|||
return (ClientLoginNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static Packet<ServerCommonPacketListener> createC2SPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return new CustomPayloadC2SPacket(new UntypedPayload(channelName, buf));
|
||||
}
|
||||
|
||||
public static Packet<ServerCommonPacketListener> createC2SPacket(FabricPacket packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
ResolvedPayload payload = new TypedPayload(packet);
|
||||
if (NetworkingImpl.FORCE_PACKET_SERIALIZATION) payload = payload.resolve(null);
|
||||
public static Packet<ServerCommonPacketListener> createC2SPacket(CustomPayload payload) {
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
return new CustomPayloadC2SPacket(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way logging into a integrated or remote dedicated server will differ, we need to obtain the login client connection differently.
|
||||
* Due to the way logging into an integrated or remote dedicated server will differ, we need to obtain the login client connection differently.
|
||||
*/
|
||||
@Nullable
|
||||
public static ClientConnection getLoginConnection() {
|
||||
|
@ -153,28 +142,26 @@ public final class ClientNetworkingImpl {
|
|||
});
|
||||
|
||||
// Version packet
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(CommonVersionPayload.PACKET_ID, (client, handler, buf, responseSender) -> {
|
||||
var payload = new CommonVersionPayload(buf);
|
||||
int negotiatedVersion = handleVersionPacket(payload, responseSender);
|
||||
ClientNetworkingImpl.getAddon(handler).onCommonVersionPacket(negotiatedVersion);
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(CommonVersionPayload.ID, (payload, context) -> {
|
||||
int negotiatedVersion = handleVersionPacket(payload, context.responseSender());
|
||||
ClientNetworkingImpl.getClientConfigurationAddon().onCommonVersionPacket(negotiatedVersion);
|
||||
});
|
||||
|
||||
// Register packet
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(CommonRegisterPayload.PACKET_ID, (client, handler, buf, responseSender) -> {
|
||||
var payload = new CommonRegisterPayload(buf);
|
||||
ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getAddon(handler);
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(CommonRegisterPayload.ID, (payload, context) -> {
|
||||
ClientConfigurationNetworkAddon addon = ClientNetworkingImpl.getClientConfigurationAddon();
|
||||
|
||||
if (CommonRegisterPayload.PLAY_PHASE.equals(payload.phase())) {
|
||||
if (payload.version() != addon.getNegotiatedVersion()) {
|
||||
throw new IllegalStateException("Negotiated common packet version: %d but received packet with version: %d".formatted(addon.getNegotiatedVersion(), payload.version()));
|
||||
}
|
||||
|
||||
addon.getChannelInfoHolder().getPendingChannelsNames(NetworkState.PLAY).addAll(payload.channels());
|
||||
addon.getChannelInfoHolder().fabric_getPendingChannelsNames(NetworkPhase.PLAY).addAll(payload.channels());
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the server");
|
||||
responseSender.sendPacket(new CommonRegisterPayload(addon.getNegotiatedVersion(), CommonRegisterPayload.PLAY_PHASE, ClientPlayNetworking.getGlobalReceivers()));
|
||||
context.responseSender().sendPacket(new CommonRegisterPayload(addon.getNegotiatedVersion(), CommonRegisterPayload.PLAY_PHASE, ClientPlayNetworking.getGlobalReceivers()));
|
||||
} else {
|
||||
addon.onCommonRegisterPacket(payload);
|
||||
responseSender.sendPacket(addon.createRegisterPayload());
|
||||
context.responseSender().sendPacket(addon.createRegisterPayload());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -24,24 +24,25 @@ import org.slf4j.Logger;
|
|||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.C2SPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractChanneledNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.RegistrationPayload;
|
||||
|
||||
public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<ClientPlayNetworkAddon.Handler> {
|
||||
public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<ClientPlayNetworking.PlayPayloadHandler<?>> {
|
||||
private final ClientPlayNetworkHandler handler;
|
||||
private final MinecraftClient client;
|
||||
private final ClientPlayNetworking.Context context;
|
||||
private boolean sentInitialRegisterPacket;
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
@ -50,9 +51,10 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
super(ClientNetworkingImpl.PLAY, handler.getConnection(), "ClientPlayNetworkAddon for " + handler.getProfile().getName());
|
||||
this.handler = handler;
|
||||
this.client = client;
|
||||
this.context = new ContextImpl(client, client.player, this);
|
||||
|
||||
// Must register pending channels via lateinit
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkState.PLAY);
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkPhase.PLAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,8 +75,10 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void receive(Handler handler, ResolvedPayload payload) {
|
||||
handler.receive(this.client, this.handler, payload, this);
|
||||
protected void receive(ClientPlayNetworking.PlayPayloadHandler<?> handler, CustomPayload payload) {
|
||||
this.client.execute(() -> {
|
||||
((ClientPlayNetworking.PlayPayloadHandler) handler).receive(payload, context);
|
||||
});
|
||||
}
|
||||
|
||||
// impl details
|
||||
|
@ -85,12 +89,7 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return ClientPlayNetworking.createC2SPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
public Packet<?> createPacket(CustomPayload packet) {
|
||||
return ClientPlayNetworking.createC2SPacket(packet);
|
||||
}
|
||||
|
||||
|
@ -108,10 +107,10 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
protected void handleRegistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the register packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
final RegistrationPayload payload = this.createRegistrationPayload(RegistrationPayload.REGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
if (payload != null) {
|
||||
this.sendPacket(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +119,10 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
final RegistrationPayload payload = this.createRegistrationPayload(RegistrationPayload.UNREGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
|
||||
if (payload != null) {
|
||||
this.sendPacket(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +137,6 @@ public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
return NetworkingImpl.isReservedCommonChannel(channelName);
|
||||
}
|
||||
|
||||
public interface Handler {
|
||||
void receive(MinecraftClient client, ClientPlayNetworkHandler handler, ResolvedPayload payload, PacketSender responseSender);
|
||||
private record ContextImpl(MinecraftClient client, ClientPlayerEntity player, PacketSender responseSender) implements ClientPlayNetworking.Context {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,12 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.network.ClientCommonNetworkHandler;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientPlayNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.RetainedPayload;
|
||||
|
||||
@Mixin(ClientCommonNetworkHandler.class)
|
||||
public abstract class ClientCommonNetworkHandlerMixin implements NetworkHandlerExtensions {
|
||||
|
@ -41,25 +40,18 @@ public abstract class ClientCommonNetworkHandlerMixin implements NetworkHandlerE
|
|||
|
||||
@Inject(method = "onCustomPayload(Lnet/minecraft/network/packet/s2c/common/CustomPayloadS2CPacket;)V", at = @At("HEAD"), cancellable = true)
|
||||
public void onCustomPayload(CustomPayloadS2CPacket packet, CallbackInfo ci) {
|
||||
if (packet.payload() instanceof ResolvablePayload payload) {
|
||||
boolean handled;
|
||||
final CustomPayload payload = packet.payload();
|
||||
boolean handled;
|
||||
|
||||
if (this.getAddon() instanceof ClientPlayNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else if (this.getAddon() instanceof ClientConfigurationNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown network addon");
|
||||
}
|
||||
|
||||
if (!handled && payload instanceof RetainedPayload retained && retained.buf().refCnt() > 0) {
|
||||
// Duplicate the vanilla log message, as we cancel further processing.
|
||||
LOGGER.warn("Unknown custom packet payload: {}", payload.id());
|
||||
|
||||
retained.buf().skipBytes(retained.buf().readableBytes());
|
||||
retained.buf().release();
|
||||
}
|
||||
if (this.getAddon() instanceof ClientPlayNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else if (this.getAddon() instanceof ClientConfigurationNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown network addon");
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public abstract class ClientConfigurationNetworkHandlerMixin extends ClientCommo
|
|||
this.addon.lateInit();
|
||||
}
|
||||
|
||||
@Inject(method = "onReady", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;setPacketListener(Lnet/minecraft/network/listener/PacketListener;)V", shift = At.Shift.BEFORE))
|
||||
@Inject(method = "onReady", at = @At(value = "NEW", target = "(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/network/ClientConnection;Lnet/minecraft/client/network/ClientConnectionState;)Lnet/minecraft/client/network/ClientPlayNetworkHandler;"))
|
||||
public void onReady(ReadyS2CPacket packet, CallbackInfo ci) {
|
||||
this.addon.handleReady();
|
||||
}
|
||||
|
|
|
@ -1,78 +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.api.networking.v1;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
/**
|
||||
* A packet to be sent using Networking API. An instance of this class is created
|
||||
* each time the packet is sent. This can be used on both the client and the server.
|
||||
*
|
||||
* <p>Implementations should have fields of values sent over the network.
|
||||
* For example, a packet consisting of two integers should have two {@code int}
|
||||
* fields with appropriate name. This is written to the buffer in {@link #write}.
|
||||
* The packet should have two constructors: one that creates a packet on the sender,
|
||||
* which initializes the fields to be written, and one that takes a {@link PacketByteBuf}
|
||||
* and reads the packet.
|
||||
*
|
||||
* <p>For each packet class, a corresponding {@link PacketType} instance should be created.
|
||||
* The type should be stored in a {@code static final} field, and {@link #getType} should
|
||||
* return that type.
|
||||
*
|
||||
* <p>Example of a packet:
|
||||
* <pre>{@code
|
||||
* public record BoomPacket(boolean fire) implements FabricPacket {
|
||||
* public static final PacketType<BoomPacket> TYPE = PacketType.create(new Identifier("example:boom"), BoomPacket::new);
|
||||
*
|
||||
* public BoomPacket(PacketByteBuf buf) {
|
||||
* this(buf.readBoolean());
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void write(PacketByteBuf buf) {
|
||||
* buf.writeBoolean(this.fire);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public PacketType<?> getType() {
|
||||
* return TYPE;
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @see ServerPlayNetworking#registerGlobalReceiver(PacketType, ServerPlayNetworking.PlayPacketHandler)
|
||||
* @see ServerPlayNetworking#send(ServerPlayerEntity, PacketType, FabricPacket)
|
||||
* @see PacketSender#sendPacket(FabricPacket)
|
||||
*/
|
||||
public interface FabricPacket {
|
||||
/**
|
||||
* Writes the contents of this packet to the buffer.
|
||||
* @param buf the output buffer
|
||||
*/
|
||||
void write(PacketByteBuf buf);
|
||||
|
||||
/**
|
||||
* Returns the packet type of this packet.
|
||||
*
|
||||
* <p>Implementations should store the packet type instance in a {@code static final}
|
||||
* field and return that here, instead of creating a new instance.
|
||||
*
|
||||
* @return the type of this packet
|
||||
*/
|
||||
PacketType<?> getType();
|
||||
}
|
|
@ -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.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,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.Objects;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Represents something that supports sending packets to login channels.
|
||||
* @see PacketSender
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface LoginPacketSender extends PacketSender {
|
||||
/**
|
||||
* Creates a packet for sending to a login channel.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param buf the content of the packet
|
||||
* @return the created packet
|
||||
*/
|
||||
Packet<?> createPacket(Identifier channelName, PacketByteBuf buf);
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
default void sendPacket(Identifier channel, PacketByteBuf buf, @Nullable PacketCallbacks callback) {
|
||||
Objects.requireNonNull(channel, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Payload cannot be null");
|
||||
|
||||
this.sendPacket(this.createPacket(channel, buf), callback);
|
||||
}
|
||||
}
|
|
@ -16,42 +16,26 @@
|
|||
|
||||
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.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.GenericFutureListenerHolder;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
/**
|
||||
* Represents something that supports sending packets to channels.
|
||||
* @see PacketByteBufs
|
||||
* Any packets sent must be {@linkplain PayloadTypeRegistry registered} in the appropriate registry.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface PacketSender {
|
||||
/**
|
||||
* Makes a packet for a channel.
|
||||
* Creates a packet from a packet payload.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param buf the content of the packet
|
||||
* @param payload the packet payload
|
||||
*/
|
||||
Packet<?> createPacket(Identifier channelName, PacketByteBuf buf);
|
||||
|
||||
/**
|
||||
* Makes a packet for a fabric packet.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
*/
|
||||
Packet<?> createPacket(FabricPacket packet);
|
||||
Packet<?> createPacket(CustomPayload payload);
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
|
@ -59,15 +43,7 @@ public interface PacketSender {
|
|||
* @param packet the packet
|
||||
*/
|
||||
default void sendPacket(Packet<?> packet) {
|
||||
sendPacket(packet, (PacketCallbacks) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
* @param packet the packet
|
||||
*/
|
||||
default <T extends FabricPacket> void sendPacket(T packet) {
|
||||
sendPacket(createPacket(packet));
|
||||
sendPacket(packet, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,107 +51,30 @@ public interface PacketSender {
|
|||
* @param payload the payload
|
||||
*/
|
||||
default void sendPacket(CustomPayload payload) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
payload.write(buf);
|
||||
sendPacket(payload.id(), buf);
|
||||
sendPacket(createPacket(payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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}.
|
||||
*/
|
||||
default <T extends FabricPacket> void sendPacket(T packet, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
sendPacket(createPacket(packet), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
*/
|
||||
default void sendPacket(CustomPayload payload, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
payload.write(buf);
|
||||
sendPacket(payload.id(), buf, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}.
|
||||
*/
|
||||
void sendPacket(Packet<?> packet, @Nullable PacketCallbacks callback);
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
default <T extends FabricPacket> void sendPacket(T packet, @Nullable PacketCallbacks callback) {
|
||||
sendPacket(createPacket(packet), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param payload the payload
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
|
||||
* @param callback an optional callback to execute after the packet is sent, may be {@code null}.
|
||||
*/
|
||||
default void sendPacket(CustomPayload payload, @Nullable PacketCallbacks callback) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
payload.write(buf);
|
||||
sendPacket(payload.id(), buf, callback);
|
||||
sendPacket(createPacket(payload), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to a channel.
|
||||
*
|
||||
* @param channel the id of the channel
|
||||
* @param buf the content of the packet
|
||||
* Disconnects the player.
|
||||
* @param disconnectReason the reason for disconnection
|
||||
*/
|
||||
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) {
|
||||
sendPacket(channel, buf, GenericFutureListenerHolder.create(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
default void sendPacket(Identifier channel, PacketByteBuf buf, @Nullable PacketCallbacks callback) {
|
||||
Objects.requireNonNull(channel, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Payload cannot be null");
|
||||
|
||||
this.sendPacket(this.createPacket(channel, buf), callback);
|
||||
}
|
||||
void disconnect(Text disconnectReason);
|
||||
}
|
||||
|
|
|
@ -1,71 +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.api.networking.v1;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* A type of packet. An instance of this should be created per a {@link FabricPacket} implementation.
|
||||
* This holds the channel ID used for the packet.
|
||||
*
|
||||
* @param <T> the type of the packet
|
||||
* @see FabricPacket
|
||||
*/
|
||||
public final class PacketType<T extends FabricPacket> {
|
||||
private final Identifier id;
|
||||
private final Function<PacketByteBuf, T> constructor;
|
||||
|
||||
private PacketType(Identifier id, Function<PacketByteBuf, T> constructor) {
|
||||
this.id = id;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new packet type.
|
||||
* @param id the channel ID used for the packets
|
||||
* @param constructor the reader that reads the received buffer
|
||||
* @param <P> the type of the packet
|
||||
* @return the newly created type
|
||||
*/
|
||||
public static <P extends FabricPacket> PacketType<P> create(Identifier id, Function<PacketByteBuf, P> constructor) {
|
||||
return new PacketType<>(id, constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of the channel used to send the packet.
|
||||
* @return the identifier of the associated channel.
|
||||
*/
|
||||
public Identifier getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the packet from the buffer.
|
||||
* @param buf the buffer
|
||||
* @return the packet
|
||||
*/
|
||||
public T read(PacketByteBuf buf) {
|
||||
try {
|
||||
return this.constructor.apply(buf);
|
||||
} catch (RuntimeException e) {
|
||||
throw new RuntimeException("Error while handling packet \"%s\": %s".formatted(this.id, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.api.networking.v1;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.PayloadTypeRegistryImpl;
|
||||
|
||||
/**
|
||||
* A registry for payload types.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface PayloadTypeRegistry<B extends PacketByteBuf> {
|
||||
/**
|
||||
* Registers a custom payload type.
|
||||
*
|
||||
* <p>This must be done on both the sending and receiving side, usually during mod initialization
|
||||
* and <strong>before registering a packet handler</strong>.
|
||||
*
|
||||
* @param id the id of the payload type
|
||||
* @param codec the codec for the payload type
|
||||
* @param <T> the payload type
|
||||
* @return the registered payload type
|
||||
*/
|
||||
<T extends CustomPayload> CustomPayload.Type<? super B, T> register(CustomPayload.Id<T> id, PacketCodec<? super B, T> codec);
|
||||
|
||||
/**
|
||||
* @return the {@link PayloadTypeRegistry} instance for the client to server configuration channel.
|
||||
*/
|
||||
static PayloadTypeRegistry<PacketByteBuf> configurationC2S() {
|
||||
return PayloadTypeRegistryImpl.CONFIGURATION_C2S;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link PayloadTypeRegistry} instance for the server to client configuration channel.
|
||||
*/
|
||||
static PayloadTypeRegistry<PacketByteBuf> configurationS2C() {
|
||||
return PayloadTypeRegistryImpl.CONFIGURATION_S2C;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link PayloadTypeRegistry} instance for the client to server play channel.
|
||||
*/
|
||||
static PayloadTypeRegistry<RegistryByteBuf> playC2S() {
|
||||
return PayloadTypeRegistryImpl.PLAY_C2S;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link PayloadTypeRegistry} instance for the server to client play channel.
|
||||
*/
|
||||
static PayloadTypeRegistry<RegistryByteBuf> playS2C() {
|
||||
return PayloadTypeRegistryImpl.PLAY_S2C;
|
||||
}
|
||||
}
|
|
@ -19,20 +19,17 @@ package net.fabricmc.fabric.api.networking.v1;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.ClientCommonPacketListener;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.thread.ThreadExecutor;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerCommonNetworkHandlerAccessor;
|
||||
|
||||
|
@ -40,11 +37,13 @@ import net.fabricmc.fabric.mixin.networking.accessor.ServerCommonNetworkHandlerA
|
|||
* Offers access to configuration 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.
|
||||
* Packets <strong>received</strong> by this class must be registered to {@link PayloadTypeRegistry#configurationC2S()} on both ends.
|
||||
* Packets <strong>sent</strong> by this class must be registered to {@link PayloadTypeRegistry#configurationS2C()} on both ends.
|
||||
* Packets must be registered before registering any receivers.
|
||||
*
|
||||
* <p>This class should be only used for the logical server.
|
||||
*
|
||||
* <p>See {@link ServerPlayNetworking} for information on how to use the packet
|
||||
* object-based API.
|
||||
* <p>See {@link ServerPlayNetworking} for information on sending and receiving play phase packets.
|
||||
*
|
||||
* <p>See the documentation on each class for more information.
|
||||
*
|
||||
|
@ -53,76 +52,38 @@ import net.fabricmc.fabric.mixin.networking.accessor.ServerCommonNetworkHandlerA
|
|||
*/
|
||||
public final class ServerConfigurationNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, the server
|
||||
* must be modified in the server thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
*
|
||||
* <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(ServerConfigurationNetworkHandler, Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* <p>For new code, {@link #registerGlobalReceiver(PacketType, ConfigurationPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @param channelHandler the handler
|
||||
* @return false if a handler is already registered to the channel
|
||||
* @see ServerConfigurationNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ServerConfigurationNetworking#registerReceiver(ServerConfigurationNetworkHandler, Identifier, ConfigurationChannelHandler)
|
||||
*/
|
||||
public static boolean registerGlobalReceiver(Identifier channelName, ConfigurationChannelHandler channelHandler) {
|
||||
return ServerNetworkingImpl.CONFIGURATION.registerGlobalReceiver(channelName, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* Registers a handler for a payload type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerConfigurationNetworkHandler, PacketType)} to unregister the existing handler.
|
||||
* Use {@link #unregisterReceiver(ServerConfigurationNetworkHandler, Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param handler the handler
|
||||
* @return {@code false} if a handler is already registered to the channel
|
||||
* @see ServerConfigurationNetworking#unregisterGlobalReceiver(PacketType)
|
||||
* @see ServerConfigurationNetworking#registerReceiver(ServerConfigurationNetworkHandler, PacketType, ConfigurationPacketHandler)
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#configurationC2S() registered} yet
|
||||
* @see ServerConfigurationNetworking#unregisterGlobalReceiver(Identifier)
|
||||
* @see ServerConfigurationNetworking#registerReceiver(ServerConfigurationNetworkHandler, CustomPayload.Id, ConfigurationPacketHandler)
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerGlobalReceiver(PacketType<T> type, ConfigurationPacketHandler<T> handler) {
|
||||
return ServerNetworkingImpl.CONFIGURATION.registerGlobalReceiver(type.getId(), wrapTyped(type, handler));
|
||||
public static <T extends CustomPayload> boolean registerGlobalReceiver(CustomPayload.Id<T> type, ConfigurationPacketHandler<T> handler) {
|
||||
return ServerNetworkingImpl.CONFIGURATION.registerGlobalReceiver(type.id(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param channelName the id of the channel
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel
|
||||
* @see ServerConfigurationNetworking#registerGlobalReceiver(Identifier, ConfigurationChannelHandler)
|
||||
* @see ServerConfigurationNetworking#unregisterReceiver(ServerConfigurationNetworkHandler, Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static ServerConfigurationNetworking.ConfigurationChannelHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return unwrapUntyped(ServerNetworkingImpl.CONFIGURATION.unregisterGlobalReceiver(channelName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
* Removes the handler for a payload type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param id the packet payload id
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerGlobalReceiver(PacketType, ConfigurationPacketHandler)}
|
||||
* @see ServerConfigurationNetworking#registerGlobalReceiver(PacketType, ConfigurationPacketHandler)
|
||||
* @see ServerConfigurationNetworking#unregisterReceiver(ServerConfigurationNetworkHandler, PacketType)
|
||||
* or it was not registered using {@link #registerGlobalReceiver(CustomPayload.Id, ConfigurationPacketHandler)}
|
||||
* @see ServerConfigurationNetworking#registerGlobalReceiver(CustomPayload.Id, ConfigurationPacketHandler)
|
||||
* @see ServerConfigurationNetworking#unregisterReceiver(ServerConfigurationNetworkHandler, Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> ServerConfigurationNetworking.ConfigurationPacketHandler<T> unregisterGlobalReceiver(PacketType<T> type) {
|
||||
return unwrapTyped(ServerNetworkingImpl.CONFIGURATION.unregisterGlobalReceiver(type.getId()));
|
||||
public static ServerConfigurationNetworking.ConfigurationPacketHandler<?> unregisterGlobalReceiver(Identifier id) {
|
||||
return ServerNetworkingImpl.CONFIGURATION.unregisterGlobalReceiver(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,82 +97,36 @@ public final class ServerConfigurationNetworking {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* This method differs from {@link ServerConfigurationNetworking#registerGlobalReceiver(Identifier, ConfigurationChannelHandler)} since
|
||||
* Registers a handler for a payload type.
|
||||
* This method differs from {@link ServerConfigurationNetworking#registerGlobalReceiver(CustomPayload.Id, ConfigurationPacketHandler)} since
|
||||
* the channel handler will only be applied to the client represented by the {@link ServerConfigurationNetworkHandler}.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, the world
|
||||
* must be modified in the server thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ServerLoginNetworking#registerGlobalReceiver(Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}
|
||||
* login response has been received, you should use {@link ServerPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* <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(ServerConfigurationNetworkHandler, Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* <p>For new code, {@link #registerReceiver(ServerConfigurationNetworkHandler, PacketType, ConfigurationPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @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
|
||||
* @see ServerPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static boolean registerReceiver(ServerConfigurationNetworkHandler networkHandler, Identifier channelName, ConfigurationChannelHandler channelHandler) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(channelName, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* This method differs from {@link ServerConfigurationNetworking#registerGlobalReceiver(PacketType, ConfigurationPacketHandler)} since
|
||||
* the channel handler will only be applied to the client represented by the {@link ServerConfigurationNetworkHandler}.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ServerLoginNetworking#registerGlobalReceiver(Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}
|
||||
* login response has been received, you should use {@link ServerPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerConfigurationNetworkHandler, PacketType)} to unregister the existing handler.
|
||||
* Use {@link #unregisterReceiver(ServerConfigurationNetworkHandler, Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* @param networkHandler the network handler
|
||||
* @param type the packet type
|
||||
* @param handler the handler
|
||||
* @return {@code false} if a handler is already registered to the channel name
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#configurationC2S() registered} yet
|
||||
* @see ServerPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerReceiver(ServerConfigurationNetworkHandler networkHandler, PacketType<T> type, ConfigurationPacketHandler<T> handler) {
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(type.getId(), wrapTyped(type, handler));
|
||||
public static <T extends CustomPayload> boolean registerReceiver(ServerConfigurationNetworkHandler networkHandler, CustomPayload.Id<T> type, ConfigurationPacketHandler<T> handler) {
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(type.id(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
*
|
||||
* <p>The {@code channelName} 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 name
|
||||
*/
|
||||
@Nullable
|
||||
public static ServerConfigurationNetworking.ConfigurationChannelHandler unregisterReceiver(ServerConfigurationNetworkHandler networkHandler, Identifier channelName) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return unwrapUntyped(ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(channelName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
* Removes the handler for a payload type.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the type of the packet
|
||||
* @param id the id of the payload
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerReceiver(ServerConfigurationNetworkHandler, PacketType, ConfigurationPacketHandler)}
|
||||
* or it was not registered using {@link #registerReceiver(ServerConfigurationNetworkHandler, CustomPayload.Id, ConfigurationPacketHandler)}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> ServerConfigurationNetworking.ConfigurationPacketHandler<T> unregisterReceiver(ServerConfigurationNetworkHandler networkHandler, PacketType<T> type) {
|
||||
return unwrapTyped(ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(type.getId()));
|
||||
public static ServerConfigurationNetworking.ConfigurationPacketHandler<?> unregisterReceiver(ServerConfigurationNetworkHandler networkHandler, Identifier id) {
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,41 +171,27 @@ public final class ServerConfigurationNetworking {
|
|||
* Checks if the connected client declared the ability to receive a specific type of packet.
|
||||
*
|
||||
* @param handler the network handler
|
||||
* @param type the packet type
|
||||
* @param id the payload id
|
||||
* @return {@code true} if the connected client has declared the ability to receive a specific type of packet
|
||||
*/
|
||||
public static boolean canSend(ServerConfigurationNetworkHandler handler, PacketType<?> type) {
|
||||
public static boolean canSend(ServerConfigurationNetworkHandler handler, CustomPayload.Id<?> id) {
|
||||
Objects.requireNonNull(handler, "Server configuration network handler cannot be null");
|
||||
Objects.requireNonNull(type, "Packet type cannot be null");
|
||||
Objects.requireNonNull(id, "Payload id cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(handler).getSendableChannels().contains(type.getId());
|
||||
return ServerNetworkingImpl.getAddon(handler).getSendableChannels().contains(id.id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a connected client.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @param buf the packet byte buf which represents the payload of the packet
|
||||
* @param payload the payload
|
||||
* @return a new packet
|
||||
*/
|
||||
public static Packet<ClientCommonPacketListener> createS2CPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channelName, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Buf cannot be null");
|
||||
public static Packet<ClientCommonPacketListener> createS2CPacket(CustomPayload payload) {
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
return ServerNetworkingImpl.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a connected client.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static <T extends FabricPacket> Packet<ClientCommonPacketListener> createS2CPacket(T packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
return ServerNetworkingImpl.createS2CPacket(packet);
|
||||
return ServerNetworkingImpl.createS2CPacket(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -308,30 +209,17 @@ public final class ServerConfigurationNetworking {
|
|||
/**
|
||||
* Sends a packet to a configuring player.
|
||||
*
|
||||
* @param handler the handler to send the packet to
|
||||
* @param channelName the channel of the packet
|
||||
* @param buf the payload of the packet.
|
||||
*/
|
||||
public static void send(ServerConfigurationNetworkHandler handler, Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(handler, "Server configuration entity cannot be null");
|
||||
Objects.requireNonNull(channelName, "Channel name cannot be null");
|
||||
Objects.requireNonNull(buf, "Packet byte buf cannot be null");
|
||||
|
||||
handler.sendPacket(createS2CPacket(channelName, buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to a configuring player.
|
||||
* <p>Any packets sent must be {@linkplain PayloadTypeRegistry#configurationS2C() registered}.</p>
|
||||
*
|
||||
* @param handler the network handler to send the packet to
|
||||
* @param packet the packet
|
||||
* @param payload to be sent
|
||||
*/
|
||||
public static <T extends FabricPacket> void send(ServerConfigurationNetworkHandler handler, T packet) {
|
||||
public static void send(ServerConfigurationNetworkHandler handler, CustomPayload payload) {
|
||||
Objects.requireNonNull(handler, "Server configuration handler cannot be null");
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
handler.sendPacket(createS2CPacket(packet));
|
||||
handler.sendPacket(createS2CPacket(payload));
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
@ -350,72 +238,16 @@ public final class ServerConfigurationNetworking {
|
|||
private ServerConfigurationNetworking() {
|
||||
}
|
||||
|
||||
private static ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> wrapUntyped(ConfigurationChannelHandler actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(null, actualHandler, (server, handler, payload, responseSender) -> {
|
||||
actualHandler.receive(server, handler, ((UntypedPayload) payload).buffer(), responseSender);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends FabricPacket> ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> wrapTyped(PacketType<T> type, ConfigurationPacketHandler<T> actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(type, actualHandler, (server, handler, payload, responseSender) -> {
|
||||
T packet = (T) ((TypedPayload) payload).packet();
|
||||
actualHandler.receive(packet, handler, responseSender);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ConfigurationChannelHandler unwrapUntyped(@Nullable ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof ConfigurationChannelHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private static <T extends FabricPacket> ConfigurationPacketHandler<T> unwrapTyped(@Nullable ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof ConfigurationPacketHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationChannelHandler {
|
||||
/**
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain ThreadExecutor#submit(Runnable) scheduled} using the server instance from {@link ServerConfigurationNetworking#getServer(ServerConfigurationNetworkHandler)}.
|
||||
*
|
||||
* <p>An example usage of this is:
|
||||
* <pre>{@code
|
||||
* ServerConfigurationNetworking.registerReceiver(new Identifier("mymod", "boom"), (server, handler, buf, responseSender) -> {
|
||||
* boolean fire = buf.readBoolean();
|
||||
*
|
||||
* // All operations on the server must be executed on the server thread
|
||||
* server.execute(() -> {
|
||||
*
|
||||
* });
|
||||
* });
|
||||
* }</pre>
|
||||
* @param server the server
|
||||
* @param handler the network handler that received this packet, representing the client who sent the packet
|
||||
* @param buf the payload of the packet
|
||||
* @param responseSender the packet sender
|
||||
*/
|
||||
void receive(MinecraftServer server, ServerConfigurationNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender);
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread-safe packet handler utilizing {@link FabricPacket}.
|
||||
* A packet handler utilizing {@link CustomPayload}.
|
||||
* @param <T> the type of the packet
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationPacketHandler<T extends FabricPacket> {
|
||||
public interface ConfigurationPacketHandler<T extends CustomPayload> {
|
||||
/**
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* <p>Unlike {@link ServerPlayNetworking.PlayPacketHandler} this method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* <p>Unlike {@link ServerPlayNetworking.PlayPayloadHandler} this method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
|
||||
* Modification to the game should be {@linkplain ThreadExecutor#submit(Runnable) scheduled} using the Minecraft server instance from {@link ServerConfigurationNetworking#getServer(ServerConfigurationNetworkHandler)}.
|
||||
*
|
||||
* <p>An example usage of this:
|
||||
|
@ -427,11 +259,23 @@ public final class ServerConfigurationNetworking {
|
|||
* }</pre>
|
||||
*
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param networkHandler the network handler
|
||||
* @param responseSender the packet sender
|
||||
* @see FabricPacket
|
||||
* @param payload the packet payload
|
||||
* @param context the configuration networking context
|
||||
* @see CustomPayload
|
||||
*/
|
||||
void receive(T packet, ServerConfigurationNetworkHandler networkHandler, PacketSender responseSender);
|
||||
void receive(T payload, Context context);
|
||||
}
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public interface Context {
|
||||
/**
|
||||
* @return The ServerConfigurationNetworkHandler instance
|
||||
*/
|
||||
ServerConfigurationNetworkHandler networkHandler();
|
||||
|
||||
/**
|
||||
* @return The packet sender
|
||||
*/
|
||||
PacketSender responseSender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public final class ServerLoginConnectionEvents {
|
|||
* 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}.
|
||||
* <p>You may send login queries to the connected client using the provided {@link LoginPacketSender}.
|
||||
*/
|
||||
public static final Event<QueryStart> QUERY_START = EventFactory.createArrayBacked(QueryStart.class, callbacks -> (handler, server, sender, synchronizer) -> {
|
||||
for (QueryStart callback : callbacks) {
|
||||
|
@ -79,7 +79,7 @@ public final class ServerLoginConnectionEvents {
|
|||
*/
|
||||
@FunctionalInterface
|
||||
public interface QueryStart {
|
||||
void onLoginStart(ServerLoginNetworkHandler handler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer);
|
||||
void onLoginStart(ServerLoginNetworkHandler handler, MinecraftServer server, LoginPacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,46 +19,39 @@ package net.fabricmc.fabric.api.networking.v1;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.ClientCommonPacketListener;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Packets <strong>received</strong> by this class must be registered to {@link PayloadTypeRegistry#playC2S()} on both ends.
|
||||
* Packets <strong>sent</strong> by this class must be registered to {@link PayloadTypeRegistry#playS2C()} on both ends.
|
||||
* Packets must be registered before registering any receivers.
|
||||
*
|
||||
* <p>This class should be only used for the logical server.
|
||||
*
|
||||
* <h2>Packet object-based API</h2>
|
||||
*
|
||||
* <p>This class provides a classic registration method, {@link #registerGlobalReceiver(Identifier, PlayChannelHandler)},
|
||||
* and a newer method utilizing packet objects, {@link #registerGlobalReceiver(PacketType, PlayPacketHandler)}.
|
||||
* For most mods, using the newer method will improve the readability of the code by separating packet
|
||||
* reading/writing code to a separate class. Additionally, the newer method executes the callback in the
|
||||
* server thread, ensuring thread safety. For this reason using the newer method is highly recommended.
|
||||
* The two methods are network-compatible with each other, so long as the buffer contents are read and written
|
||||
* in the same order.
|
||||
* <p>This class provides a registration method, utilizing packet objects, {@link #registerGlobalReceiver(CustomPayload.Id, PlayPayloadHandler)}.
|
||||
* This handler executes the callback in the server thread, ensuring thread safety.
|
||||
*
|
||||
* <p>The newer, packet object-based API involves three classes:
|
||||
* <p>This payload object-based API involves three classes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A class implementing {@link FabricPacket} that is "sent" over the network</li>
|
||||
* <li>{@link PacketType} instance, which represents the packet's type (and its channel)</li>
|
||||
* <li>{@link PlayPacketHandler}, which handles the packet (usually implemented as a functional interface)</li>
|
||||
* <li>A class implementing {@link CustomPayload} that is "sent" over the network</li>
|
||||
* <li>{@link CustomPayload.Type} instance, which represents the packet's type (and its codec)</li>
|
||||
* <li>{@link PlayPayloadHandler}, which handles the packet (usually implemented as a functional interface)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>See the documentation on each class for more information.
|
||||
|
@ -68,76 +61,37 @@ import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon;
|
|||
*/
|
||||
public final class ServerPlayNetworking {
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, the world
|
||||
* must be modified in the server thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
*
|
||||
* <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>For new code, {@link #registerGlobalReceiver(PacketType, PlayPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @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, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* Registers a handler for a payload type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerPlayNetworkHandler, PacketType)} to unregister the existing handler.
|
||||
* Use {@link #unregisterGlobalReceiver(Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @param handler the handler
|
||||
* @return {@code false} if a handler is already registered to the channel
|
||||
* @see ServerPlayNetworking#unregisterGlobalReceiver(PacketType)
|
||||
* @see ServerPlayNetworking#registerReceiver(ServerPlayNetworkHandler, PacketType, PlayPacketHandler)
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#playC2S() registered} yet
|
||||
* @see ServerPlayNetworking#unregisterGlobalReceiver(Identifier)
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerGlobalReceiver(PacketType<T> type, PlayPacketHandler<T> handler) {
|
||||
return ServerNetworkingImpl.PLAY.registerGlobalReceiver(type.getId(), wrapTyped(type, handler));
|
||||
public static <T extends CustomPayload> boolean registerGlobalReceiver(CustomPayload.Id<T> type, PlayPayloadHandler<T> handler) {
|
||||
return ServerNetworkingImpl.PLAY.registerGlobalReceiver(type.id(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
* Removes the handler for a payload type.
|
||||
* 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>The {@code id} is guaranteed not to have an associated 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 ServerPlayNetworking#registerGlobalReceiver(Identifier, PlayChannelHandler)
|
||||
* @param id the payload id
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerGlobalReceiver(CustomPayload.Id, PlayPayloadHandler)}
|
||||
* @see ServerPlayNetworking#registerGlobalReceiver(CustomPayload.Id, PlayPayloadHandler)
|
||||
* @see ServerPlayNetworking#unregisterReceiver(ServerPlayNetworkHandler, Identifier)
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterGlobalReceiver(Identifier channelName) {
|
||||
return unwrapUntyped(ServerNetworkingImpl.PLAY.unregisterGlobalReceiver(channelName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
* A global receiver is registered to all connections, in the present and future.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the packet type
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerGlobalReceiver(PacketType, PlayPacketHandler)}
|
||||
* @see ServerPlayNetworking#registerGlobalReceiver(PacketType, PlayPacketHandler)
|
||||
* @see ServerPlayNetworking#unregisterReceiver(ServerPlayNetworkHandler, PacketType)
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> PlayPacketHandler<T> unregisterGlobalReceiver(PacketType<T> type) {
|
||||
return unwrapTyped(ServerNetworkingImpl.PLAY.unregisterGlobalReceiver(type.getId()));
|
||||
public static ServerPlayNetworking.PlayPayloadHandler<?> unregisterGlobalReceiver(Identifier id) {
|
||||
return ServerNetworkingImpl.PLAY.unregisterGlobalReceiver(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,82 +105,39 @@ public final class ServerPlayNetworking {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers a handler to a channel.
|
||||
* This method differs from {@link ServerPlayNetworking#registerGlobalReceiver(Identifier, PlayChannelHandler)} since
|
||||
* the channel handler will only be applied to the player represented by the {@link ServerPlayNetworkHandler}.
|
||||
*
|
||||
* <p>The handler runs on the network thread. After reading the buffer there, the world
|
||||
* must be modified in the server thread by calling {@link ThreadExecutor#execute(Runnable)}.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ServerLoginNetworking#registerGlobalReceiver(Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}
|
||||
* login response has been received, you should use {@link ServerPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* <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>For new code, {@link #registerReceiver(ServerPlayNetworkHandler, PacketType, PlayPacketHandler)}
|
||||
* is preferred, as it is designed in a way that prevents thread safety issues.
|
||||
*
|
||||
* @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
|
||||
* @see ServerPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static boolean registerReceiver(ServerPlayNetworkHandler networkHandler, Identifier channelName, PlayChannelHandler channelHandler) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(channelName, wrapUntyped(channelHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for a packet type.
|
||||
* This method differs from {@link ServerPlayNetworking#registerGlobalReceiver(PacketType, PlayPacketHandler)} since
|
||||
* Registers a handler for a payload type.
|
||||
* This method differs from {@link ServerPlayNetworking#registerGlobalReceiver(CustomPayload.Id, PlayPayloadHandler)} since
|
||||
* the channel handler will only be applied to the player represented by the {@link ServerPlayNetworkHandler}.
|
||||
*
|
||||
* <p>For example, if you only register a receiver using this method when a {@linkplain ServerLoginNetworking#registerGlobalReceiver(Identifier, ServerLoginNetworking.LoginQueryResponseHandler)}
|
||||
* login response has been received, you should use {@link ServerPlayConnectionEvents#INIT} to register the channel handler.
|
||||
*
|
||||
* <p>If a handler is already registered for the {@code type}, this method will return {@code false}, and no change will be made.
|
||||
* Use {@link #unregisterReceiver(ServerPlayNetworkHandler, PacketType)} to unregister the existing handler.
|
||||
* Use {@link #unregisterReceiver(ServerPlayNetworkHandler, Identifier)} to unregister the existing handler.
|
||||
*
|
||||
* @param networkHandler the network handler
|
||||
* @param type the packet type
|
||||
* @param handler the handler
|
||||
* @return {@code false} if a handler is already registered to the channel name
|
||||
* @throws IllegalArgumentException if the codec for {@code type} has not been {@linkplain PayloadTypeRegistry#playC2S() registered} yet
|
||||
* @see ServerPlayConnectionEvents#INIT
|
||||
*/
|
||||
public static <T extends FabricPacket> boolean registerReceiver(ServerPlayNetworkHandler networkHandler, PacketType<T> type, PlayPacketHandler<T> handler) {
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(type.getId(), wrapTyped(type, handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler of a channel.
|
||||
*
|
||||
* <p>The {@code channelName} 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 name
|
||||
*/
|
||||
@Nullable
|
||||
public static PlayChannelHandler unregisterReceiver(ServerPlayNetworkHandler networkHandler, Identifier channelName) {
|
||||
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
|
||||
|
||||
return unwrapUntyped(ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(channelName));
|
||||
public static <T extends CustomPayload> boolean registerReceiver(ServerPlayNetworkHandler networkHandler, CustomPayload.Id<T> type, PlayPayloadHandler<T> handler) {
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(type.id(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the handler for a packet type.
|
||||
*
|
||||
* <p>The {@code type} is guaranteed not to have an associated handler after this call.
|
||||
* <p>The {@code id} is guaranteed not to have an associated handler after this call.
|
||||
*
|
||||
* @param type the type of the packet
|
||||
* @param id the id of the payload
|
||||
* @return the previous handler, or {@code null} if no handler was bound to the channel,
|
||||
* or it was not registered using {@link #registerReceiver(ServerPlayNetworkHandler, PacketType, PlayPacketHandler)}
|
||||
* or it was not registered using {@link #registerReceiver(ServerPlayNetworkHandler, CustomPayload.Id, PlayPayloadHandler)}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends FabricPacket> PlayPacketHandler<T> unregisterReceiver(ServerPlayNetworkHandler networkHandler, PacketType<T> type) {
|
||||
return unwrapTyped(ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(type.getId()));
|
||||
public static ServerPlayNetworking.PlayPayloadHandler<?> unregisterReceiver(ServerPlayNetworkHandler networkHandler, Identifier id) {
|
||||
return ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,10 +208,10 @@ public final class ServerPlayNetworking {
|
|||
* @param type the packet type
|
||||
* @return {@code true} if the connected client has declared the ability to receive a specific type of packet
|
||||
*/
|
||||
public static boolean canSend(ServerPlayerEntity player, PacketType<?> type) {
|
||||
public static boolean canSend(ServerPlayerEntity player, CustomPayload.Id<?> type) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
|
||||
return canSend(player.networkHandler, type.getId());
|
||||
return canSend(player.networkHandler, type.id());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,34 +235,20 @@ public final class ServerPlayNetworking {
|
|||
* @param type the packet type
|
||||
* @return {@code true} if the connected client has declared the ability to receive a specific type of packet
|
||||
*/
|
||||
public static boolean canSend(ServerPlayNetworkHandler handler, PacketType<?> type) {
|
||||
public static boolean canSend(ServerPlayNetworkHandler handler, CustomPayload.Id<?> type) {
|
||||
Objects.requireNonNull(handler, "Server play network handler cannot be null");
|
||||
Objects.requireNonNull(type, "Packet type cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.getAddon(handler).getSendableChannels().contains(type.getId());
|
||||
return ServerNetworkingImpl.getAddon(handler).getSendableChannels().contains(type.id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a connected client.
|
||||
*
|
||||
* @param channelName the channel name
|
||||
* @param buf the packet byte buf which represents the payload of the packet
|
||||
* @param packet the packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static Packet<ClientCommonPacketListener> createS2CPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
Objects.requireNonNull(channelName, "Channel cannot be null");
|
||||
Objects.requireNonNull(buf, "Buf cannot be null");
|
||||
|
||||
return ServerNetworkingImpl.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet which may be sent to a connected client.
|
||||
*
|
||||
* @param packet the fabric packet
|
||||
* @return a new packet
|
||||
*/
|
||||
public static <T extends FabricPacket> Packet<ClientCommonPacketListener> createS2CPacket(T packet) {
|
||||
public static <T extends CustomPayload> Packet<ClientCommonPacketListener> createS2CPacket(T packet) {
|
||||
return ServerNetworkingImpl.createS2CPacket(packet);
|
||||
}
|
||||
|
||||
|
@ -382,142 +279,60 @@ public final class ServerPlayNetworking {
|
|||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a packet to a player.
|
||||
* <p>Any packets sent must be {@linkplain PayloadTypeRegistry#playS2C() registered}.</p>
|
||||
*
|
||||
* @param player the player to send the packet to
|
||||
* @param packet the packet
|
||||
* @param payload the payload to send
|
||||
*/
|
||||
public static <T extends FabricPacket> void send(ServerPlayerEntity player, T packet) {
|
||||
public static void send(ServerPlayerEntity player, CustomPayload payload) {
|
||||
Objects.requireNonNull(player, "Server player entity cannot be null");
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
player.networkHandler.sendPacket(createS2CPacket(packet));
|
||||
}
|
||||
|
||||
// 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;
|
||||
player.networkHandler.sendPacket(createS2CPacket(payload));
|
||||
}
|
||||
|
||||
private ServerPlayNetworking() {
|
||||
}
|
||||
|
||||
private static ResolvablePayload.Handler<ServerPlayNetworkAddon.Handler> wrapUntyped(PlayChannelHandler actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(null, actualHandler, (server, player, handler, payload, responseSender) -> {
|
||||
actualHandler.receive(server, player, handler, ((UntypedPayload) payload).buffer(), responseSender);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends FabricPacket> ResolvablePayload.Handler<ServerPlayNetworkAddon.Handler> wrapTyped(PacketType<T> type, PlayPacketHandler<T> actualHandler) {
|
||||
return new ResolvablePayload.Handler<>(type, actualHandler, (server, player, handler, payload, responseSender) -> {
|
||||
T packet = (T) ((TypedPayload) payload).packet();
|
||||
|
||||
if (server.isOnThread()) {
|
||||
// Do not submit to the server thread if we're already running there.
|
||||
// Normally, packets are handled on the network IO thread - though it is
|
||||
// not guaranteed (for example, with 1.19.4 S2C packet bundling)
|
||||
// Since we're handling it right now, connection check is redundant.
|
||||
actualHandler.receive(packet, player, responseSender);
|
||||
} else {
|
||||
server.execute(() -> {
|
||||
if (handler.isConnectionOpen()) actualHandler.receive(packet, player, responseSender);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PlayChannelHandler unwrapUntyped(@Nullable ResolvablePayload.Handler<ServerPlayNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof PlayChannelHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private static <T extends FabricPacket> PlayPacketHandler<T> unwrapTyped(@Nullable ResolvablePayload.Handler<ServerPlayNetworkAddon.Handler> handler) {
|
||||
if (handler == null) return null;
|
||||
if (handler.actual() instanceof PlayPacketHandler actual) return actual;
|
||||
return null;
|
||||
}
|
||||
|
||||
@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 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) -> {
|
||||
* boolean fire = buf.readBoolean();
|
||||
*
|
||||
* // All operations on the server or world must be executed on the server thread
|
||||
* server.execute(() -> {
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread-safe packet handler utilizing {@link FabricPacket}.
|
||||
* A thread-safe packet handler utilizing {@link CustomPayload}.
|
||||
* @param <T> the type of the packet
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PlayPacketHandler<T extends FabricPacket> {
|
||||
public interface PlayPayloadHandler<T extends CustomPayload> {
|
||||
/**
|
||||
* Handles the incoming packet. This is called on the server thread, and can safely
|
||||
* manipulate the world.
|
||||
*
|
||||
* <p>An example usage of this is to create an explosion where the player is looking:
|
||||
* <pre>{@code
|
||||
* // See FabricPacket for creating the packet
|
||||
* ServerPlayNetworking.registerReceiver(BOOM_PACKET_TYPE, (player, packet, responseSender) -> {
|
||||
* ModPacketHandler.createExplosion(player, packet.fire());
|
||||
* // use PayloadTypeRegistry for registering the payload
|
||||
* ServerPlayNetworking.registerReceiver(BoomPayload.ID, (payload, player, responseSender) -> {
|
||||
* ModPacketHandler.createExplosion(player, payload.fire());
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The server and the network handler can be accessed via {@link ServerPlayerEntity#server}
|
||||
* and {@link ServerPlayerEntity#networkHandler}, respectively.
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param player the player that received the packet
|
||||
* @param responseSender the packet sender
|
||||
* @see FabricPacket
|
||||
* @param payload the packet payload
|
||||
* @param context the play networking context
|
||||
* @see CustomPayload
|
||||
*/
|
||||
void receive(T packet, ServerPlayerEntity player, PacketSender responseSender);
|
||||
void receive(T payload, Context context);
|
||||
}
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public interface Context {
|
||||
/**
|
||||
* @return The player that received the packet
|
||||
*/
|
||||
ServerPlayerEntity player();
|
||||
|
||||
/**
|
||||
* @return The packet sender
|
||||
*/
|
||||
PacketSender responseSender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
* </dl>
|
||||
*
|
||||
* <p>In addition, this API includes helpers for {@linkplain
|
||||
* net.fabricmc.fabric.api.networking.v1.PacketByteBufs buffer creations} and {@linkplain
|
||||
* net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry registering custom packet payloads} and {@linkplain
|
||||
* net.fabricmc.fabric.api.networking.v1.PlayerLookup player lookups}.
|
||||
*/
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -25,46 +24,39 @@ 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.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.text.Text;
|
||||
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;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
|
||||
/**
|
||||
* 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<ResolvablePayload.Handler<H>> implements PacketSender, CommonPacketHandler {
|
||||
public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAddon<H> implements PacketSender, CommonPacketHandler {
|
||||
protected final ClientConnection connection;
|
||||
protected final GlobalReceiverRegistry<ResolvablePayload.Handler<H>> receiver;
|
||||
protected final GlobalReceiverRegistry<H> receiver;
|
||||
protected final Set<Identifier> sendableChannels;
|
||||
|
||||
protected int commonVersion = -1;
|
||||
|
||||
protected AbstractChanneledNetworkAddon(GlobalReceiverRegistry<ResolvablePayload.Handler<H>> receiver, ClientConnection connection, String description) {
|
||||
protected AbstractChanneledNetworkAddon(GlobalReceiverRegistry<H> receiver, ClientConnection connection, String description) {
|
||||
super(receiver, description);
|
||||
this.connection = connection;
|
||||
this.receiver = receiver;
|
||||
this.sendableChannels = Collections.synchronizedSet(new HashSet<>());
|
||||
}
|
||||
|
||||
protected void registerPendingChannels(ChannelInfoHolder holder, NetworkState state) {
|
||||
final Collection<Identifier> pending = holder.getPendingChannelsNames(state);
|
||||
protected void registerPendingChannels(ChannelInfoHolder holder, NetworkPhase state) {
|
||||
final Collection<Identifier> pending = holder.fabric_getPendingChannelsNames(state);
|
||||
|
||||
if (!pending.isEmpty()) {
|
||||
register(new ArrayList<>(pending));
|
||||
|
@ -73,30 +65,31 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
}
|
||||
|
||||
// always supposed to handle async!
|
||||
public boolean handle(ResolvablePayload resolvable) {
|
||||
Identifier channelName = resolvable.id();
|
||||
public boolean handle(CustomPayload payload) {
|
||||
final Identifier channelName = payload.getId().id();
|
||||
this.logger.debug("Handling inbound packet from channel with name \"{}\"", channelName);
|
||||
|
||||
// Handle reserved packets
|
||||
if (NetworkingImpl.REGISTER_CHANNEL.equals(channelName)) {
|
||||
this.receiveRegistration(true, resolvable);
|
||||
return true;
|
||||
if (payload instanceof RegistrationPayload registrationPayload) {
|
||||
if (NetworkingImpl.REGISTER_CHANNEL.equals(channelName)) {
|
||||
this.receiveRegistration(true, registrationPayload);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NetworkingImpl.UNREGISTER_CHANNEL.equals(channelName)) {
|
||||
this.receiveRegistration(false, registrationPayload);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (NetworkingImpl.UNREGISTER_CHANNEL.equals(channelName)) {
|
||||
this.receiveRegistration(false, resolvable);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable ResolvablePayload.Handler<H> handler = this.getHandler(channelName);
|
||||
@Nullable H handler = this.getHandler(channelName);
|
||||
|
||||
if (handler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ResolvedPayload resolved = resolvable.resolve(handler.type());
|
||||
this.receive(handler.internal(), resolved);
|
||||
this.receive(handler, payload);
|
||||
} catch (Throwable ex) {
|
||||
this.logger.error("Encountered exception while handling in channel with name \"{}\"", channelName, ex);
|
||||
throw ex;
|
||||
|
@ -105,63 +98,31 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
return true;
|
||||
}
|
||||
|
||||
protected abstract void receive(H handler, ResolvedPayload payload);
|
||||
protected abstract void receive(H handler, CustomPayload payload);
|
||||
|
||||
protected void sendInitialChannelRegistrationPacket() {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(this.getReceivableChannels());
|
||||
final RegistrationPayload payload = createRegistrationPayload(RegistrationPayload.REGISTER, this.getReceivableChannels());
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
if (payload != null) {
|
||||
this.sendPacket(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected PacketByteBuf createRegistrationPacket(Collection<Identifier> channels) {
|
||||
protected RegistrationPayload createRegistrationPayload(CustomPayload.Id<RegistrationPayload> id, 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;
|
||||
return new RegistrationPayload(id, new ArrayList<>(channels));
|
||||
}
|
||||
|
||||
// wrap in try with res (buf)
|
||||
protected void receiveRegistration(boolean register, ResolvablePayload resolvable) {
|
||||
UntypedPayload payload = (UntypedPayload) resolvable.resolve(null);
|
||||
PacketByteBuf buf = payload.buffer();
|
||||
|
||||
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);
|
||||
|
||||
protected void receiveRegistration(boolean register, RegistrationPayload payload) {
|
||||
if (register) {
|
||||
register(ids);
|
||||
register(payload.channels());
|
||||
} else {
|
||||
unregister(ids);
|
||||
unregister(payload.channels());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,11 +136,6 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
schedule(() -> this.invokeUnregisterEvent(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
|
||||
sendPacket(packet, GenericFutureListenerHolder.create(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet, PacketCallbacks callback) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
|
@ -187,6 +143,13 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
this.connection.send(packet, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(Text disconnectReason) {
|
||||
Objects.requireNonNull(disconnectReason, "Disconnect reason cannot be null");
|
||||
|
||||
this.connection.disconnect(disconnectReason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to run on the main thread.
|
||||
*/
|
||||
|
@ -196,16 +159,6 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Identifier> getSendableChannels() {
|
||||
return Collections.unmodifiableSet(this.sendableChannels);
|
||||
}
|
||||
|
@ -231,7 +184,7 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
if (currentPhase == null) {
|
||||
// We don't support receiving the register packet during this phase. See getPhase() for supported phases.
|
||||
// The normal case where the play channels are sent during configuration is handled in the client/common configuration packet handlers.
|
||||
logger.warn("Received common register packet for phase {} in network state: {}", payload.phase(), receiver.getState());
|
||||
logger.warn("Received common register packet for phase {} in network state: {}", payload.phase(), receiver.getPhase());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -259,7 +212,7 @@ public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAd
|
|||
|
||||
@Nullable
|
||||
private String getPhase() {
|
||||
return switch (receiver.getState()) {
|
||||
return switch (receiver.getPhase()) {
|
||||
case PLAY -> CommonRegisterPayload.PLAY_PHASE;
|
||||
case CONFIGURATION -> CommonRegisterPayload.CONFIGURATION_PHASE;
|
||||
default -> null; // We don't support receiving this packet on any other phase
|
||||
|
|
|
@ -102,6 +102,8 @@ public abstract class AbstractNetworkAddon<H> {
|
|||
Objects.requireNonNull(handler, "Packet handler cannot be null");
|
||||
assertNotReserved(channelName);
|
||||
|
||||
receiver.assertPayloadType(channelName);
|
||||
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ package net.fabricmc.fabric.impl.networking;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
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(NetworkState state);
|
||||
Collection<Identifier> fabric_getPendingChannelsNames(NetworkPhase state);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,14 @@ package net.fabricmc.fabric.impl.networking;
|
|||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.network.ServerPlayerConfigurationTask;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
|
||||
|
@ -33,16 +35,23 @@ public class CommonPacketsImpl {
|
|||
public static final int[] SUPPORTED_COMMON_PACKET_VERSIONS = new int[]{ PACKET_VERSION_1 };
|
||||
|
||||
public static void init() {
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(CommonVersionPayload.PACKET_ID, (server, handler, buf, responseSender) -> {
|
||||
var payload = new CommonVersionPayload(buf);
|
||||
ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(handler);
|
||||
PayloadTypeRegistry.configurationC2S().register(CommonVersionPayload.ID, CommonVersionPayload.CODEC);
|
||||
PayloadTypeRegistry.configurationS2C().register(CommonVersionPayload.ID, CommonVersionPayload.CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(CommonVersionPayload.ID, CommonVersionPayload.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(CommonVersionPayload.ID, CommonVersionPayload.CODEC);
|
||||
PayloadTypeRegistry.configurationC2S().register(CommonRegisterPayload.ID, CommonRegisterPayload.CODEC);
|
||||
PayloadTypeRegistry.configurationS2C().register(CommonRegisterPayload.ID, CommonRegisterPayload.CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(CommonRegisterPayload.ID, CommonRegisterPayload.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(CommonRegisterPayload.ID, CommonRegisterPayload.CODEC);
|
||||
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(CommonVersionPayload.ID, (payload, context) -> {
|
||||
ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(context.networkHandler());
|
||||
addon.onCommonVersionPacket(getNegotiatedVersion(payload));
|
||||
handler.completeTask(CommonVersionConfigurationTask.KEY);
|
||||
context.networkHandler().completeTask(CommonVersionConfigurationTask.KEY);
|
||||
});
|
||||
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(CommonRegisterPayload.PACKET_ID, (server, handler, buf, responseSender) -> {
|
||||
var payload = new CommonRegisterPayload(buf);
|
||||
ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(handler);
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(CommonRegisterPayload.ID, (payload, context) -> {
|
||||
ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(context.networkHandler());
|
||||
|
||||
if (CommonRegisterPayload.PLAY_PHASE.equals(payload.phase())) {
|
||||
if (payload.version() != addon.getNegotiatedVersion()) {
|
||||
|
@ -50,24 +59,24 @@ public class CommonPacketsImpl {
|
|||
}
|
||||
|
||||
// Play phase hasnt started yet, add them to the pending names.
|
||||
addon.getChannelInfoHolder().getPendingChannelsNames(NetworkState.PLAY).addAll(payload.channels());
|
||||
addon.getChannelInfoHolder().fabric_getPendingChannelsNames(NetworkPhase.PLAY).addAll(payload.channels());
|
||||
NetworkingImpl.LOGGER.debug("Received accepted channels from the client for play phase");
|
||||
} else {
|
||||
addon.onCommonRegisterPacket(payload);
|
||||
}
|
||||
|
||||
handler.completeTask(CommonRegisterConfigurationTask.KEY);
|
||||
context.networkHandler().completeTask(CommonRegisterConfigurationTask.KEY);
|
||||
});
|
||||
|
||||
// Create a configuration task to send and receive the common packets
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
|
||||
final ServerConfigurationNetworkAddon addon = ServerNetworkingImpl.getAddon(handler);
|
||||
|
||||
if (ServerConfigurationNetworking.canSend(handler, CommonVersionPayload.PACKET_ID)) {
|
||||
if (ServerConfigurationNetworking.canSend(handler, CommonVersionPayload.ID)) {
|
||||
// Tasks are processed in order.
|
||||
handler.addTask(new CommonVersionConfigurationTask(addon));
|
||||
|
||||
if (ServerConfigurationNetworking.canSend(handler, CommonRegisterPayload.PACKET_ID)) {
|
||||
if (ServerConfigurationNetworking.canSend(handler, CommonRegisterPayload.ID)) {
|
||||
handler.addTask(new CommonRegisterConfigurationTask(addon));
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +85,7 @@ public class CommonPacketsImpl {
|
|||
|
||||
// A configuration phase task to send and receive the version packets.
|
||||
private record CommonVersionConfigurationTask(ServerConfigurationNetworkAddon addon) implements ServerPlayerConfigurationTask {
|
||||
public static final Key KEY = new Key(CommonVersionPayload.PACKET_ID.toString());
|
||||
public static final Key KEY = new Key(CommonVersionPayload.ID.id().toString());
|
||||
|
||||
@Override
|
||||
public void sendPacket(Consumer<Packet<?>> sender) {
|
||||
|
@ -91,11 +100,11 @@ public class CommonPacketsImpl {
|
|||
|
||||
// A configuration phase task to send and receive the registration packets.
|
||||
private record CommonRegisterConfigurationTask(ServerConfigurationNetworkAddon addon) implements ServerPlayerConfigurationTask {
|
||||
public static final Key KEY = new Key(CommonRegisterPayload.PACKET_ID.toString());
|
||||
public static final Key KEY = new Key(CommonRegisterPayload.ID.id().toString());
|
||||
|
||||
@Override
|
||||
public void sendPacket(Consumer<Packet<?>> sender) {
|
||||
addon.sendPacket(addon.createRegisterPayload());
|
||||
addon.sendPacket(new CommonRegisterPayload(addon.getNegotiatedVersion(), CommonRegisterPayload.PLAY_PHASE, ServerPlayNetworking.getGlobalReceivers()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,16 +20,18 @@ import java.util.HashSet;
|
|||
import java.util.Set;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public record CommonRegisterPayload(int version, String phase, Set<Identifier> channels) implements CustomPayload {
|
||||
public static final Identifier PACKET_ID = new Identifier("c", "register");
|
||||
public static final CustomPayload.Id<CommonRegisterPayload> ID = CustomPayload.id("c:register");
|
||||
public static final PacketCodec<PacketByteBuf, CommonRegisterPayload> CODEC = CustomPayload.codecOf(CommonRegisterPayload::write, CommonRegisterPayload::new);
|
||||
|
||||
public static final String PLAY_PHASE = "play";
|
||||
public static final String CONFIGURATION_PHASE = "configuration";
|
||||
|
||||
public CommonRegisterPayload(PacketByteBuf buf) {
|
||||
private CommonRegisterPayload(PacketByteBuf buf) {
|
||||
this(
|
||||
buf.readVarInt(),
|
||||
buf.readString(),
|
||||
|
@ -37,7 +39,6 @@ public record CommonRegisterPayload(int version, String phase, Set<Identifier> c
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeVarInt(version);
|
||||
buf.writeString(phase);
|
||||
|
@ -45,7 +46,7 @@ public record CommonRegisterPayload(int version, String phase, Set<Identifier> c
|
|||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return PACKET_ID;
|
||||
public Id<CommonRegisterPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,23 +17,23 @@
|
|||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public record CommonVersionPayload(int[] versions) implements CustomPayload {
|
||||
public static final Identifier PACKET_ID = new Identifier("c", "version");
|
||||
public static final PacketCodec<PacketByteBuf, CommonVersionPayload> CODEC = CustomPayload.codecOf(CommonVersionPayload::write, CommonVersionPayload::new);
|
||||
public static final CustomPayload.Id<CommonVersionPayload> ID = CustomPayload.id("c:version");
|
||||
|
||||
public CommonVersionPayload(PacketByteBuf buf) {
|
||||
private CommonVersionPayload(PacketByteBuf buf) {
|
||||
this(buf.readIntArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeIntArray(versions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return PACKET_ID;
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,16 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.container;
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
/**
|
||||
* This is a interface that is present on a ServerPlayerEntity, it allows access to the sync id.
|
||||
*/
|
||||
public interface ServerPlayerEntitySyncHook {
|
||||
/**
|
||||
* Gets and sets the new player sync id, and returns the new value.
|
||||
*
|
||||
* @return the new sync id of the player
|
||||
*/
|
||||
int fabric_incrementSyncId();
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public interface CustomPayloadTypeProvider<B extends PacketByteBuf> {
|
||||
CustomPayload.Type<B, ? extends CustomPayload> get(B packetByteBuf, Identifier identifier);
|
||||
}
|
|
@ -14,7 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.networking.payload;
|
||||
package net.fabricmc.fabric.impl.networking;
|
||||
|
||||
public sealed interface ResolvedPayload extends ResolvablePayload permits TypedPayload, UntypedPayload {
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
public interface FabricCustomPayloadPacketCodec<B extends PacketByteBuf> {
|
||||
void fabric_setPacketCodecProvider(CustomPayloadTypeProvider<B> customPayloadTypeProvider);
|
||||
}
|
|
@ -1,63 +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.util.Objects;
|
||||
|
||||
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.PacketCallbacks;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
|
||||
/**
|
||||
* We still need to support {@link GenericFutureListener} so we use this disguise impl {@link PacketCallbacks}
|
||||
* to get our {@link GenericFutureListener} to into {@link ClientConnection}.
|
||||
*/
|
||||
public final class GenericFutureListenerHolder implements PacketCallbacks {
|
||||
private final GenericFutureListener<? extends Future<? super Void>> delegate;
|
||||
|
||||
private GenericFutureListenerHolder(GenericFutureListener<? extends Future<? super Void>> delegate) {
|
||||
this.delegate = Objects.requireNonNull(delegate);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GenericFutureListenerHolder create(@Nullable GenericFutureListener<? extends Future<? super Void>> delegate) {
|
||||
if (delegate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GenericFutureListenerHolder(delegate);
|
||||
}
|
||||
|
||||
public GenericFutureListener<? extends Future<? super Void>> getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
throw new AssertionError("Should not be called");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Packet<?> getFailurePacket() {
|
||||
throw new AssertionError("Should not be called");
|
||||
}
|
||||
}
|
|
@ -29,25 +29,31 @@ import org.jetbrains.annotations.Nullable;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public final class GlobalReceiverRegistry<H> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalReceiverRegistry.class);
|
||||
|
||||
private final NetworkState state;
|
||||
private final NetworkSide side;
|
||||
private final NetworkPhase phase;
|
||||
@Nullable
|
||||
private final PayloadTypeRegistryImpl<?> payloadTypeRegistry;
|
||||
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Map<Identifier, H> handlers;
|
||||
private final Map<Identifier, H> handlers = new HashMap<>();
|
||||
private final Set<AbstractNetworkAddon<H>> trackedAddons = new HashSet<>();
|
||||
|
||||
public GlobalReceiverRegistry(NetworkState state) {
|
||||
this(state, new HashMap<>()); // sync map should be fine as there is little read write competitions
|
||||
}
|
||||
public GlobalReceiverRegistry(NetworkSide side, NetworkPhase phase, @Nullable PayloadTypeRegistryImpl<?> payloadTypeRegistry) {
|
||||
this.side = side;
|
||||
this.phase = phase;
|
||||
this.payloadTypeRegistry = payloadTypeRegistry;
|
||||
|
||||
public GlobalReceiverRegistry(NetworkState state, Map<Identifier, H> map) {
|
||||
this.state = state;
|
||||
this.handlers = map;
|
||||
if (payloadTypeRegistry != null) {
|
||||
assert phase == payloadTypeRegistry.getPhase();
|
||||
assert side == payloadTypeRegistry.getSide();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -70,6 +76,8 @@ public final class GlobalReceiverRegistry<H> {
|
|||
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
|
||||
}
|
||||
|
||||
assertPayloadType(channelName);
|
||||
|
||||
Lock lock = this.lock.writeLock();
|
||||
lock.lock();
|
||||
|
||||
|
@ -166,7 +174,7 @@ public final class GlobalReceiverRegistry<H> {
|
|||
*/
|
||||
private void logTrackedAddonSize() {
|
||||
if (LOGGER.isTraceEnabled() && this.trackedAddons.size() > 1) {
|
||||
LOGGER.trace("{} receiver registry tracks {} addon instances", state.getId(), trackedAddons.size());
|
||||
LOGGER.trace("{} receiver registry tracks {} addon instances", phase.getId(), trackedAddons.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +208,17 @@ public final class GlobalReceiverRegistry<H> {
|
|||
}
|
||||
}
|
||||
|
||||
public NetworkState getState() {
|
||||
return state;
|
||||
public void assertPayloadType(Identifier channelName) {
|
||||
if (payloadTypeRegistry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payloadTypeRegistry.get(channelName) == null) {
|
||||
throw new IllegalArgumentException(String.format("Cannot register handler as no payload type has been registered with name \"%s\" for %s %s", channelName, side, phase));
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkPhase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,23 +21,12 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
|
||||
public final class NetworkingImpl {
|
||||
public static final String MOD_ID = "fabric-networking-api-v1";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
/**
|
||||
* Force {@link TypedPayload} to be serialized into {@link UntypedPayload}, mimicking remote connection.
|
||||
*
|
||||
* <p>Defaults to {@code true} in dev env and {@code false} in production.
|
||||
*/
|
||||
public static final boolean FORCE_PACKET_SERIALIZATION = Boolean.parseBoolean(System.getProperty(
|
||||
"fabric-api.networking.force-packet-serialization",
|
||||
Boolean.toString(FabricLoader.getInstance().isDevelopmentEnvironment())));
|
||||
|
||||
/**
|
||||
* Id of packet used to register supported channels.
|
||||
*/
|
||||
|
@ -48,15 +37,18 @@ public final class NetworkingImpl {
|
|||
*/
|
||||
public static final Identifier UNREGISTER_CHANNEL = new Identifier("minecraft", "unregister");
|
||||
|
||||
public static final ThreadLocal<Boolean> FACTORY_RETAIN = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||
|
||||
public static boolean isReservedCommonChannel(Identifier channelName) {
|
||||
return channelName.equals(REGISTER_CHANNEL) || channelName.equals(UNREGISTER_CHANNEL);
|
||||
}
|
||||
|
||||
static {
|
||||
if (FORCE_PACKET_SERIALIZATION) {
|
||||
LOGGER.info("Force Packet Serialization is enabled to mimic remote connection on single player, this is the default behaviour on dev env. Add -Dfabric-api.networking.force-packet-serialization=false JVM arg to disable it.");
|
||||
}
|
||||
public static void init() {
|
||||
PayloadTypeRegistry.configurationS2C().register(RegistrationPayload.REGISTER, RegistrationPayload.REGISTER_CODEC);
|
||||
PayloadTypeRegistry.configurationS2C().register(RegistrationPayload.UNREGISTER, RegistrationPayload.UNREGISTER_CODEC);
|
||||
PayloadTypeRegistry.configurationC2S().register(RegistrationPayload.REGISTER, RegistrationPayload.REGISTER_CODEC);
|
||||
PayloadTypeRegistry.configurationC2S().register(RegistrationPayload.UNREGISTER, RegistrationPayload.UNREGISTER_CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(RegistrationPayload.REGISTER, RegistrationPayload.REGISTER_CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(RegistrationPayload.UNREGISTER, RegistrationPayload.UNREGISTER_CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(RegistrationPayload.REGISTER, RegistrationPayload.REGISTER_CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(RegistrationPayload.UNREGISTER, RegistrationPayload.UNREGISTER_CODEC);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
|
||||
public class PayloadTypeRegistryImpl<B extends PacketByteBuf> implements PayloadTypeRegistry<B> {
|
||||
public static final PayloadTypeRegistryImpl<PacketByteBuf> CONFIGURATION_C2S = new PayloadTypeRegistryImpl<>(NetworkPhase.CONFIGURATION, NetworkSide.SERVERBOUND);
|
||||
public static final PayloadTypeRegistryImpl<PacketByteBuf> CONFIGURATION_S2C = new PayloadTypeRegistryImpl<>(NetworkPhase.CONFIGURATION, NetworkSide.CLIENTBOUND);
|
||||
public static final PayloadTypeRegistryImpl<RegistryByteBuf> PLAY_C2S = new PayloadTypeRegistryImpl<>(NetworkPhase.PLAY, NetworkSide.SERVERBOUND);
|
||||
public static final PayloadTypeRegistryImpl<RegistryByteBuf> PLAY_S2C = new PayloadTypeRegistryImpl<>(NetworkPhase.PLAY, NetworkSide.CLIENTBOUND);
|
||||
|
||||
private final Map<Identifier, CustomPayload.Type<B, ? extends CustomPayload>> packetTypes = new HashMap<>();
|
||||
private final NetworkPhase state;
|
||||
private final NetworkSide side;
|
||||
|
||||
private PayloadTypeRegistryImpl(NetworkPhase state, NetworkSide side) {
|
||||
this.state = state;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends CustomPayload> CustomPayload.Type<? super B, T> register(CustomPayload.Id<T> id, PacketCodec<? super B, T> codec) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(codec, "codec");
|
||||
|
||||
final CustomPayload.Type<B, T> payloadType = new CustomPayload.Type<>(id, codec.cast());
|
||||
|
||||
if (packetTypes.containsKey(id.id())) {
|
||||
throw new IllegalArgumentException("Packet type " + id + " is already registered!");
|
||||
}
|
||||
|
||||
packetTypes.put(id.id(), payloadType);
|
||||
return payloadType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CustomPayload.Type<B, ? extends CustomPayload> get(Identifier id) {
|
||||
return packetTypes.get(id);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends CustomPayload> CustomPayload.Type<B, T> get(CustomPayload.Id<T> id) {
|
||||
//noinspection unchecked
|
||||
return (CustomPayload.Type<B, T>) packetTypes.get(id.id());
|
||||
}
|
||||
|
||||
public NetworkPhase getPhase() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public NetworkSide getSide() {
|
||||
return side;
|
||||
}
|
||||
}
|
|
@ -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.impl.networking;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.netty.util.AsciiString;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
|
||||
public record RegistrationPayload(Id<RegistrationPayload> id, List<Identifier> channels) implements CustomPayload {
|
||||
public static final CustomPayload.Id<RegistrationPayload> REGISTER = new CustomPayload.Id<>(NetworkingImpl.REGISTER_CHANNEL);
|
||||
public static final CustomPayload.Id<RegistrationPayload> UNREGISTER = new CustomPayload.Id<>(NetworkingImpl.UNREGISTER_CHANNEL);
|
||||
public static final PacketCodec<PacketByteBuf, RegistrationPayload> REGISTER_CODEC = codec(REGISTER);
|
||||
public static final PacketCodec<PacketByteBuf, RegistrationPayload> UNREGISTER_CODEC = codec(UNREGISTER);
|
||||
|
||||
private RegistrationPayload(Id<RegistrationPayload> id, PacketByteBuf buf) {
|
||||
this(id, read(buf));
|
||||
}
|
||||
|
||||
private void write(PacketByteBuf buf) {
|
||||
boolean first = true;
|
||||
|
||||
for (Identifier channel : channels) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
buf.writeByte(0);
|
||||
}
|
||||
|
||||
buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Identifier> read(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 {
|
||||
addId(ids, active);
|
||||
active = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
addId(ids, active);
|
||||
|
||||
return Collections.unmodifiableList(ids);
|
||||
}
|
||||
|
||||
private static void addId(List<Identifier> ids, StringBuilder sb) {
|
||||
String literal = sb.toString();
|
||||
|
||||
try {
|
||||
ids.add(new Identifier(literal));
|
||||
} catch (InvalidIdentifierException ex) {
|
||||
NetworkingImpl.LOGGER.warn("Received invalid channel identifier \"{}\"", literal);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private static PacketCodec<PacketByteBuf, RegistrationPayload> codec(Id<RegistrationPayload> id) {
|
||||
return CustomPayload.codecOf(RegistrationPayload::write, buf -> new RegistrationPayload(id, buf));
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
package net.fabricmc.fabric.impl.networking.payload;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
|
||||
|
@ -35,18 +34,6 @@ public class PayloadHelper {
|
|||
return newBuf;
|
||||
}
|
||||
|
||||
public static ResolvablePayload readCustom(Identifier id, PacketByteBuf buf, int maxSize, boolean retain) {
|
||||
assertSize(buf, maxSize);
|
||||
|
||||
if (retain) {
|
||||
RetainedPayload payload = new RetainedPayload(id, PacketByteBufs.retainedSlice(buf));
|
||||
buf.skipBytes(buf.readableBytes());
|
||||
return payload;
|
||||
} else {
|
||||
return new UntypedPayload(id, read(buf, maxSize));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertSize(PacketByteBuf buf, int maxSize) {
|
||||
int size = buf.readableBytes();
|
||||
|
||||
|
|
|
@ -1,40 +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.payload;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
|
||||
public sealed interface ResolvablePayload extends CustomPayload permits ResolvedPayload, RetainedPayload {
|
||||
/**
|
||||
* Resolve the payload to one of the resolved types.
|
||||
*
|
||||
* @return {@link UntypedPayload} if type is {@code null}, {@link TypedPayload} if otherwise.
|
||||
*/
|
||||
ResolvedPayload resolve(@Nullable PacketType<?> type);
|
||||
|
||||
/**
|
||||
* @param type the packet type, if it has any
|
||||
* @param actual the public handler that exposed to API consumer
|
||||
* @param internal the internal handler
|
||||
*/
|
||||
record Handler<H>(@Nullable PacketType<?> type, Object actual, H internal) {
|
||||
}
|
||||
}
|
|
@ -1,54 +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.payload;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.PacketType;
|
||||
|
||||
public record RetainedPayload(Identifier id, PacketByteBuf buf) implements ResolvablePayload {
|
||||
@Override
|
||||
public ResolvedPayload resolve(@Nullable PacketType<?> type) {
|
||||
try {
|
||||
if (type == null) {
|
||||
PacketByteBuf copy = PacketByteBufs.create();
|
||||
copy.writeBytes(buf);
|
||||
return new UntypedPayload(this.id, copy);
|
||||
} else {
|
||||
TypedPayload typed = new TypedPayload(type.read(buf));
|
||||
int dangling = buf.readableBytes();
|
||||
|
||||
if (dangling > 0) {
|
||||
throw new IllegalStateException("Found " + dangling + " extra bytes when reading packet " + id);
|
||||
}
|
||||
|
||||
return typed;
|
||||
}
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
throw new UnsupportedOperationException("RetainedPayload shouldn't be used to send");
|
||||
}
|
||||
}
|
|
@ -1,49 +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.payload;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
|
||||
public record TypedPayload(FabricPacket packet) implements ResolvedPayload {
|
||||
@Override
|
||||
public ResolvedPayload resolve(@Nullable PacketType<?> type) {
|
||||
if (type == null) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
write(buf);
|
||||
return new UntypedPayload(packet.getType().getId(), buf);
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
packet.write(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier id() {
|
||||
return packet.getType().getId();
|
||||
}
|
||||
}
|
|
@ -1,54 +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.payload;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.PacketType;
|
||||
|
||||
public record UntypedPayload(Identifier id, PacketByteBuf buffer) implements ResolvedPayload {
|
||||
@Override
|
||||
public ResolvedPayload resolve(@Nullable PacketType<?> type) {
|
||||
if (type == null) {
|
||||
return this;
|
||||
} else {
|
||||
PacketByteBuf copy = PacketByteBufs.copy(buffer);
|
||||
TypedPayload typed = new TypedPayload(type.read(copy));
|
||||
int dangling = copy.readableBytes();
|
||||
|
||||
if (dangling > 0) {
|
||||
throw new IllegalStateException("Found " + dangling + " extra bytes when reading packet " + id);
|
||||
}
|
||||
|
||||
return typed;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeBytes(buffer.copy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketByteBuf buffer() {
|
||||
return PacketByteBufs.copy(buffer);
|
||||
}
|
||||
}
|
|
@ -19,39 +19,40 @@ package net.fabricmc.fabric.impl.networking.server;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.common.CommonPingS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.S2CConfigurationChannelEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractChanneledNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.RegistrationPayload;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerCommonNetworkHandlerAccessor;
|
||||
|
||||
public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetworkAddon<ServerConfigurationNetworkAddon.Handler> {
|
||||
public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetworkAddon<ServerConfigurationNetworking.ConfigurationPacketHandler<?>> {
|
||||
private final ServerConfigurationNetworkHandler handler;
|
||||
private final MinecraftServer server;
|
||||
private final ServerConfigurationNetworking.Context context;
|
||||
private RegisterState registerState = RegisterState.NOT_SENT;
|
||||
|
||||
public ServerConfigurationNetworkAddon(ServerConfigurationNetworkHandler handler, MinecraftServer server) {
|
||||
super(ServerNetworkingImpl.CONFIGURATION, ((ServerCommonNetworkHandlerAccessor) handler).getConnection(), "ServerConfigurationNetworkAddon for " + handler.getDebugProfile().getName());
|
||||
this.handler = handler;
|
||||
this.server = server;
|
||||
this.context = new ContextImpl(handler, this);
|
||||
|
||||
// Must register pending channels via lateinit
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkState.CONFIGURATION);
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkPhase.CONFIGURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -84,7 +85,7 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void receiveRegistration(boolean register, ResolvablePayload resolvable) {
|
||||
protected void receiveRegistration(boolean register, RegistrationPayload resolvable) {
|
||||
super.receiveRegistration(register, resolvable);
|
||||
|
||||
if (register && registerState == RegisterState.SENT) {
|
||||
|
@ -103,8 +104,8 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void receive(Handler handler, ResolvedPayload payload) {
|
||||
handler.receive(this.server, this.handler, payload, this);
|
||||
protected void receive(ServerConfigurationNetworking.ConfigurationPacketHandler<?> handler, CustomPayload payload) {
|
||||
((ServerConfigurationNetworking.ConfigurationPacketHandler) handler).receive(payload, this.context);
|
||||
}
|
||||
|
||||
// impl details
|
||||
|
@ -115,12 +116,7 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return ServerPlayNetworking.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
public Packet<?> createPacket(CustomPayload packet) {
|
||||
return ServerPlayNetworking.createS2CPacket(packet);
|
||||
}
|
||||
|
||||
|
@ -138,10 +134,10 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
protected void handleRegistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the register packet for this channel
|
||||
if (this.registerState != RegisterState.NOT_SENT) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
RegistrationPayload registrationPayload = this.createRegistrationPayload(RegistrationPayload.REGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
if (registrationPayload != null) {
|
||||
this.sendPacket(registrationPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,10 +146,10 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.registerState != RegisterState.NOT_SENT) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
RegistrationPayload registrationPayload = this.createRegistrationPayload(RegistrationPayload.UNREGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
|
||||
if (registrationPayload != null) {
|
||||
this.sendPacket(registrationPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +180,6 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
|
|||
return (ChannelInfoHolder) ((ServerCommonNetworkHandlerAccessor) handler).getConnection();
|
||||
}
|
||||
|
||||
public interface Handler {
|
||||
void receive(MinecraftServer server, ServerConfigurationNetworkHandler handler, ResolvedPayload payload, PacketSender responseSender);
|
||||
private record ContextImpl(ServerConfigurationNetworkHandler networkHandler, PacketSender responseSender) implements ServerConfigurationNetworking.Context {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,33 +26,31 @@ 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.PacketByteBuf;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
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.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.LoginPacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.AbstractNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.GenericFutureListenerHolder;
|
||||
import net.fabricmc.fabric.impl.networking.payload.FabricPacketLoginQueryRequestPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufLoginQueryRequestPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PacketByteBufLoginQueryResponse;
|
||||
import net.fabricmc.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
|
||||
|
||||
public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLoginNetworking.LoginQueryResponseHandler> implements PacketSender {
|
||||
public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLoginNetworking.LoginQueryResponseHandler> implements LoginPacketSender {
|
||||
private final ClientConnection connection;
|
||||
private final ServerLoginNetworkHandler handler;
|
||||
private final MinecraftServer server;
|
||||
|
@ -160,23 +158,17 @@ public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLo
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(CustomPayload packet) {
|
||||
throw new UnsupportedOperationException("Cannot send CustomPayload during login");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
int queryId = this.queryIdFactory.nextId();
|
||||
return new LoginQueryRequestS2CPacket(queryId, new PacketByteBufLoginQueryRequestPayload(channelName, buf));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
int queryId = this.queryIdFactory.nextId();
|
||||
return new LoginQueryRequestS2CPacket(queryId, new FabricPacketLoginQueryRequestPayload(packet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends io.netty.util.concurrent.Future<? super Void>> callback) {
|
||||
sendPacket(packet, GenericFutureListenerHolder.create(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet<?> packet, PacketCallbacks callback) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
|
@ -184,6 +176,13 @@ public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLo
|
|||
this.connection.send(packet, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(Text disconnectReason) {
|
||||
Objects.requireNonNull(disconnectReason, "Disconnect reason cannot be null");
|
||||
|
||||
this.connection.disconnect(disconnectReason);
|
||||
}
|
||||
|
||||
public void registerOutgoingPacket(LoginQueryRequestS2CPacket packet) {
|
||||
this.channels.put(packet.queryId(), packet.payload().id());
|
||||
}
|
||||
|
|
|
@ -18,30 +18,27 @@ package net.fabricmc.fabric.impl.networking.server;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.network.listener.ClientCommonPacketListener;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.TypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.PayloadTypeRegistryImpl;
|
||||
|
||||
public final class ServerNetworkingImpl {
|
||||
public static final GlobalReceiverRegistry<ServerLoginNetworking.LoginQueryResponseHandler> LOGIN = new GlobalReceiverRegistry<>(NetworkState.LOGIN);
|
||||
public static final GlobalReceiverRegistry<ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler>> CONFIGURATION = new GlobalReceiverRegistry<>(NetworkState.CONFIGURATION);
|
||||
public static final GlobalReceiverRegistry<ResolvablePayload.Handler<ServerPlayNetworkAddon.Handler>> PLAY = new GlobalReceiverRegistry<>(NetworkState.PLAY);
|
||||
public static final GlobalReceiverRegistry<ServerLoginNetworking.LoginQueryResponseHandler> LOGIN = new GlobalReceiverRegistry<>(NetworkSide.SERVERBOUND, NetworkPhase.LOGIN, null);
|
||||
public static final GlobalReceiverRegistry<ServerConfigurationNetworking.ConfigurationPacketHandler<?>> CONFIGURATION = new GlobalReceiverRegistry<>(NetworkSide.SERVERBOUND, NetworkPhase.CONFIGURATION, PayloadTypeRegistryImpl.CONFIGURATION_C2S);
|
||||
public static final GlobalReceiverRegistry<ServerPlayNetworking.PlayPayloadHandler<?>> PLAY = new GlobalReceiverRegistry<>(NetworkSide.SERVERBOUND, NetworkPhase.PLAY, PayloadTypeRegistryImpl.PLAY_C2S);
|
||||
|
||||
public static ServerPlayNetworkAddon getAddon(ServerPlayNetworkHandler handler) {
|
||||
return (ServerPlayNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
|
@ -55,16 +52,9 @@ public final class ServerNetworkingImpl {
|
|||
return (ServerConfigurationNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
|
||||
}
|
||||
|
||||
public static Packet<ClientCommonPacketListener> createS2CPacket(Identifier channel, PacketByteBuf buf) {
|
||||
return new CustomPayloadS2CPacket(new UntypedPayload(channel, buf));
|
||||
}
|
||||
|
||||
public static Packet<ClientCommonPacketListener> createS2CPacket(FabricPacket packet) {
|
||||
Objects.requireNonNull(packet, "Packet cannot be null");
|
||||
Objects.requireNonNull(packet.getType(), "Packet#getType cannot return null");
|
||||
|
||||
ResolvedPayload payload = new TypedPayload(packet);
|
||||
if (NetworkingImpl.FORCE_PACKET_SERIALIZATION) payload = payload.resolve(null);
|
||||
public static Packet<ClientCommonPacketListener> createS2CPacket(CustomPayload payload) {
|
||||
Objects.requireNonNull(payload, "Payload cannot be null");
|
||||
Objects.requireNonNull(payload.getId(), "CustomPayload#getId() cannot return null for payload class: " + payload.getClass());
|
||||
|
||||
return new CustomPayloadS2CPacket(payload);
|
||||
}
|
||||
|
|
|
@ -20,15 +20,14 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
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.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.S2CPlayChannelEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
|
@ -36,20 +35,22 @@ 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.impl.networking.payload.ResolvedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.RegistrationPayload;
|
||||
|
||||
public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<ServerPlayNetworkAddon.Handler> {
|
||||
public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<ServerPlayNetworking.PlayPayloadHandler<?>> {
|
||||
private final ServerPlayNetworkHandler handler;
|
||||
private final MinecraftServer server;
|
||||
private boolean sentInitialRegisterPacket;
|
||||
private final ServerPlayNetworking.Context context;
|
||||
|
||||
public ServerPlayNetworkAddon(ServerPlayNetworkHandler handler, ClientConnection connection, MinecraftServer server) {
|
||||
super(ServerNetworkingImpl.PLAY, connection, "ServerPlayNetworkAddon for " + handler.player.getDisplayName());
|
||||
this.handler = handler;
|
||||
this.server = server;
|
||||
this.context = new ContextImpl(handler.player, this);
|
||||
|
||||
// Must register pending channels via lateinit
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkState.PLAY);
|
||||
this.registerPendingChannels((ChannelInfoHolder) this.connection, NetworkPhase.PLAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,8 +66,10 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void receive(Handler handler, ResolvedPayload payload) {
|
||||
handler.receive(this.server, this.handler.player, this.handler, payload, this);
|
||||
protected void receive(ServerPlayNetworking.PlayPayloadHandler<?> payloadHandler, CustomPayload payload) {
|
||||
this.server.execute(() -> {
|
||||
((ServerPlayNetworking.PlayPayloadHandler) payloadHandler).receive(payload, ServerPlayNetworkAddon.this.context);
|
||||
});
|
||||
}
|
||||
|
||||
// impl details
|
||||
|
@ -77,12 +80,7 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(Identifier channelName, PacketByteBuf buf) {
|
||||
return ServerPlayNetworking.createS2CPacket(channelName, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createPacket(FabricPacket packet) {
|
||||
public Packet<?> createPacket(CustomPayload packet) {
|
||||
return ServerPlayNetworking.createS2CPacket(packet);
|
||||
}
|
||||
|
||||
|
@ -100,10 +98,10 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
protected void handleRegistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the register packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
RegistrationPayload registrationPayload = this.createRegistrationPayload(RegistrationPayload.REGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
|
||||
if (registrationPayload != null) {
|
||||
this.sendPacket(registrationPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +110,10 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
protected void handleUnregistration(Identifier channelName) {
|
||||
// If we can already send packets, immediately send the unregister packet for this channel
|
||||
if (this.sentInitialRegisterPacket) {
|
||||
final PacketByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
|
||||
RegistrationPayload registrationPayload = this.createRegistrationPayload(RegistrationPayload.UNREGISTER, Collections.singleton(channelName));
|
||||
|
||||
if (buf != null) {
|
||||
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
|
||||
if (registrationPayload != null) {
|
||||
this.sendPacket(registrationPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +128,6 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
|
|||
return NetworkingImpl.isReservedCommonChannel(channelName);
|
||||
}
|
||||
|
||||
public interface Handler {
|
||||
void receive(MinecraftServer server, ServerPlayerEntity player, ServerPlayNetworkHandler handler, ResolvedPayload payload, PacketSender responseSender);
|
||||
private record ContextImpl(ServerPlayerEntity player, PacketSender responseSender) implements ServerPlayNetworking.Context {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ import java.util.Collections;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
@ -32,9 +30,9 @@ 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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
|
@ -45,7 +43,6 @@ 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.GenericFutureListenerHolder;
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.PacketCallbackListener;
|
||||
|
||||
|
@ -61,7 +58,7 @@ abstract class ClientConnectionMixin implements ChannelInfoHolder {
|
|||
public abstract void send(Packet<?> packet, @Nullable PacketCallbacks arg);
|
||||
|
||||
@Unique
|
||||
private Map<NetworkState, Collection<Identifier>> playChannels;
|
||||
private Map<NetworkPhase, Collection<Identifier>> playChannels;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void initAddedFields(NetworkSide side, CallbackInfo ci) {
|
||||
|
@ -89,7 +86,7 @@ abstract class ClientConnectionMixin implements ChannelInfoHolder {
|
|||
}
|
||||
|
||||
@Inject(method = "setPacketListener", at = @At("HEAD"))
|
||||
private void unwatchAddon(PacketListener packetListener, CallbackInfo ci) {
|
||||
private void unwatchAddon(NetworkState<?> state, PacketListener listener, CallbackInfo ci) {
|
||||
if (this.packetListener instanceof NetworkHandlerExtensions oldListener) {
|
||||
oldListener.getAddon().endSession();
|
||||
}
|
||||
|
@ -109,17 +106,8 @@ abstract class ClientConnectionMixin implements ChannelInfoHolder {
|
|||
}
|
||||
}
|
||||
|
||||
@Inject(method = "sendInternal", at = @At(value = "INVOKE", target = "Lio/netty/channel/ChannelFuture;addListener(Lio/netty/util/concurrent/GenericFutureListener;)Lio/netty/channel/ChannelFuture;", remap = false), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
|
||||
private void sendInternal(Packet<?> packet, @Nullable PacketCallbacks callbacks, boolean flush, CallbackInfo ci, ChannelFuture channelFuture) {
|
||||
if (callbacks instanceof GenericFutureListenerHolder holder) {
|
||||
channelFuture.addListener(holder.getDelegate());
|
||||
channelFuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getPendingChannelsNames(NetworkState state) {
|
||||
public Collection<Identifier> fabric_getPendingChannelsNames(NetworkPhase state) {
|
||||
return this.playChannels.computeIfAbsent(state, (key) -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,33 +16,42 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.networking;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import java.util.List;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PayloadHelper;
|
||||
import net.fabricmc.fabric.impl.networking.FabricCustomPayloadPacketCodec;
|
||||
import net.fabricmc.fabric.impl.networking.PayloadTypeRegistryImpl;
|
||||
|
||||
@Mixin(CustomPayloadC2SPacket.class)
|
||||
public class CustomPayloadC2SPacketMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private static int MAX_PAYLOAD_SIZE;
|
||||
|
||||
@Inject(
|
||||
method = "readPayload",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/c2s/common/CustomPayloadC2SPacket;readUnknownPayload(Lnet/minecraft/util/Identifier;Lnet/minecraft/network/PacketByteBuf;)Lnet/minecraft/network/packet/UnknownCustomPayload;"),
|
||||
cancellable = true
|
||||
@WrapOperation(
|
||||
method = "<clinit>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/network/packet/CustomPayload;createCodec(Lnet/minecraft/network/packet/CustomPayload$CodecFactory;Ljava/util/List;)Lnet/minecraft/network/codec/PacketCodec;"
|
||||
)
|
||||
)
|
||||
private static void readPayload(Identifier id, PacketByteBuf buf, CallbackInfoReturnable<CustomPayload> cir) {
|
||||
cir.setReturnValue(PayloadHelper.readCustom(id, buf, MAX_PAYLOAD_SIZE, NetworkingImpl.FACTORY_RETAIN.get()));
|
||||
private static PacketCodec<PacketByteBuf, CustomPayload> wrapCodec(CustomPayload.CodecFactory<PacketByteBuf> unknownCodecFactory, List<CustomPayload.Type<PacketByteBuf, ?>> types, Operation<PacketCodec<PacketByteBuf, CustomPayload>> original) {
|
||||
PacketCodec<PacketByteBuf, CustomPayload> codec = original.call(unknownCodecFactory, types);
|
||||
FabricCustomPayloadPacketCodec<PacketByteBuf> fabricCodec = (FabricCustomPayloadPacketCodec<PacketByteBuf>) codec;
|
||||
fabricCodec.fabric_setPacketCodecProvider((packetByteBuf, identifier) -> {
|
||||
// CustomPayloadC2SPacket does not have a separate codec for play/configuration. We know if the packetByteBuf is a PacketByteBuf we are in the play phase.
|
||||
if (packetByteBuf instanceof RegistryByteBuf) {
|
||||
return (CustomPayload.Type<PacketByteBuf, ? extends CustomPayload>) (Object) PayloadTypeRegistryImpl.PLAY_C2S.get(identifier);
|
||||
}
|
||||
|
||||
return PayloadTypeRegistryImpl.CONFIGURATION_C2S.get(identifier);
|
||||
});
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.mixin.networking;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.CustomPayloadTypeProvider;
|
||||
import net.fabricmc.fabric.impl.networking.FabricCustomPayloadPacketCodec;
|
||||
|
||||
@Mixin(targets = "net/minecraft/network/packet/CustomPayload$1")
|
||||
public abstract class CustomPayloadPacketCodecMixin<B extends PacketByteBuf> implements PacketCodec<B, CustomPayload>, FabricCustomPayloadPacketCodec<B> {
|
||||
@Unique
|
||||
private CustomPayloadTypeProvider<B> customPayloadTypeProvider;
|
||||
|
||||
@Override
|
||||
public void fabric_setPacketCodecProvider(CustomPayloadTypeProvider<B> customPayloadTypeProvider) {
|
||||
if (this.customPayloadTypeProvider != null) {
|
||||
throw new IllegalStateException("Payload codec provider is already set!");
|
||||
}
|
||||
|
||||
this.customPayloadTypeProvider = customPayloadTypeProvider;
|
||||
}
|
||||
|
||||
@WrapOperation(method = {
|
||||
"encode(Lnet/minecraft/network/PacketByteBuf;Lnet/minecraft/network/packet/CustomPayload$Id;Lnet/minecraft/network/packet/CustomPayload;)V",
|
||||
"decode(Lnet/minecraft/network/PacketByteBuf;)Lnet/minecraft/network/packet/CustomPayload;"
|
||||
}, at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/CustomPayload$1;getCodec(Lnet/minecraft/util/Identifier;)Lnet/minecraft/network/codec/PacketCodec;"))
|
||||
private PacketCodec<B, ? extends CustomPayload> wrapGetCodec(@Coerce PacketCodec<B, CustomPayload> instance, Identifier identifier, Operation<PacketCodec<B, CustomPayload>> original, B packetByteBuf) {
|
||||
if (customPayloadTypeProvider != null) {
|
||||
CustomPayload.Type<B, ? extends CustomPayload> payloadType = customPayloadTypeProvider.get(packetByteBuf, identifier);
|
||||
|
||||
if (payloadType != null) {
|
||||
return payloadType.codec();
|
||||
}
|
||||
}
|
||||
|
||||
return original.call(instance, identifier);
|
||||
}
|
||||
}
|
|
@ -16,33 +16,51 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.networking;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import java.util.List;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.PayloadHelper;
|
||||
import net.fabricmc.fabric.impl.networking.FabricCustomPayloadPacketCodec;
|
||||
import net.fabricmc.fabric.impl.networking.PayloadTypeRegistryImpl;
|
||||
|
||||
@Mixin(CustomPayloadS2CPacket.class)
|
||||
public class CustomPayloadS2CPacketMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private static int MAX_PAYLOAD_SIZE;
|
||||
|
||||
@Inject(
|
||||
method = "readPayload",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/common/CustomPayloadS2CPacket;readUnknownPayload(Lnet/minecraft/util/Identifier;Lnet/minecraft/network/PacketByteBuf;)Lnet/minecraft/network/packet/UnknownCustomPayload;"),
|
||||
cancellable = true
|
||||
@WrapOperation(
|
||||
method = "<clinit>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/network/packet/CustomPayload;createCodec(Lnet/minecraft/network/packet/CustomPayload$CodecFactory;Ljava/util/List;)Lnet/minecraft/network/codec/PacketCodec;",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private static void readPayload(Identifier id, PacketByteBuf buf, CallbackInfoReturnable<CustomPayload> cir) {
|
||||
cir.setReturnValue(PayloadHelper.readCustom(id, buf, MAX_PAYLOAD_SIZE, NetworkingImpl.FACTORY_RETAIN.get()));
|
||||
private static PacketCodec<RegistryByteBuf, CustomPayload> wrapPlayCodec(CustomPayload.CodecFactory<RegistryByteBuf> unknownCodecFactory, List<CustomPayload.Type<RegistryByteBuf, ?>> types, Operation<PacketCodec<RegistryByteBuf, CustomPayload>> original) {
|
||||
PacketCodec<RegistryByteBuf, CustomPayload> codec = original.call(unknownCodecFactory, types);
|
||||
FabricCustomPayloadPacketCodec<RegistryByteBuf> fabricCodec = (FabricCustomPayloadPacketCodec<RegistryByteBuf>) codec;
|
||||
fabricCodec.fabric_setPacketCodecProvider((packetByteBuf, identifier) -> PayloadTypeRegistryImpl.PLAY_S2C.get(identifier));
|
||||
return codec;
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "<clinit>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/network/packet/CustomPayload;createCodec(Lnet/minecraft/network/packet/CustomPayload$CodecFactory;Ljava/util/List;)Lnet/minecraft/network/codec/PacketCodec;",
|
||||
ordinal = 1
|
||||
)
|
||||
)
|
||||
private static PacketCodec<PacketByteBuf, CustomPayload> wrapConfigCodec(CustomPayload.CodecFactory<PacketByteBuf> unknownCodecFactory, List<CustomPayload.Type<PacketByteBuf, ?>> types, Operation<PacketCodec<PacketByteBuf, CustomPayload>> original) {
|
||||
PacketCodec<PacketByteBuf, CustomPayload> codec = original.call(unknownCodecFactory, types);
|
||||
FabricCustomPayloadPacketCodec<PacketByteBuf> fabricCodec = (FabricCustomPayloadPacketCodec<PacketByteBuf>) codec;
|
||||
fabricCodec.fabric_setPacketCodecProvider((packetByteBuf, identifier) -> PayloadTypeRegistryImpl.CONFIGURATION_S2C.get(identifier));
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +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.function.Function;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.NetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.RetainedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.UntypedPayload;
|
||||
|
||||
@Mixin(targets = "net.minecraft.network.NetworkState$InternalPacketHandler")
|
||||
public class NetworkStateInternalPacketHandlerMixin {
|
||||
/**
|
||||
* Only retain custom packet buffer to {@link RetainedPayload} on the receiving side,
|
||||
* otherwise resolve to {@link UntypedPayload}.
|
||||
*/
|
||||
@ModifyVariable(method = "register", at = @At("HEAD"), argsOnly = true)
|
||||
private Function<PacketByteBuf, Packet<?>> replaceCustomPayloadFactory(Function<PacketByteBuf, Packet<?>> original, Class<?> type) {
|
||||
if (type == CustomPayloadC2SPacket.class) {
|
||||
return buf -> {
|
||||
try {
|
||||
NetworkingImpl.FACTORY_RETAIN.set(true);
|
||||
return new CustomPayloadC2SPacket(buf);
|
||||
} finally {
|
||||
NetworkingImpl.FACTORY_RETAIN.set(false);
|
||||
}
|
||||
};
|
||||
} else if (type == CustomPayloadS2CPacket.class) {
|
||||
return buf -> {
|
||||
try {
|
||||
NetworkingImpl.FACTORY_RETAIN.set(true);
|
||||
return new CustomPayloadS2CPacket(buf);
|
||||
} finally {
|
||||
NetworkingImpl.FACTORY_RETAIN.set(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
}
|
|
@ -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.mixin.networking;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
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.codec.PacketCodec;
|
||||
import net.minecraft.network.handler.PacketCodecDispatcher;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
|
||||
@Mixin(PacketCodecDispatcher.class)
|
||||
public abstract class PacketCodecDispatcherMixin<B extends ByteBuf, V, T> implements PacketCodec<B, V> {
|
||||
// Add the custom payload id to the error message
|
||||
@Inject(method = "encode(Lio/netty/buffer/ByteBuf;Ljava/lang/Object;)V", at = @At(value = "NEW", target = "(Ljava/lang/String;Ljava/lang/Throwable;)Lio/netty/handler/codec/EncoderException;"))
|
||||
public void encode(B byteBuf, V packet, CallbackInfo ci, @Local(ordinal = 1) T packetId, @Local Exception e) {
|
||||
CustomPayload payload = null;
|
||||
|
||||
if (packet instanceof CustomPayloadC2SPacket customPayloadC2SPacket) {
|
||||
payload = customPayloadC2SPacket.payload();
|
||||
} else if (packet instanceof CustomPayloadS2CPacket customPayloadS2CPacket) {
|
||||
payload = customPayloadS2CPacket.payload();
|
||||
}
|
||||
|
||||
if (payload != null && payload.getId() != null) {
|
||||
throw new EncoderException("Failed to encode packet '%s' (%s)".formatted(packetId, payload.getId().id().toString()), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,13 +21,12 @@ 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.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.c2s.common.CommonPongC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.server.network.ServerCommonNetworkHandler;
|
||||
|
||||
import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.payload.RetainedPayload;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon;
|
||||
|
||||
|
@ -35,23 +34,20 @@ import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon;
|
|||
public abstract class ServerCommonNetworkHandlerMixin implements NetworkHandlerExtensions {
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
private void handleCustomPayloadReceivedAsync(CustomPayloadC2SPacket packet, CallbackInfo ci) {
|
||||
if (packet.payload() instanceof ResolvablePayload payload) {
|
||||
boolean handled;
|
||||
final CustomPayload payload = packet.payload();
|
||||
|
||||
if (getAddon() instanceof ServerPlayNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else if (getAddon() instanceof ServerConfigurationNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown addon");
|
||||
}
|
||||
boolean handled;
|
||||
|
||||
if (handled) {
|
||||
ci.cancel();
|
||||
} else if (payload instanceof RetainedPayload retained) {
|
||||
retained.buf().skipBytes(retained.buf().readableBytes());
|
||||
retained.buf().release();
|
||||
}
|
||||
if (getAddon() instanceof ServerPlayNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else if (getAddon() instanceof ServerConfigurationNetworkAddon addon) {
|
||||
handled = addon.handle(payload);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown addon");
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
accessWidener v2 named
|
||||
|
||||
accessible class net/minecraft/network/NetworkState$InternalPacketHandler
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
"package": "net.fabricmc.fabric.mixin.networking",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"PacketCodecDispatcherMixin",
|
||||
"ClientConnectionMixin",
|
||||
"CommandManagerMixin",
|
||||
"CustomPayloadC2SPacketMixin",
|
||||
"CustomPayloadS2CPacketMixin",
|
||||
"CustomPayloadPacketCodecMixin",
|
||||
"EntityTrackerEntryMixin",
|
||||
"LoginQueryRequestS2CPacketMixin",
|
||||
"LoginQueryResponseC2SPacketMixin",
|
||||
"NetworkStateInternalPacketHandlerMixin",
|
||||
"PlayerManagerMixin",
|
||||
"ServerCommonNetworkHandlerMixin",
|
||||
"ServerConfigurationNetworkHandlerMixin",
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
],
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.impl.networking.CommonPacketsImpl::init"
|
||||
"net.fabricmc.fabric.impl.networking.CommonPacketsImpl::init",
|
||||
"net.fabricmc.fabric.impl.networking.NetworkingImpl::init"
|
||||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl::clientInit"
|
||||
|
|
|
@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -41,8 +41,10 @@ import org.junit.jupiter.api.Test;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.NetworkPhase;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
@ -51,6 +53,7 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworkin
|
|||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.ChannelInfoHolder;
|
||||
import net.fabricmc.fabric.impl.networking.CommonPacketHandler;
|
||||
|
@ -59,11 +62,13 @@ import net.fabricmc.fabric.impl.networking.CommonRegisterPayload;
|
|||
import net.fabricmc.fabric.impl.networking.CommonVersionPayload;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.client.ClientNetworkingImpl;
|
||||
import net.fabricmc.fabric.impl.networking.payload.ResolvablePayload;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerConfigurationNetworkAddon;
|
||||
import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
|
||||
|
||||
public class CommonPacketTests {
|
||||
private static final CustomPayload.Type<PacketByteBuf, CommonVersionPayload> VERSION_PAYLOAD_TYPE = new CustomPayload.Type<>(CommonVersionPayload.ID, CommonVersionPayload.CODEC);
|
||||
private static final CustomPayload.Type<PacketByteBuf, CommonRegisterPayload> REGISTER_PAYLOAD_TYPE = new CustomPayload.Type<>(CommonRegisterPayload.ID, CommonRegisterPayload.CODEC);
|
||||
|
||||
private PacketSender packetSender;
|
||||
private ChannelInfoHolder channelInfoHolder;
|
||||
|
||||
|
@ -73,16 +78,41 @@ public class CommonPacketTests {
|
|||
private ServerConfigurationNetworkHandler serverNetworkHandler;
|
||||
private ServerConfigurationNetworkAddon serverAddon;
|
||||
|
||||
private ClientConfigurationNetworking.Context clientContext;
|
||||
private ServerConfigurationNetworking.Context serverContext;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
CommonPacketsImpl.init();
|
||||
ClientNetworkingImpl.clientInit();
|
||||
|
||||
// Register a receiver to send in the play registry response
|
||||
ClientPlayNetworking.registerGlobalReceiver(new Identifier("fabric", "global_client"), (client, handler, buf, responseSender) -> {
|
||||
// Register the packet codec on both sides
|
||||
PayloadTypeRegistry.playS2C().register(TestPayload.ID, TestPayload.CODEC);
|
||||
|
||||
// Listen for the payload on the client
|
||||
ClientPlayNetworking.registerGlobalReceiver(TestPayload.ID, (payload, context) -> {
|
||||
System.out.println(payload.data());
|
||||
});
|
||||
}
|
||||
|
||||
private record TestPayload(String data) implements CustomPayload {
|
||||
static final CustomPayload.Id<TestPayload> ID = new CustomPayload.Id<>(new Identifier("fabric", "global_client"));
|
||||
static final PacketCodec<RegistryByteBuf, TestPayload> CODEC = CustomPayload.codecOf(TestPayload::write, TestPayload::new);
|
||||
|
||||
TestPayload(RegistryByteBuf buf) {
|
||||
this(buf.readString());
|
||||
}
|
||||
|
||||
private void write(RegistryByteBuf buf) {
|
||||
buf.writeString(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
packetSender = mock(PacketSender.class);
|
||||
|
@ -97,27 +127,41 @@ public class CommonPacketTests {
|
|||
serverAddon = mock(ServerConfigurationNetworkAddon.class);
|
||||
when(ServerNetworkingImpl.getAddon(serverNetworkHandler)).thenReturn(serverAddon);
|
||||
when(serverAddon.getChannelInfoHolder()).thenReturn(channelInfoHolder);
|
||||
|
||||
ClientNetworkingImpl.setClientConfigurationAddon(clientAddon);
|
||||
|
||||
clientContext = () -> packetSender;
|
||||
serverContext = new ServerConfigurationNetworking.Context() {
|
||||
@Override
|
||||
public ServerConfigurationNetworkHandler networkHandler() {
|
||||
return serverNetworkHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSender responseSender() {
|
||||
return packetSender;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Test handling the version packet on the client
|
||||
@Test
|
||||
void handleVersionPacketClient() {
|
||||
ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonVersionPayload> packetHandler = (ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonVersionPayload>) ClientNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the server
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIntArray(new int[]{1, 2, 3});
|
||||
|
||||
// The actual handler doesn't copy the buffer
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ClientConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
CommonVersionPayload payload = CommonVersionPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, clientContext);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
|
||||
// Check the response we are sending back to the server
|
||||
PacketByteBuf response = readResponse(packetSender);
|
||||
PacketByteBuf response = readResponse(packetSender, VERSION_PAYLOAD_TYPE);
|
||||
assertArrayEquals(new int[]{1}, response.readIntArray());
|
||||
assertEquals(0, response.readableBytes());
|
||||
|
||||
|
@ -127,7 +171,7 @@ public class CommonPacketTests {
|
|||
// Test handling the version packet on the client, when the server sends unsupported versions
|
||||
@Test
|
||||
void handleVersionPacketClientUnsupported() {
|
||||
ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonVersionPayload> packetHandler = (ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonVersionPayload>) ClientNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the server
|
||||
|
@ -135,8 +179,8 @@ public class CommonPacketTests {
|
|||
buf.writeIntArray(new int[]{2, 3}); // We only support version 1
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> {
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ClientConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
CommonVersionPayload payload = CommonVersionPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, clientContext);
|
||||
});
|
||||
|
||||
// Assert the entire packet was read
|
||||
|
@ -146,15 +190,15 @@ public class CommonPacketTests {
|
|||
// Test handling the version packet on the server
|
||||
@Test
|
||||
void handleVersionPacketServer() {
|
||||
ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
ServerConfigurationNetworking.ConfigurationPacketHandler<CommonVersionPayload> packetHandler = (ServerConfigurationNetworking.ConfigurationPacketHandler<CommonVersionPayload>) ServerNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the client
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIntArray(new int[]{1, 2, 3});
|
||||
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ServerConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, serverNetworkHandler, buf, null);
|
||||
CommonVersionPayload payload = CommonVersionPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, serverContext);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
|
@ -164,7 +208,7 @@ public class CommonPacketTests {
|
|||
// Test handling the version packet on the server unsupported version
|
||||
@Test
|
||||
void handleVersionPacketServerUnsupported() {
|
||||
ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.PACKET_ID);
|
||||
ServerConfigurationNetworking.ConfigurationPacketHandler<CommonVersionPayload> packetHandler = (ServerConfigurationNetworking.ConfigurationPacketHandler<CommonVersionPayload>) ServerNetworkingImpl.CONFIGURATION.getHandler(CommonVersionPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
// Receive a packet from the client
|
||||
|
@ -172,8 +216,8 @@ public class CommonPacketTests {
|
|||
buf.writeIntArray(new int[]{3}); // Server only supports version 1
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> {
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ServerConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, serverNetworkHandler, buf, null);
|
||||
CommonVersionPayload payload = CommonVersionPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, serverContext);
|
||||
});
|
||||
|
||||
// Assert the entire packet was read
|
||||
|
@ -183,7 +227,7 @@ public class CommonPacketTests {
|
|||
// Test handing the play registry packet on the client configuration handler
|
||||
@Test
|
||||
void handlePlayRegistryClient() {
|
||||
ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonRegisterPayload> packetHandler = (ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonRegisterPayload>) ClientNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(clientAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
@ -194,15 +238,15 @@ public class CommonPacketTests {
|
|||
buf.writeString("play"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ClientConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
CommonRegisterPayload payload = CommonRegisterPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, clientContext);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "test")), channelInfoHolder.getPendingChannelsNames(NetworkState.PLAY));
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "test")), channelInfoHolder.fabric_getPendingChannelsNames(NetworkPhase.PLAY));
|
||||
|
||||
// Check the response we are sending back to the server
|
||||
PacketByteBuf response = readResponse(packetSender);
|
||||
PacketByteBuf response = readResponse(packetSender, REGISTER_PAYLOAD_TYPE);
|
||||
assertEquals(1, response.readVarInt());
|
||||
assertEquals("play", response.readString());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "global_client")), response.readCollection(HashSet::new, PacketByteBuf::readIdentifier));
|
||||
|
@ -212,7 +256,7 @@ public class CommonPacketTests {
|
|||
// Test handling the configuration registry packet on the client configuration handler
|
||||
@Test
|
||||
void handleConfigurationRegistryClient() {
|
||||
ResolvablePayload.Handler<ClientConfigurationNetworkAddon.Handler> packetHandler = ClientNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonRegisterPayload> packetHandler = (ClientConfigurationNetworking.ConfigurationPayloadHandler<CommonRegisterPayload>) ClientNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(clientAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
@ -224,15 +268,15 @@ public class CommonPacketTests {
|
|||
buf.writeString("configuration"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
ClientConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ClientConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, clientNetworkHandler, buf, packetSender);
|
||||
CommonRegisterPayload payload = CommonRegisterPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, clientContext);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
verify(clientAddon, times(1)).onCommonRegisterPacket(any());
|
||||
|
||||
// Check the response we are sending back to the server
|
||||
PacketByteBuf response = readResponse(packetSender);
|
||||
PacketByteBuf response = readResponse(packetSender, REGISTER_PAYLOAD_TYPE);
|
||||
assertEquals(1, response.readVarInt());
|
||||
assertEquals("configuration", response.readString());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "global_configuration_client")), response.readCollection(HashSet::new, PacketByteBuf::readIdentifier));
|
||||
|
@ -242,7 +286,7 @@ public class CommonPacketTests {
|
|||
// Test handing the play registry packet on the server configuration handler
|
||||
@Test
|
||||
void handlePlayRegistryServer() {
|
||||
ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
ServerConfigurationNetworking.ConfigurationPacketHandler<CommonRegisterPayload> packetHandler = (ServerConfigurationNetworking.ConfigurationPacketHandler<CommonRegisterPayload>) ServerNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(serverAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
@ -253,18 +297,18 @@ public class CommonPacketTests {
|
|||
buf.writeString("play"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ServerConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, serverNetworkHandler, buf, null);
|
||||
CommonRegisterPayload payload = CommonRegisterPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, serverContext);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "test")), channelInfoHolder.getPendingChannelsNames(NetworkState.PLAY));
|
||||
assertIterableEquals(List.of(new Identifier("fabric", "test")), channelInfoHolder.fabric_getPendingChannelsNames(NetworkPhase.PLAY));
|
||||
}
|
||||
|
||||
// Test handing the configuration registry packet on the server configuration handler
|
||||
@Test
|
||||
void handleConfigurationRegistryServer() {
|
||||
ResolvablePayload.Handler<ServerConfigurationNetworkAddon.Handler> packetHandler = ServerNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.PACKET_ID);
|
||||
ServerConfigurationNetworking.ConfigurationPacketHandler<CommonRegisterPayload> packetHandler = (ServerConfigurationNetworking.ConfigurationPacketHandler<CommonRegisterPayload>) ServerNetworkingImpl.CONFIGURATION.getHandler(CommonRegisterPayload.ID.id());
|
||||
assertNotNull(packetHandler);
|
||||
|
||||
when(serverAddon.getNegotiatedVersion()).thenReturn(1);
|
||||
|
@ -275,8 +319,8 @@ public class CommonPacketTests {
|
|||
buf.writeString("configuration"); // Target phase
|
||||
buf.writeCollection(List.of(new Identifier("fabric", "test")), PacketByteBuf::writeIdentifier);
|
||||
|
||||
ServerConfigurationNetworking.ConfigurationChannelHandler actualHandler = (ServerConfigurationNetworking.ConfigurationChannelHandler) packetHandler.actual();
|
||||
actualHandler.receive(null, serverNetworkHandler, buf, null);
|
||||
CommonRegisterPayload payload = CommonRegisterPayload.CODEC.decode(buf);
|
||||
packetHandler.receive(payload, serverContext);
|
||||
|
||||
// Assert the entire packet was read
|
||||
assertEquals(0, buf.readableBytes());
|
||||
|
@ -318,12 +362,13 @@ public class CommonPacketTests {
|
|||
assertEquals(3, CommonPacketsImpl.getHighestCommonVersion(a, b));
|
||||
}
|
||||
|
||||
private static PacketByteBuf readResponse(PacketSender packetSender) {
|
||||
private static <T extends CustomPayload> PacketByteBuf readResponse(PacketSender packetSender, CustomPayload.Type<PacketByteBuf, T> type) {
|
||||
ArgumentCaptor<CustomPayload> responseCaptor = ArgumentCaptor.forClass(CustomPayload.class);
|
||||
verify(packetSender, times(1)).sendPacket(responseCaptor.capture());
|
||||
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
responseCaptor.getValue().write(buf);
|
||||
final T payload = (T) responseCaptor.getValue();
|
||||
final PacketByteBuf buf = PacketByteBufs.create();
|
||||
type.codec().encode(buf, payload);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
@ -335,10 +380,10 @@ public class CommonPacketTests {
|
|||
}
|
||||
|
||||
private static class MockChannelInfoHolder implements ChannelInfoHolder {
|
||||
private final Map<NetworkState, Collection<Identifier>> playChannels = new ConcurrentHashMap<>();
|
||||
private final Map<NetworkPhase, Collection<Identifier>> playChannels = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getPendingChannelsNames(NetworkState state) {
|
||||
public Collection<Identifier> fabric_getPendingChannelsNames(NetworkPhase state) {
|
||||
return this.playChannels.computeIfAbsent(state, (key) -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.networking.unit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import net.minecraft.Bootstrap;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.codec.PacketCodecs;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
|
||||
public class PayloadTypeRegistryTests {
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
SharedConstants.createGameVersion();
|
||||
Bootstrap.initialize();
|
||||
|
||||
PayloadTypeRegistry.playC2S().register(C2SPlayPayload.ID, C2SPlayPayload.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(S2CPlayPayload.ID, S2CPlayPayload.CODEC);
|
||||
|
||||
PayloadTypeRegistry.configurationC2S().register(C2SConfigPayload.ID, C2SConfigPayload.CODEC);
|
||||
PayloadTypeRegistry.configurationS2C().register(S2CConfigPayload.ID, S2CConfigPayload.CODEC);
|
||||
}
|
||||
|
||||
@Test
|
||||
void C2SPlay() {
|
||||
RegistryByteBuf buf = new RegistryByteBuf(PacketByteBufs.create(), null);
|
||||
|
||||
var packetToSend = new CustomPayloadC2SPacket(new C2SPlayPayload("Hello"));
|
||||
CustomPayloadC2SPacket.CODEC.encode(buf, packetToSend);
|
||||
|
||||
CustomPayloadC2SPacket decodedPacket = CustomPayloadC2SPacket.CODEC.decode(buf);
|
||||
|
||||
if (decodedPacket.payload() instanceof C2SPlayPayload payload) {
|
||||
assertEquals("Hello", payload.value());
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void S2CPlay() {
|
||||
RegistryByteBuf buf = new RegistryByteBuf(PacketByteBufs.create(), null);
|
||||
|
||||
var packetToSend = new CustomPayloadS2CPacket(new S2CPlayPayload("Hello"));
|
||||
CustomPayloadS2CPacket.PLAY_CODEC.encode(buf, packetToSend);
|
||||
|
||||
CustomPayloadS2CPacket decodedPacket = CustomPayloadS2CPacket.PLAY_CODEC.decode(buf);
|
||||
|
||||
if (decodedPacket.payload() instanceof S2CPlayPayload payload) {
|
||||
assertEquals("Hello", payload.value());
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void C2SConfig() {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
|
||||
var packetToSend = new CustomPayloadC2SPacket(new C2SConfigPayload("Hello"));
|
||||
CustomPayloadC2SPacket.CODEC.encode(buf, packetToSend);
|
||||
|
||||
CustomPayloadC2SPacket decodedPacket = CustomPayloadC2SPacket.CODEC.decode(buf);
|
||||
|
||||
if (decodedPacket.payload() instanceof C2SConfigPayload payload) {
|
||||
assertEquals("Hello", payload.value());
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void S2CConfig() {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
|
||||
var packetToSend = new CustomPayloadS2CPacket(new S2CConfigPayload("Hello"));
|
||||
CustomPayloadS2CPacket.CONFIGURATION_CODEC.encode(buf, packetToSend);
|
||||
|
||||
CustomPayloadS2CPacket decodedPacket = CustomPayloadS2CPacket.CONFIGURATION_CODEC.decode(buf);
|
||||
|
||||
if (decodedPacket.payload() instanceof S2CConfigPayload payload) {
|
||||
assertEquals("Hello", payload.value());
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
private record C2SPlayPayload(String value) implements CustomPayload {
|
||||
public static final CustomPayload.Id<C2SPlayPayload> ID = CustomPayload.id("fabric:c2s_play");
|
||||
public static final PacketCodec<RegistryByteBuf, C2SPlayPayload> CODEC = PacketCodecs.STRING.xmap(C2SPlayPayload::new, C2SPlayPayload::value).cast();
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
private record S2CPlayPayload(String value) implements CustomPayload {
|
||||
public static final CustomPayload.Id<S2CPlayPayload> ID = CustomPayload.id("fabric:s2c_play");
|
||||
public static final PacketCodec<RegistryByteBuf, S2CPlayPayload> CODEC = PacketCodecs.STRING.xmap(S2CPlayPayload::new, S2CPlayPayload::value).cast();
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
private record C2SConfigPayload(String value) implements CustomPayload {
|
||||
public static final CustomPayload.Id<C2SConfigPayload> ID = CustomPayload.id("fabric:c2s_config");
|
||||
public static final PacketCodec<PacketByteBuf, C2SConfigPayload> CODEC = PacketCodecs.STRING.xmap(C2SConfigPayload::new, C2SConfigPayload::value).cast();
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
private record S2CConfigPayload(String value) implements CustomPayload {
|
||||
public static final CustomPayload.Id<S2CConfigPayload> ID = CustomPayload.id("fabric:s2c_config");
|
||||
public static final PacketCodec<PacketByteBuf, S2CConfigPayload> CODEC = PacketCodecs.STRING.xmap(S2CConfigPayload::new, S2CConfigPayload::value).cast();
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,8 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
|
|||
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.command.EntitySelector;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
|
@ -43,6 +45,7 @@ import net.minecraft.util.Identifier;
|
|||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.impl.networking.PayloadTypeRegistryImpl;
|
||||
|
||||
public final class NetworkingChannelTest implements ModInitializer {
|
||||
@Override
|
||||
|
@ -88,7 +91,7 @@ public final class NetworkingChannelTest implements ModInitializer {
|
|||
});
|
||||
}
|
||||
|
||||
private static CompletableFuture<Suggestions> suggestReceivableChannels(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) throws CommandSyntaxException {
|
||||
private static CompletableFuture<Suggestions> suggestReceivableChannels(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) {
|
||||
final ServerPlayerEntity player = context.getSource().getPlayer();
|
||||
|
||||
return CommandSource.suggestIdentifiers(ServerPlayNetworking.getReceived(player), builder);
|
||||
|
@ -101,20 +104,24 @@ public final class NetworkingChannelTest implements ModInitializer {
|
|||
throw new SimpleCommandExceptionType(Text.literal(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);
|
||||
});
|
||||
CustomPayload.Type<RegistryByteBuf, ? extends CustomPayload> payloadType = PayloadTypeRegistryImpl.PLAY_C2S.get(channel);
|
||||
|
||||
context.getSource().sendFeedback(() -> Text.literal(String.format("Registered channel %s for %s", channel, executor.getDisplayName())), false);
|
||||
|
||||
return 1;
|
||||
if (payloadType != null) {
|
||||
ServerPlayNetworking.registerReceiver(executor.networkHandler, payloadType.id(), (payload, ctx) -> {
|
||||
System.out.printf("Received packet on channel %s%n", payloadType.id().id());
|
||||
});
|
||||
context.getSource().sendFeedback(() -> Text.literal(String.format("Registered channel %s for %s", channel, executor.getDisplayName())), false);
|
||||
return 1;
|
||||
} else {
|
||||
throw new SimpleCommandExceptionType(Text.literal("Unknown channel id")).create();
|
||||
}
|
||||
}
|
||||
|
||||
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(Text.literal("Cannot unregister channel the server player entity cannot recieve packets on")).create();
|
||||
throw new SimpleCommandExceptionType(Text.literal("Cannot unregister channel the server player entity cannot receive packets on")).create();
|
||||
}
|
||||
|
||||
ServerPlayNetworking.unregisterReceiver(player.networkHandler, channel);
|
||||
|
|
|
@ -19,14 +19,15 @@ package net.fabricmc.fabric.test.networking.configuration;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.server.network.ServerPlayerConfigurationTask;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerConfigurationNetworking;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
|
@ -37,9 +38,12 @@ import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
|||
public class NetworkingConfigurationTest implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
PayloadTypeRegistry.configurationS2C().register(ConfigurationPacket.ID, ConfigurationPacket.CODEC);
|
||||
PayloadTypeRegistry.configurationC2S().register(ConfigurationCompletePacket.ID, ConfigurationCompletePacket.CODEC);
|
||||
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
|
||||
// You must check to see if the client can handle your config task
|
||||
if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.PACKET_TYPE)) {
|
||||
if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.ID)) {
|
||||
handler.addTask(new TestConfigurationTask("Example data"));
|
||||
} else {
|
||||
// You can opt to disconnect the client if it cannot handle the configuration task
|
||||
|
@ -47,8 +51,8 @@ public class NetworkingConfigurationTest implements ModInitializer {
|
|||
}
|
||||
});
|
||||
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(ConfigurationCompletePacket.PACKET_TYPE, (packet, networkHandler, responseSender) -> {
|
||||
networkHandler.completeTask(TestConfigurationTask.KEY);
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(ConfigurationCompletePacket.ID, (packet, context) -> {
|
||||
context.networkHandler().completeTask(TestConfigurationTask.KEY);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,38 +71,35 @@ public class NetworkingConfigurationTest implements ModInitializer {
|
|||
}
|
||||
}
|
||||
|
||||
public record ConfigurationPacket(String data) implements FabricPacket {
|
||||
public static final PacketType<ConfigurationPacket> PACKET_TYPE = PacketType.create(new Identifier(NetworkingTestmods.ID, "configure"), ConfigurationPacket::new);
|
||||
public record ConfigurationPacket(String data) implements CustomPayload {
|
||||
public static final CustomPayload.Id<ConfigurationPacket> ID = new Id<>(new Identifier(NetworkingTestmods.ID, "configure"));
|
||||
public static final PacketCodec<PacketByteBuf, ConfigurationPacket> CODEC = CustomPayload.codecOf(ConfigurationPacket::write, ConfigurationPacket::new);
|
||||
|
||||
public ConfigurationPacket(PacketByteBuf buf) {
|
||||
this(buf.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeString(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
public record ConfigurationCompletePacket() implements FabricPacket {
|
||||
public static final PacketType<ConfigurationCompletePacket> PACKET_TYPE = PacketType.create(new Identifier(NetworkingTestmods.ID, "configure_complete"), ConfigurationCompletePacket::new);
|
||||
public static class ConfigurationCompletePacket implements CustomPayload {
|
||||
public static final ConfigurationCompletePacket INSTANCE = new ConfigurationCompletePacket();
|
||||
public static final CustomPayload.Id<ConfigurationCompletePacket> ID = new Id<>(new Identifier(NetworkingTestmods.ID, "configure_complete"));
|
||||
public static final PacketCodec<PacketByteBuf, ConfigurationCompletePacket> CODEC = PacketCodec.unit(INSTANCE);
|
||||
|
||||
public ConfigurationCompletePacket(PacketByteBuf buf) {
|
||||
this();
|
||||
private ConfigurationCompletePacket() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
|
||||
public class KeybindPayload implements CustomPayload {
|
||||
public static final KeybindPayload INSTANCE = new KeybindPayload();
|
||||
public static final CustomPayload.Id<KeybindPayload> ID = new CustomPayload.Id<>(NetworkingTestmods.id("keybind_press_test"));
|
||||
public static final PacketCodec<RegistryByteBuf, KeybindPayload> CODEC = PacketCodec.unit(INSTANCE);
|
||||
|
||||
private KeybindPayload() { }
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
|
@ -16,31 +16,24 @@
|
|||
|
||||
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.Text;
|
||||
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.PayloadTypeRegistry;
|
||||
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) {
|
||||
server.execute(() -> player.sendMessage(Text.literal("So you pressed ").append(Text.keybind("fabric-networking-api-v1-testmod-keybind").styled(style -> style.withFormatting(Formatting.BLUE))), false));
|
||||
private static void receive(KeybindPayload payload, ServerPlayNetworking.Context context) {
|
||||
context.player().server.execute(() -> context.player().sendMessage(Text.literal("So you pressed ").append(Text.keybind("fabric-networking-api-v1-testmod-keybind").styled(style -> style.withFormatting(Formatting.BLUE))), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ServerPlayConnectionEvents.INIT.register((handler, server) -> ServerPlayNetworking.registerReceiver(handler, KEYBINDING_PACKET_ID, NetworkingKeybindPacketTest::receive));
|
||||
PayloadTypeRegistry.playC2S().register(KeybindPayload.ID, KeybindPayload.CODEC);
|
||||
ServerPlayConnectionEvents.INIT.register((handler, server) -> ServerPlayNetworking.registerReceiver(handler, KeybindPayload.ID, NetworkingKeybindPacketTest::receive));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import net.minecraft.util.Identifier;
|
|||
import net.minecraft.util.Util;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.networking.v1.LoginPacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
|
||||
|
@ -94,7 +95,7 @@ public final class NetworkingLoginQueryTest implements ModInitializer {
|
|||
}
|
||||
}
|
||||
|
||||
private void onLoginStart(ServerLoginNetworkHandler networkHandler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer) {
|
||||
private void onLoginStart(ServerLoginNetworkHandler networkHandler, MinecraftServer server, LoginPacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer) {
|
||||
// Send a dummy query when the client starts accepting queries.
|
||||
sender.sendPacket(GLOBAL_TEST_CHANNEL, PacketByteBufs.empty()); // dummy packet
|
||||
}
|
||||
|
|
|
@ -21,50 +21,42 @@ import static net.minecraft.server.command.CommandManager.argument;
|
|||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket;
|
||||
import net.minecraft.network.PacketCallbacks;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.codec.PacketCodecs;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.network.packet.s2c.play.BundleS2CPacket;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public final class NetworkingPlayPacketTest implements ModInitializer {
|
||||
public static final Identifier TEST_CHANNEL = NetworkingTestmods.id("test_channel");
|
||||
private static final Identifier UNKNOWN_TEST_CHANNEL = NetworkingTestmods.id("unknown_test_channel");
|
||||
private static boolean spamUnknownPackets = false;
|
||||
|
||||
public static void sendToTestChannel(ServerPlayerEntity player, String stuff) {
|
||||
ServerPlayNetworking.getSender(player).sendPacket(new OverlayPacket(Text.literal(stuff)), future -> {
|
||||
NetworkingTestmods.LOGGER.info("Sent custom payload packet in {}", TEST_CHANNEL);
|
||||
});
|
||||
ServerPlayNetworking.getSender(player).sendPacket(new OverlayPacket(Text.literal(stuff)), PacketCallbacks.always(() -> {
|
||||
NetworkingTestmods.LOGGER.info("Sent custom payload packet");
|
||||
}));
|
||||
}
|
||||
|
||||
private static void sendToUnknownChannel(ServerPlayerEntity player) {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
buf.writeUuid(UUID.randomUUID());
|
||||
}
|
||||
|
||||
ServerPlayNetworking.getSender(player).sendPacket(UNKNOWN_TEST_CHANNEL, buf);
|
||||
ServerPlayNetworking.getSender(player).sendPacket(new UnknownPayload("Hello"));
|
||||
}
|
||||
|
||||
public static void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
|
@ -85,30 +77,16 @@ public final class NetworkingPlayPacketTest implements ModInitializer {
|
|||
ctx.getSource().sendMessage(Text.literal("Spamming unknown packets state:" + spamUnknownPackets));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}))
|
||||
.then(literal("bufctor").executes(ctx -> {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeIdentifier(TEST_CHANNEL);
|
||||
buf.writeText(Text.literal("bufctor"));
|
||||
ctx.getSource().getPlayer().networkHandler.sendPacket(new CustomPayloadS2CPacket(buf));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}))
|
||||
.then(literal("repeat").executes(ctx -> {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
buf.writeText(Text.literal("repeat"));
|
||||
ServerPlayNetworking.send(ctx.getSource().getPlayer(), TEST_CHANNEL, buf);
|
||||
ServerPlayNetworking.send(ctx.getSource().getPlayer(), TEST_CHANNEL, buf);
|
||||
.then(literal("simple").executes(ctx -> {
|
||||
ServerPlayNetworking.send(ctx.getSource().getPlayer(), new OverlayPacket(Text.literal("simple")));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}))
|
||||
.then(literal("bundled").executes(ctx -> {
|
||||
PacketByteBuf buf1 = PacketByteBufs.create();
|
||||
buf1.writeText(Text.literal("bundled #1"));
|
||||
PacketByteBuf buf2 = PacketByteBufs.create();
|
||||
buf2.writeText(Text.literal("bundled #2"));
|
||||
|
||||
BundleS2CPacket packet = new BundleS2CPacket((List<Packet<ClientPlayPacketListener>>) (Object) List.of(
|
||||
ServerPlayNetworking.createS2CPacket(TEST_CHANNEL, buf1),
|
||||
ServerPlayNetworking.createS2CPacket(TEST_CHANNEL, buf2)));
|
||||
ctx.getSource().getPlayer().networkHandler.sendPacket(packet);
|
||||
BundleS2CPacket packet = new BundleS2CPacket(List.of(
|
||||
ServerPlayNetworking.createS2CPacket(new OverlayPacket(Text.literal("bundled #1"))),
|
||||
ServerPlayNetworking.createS2CPacket(new OverlayPacket(Text.literal("bundled #2")))
|
||||
));
|
||||
ServerPlayNetworking.getSender(ctx.getSource().getPlayer()).sendPacket(packet);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})));
|
||||
}
|
||||
|
@ -117,6 +95,12 @@ public final class NetworkingPlayPacketTest implements ModInitializer {
|
|||
public void onInitialize() {
|
||||
NetworkingTestmods.LOGGER.info("Hello from networking user!");
|
||||
|
||||
PayloadTypeRegistry.playS2C().register(OverlayPacket.ID, OverlayPacket.CODEC);
|
||||
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
|
||||
PayloadTypeRegistry.playS2C().register(UnknownPayload.ID, UnknownPayload.CODEC);
|
||||
}
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
|
||||
NetworkingPlayPacketTest.registerCommand(dispatcher);
|
||||
});
|
||||
|
@ -135,21 +119,31 @@ public final class NetworkingPlayPacketTest implements ModInitializer {
|
|||
});
|
||||
}
|
||||
|
||||
public record OverlayPacket(Text message) implements FabricPacket {
|
||||
public static final PacketType<OverlayPacket> PACKET_TYPE = PacketType.create(TEST_CHANNEL, OverlayPacket::new);
|
||||
public record OverlayPacket(Text message) implements CustomPayload {
|
||||
public static final CustomPayload.Id<OverlayPacket> ID = new Id<>(NetworkingTestmods.id("test_channel"));
|
||||
public static final PacketCodec<RegistryByteBuf, OverlayPacket> CODEC = CustomPayload.codecOf(OverlayPacket::write, OverlayPacket::new);
|
||||
|
||||
public OverlayPacket(PacketByteBuf buf) {
|
||||
this(buf.readText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeText(this.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
private record UnknownPayload(String data) implements CustomPayload {
|
||||
private static final CustomPayload.Id<UnknownPayload> ID = new Id<>(NetworkingTestmods.id("unknown_test_channel_s2c"));
|
||||
private static final PacketCodec<PacketByteBuf, UnknownPayload> CODEC = PacketCodecs.STRING.xmap(UnknownPayload::new, UnknownPayload::data).cast();
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ import net.fabricmc.fabric.test.networking.configuration.NetworkingConfiguration
|
|||
public class NetworkingConfigurationClientTest implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(NetworkingConfigurationTest.ConfigurationPacket.PACKET_TYPE, (packet, responseSender) -> {
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(NetworkingConfigurationTest.ConfigurationPacket.ID, (packet, context) -> {
|
||||
// Handle stuff here
|
||||
|
||||
// Respond back to the server that the task is complete
|
||||
responseSender.sendPacket(new NetworkingConfigurationTest.ConfigurationCompletePacket());
|
||||
context.responseSender().sendPacket(NetworkingConfigurationTest.ConfigurationCompletePacket.INSTANCE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ 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.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindPacketTest;
|
||||
import net.fabricmc.fabric.test.networking.keybindreciever.KeybindPayload;
|
||||
|
||||
// Sends a packet to the server when a keybinding was pressed
|
||||
// The server in response will send a chat message to the client.
|
||||
|
@ -40,7 +39,8 @@ public class NetworkingKeybindClientPacketTest implements ClientModInitializer {
|
|||
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());
|
||||
// Since KeybindPayload is an empty payload, it can be a singleton.
|
||||
ClientPlayNetworking.send(KeybindPayload.INSTANCE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -18,38 +18,44 @@ package net.fabricmc.fabric.test.networking.client.play;
|
|||
|
||||
import com.mojang.brigadier.Command;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.codec.PacketCodecs;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
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.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.test.networking.NetworkingTestmods;
|
||||
import net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketTest;
|
||||
|
||||
public final class NetworkingPlayPacketClientTest implements ClientModInitializer, ClientPlayNetworking.PlayPacketHandler<NetworkingPlayPacketTest.OverlayPacket> {
|
||||
private static final Identifier UNKNOWN_TEST_CHANNEL = NetworkingTestmods.id("unknown_test_channel");
|
||||
|
||||
public final class NetworkingPlayPacketClientTest implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientPlayConnectionEvents.INIT.register((handler, client) -> ClientPlayNetworking.registerReceiver(NetworkingPlayPacketTest.OverlayPacket.PACKET_TYPE, this));
|
||||
// Register the payload only on the client.
|
||||
PayloadTypeRegistry.playC2S().register(UnknownPayload.ID, UnknownPayload.CODEC);
|
||||
|
||||
ClientPlayConnectionEvents.INIT.register((handler, client) -> ClientPlayNetworking.registerReceiver(NetworkingPlayPacketTest.OverlayPacket.ID, (payload, context) -> context.client().inGameHud.setOverlayMessage(payload.message(), true)));
|
||||
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(
|
||||
ClientCommandManager.literal("clientnetworktestcommand")
|
||||
.then(ClientCommandManager.literal("unknown").executes(context -> {
|
||||
ClientPlayNetworking.send(UNKNOWN_TEST_CHANNEL, PacketByteBufs.create());
|
||||
ClientPlayNetworking.send(new UnknownPayload("Hello"));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(NetworkingPlayPacketTest.OverlayPacket packet, ClientPlayerEntity player, PacketSender sender) {
|
||||
MinecraftClient.getInstance().inGameHud.setOverlayMessage(packet.message(), true);
|
||||
private record UnknownPayload(String data) implements CustomPayload {
|
||||
private static final CustomPayload.Id<UnknownPayload> ID = new Id<>(NetworkingTestmods.id("unknown_test_channel_c2s"));
|
||||
private static final PacketCodec<PacketByteBuf, UnknownPayload> CODEC = PacketCodecs.STRING.xmap(UnknownPayload::new, UnknownPayload::data).cast();
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,15 +35,9 @@ import net.fabricmc.fabric.mixin.object.builder.AbstractBlockAccessor;
|
|||
import net.fabricmc.fabric.mixin.object.builder.AbstractBlockSettingsAccessor;
|
||||
|
||||
/**
|
||||
* Fabric's version of Block.Settings. Adds additional methods and hooks
|
||||
* not found in the original class.
|
||||
*
|
||||
* <p>Make note that this behaves slightly different from the
|
||||
* vanilla counterpart, copying some settings that vanilla does not.
|
||||
*
|
||||
* <p>To use it, simply replace Block.Settings.of() with
|
||||
* FabricBlockSettings.of().
|
||||
* @deprecated replace with {@link AbstractBlock.Settings}
|
||||
*/
|
||||
@Deprecated
|
||||
public class FabricBlockSettings extends AbstractBlock.Settings {
|
||||
protected FabricBlockSettings() {
|
||||
super();
|
||||
|
@ -97,56 +91,74 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
|
|||
this.postProcess(otherAccessor.getPostProcessPredicate());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated replace with {@link AbstractBlock.Settings#create()}
|
||||
*/
|
||||
@Deprecated
|
||||
public static FabricBlockSettings create() {
|
||||
return new FabricBlockSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link FabricBlockSettings#create()} instead.
|
||||
* @deprecated replace with {@link AbstractBlock.Settings#create()}
|
||||
*/
|
||||
@Deprecated
|
||||
public static FabricBlockSettings of() {
|
||||
return create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated replace with {@link AbstractBlock.Settings#copy(AbstractBlock)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static FabricBlockSettings copyOf(AbstractBlock block) {
|
||||
return new FabricBlockSettings(((AbstractBlockAccessor) block).getSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated replace with {@link AbstractBlock.Settings#copy(AbstractBlock)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static FabricBlockSettings copyOf(AbstractBlock.Settings settings) {
|
||||
return new FabricBlockSettings(settings);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings noCollision() {
|
||||
super.noCollision();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings nonOpaque() {
|
||||
super.nonOpaque();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings slipperiness(float value) {
|
||||
super.slipperiness(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings velocityMultiplier(float velocityMultiplier) {
|
||||
super.velocityMultiplier(velocityMultiplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings jumpVelocityMultiplier(float jumpVelocityMultiplier) {
|
||||
super.jumpVelocityMultiplier(jumpVelocityMultiplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings sounds(BlockSoundGroup group) {
|
||||
super.sounds(group);
|
||||
|
@ -161,89 +173,105 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
|
|||
return this.luminance(levelFunction);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings luminance(ToIntFunction<BlockState> luminanceFunction) {
|
||||
super.luminance(luminanceFunction);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings strength(float hardness, float resistance) {
|
||||
super.strength(hardness, resistance);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings breakInstantly() {
|
||||
super.breakInstantly();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings strength(float strength) {
|
||||
super.strength(strength);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings ticksRandomly() {
|
||||
super.ticksRandomly();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings dynamicBounds() {
|
||||
super.dynamicBounds();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings dropsNothing() {
|
||||
super.dropsNothing();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings dropsLike(Block block) {
|
||||
super.dropsLike(block);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings air() {
|
||||
super.air();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings allowsSpawning(AbstractBlock.TypedContextPredicate<EntityType<?>> predicate) {
|
||||
super.allowsSpawning(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings solidBlock(AbstractBlock.ContextPredicate predicate) {
|
||||
super.solidBlock(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings suffocates(AbstractBlock.ContextPredicate predicate) {
|
||||
super.suffocates(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings blockVision(AbstractBlock.ContextPredicate predicate) {
|
||||
super.blockVision(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings postProcess(AbstractBlock.ContextPredicate predicate) {
|
||||
super.postProcess(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings emissiveLighting(AbstractBlock.ContextPredicate predicate) {
|
||||
super.emissiveLighting(predicate);
|
||||
|
@ -253,90 +281,105 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
|
|||
/**
|
||||
* Make the block require tool to drop and slows down mining speed if the incorrect tool is used.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings requiresTool() {
|
||||
super.requiresTool();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings mapColor(MapColor color) {
|
||||
super.mapColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings hardness(float hardness) {
|
||||
super.hardness(hardness);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings resistance(float resistance) {
|
||||
super.resistance(resistance);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings offset(AbstractBlock.OffsetType offsetType) {
|
||||
super.offset(offsetType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings noBlockBreakParticles() {
|
||||
super.noBlockBreakParticles();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings requires(FeatureFlag... features) {
|
||||
super.requires(features);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings mapColor(Function<BlockState, MapColor> mapColorProvider) {
|
||||
super.mapColor(mapColorProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings burnable() {
|
||||
super.burnable();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings liquid() {
|
||||
super.liquid();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings solid() {
|
||||
super.solid();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings notSolid() {
|
||||
super.notSolid();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings pistonBehavior(PistonBehavior pistonBehavior) {
|
||||
super.pistonBehavior(pistonBehavior);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings instrument(Instrument instrument) {
|
||||
super.instrument(instrument);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public FabricBlockSettings replaceable() {
|
||||
super.replaceable();
|
||||
|
@ -354,11 +397,16 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated replace with {@link AbstractBlock.Settings#luminance(ToIntFunction)}
|
||||
*/
|
||||
@Deprecated
|
||||
public FabricBlockSettings luminance(int luminance) {
|
||||
this.luminance(ignored -> luminance);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public FabricBlockSettings drops(Identifier dropTableId) {
|
||||
((AbstractBlockSettingsAccessor) this).setLootTableId(dropTableId);
|
||||
return this;
|
||||
|
@ -367,7 +415,7 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
|
|||
/* FABRIC DELEGATE WRAPPERS */
|
||||
|
||||
/**
|
||||
* @deprecated Please migrate to {@link FabricBlockSettings#mapColor(MapColor)}
|
||||
* @deprecated Please migrate to {@link AbstractBlock.Settings#mapColor(MapColor)}
|
||||
*/
|
||||
@Deprecated
|
||||
public FabricBlockSettings materialColor(MapColor color) {
|
||||
|
@ -375,17 +423,22 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
|
|||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please migrate to {@link FabricBlockSettings#mapColor(DyeColor)}
|
||||
* @deprecated Please migrate to {@link AbstractBlock.Settings#mapColor(DyeColor)}
|
||||
*/
|
||||
@Deprecated
|
||||
public FabricBlockSettings materialColor(DyeColor color) {
|
||||
return this.mapColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please migrate to {@link AbstractBlock.Settings#mapColor(DyeColor)}
|
||||
*/
|
||||
@Deprecated
|
||||
public FabricBlockSettings mapColor(DyeColor color) {
|
||||
return this.mapColor(color.getMapColor());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public FabricBlockSettings collidable(boolean collidable) {
|
||||
((AbstractBlockSettingsAccessor) this).setCollidable(collidable);
|
||||
return this;
|
||||
|
|
|
@ -81,7 +81,7 @@ public class BlockEntityTypeBuilderTest implements ModInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ActionResult method_55766(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||
if (!world.isClient()) {
|
||||
BlockEntity blockEntity = world.getBlockEntity(pos);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package net.fabricmc.fabric.test.object.builder;
|
||||
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
|
@ -36,7 +37,6 @@ import net.minecraft.util.Identifier;
|
|||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.type.BlockSetTypeBuilder;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.type.WoodTypeBuilder;
|
||||
|
@ -45,25 +45,25 @@ public class TealSignTest implements ModInitializer {
|
|||
public static final Identifier TEAL_TYPE_ID = ObjectBuilderTestConstants.id("teal");
|
||||
public static final BlockSetType TEAL_BLOCK_SET_TYPE = BlockSetTypeBuilder.copyOf(BlockSetType.OAK).build(TEAL_TYPE_ID);
|
||||
public static final WoodType TEAL_WOOD_TYPE = WoodTypeBuilder.copyOf(WoodType.OAK).build(TEAL_TYPE_ID, TEAL_BLOCK_SET_TYPE);
|
||||
public static final SignBlock TEAL_SIGN = new SignBlock(TEAL_WOOD_TYPE, FabricBlockSettings.copy(Blocks.OAK_SIGN)) {
|
||||
public static final SignBlock TEAL_SIGN = new SignBlock(TEAL_WOOD_TYPE, AbstractBlock.Settings.copy(Blocks.OAK_SIGN)) {
|
||||
@Override
|
||||
public TealSign createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new TealSign(pos, state);
|
||||
}
|
||||
};
|
||||
public static final WallSignBlock TEAL_WALL_SIGN = new WallSignBlock(TEAL_WOOD_TYPE, FabricBlockSettings.copy(Blocks.OAK_SIGN)) {
|
||||
public static final WallSignBlock TEAL_WALL_SIGN = new WallSignBlock(TEAL_WOOD_TYPE, AbstractBlock.Settings.copy(Blocks.OAK_SIGN)) {
|
||||
@Override
|
||||
public TealSign createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new TealSign(pos, state);
|
||||
}
|
||||
};
|
||||
public static final HangingSignBlock TEAL_HANGING_SIGN = new HangingSignBlock(TEAL_WOOD_TYPE, FabricBlockSettings.copy(Blocks.OAK_HANGING_SIGN)) {
|
||||
public static final HangingSignBlock TEAL_HANGING_SIGN = new HangingSignBlock(TEAL_WOOD_TYPE, AbstractBlock.Settings.copy(Blocks.OAK_HANGING_SIGN)) {
|
||||
@Override
|
||||
public TealHangingSign createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new TealHangingSign(pos, state);
|
||||
}
|
||||
};
|
||||
public static final WallHangingSignBlock TEAL_WALL_HANGING_SIGN = new WallHangingSignBlock(TEAL_WOOD_TYPE, FabricBlockSettings.copy(Blocks.OAK_HANGING_SIGN)) {
|
||||
public static final WallHangingSignBlock TEAL_WALL_HANGING_SIGN = new WallHangingSignBlock(TEAL_WOOD_TYPE, AbstractBlock.Settings.copy(Blocks.OAK_HANGING_SIGN)) {
|
||||
@Override
|
||||
public TealHangingSign createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new TealHangingSign(pos, state);
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.api.particle.v1;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.particle.DefaultParticleType;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
import net.minecraft.particle.ParticleType;
|
||||
|
@ -59,26 +63,69 @@ public final class FabricParticleTypes {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle type with a custom factory for packet/data serialization.
|
||||
* Creates a new particle type with a custom factory and codecs for packet/data serialization.
|
||||
*
|
||||
* @param factory A factory for serializing packet data and string command parameters into a particle effect.
|
||||
* @param factory A factory for serializing string command parameters into a particle effect.
|
||||
* @param codec The codec for serialization.
|
||||
* @param packetCodec The packet codec for network serialization.
|
||||
*/
|
||||
public static <T extends ParticleEffect> ParticleType<T> complex(ParticleEffect.Factory<T> factory) {
|
||||
return complex(false, factory);
|
||||
public static <T extends ParticleEffect> ParticleType<T> complex(ParticleEffect.Factory<T> factory, final Function<ParticleType<T>, Codec<T>> codecGetter, final Codec<T> codec, final PacketCodec<? super RegistryByteBuf, T> packetCodec) {
|
||||
return complex(false, factory, codec, packetCodec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle type with a custom factory for packet/data serialization.
|
||||
* Creates a new particle type with a custom factory and codecs for packet/data serialization.
|
||||
*
|
||||
* @param alwaysSpawn True to always spawn the particle regardless of distance.
|
||||
* @param factory A factory for serializing packet data and string command parameters into a particle effect.
|
||||
* @param factory A factory for serializing string command parameters into a particle effect.
|
||||
* @param codec The codec for serialization.
|
||||
* @param packetCodec The packet codec for network serialization.
|
||||
*/
|
||||
public static <T extends ParticleEffect> ParticleType<T> complex(boolean alwaysSpawn, ParticleEffect.Factory<T> factory) {
|
||||
public static <T extends ParticleEffect> ParticleType<T> complex(boolean alwaysSpawn, ParticleEffect.Factory<T> factory, final Codec<T> codec, final PacketCodec<? super RegistryByteBuf, T> packetCodec) {
|
||||
return new ParticleType<T>(alwaysSpawn, factory) {
|
||||
@Override
|
||||
public Codec<T> getCodec() {
|
||||
//TODO fix me
|
||||
return null;
|
||||
return codec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketCodec<? super RegistryByteBuf, T> getPacketCodec() {
|
||||
return packetCodec;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle type with a custom factory and codecs for packet/data serialization.
|
||||
* This method is useful when two different {@link ParticleType}s share the same {@link ParticleEffect} implementation.
|
||||
*
|
||||
* @param factory A factory for serializing string command parameters into a particle effect.
|
||||
* @param codecGetter A function that, given the newly created type, returns the codec for serialization.
|
||||
* @param packetCodecGetter A function that, given the newly created type, returns the packet codec for network serialization.
|
||||
*/
|
||||
public static <T extends ParticleEffect> ParticleType<T> complex(ParticleEffect.Factory<T> factory, final Function<ParticleType<T>, Codec<T>> codecGetter, final Function<ParticleType<T>, PacketCodec<? super RegistryByteBuf, T>> packetCodecGetter) {
|
||||
return complex(false, factory, codecGetter, packetCodecGetter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle type with a custom factory and codecs for packet/data serialization.
|
||||
* This method is useful when two different {@link ParticleType}s share the same {@link ParticleEffect} implementation.
|
||||
*
|
||||
* @param alwaysSpawn True to always spawn the particle regardless of distance.
|
||||
* @param factory A factory for serializing string command parameters into a particle effect.
|
||||
* @param codecGetter A function that, given the newly created type, returns the codec for serialization.
|
||||
* @param packetCodecGetter A function that, given the newly created type, returns the packet codec for network serialization.
|
||||
*/
|
||||
public static <T extends ParticleEffect> ParticleType<T> complex(boolean alwaysSpawn, ParticleEffect.Factory<T> factory, final Function<ParticleType<T>, Codec<T>> codecGetter, final Function<ParticleType<T>, PacketCodec<? super RegistryByteBuf, T>> packetCodecGetter) {
|
||||
return new ParticleType<T>(alwaysSpawn, factory) {
|
||||
@Override
|
||||
public Codec<T> getCodec() {
|
||||
return codecGetter.apply(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketCodec<? super RegistryByteBuf, T> getPacketCodec() {
|
||||
return packetCodecGetter.apply(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue