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 <cpw+github@weeksfamily.ca>
This commit is contained in:
cpw committed 2019-03-15 23:22:28 -04:00
1 parent 0141a959eb
commit d99a55a7d1
14 files changed
+127 -27

No files matched your search

@@ -79,7 +79,7 @@ public class ConfigTracker {
this.configSets.get(type).forEach(config -> openConfig(config, configBasePath));
}
public List<Pair<String, FMLHandshakeMessages.S2CConfigData>> syncConfigs() {
public List<Pair<String, FMLHandshakeMessages.S2CConfigData>> syncConfigs(boolean isLocal) {
final Map<String, byte[]> 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());
@@ -30,6 +30,7 @@ public enum ConnectionType
ConnectionType(Function<String, Integer> versionExtractor) {
this.versionExtractor = versionExtractor;
}
public static ConnectionType forVersionFlag(String vers)
{
return vers.startsWith(FMLNetworkConstants.FMLNETMARKER) ? MODDED : VANILLA;
@@ -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<NetworkEvent.Context> 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<ResourceLocation, ResourceLocation> 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<NetworkEvent.Context> contextSupplier) {
@@ -101,14 +101,20 @@ public class NetworkEvent extends Event
public static class GatherLoginPayloadsEvent extends Event {
private final List<NetworkRegistry.LoginPayload> collected;
private final boolean isLocal;
public GatherLoginPayloadsEvent(final List<NetworkRegistry.LoginPayload> loginPayloadList) {
public GatherLoginPayloadsEvent(final List<NetworkRegistry.LoginPayload> 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 {
@@ -96,8 +96,8 @@ public class NetworkInstance
return this.serverAcceptedVersions.test(clientVersion);
}
void dispatchGatherLogin(final List<NetworkRegistry.LoginPayload> loginPayloadList) {
this.networkEventBus.post(new NetworkEvent.GatherLoginPayloadsEvent(loginPayloadList));
void dispatchGatherLogin(final List<NetworkRegistry.LoginPayload> loginPayloadList, boolean isLocal) {
this.networkEventBus.post(new NetworkEvent.GatherLoginPayloadsEvent(loginPayloadList, isLocal));
}
void dispatchLoginPacket(final NetworkEvent.LoginPayloadEvent loginPayloadEvent) {
@@ -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<LoginPayload> gatherLoginPayloads(final NetworkDirection direction) {
static List<LoginPayload> gatherLoginPayloads(final NetworkDirection direction, boolean isLocal) {
if (direction!=NetworkDirection.LOGIN_TO_CLIENT) return Collections.emptyList();
List<LoginPayload> gatheredPayloads = new ArrayList<>();
instances.values().forEach(ni->ni.dispatchGatherLogin(gatheredPayloads));
instances.values().forEach(ni->ni.dispatchGatherLogin(gatheredPayloads, isLocal));
return gatheredPayloads;
}
@@ -39,7 +39,7 @@ public class SimpleChannel
{
private final NetworkInstance instance;
private final IndexedMessageCodec indexedCodec;
private List<Supplier<? extends List<? extends Pair<String,?>>>> loginPackets;
private List<Function<Boolean, ? extends List<? extends Pair<String,?>>>> 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<MSG, Supplier<NetworkEvent.Context>> consumer;
private Function<MSG, Integer> loginIndexGetter;
private BiConsumer<MSG, Integer> loginIndexSetter;
private Supplier<List<Pair<String,MSG>>> loginPacketGenerators;
private Function<Boolean, List<Pair<String, MSG>>> loginPacketGenerators;
private static <MSG> MessageBuilder<MSG> forType(final SimpleChannel channel, final Class<MSG> type, int id) {
MessageBuilder<MSG> builder = new MessageBuilder<>();
@@ -151,18 +151,18 @@ public class SimpleChannel
return this;
}
public MessageBuilder<MSG> buildLoginPacketList(Supplier<List<Pair<String,MSG>>> loginPacketGenerators) {
public MessageBuilder<MSG> buildLoginPacketList(Function<Boolean, List<Pair<String,MSG>>> loginPacketGenerators) {
this.loginPacketGenerators = loginPacketGenerators;
return this;
}
public MessageBuilder<MSG> 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;
@@ -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<Pair<String, FMLHandshakeMessages.S2CRegistry>> generateRegistryPackets()
public static List<Pair<String, FMLHandshakeMessages.S2CRegistry>> 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<ResourceLocation> registryNames()
{
return ACTIVE.registries.entrySet().stream().map(Map.Entry::getKey).collect(Collectors.toList());
return new ArrayList<>(ACTIVE.registries.keySet());
}
}
@@ -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<CommandSource, ?> 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;
}
);
}
}
@@ -35,6 +35,7 @@ public class ForgeCommand
.then(CommandGenerate.register())
.then(CommandDimensions.register())
.then(CommandSetDimension.register())
.then(CommandModList.register())
);
}
}