Java tutorial
/* * Minecraft Forge * Copyright (c) 2016-2019. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.fml.network; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.util.registry.Registry; import net.minecraft.world.dimension.DimensionType; import net.minecraftforge.registries.ClearableRegistry; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import io.netty.buffer.Unpooled; import net.minecraft.entity.Entity; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.ContainerType; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.network.IPacket; import net.minecraft.network.NetworkManager; import net.minecraft.network.PacketBuffer; import net.minecraft.network.handshake.client.CHandshakePacket; import net.minecraft.network.login.ServerLoginNetHandler; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.fml.config.ConfigTracker; public class NetworkHooks { private static final Logger LOGGER = LogManager.getLogger(); public static String getFMLVersion(final String ip) { return ip.contains("\0") ? Objects.equals(ip.split("\0")[1], FMLNetworkConstants.NETVERSION) ? FMLNetworkConstants.NETVERSION : ip.split("\0")[1] : FMLNetworkConstants.NOVERSION; } public static ConnectionType getConnectionType(final Supplier<NetworkManager> connection) { return ConnectionType .forVersionFlag(connection.get().channel().attr(FMLNetworkConstants.FML_NETVERSION).get()); } public static IPacket<?> getEntitySpawningPacket(Entity entity) { return FMLNetworkConstants.playChannel.toVanillaPacket(new FMLPlayMessages.SpawnEntity(entity), NetworkDirection.PLAY_TO_CLIENT); } public static boolean onCustomPayload(final ICustomPacket<?> packet, final NetworkManager manager) { return NetworkRegistry.findTarget(packet.getName()) .map(ni -> ni.dispatch(packet.getDirection(), packet, manager)).orElse(Boolean.FALSE); } public static void registerServerLoginChannel(NetworkManager manager, CHandshakePacket packet) { manager.channel().attr(FMLNetworkConstants.FML_NETVERSION).set(packet.getFMLVersion()); FMLHandshakeHandler.registerHandshake(manager, NetworkDirection.LOGIN_TO_CLIENT); } public synchronized static void registerClientLoginChannel(NetworkManager manager) { manager.channel().attr(FMLNetworkConstants.FML_NETVERSION).set(FMLNetworkConstants.NOVERSION); FMLHandshakeHandler.registerHandshake(manager, NetworkDirection.LOGIN_TO_SERVER); } public synchronized static void sendMCRegistryPackets(NetworkManager manager, String direction) { final Set<ResourceLocation> resourceLocations = NetworkRegistry.buildChannelVersions().keySet().stream() .filter(rl -> !Objects.equals(rl.getNamespace(), "minecraft")).collect(Collectors.toSet()); FMLMCRegisterPacketHandler.INSTANCE.addChannels(resourceLocations, manager); FMLMCRegisterPacketHandler.INSTANCE.sendRegistry(manager, NetworkDirection.valueOf(direction)); } public synchronized static void sendDimensionDataPacket(NetworkManager manager, ServerPlayerEntity player) { // don't send vanilla dims if (player.dimension.isVanilla()) return; // don't sent to local - we already have a valid dim registry locally if (manager.isLocalChannel()) return; FMLNetworkConstants.playChannel.sendTo(new FMLPlayMessages.DimensionInfoMessage(player.dimension), manager, NetworkDirection.PLAY_TO_CLIENT); } public static void handleClientLoginSuccess(NetworkManager manager) { if (manager == null || manager.channel() == null) throw new NullPointerException( "ARGH! Network Manager is null (" + manager != null ? "CHANNEL" : "MANAGER" + ")"); if (getConnectionType(() -> manager) == ConnectionType.VANILLA) { LOGGER.info("Connected to a vanilla server. Catching up missing behaviour."); ConfigTracker.INSTANCE.loadDefaultServerConfigs(); } else { LOGGER.info("Connected to a modded server."); } } public static boolean tickNegotiation(ServerLoginNetHandler netHandlerLoginServer, NetworkManager networkManager, ServerPlayerEntity player) { return FMLHandshakeHandler.tickLogin(networkManager); } /** * Request to open a GUI on the client, from the server * * Refer to {@link net.minecraftforge.fml.ExtensionPoint#GUIFACTORY} for how to provide a function to consume * these GUI requests on the client. * * The {@link IInteractionObject#getGuiID()} is treated as a {@link ResourceLocation}. * It should refer to a valid modId namespace, to trigger opening on the client. * The namespace is directly used to lookup the modId in the client side. * * @param player The player to open the GUI for * @param containerSupplier A supplier of container properties including the registry name of the container */ public static void openGui(ServerPlayerEntity player, INamedContainerProvider containerSupplier) { openGui(player, containerSupplier, buf -> { }); } /** * Request to open a GUI on the client, from the server * * Refer to {@link net.minecraftforge.fml.ExtensionPoint#GUIFACTORY} for how to provide a function to consume * these GUI requests on the client. * * The {@link IInteractionObject#getGuiID()} is treated as a {@link ResourceLocation}. * It should refer to a valid modId namespace, to trigger opening on the client. * The namespace is directly used to lookup the modId in the client side. * * @param player The player to open the GUI for * @param containerSupplier A supplier of container properties including the registry name of the container * @param pos A block pos, which will be encoded into the auxillary data for this request */ public static void openGui(ServerPlayerEntity player, INamedContainerProvider containerSupplier, BlockPos pos) { openGui(player, containerSupplier, buf -> buf.writeBlockPos(pos)); } /** * Request to open a GUI on the client, from the server * * Refer to {@link net.minecraftforge.fml.ExtensionPoint#GUIFACTORY} for how to provide a function to consume * these GUI requests on the client. * * The {@link IInteractionObject#getGuiID()} is treated as a {@link ResourceLocation}. * It should refer to a valid modId namespace, to trigger opening on the client. * The namespace is directly used to lookup the modId in the client side. * The maximum size for #extraDataWriter is 32600 bytes. * * @param player The player to open the GUI for * @param containerSupplier A supplier of container properties including the registry name of the container * @param extraDataWriter Consumer to write any additional data the GUI needs */ public static void openGui(ServerPlayerEntity player, INamedContainerProvider containerSupplier, Consumer<PacketBuffer> extraDataWriter) { if (player.world.isRemote) return; player.closeContainer(); player.getNextWindowId(); int openContainerId = player.currentWindowId; PacketBuffer extraData = new PacketBuffer(Unpooled.buffer()); extraDataWriter.accept(extraData); extraData.readerIndex(0); // reset to beginning in case modders read for whatever reason PacketBuffer output = new PacketBuffer(Unpooled.buffer()); output.writeVarInt(extraData.readableBytes()); output.writeBytes(extraData); if (output.readableBytes() > 32600 || output.readableBytes() < 1) { throw new IllegalArgumentException( "Invalid PacketBuffer for openGui, found " + output.readableBytes() + " bytes"); } Container c = containerSupplier.createMenu(openContainerId, player.inventory, player); ContainerType<?> type = c.getType(); FMLPlayMessages.OpenContainer msg = new FMLPlayMessages.OpenContainer(type, openContainerId, containerSupplier.getDisplayName(), output); FMLNetworkConstants.playChannel.sendTo(msg, player.connection.getNetworkManager(), NetworkDirection.PLAY_TO_CLIENT); player.openContainer = c; player.openContainer.addListener(player); MinecraftForge.EVENT_BUS.post(new PlayerContainerEvent.Open(player, c)); } // internal tracking map for custom dimensions received from servers for use on client. private static Int2ObjectMap<DimensionType> trackingMap = new Int2ObjectOpenHashMap<>(); public static DimensionType getDummyDimType(final int dimension) { return trackingMap.computeIfAbsent(dimension, id -> DimensionType.getById(id)); } static void addCachedDimensionType(final DimensionType dimensionType, final ResourceLocation dimName) { trackingMap.put(dimensionType.getId(), dimensionType); final ClearableRegistry<DimensionType> dimtypereg = (ClearableRegistry<DimensionType>) Registry.DIMENSION_TYPE; dimtypereg.register(dimensionType.getId(), dimName, dimensionType); } }