From d99a55a7d18e6e6035156970565ef7fcf77ea812 Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 15 Mar 2019 23:19:28 -0400 Subject: [PATCH] Allow login packets to only generate on server based connections Run injectSnapshot on the client thread, wait for result and disconnect when injectSnapshot fails. Fix DevServer to load MOD_CLASSES Fix LaunchTesting to properly load log4j2 markers (don't use a static variable in that class!) Signed-off-by: cpw --- .../loading/moddiscovery/ModDiscoverer.java | 3 +- .../fml/config/ConfigTracker.java | 2 +- .../fml/network/ConnectionType.java | 1 + .../fml/network/FMLHandshakeHandler.java | 55 +++++++++++++++++-- .../fml/network/NetworkEvent.java | 8 ++- .../fml/network/NetworkInstance.java | 4 +- .../fml/network/NetworkRegistry.java | 4 +- .../fml/network/simple/SimpleChannel.java | 12 ++-- .../registries/RegistryManager.java | 10 ++-- .../server/command/CommandModList.java | 45 +++++++++++++++ .../server/command/ForgeCommand.java | 1 + .../resources/assets/forge/lang/en_us.json | 1 + .../userdev/FMLDevServerLaunchProvider.java | 3 + .../minecraftforge/userdev/LaunchTesting.java | 5 +- 14 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 src/main/java/net/minecraftforge/server/command/CommandModList.java diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java index d624df4011..5340c541d9 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java @@ -36,6 +36,7 @@ import java.util.Map; import java.util.ServiceLoader; import java.util.stream.Collectors; +import static net.minecraftforge.fml.loading.LogMarkers.CORE; import static net.minecraftforge.fml.loading.LogMarkers.SCAN; @@ -48,7 +49,7 @@ public class ModDiscoverer { locators = ServiceLoader.load(IModLocator.class); locatorList = ServiceLoaderStreamUtils.toList(this.locators); locatorList.forEach(l->l.initArguments(arguments)); - LOGGER.debug(SCAN,"Found Mod Locators : {}", ()->locatorList.stream().map(iModLocator -> "("+iModLocator.name() + ":" + iModLocator.getClass().getPackage().getImplementationVersion()+")").collect(Collectors.joining(","))); + LOGGER.debug(CORE,"Found Mod Locators : {}", ()->locatorList.stream().map(iModLocator -> "("+iModLocator.name() + ":" + iModLocator.getClass().getPackage().getImplementationVersion()+")").collect(Collectors.joining(","))); } ModDiscoverer(List locatorList) { diff --git a/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java b/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java index ed0dec64f7..089a2d205a 100644 --- a/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java +++ b/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java @@ -79,7 +79,7 @@ public class ConfigTracker { this.configSets.get(type).forEach(config -> openConfig(config, configBasePath)); } - public List> syncConfigs() { + public List> syncConfigs(boolean isLocal) { final Map configData = configSets.get(ModConfig.Type.SERVER).stream().collect(Collectors.toMap(ModConfig::getFileName, mc -> { //TODO: Test cpw's LambdaExceptionUtils on Oracle javac. try { return Files.readAllBytes(mc.getFullPath()); diff --git a/src/main/java/net/minecraftforge/fml/network/ConnectionType.java b/src/main/java/net/minecraftforge/fml/network/ConnectionType.java index c4379085ea..1034e55563 100644 --- a/src/main/java/net/minecraftforge/fml/network/ConnectionType.java +++ b/src/main/java/net/minecraftforge/fml/network/ConnectionType.java @@ -30,6 +30,7 @@ public enum ConnectionType ConnectionType(Function versionExtractor) { this.versionExtractor = versionExtractor; } + public static ConnectionType forVersionFlag(String vers) { return vers.startsWith(FMLNetworkConstants.FMLNETMARKER) ? MODDED : VANILLA; diff --git a/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java b/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java index 7a8cb91c98..b3ee0f0fe9 100644 --- a/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java +++ b/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java @@ -19,14 +19,17 @@ package net.minecraftforge.fml.network; +import com.google.common.collect.Multimap; import net.minecraft.network.NetworkManager; import net.minecraft.network.PacketBuffer; import net.minecraft.network.NetHandlerLoginServer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.TextComponentString; import net.minecraftforge.fml.config.ConfigTracker; +import net.minecraftforge.fml.loading.AdvancedLogMessageAdapter; import net.minecraftforge.fml.network.simple.SimpleChannel; import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.GameData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -40,6 +43,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES; @@ -114,11 +119,14 @@ public class FMLHandshakeHandler { { this.direction = side; this.manager = networkManager; - if (NetworkHooks.getConnectionType(()->this.manager)==ConnectionType.VANILLA) { + if (networkManager.isLocalChannel()) { + this.messageList = NetworkRegistry.gatherLoginPayloads(this.direction, true); + LOGGER.debug(FMLHSMARKER, "Starting local connection."); + } else if (NetworkHooks.getConnectionType(()->this.manager)==ConnectionType.VANILLA) { this.messageList = Collections.emptyList(); LOGGER.debug(FMLHSMARKER, "Starting new vanilla network connection."); } else { - this.messageList = NetworkRegistry.gatherLoginPayloads(this.direction); + this.messageList = NetworkRegistry.gatherLoginPayloads(this.direction, false); LOGGER.debug(FMLHSMARKER, "Starting new modded network connection. Found {} messages to dispatch.", this.messageList.size()); } } @@ -170,13 +178,48 @@ public class FMLHandshakeHandler { LOGGER.debug(FMLHSMARKER,"Received registry packet for {}", registryPacket.getRegistryName()); this.registriesToReceive.remove(registryPacket.getRegistryName()); this.registrySnapshots.put(registryPacket.getRegistryName(), registryPacket.getSnapshot()); - contextSupplier.get().setPacketHandled(true); - FMLNetworkConstants.handshakeChannel.reply(new FMLHandshakeMessages.C2SAcknowledge(), contextSupplier.get()); + boolean continueHandshake = true; if (this.registriesToReceive.isEmpty()) { - //TODO: @cpw injectSnapshot Needs to be on the world thread. And maybe block the network/login so we don't get world data before we finish? - registrySnapshots = null; + continueHandshake = handleRegistryLoading(contextSupplier); } + // The handshake reply isn't sent until we have processed the message + contextSupplier.get().setPacketHandled(true); + if (!continueHandshake) { + LOGGER.error(FMLHSMARKER, "Connection closed, not continuing handshake"); + } else { + FMLNetworkConstants.handshakeChannel.reply(new FMLHandshakeMessages.C2SAcknowledge(), contextSupplier.get()); + } + } + + private boolean handleRegistryLoading(final Supplier contextSupplier) { + // We use a countdown latch to suspend the network thread pending the client thread processing the registry data + AtomicBoolean successfulConnection = new AtomicBoolean(false); + CountDownLatch block = new CountDownLatch(1); + contextSupplier.get().enqueueWork(() -> { + LOGGER.debug(FMLHSMARKER, "Injecting registry snapshot from server."); + final Multimap missingData = GameData.injectSnapshot(registrySnapshots, false, false); + LOGGER.debug(FMLHSMARKER, "Snapshot injected."); + if (!missingData.isEmpty()) { + LOGGER.error(FMLHSMARKER, "Missing registry data for network connection:\n{}", new AdvancedLogMessageAdapter(sb-> + missingData.forEach((reg, entry)-> sb.append("\t").append(reg).append(": ").append(entry).append('\n')))); + } + successfulConnection.set(missingData.isEmpty()); + block.countDown(); + }); + LOGGER.debug(FMLHSMARKER, "Waiting for registries to load."); + try { + block.await(); + } catch (InterruptedException e) { + Thread.interrupted(); + } + if (successfulConnection.get()) { + LOGGER.debug(FMLHSMARKER, "Registry load complete, continuing handshake."); + } else { + LOGGER.error(FMLHSMARKER, "Failed to load registry, closing connection."); + this.manager.closeChannel(new TextComponentString("Failed to synchronize registry data from server, closing connection")); + } + return successfulConnection.get(); } void handleClientAck(final FMLHandshakeMessages.C2SAcknowledge msg, final Supplier contextSupplier) { diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkEvent.java b/src/main/java/net/minecraftforge/fml/network/NetworkEvent.java index eb2791e8e3..47e61c9fb0 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkEvent.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkEvent.java @@ -101,14 +101,20 @@ public class NetworkEvent extends Event public static class GatherLoginPayloadsEvent extends Event { private final List collected; + private final boolean isLocal; - public GatherLoginPayloadsEvent(final List loginPayloadList) { + public GatherLoginPayloadsEvent(final List loginPayloadList, boolean isLocal) { this.collected = loginPayloadList; + this.isLocal = isLocal; } public void add(PacketBuffer buffer, ResourceLocation channelName, String context) { collected.add(new NetworkRegistry.LoginPayload(buffer, channelName, context)); } + + public boolean isLocal() { + return isLocal; + } } public static class LoginPayloadEvent extends NetworkEvent { diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java b/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java index 30f9ddec4a..38456d1bbb 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java @@ -96,8 +96,8 @@ public class NetworkInstance return this.serverAcceptedVersions.test(clientVersion); } - void dispatchGatherLogin(final List loginPayloadList) { - this.networkEventBus.post(new NetworkEvent.GatherLoginPayloadsEvent(loginPayloadList)); + void dispatchGatherLogin(final List loginPayloadList, boolean isLocal) { + this.networkEventBus.post(new NetworkEvent.GatherLoginPayloadsEvent(loginPayloadList, isLocal)); } void dispatchLoginPacket(final NetworkEvent.LoginPayloadEvent loginPayloadEvent) { diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java b/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java index bec35da364..5e7f8e34e6 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java @@ -218,10 +218,10 @@ public class NetworkRegistry * @return The {@link LoginPayload} list * @param direction the network direction for the request - only gathers for LOGIN_TO_CLIENT */ - static List gatherLoginPayloads(final NetworkDirection direction) { + static List gatherLoginPayloads(final NetworkDirection direction, boolean isLocal) { if (direction!=NetworkDirection.LOGIN_TO_CLIENT) return Collections.emptyList(); List gatheredPayloads = new ArrayList<>(); - instances.values().forEach(ni->ni.dispatchGatherLogin(gatheredPayloads)); + instances.values().forEach(ni->ni.dispatchGatherLogin(gatheredPayloads, isLocal)); return gatheredPayloads; } diff --git a/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java b/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java index 0309144072..e2dbd42f37 100644 --- a/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java +++ b/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java @@ -39,7 +39,7 @@ public class SimpleChannel { private final NetworkInstance instance; private final IndexedMessageCodec indexedCodec; - private List>>> loginPackets; + private List>>> loginPackets; public SimpleChannel(NetworkInstance instance) { @@ -52,7 +52,7 @@ public class SimpleChannel private void networkLoginGather(final NetworkEvent.GatherLoginPayloadsEvent gatherEvent) { loginPackets.forEach(packetGenerator->{ - packetGenerator.get().forEach(p->{ + packetGenerator.apply(gatherEvent.isLocal()).forEach(p->{ PacketBuffer pb = new PacketBuffer(Unpooled.buffer()); this.indexedCodec.build(p.getRight(), pb); gatherEvent.add(pb, this.instance.getChannelName(), p.getLeft()); @@ -125,7 +125,7 @@ public class SimpleChannel private BiConsumer> consumer; private Function loginIndexGetter; private BiConsumer loginIndexSetter; - private Supplier>> loginPacketGenerators; + private Function>> loginPacketGenerators; private static MessageBuilder forType(final SimpleChannel channel, final Class type, int id) { MessageBuilder builder = new MessageBuilder<>(); @@ -151,18 +151,18 @@ public class SimpleChannel return this; } - public MessageBuilder buildLoginPacketList(Supplier>> loginPacketGenerators) { + public MessageBuilder buildLoginPacketList(Function>> loginPacketGenerators) { this.loginPacketGenerators = loginPacketGenerators; return this; } public MessageBuilder markAsLoginPacket() { - this.loginPacketGenerators = () -> { + this.loginPacketGenerators = (isLocal) -> { try { return Collections.singletonList(Pair.of(type.getName(), type.newInstance())); } catch (InstantiationException | IllegalAccessException e) { - throw new RuntimeException("Inaccessible no-arg constructor for message "+type.getName(),e); + throw new RuntimeException("Inaccessible no-arg constructor for message "+type.getName(), e); } }; return this; diff --git a/src/main/java/net/minecraftforge/registries/RegistryManager.java b/src/main/java/net/minecraftforge/registries/RegistryManager.java index cab6b67063..3c573aa484 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryManager.java +++ b/src/main/java/net/minecraftforge/registries/RegistryManager.java @@ -19,6 +19,8 @@ package net.minecraftforge.registries; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -146,15 +148,15 @@ public class RegistryManager this.superTypes.clear(); } - public static List> generateRegistryPackets() + public static List> generateRegistryPackets(boolean isLocal) { - return ACTIVE.takeSnapshot(false).entrySet().stream(). + return !isLocal ? ACTIVE.takeSnapshot(false).entrySet().stream(). map(e->Pair.of("Registry " + e.getKey(), new FMLHandshakeMessages.S2CRegistry(e.getKey(), e.getValue()))). - collect(Collectors.toList()); + collect(Collectors.toList()) : Collections.emptyList(); } public static List registryNames() { - return ACTIVE.registries.entrySet().stream().map(Map.Entry::getKey).collect(Collectors.toList()); + return new ArrayList<>(ACTIVE.registries.keySet()); } } diff --git a/src/main/java/net/minecraftforge/server/command/CommandModList.java b/src/main/java/net/minecraftforge/server/command/CommandModList.java new file mode 100644 index 0000000000..8ab79ca318 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/command/CommandModList.java @@ -0,0 +1,45 @@ +/* + * 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.server.command; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; + +import java.util.stream.Collectors; + +public class CommandModList { + static ArgumentBuilder register() + { + return Commands.literal("mods") + .requires(cs->cs.hasPermissionLevel(0)) //permission + .executes(ctx -> { + ctx.getSource().sendFeedback(new TextComponentTranslation("commands.forge.mods.list", + ModList.get().getMods().stream().map(ModInfo::getModId).collect(Collectors.joining(","))), + true); + return 0; + } + ); + } + +} diff --git a/src/main/java/net/minecraftforge/server/command/ForgeCommand.java b/src/main/java/net/minecraftforge/server/command/ForgeCommand.java index 9d532151ed..e3f2bad9a4 100644 --- a/src/main/java/net/minecraftforge/server/command/ForgeCommand.java +++ b/src/main/java/net/minecraftforge/server/command/ForgeCommand.java @@ -35,6 +35,7 @@ public class ForgeCommand .then(CommandGenerate.register()) .then(CommandDimensions.register()) .then(CommandSetDimension.register()) + .then(CommandModList.register()) ); } } diff --git a/src/main/resources/assets/forge/lang/en_us.json b/src/main/resources/assets/forge/lang/en_us.json index 2b67a3fc09..7ea486dbd6 100644 --- a/src/main/resources/assets/forge/lang/en_us.json +++ b/src/main/resources/assets/forge/lang/en_us.json @@ -63,6 +63,7 @@ "commands.forge.setdim.invalid.nochange": "The entity selected ({0}) is already in the dimension specified ({1}).", "commands.forge.tps.invalid": "Invalid dimension {0} Possible values: {1}", "commands.forge.tps.summary.all": "Overall: Mean tick time: {0} ms. Mean TPS: {1}", + "commands.forge.mods.list": "Mod List: {0}", "commands.forge.tps.summary.basic": "Dim {0}: Mean tick time: {1} ms. Mean TPS: {2}", "commands.forge.tps.summary.named": "Dim {0} ({1}): Mean tick time: {2} ms. Mean TPS: {3}", "commands.forge.tracking.entity.enabled": "Entity tracking enabled for %d seconds.", diff --git a/src/userdev/java/net/minecraftforge/userdev/FMLDevServerLaunchProvider.java b/src/userdev/java/net/minecraftforge/userdev/FMLDevServerLaunchProvider.java index dfe3812f30..73b082c52f 100644 --- a/src/userdev/java/net/minecraftforge/userdev/FMLDevServerLaunchProvider.java +++ b/src/userdev/java/net/minecraftforge/userdev/FMLDevServerLaunchProvider.java @@ -75,6 +75,7 @@ public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implement }; } + @SuppressWarnings("unchecked") @Override public void setup(IEnvironment environment, final Map arguments) { @@ -82,6 +83,8 @@ public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implement final Path forgemodstoml = LibraryFinder.findJarPathFor("META-INF/mods.toml", "forgemodstoml"); ((Map>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()). add(Pair.of(forgemodstoml, Collections.singletonList(compiledClasses))); + + processModClassesEnvironmentVariable((Map>>>) arguments); } @Override diff --git a/src/userdev/java/net/minecraftforge/userdev/LaunchTesting.java b/src/userdev/java/net/minecraftforge/userdev/LaunchTesting.java index a87263397b..622e5b1a05 100644 --- a/src/userdev/java/net/minecraftforge/userdev/LaunchTesting.java +++ b/src/userdev/java/net/minecraftforge/userdev/LaunchTesting.java @@ -32,12 +32,9 @@ import java.net.Proxy; import java.util.Arrays; import java.util.Locale; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; public class LaunchTesting { - private static final Logger LOGGER = LogManager.getLogger(); - public static void main(String... args) throws InterruptedException { final String markerselection = System.getProperty("forge.logging.markers", ""); @@ -130,7 +127,7 @@ public class LaunchTesting try { auth.logIn(); } catch (AuthenticationException e) { - LOGGER.error("Login failed!", e); + LogManager.getLogger().error("Login failed!", e); throw new RuntimeException(e); // don't set other variables }