Re-Add the list ping compatibility checker

Signed-off-by: JoJoDeveloping <jojohostert@gmail.com>
This commit is contained in:
JoJoDeveloping 2019-03-18 00:01:23 +01:00
parent 18400e8457
commit 4764136d47
No known key found for this signature in database
GPG key ID: 124FAA37C4711CBF
13 changed files with 365 additions and 134 deletions

View file

@ -0,0 +1,10 @@
--- a/net/minecraft/client/gui/ServerListEntryNormal.java
+++ b/net/minecraft/client/gui/ServerListEntryNormal.java
@@ -143,6 +143,7 @@
} else if (k1 >= p_194999_1_ - l - 15 - 2 && k1 <= p_194999_1_ - 15 - 2 && l1 >= 0 && l1 <= 8) {
this.field_148303_c.func_146793_a(s);
}
+ net.minecraftforge.fml.client.ClientHooks.drawForgePingInfo(this.field_148303_c, field_148301_e, j, i, p_194999_1_, k1, l1);
if (this.field_148300_d.field_71474_y.field_85185_A || p_194999_5_) {
this.field_148300_d.func_110434_K().func_110577_a(field_178014_d);

View file

@ -0,0 +1,10 @@
--- a/net/minecraft/client/multiplayer/ServerData.java
+++ b/net/minecraft/client/multiplayer/ServerData.java
@@ -20,6 +20,7 @@
private ServerData.ServerResourceMode field_152587_j = ServerData.ServerResourceMode.PROMPT;
private String field_147411_m;
private boolean field_181042_l;
+ public net.minecraftforge.fml.client.ExtendedServerListData forgeData = null;
public ServerData(String p_i46420_1_, String p_i46420_2_, boolean p_i46420_3_) {
this.field_78847_a = p_i46420_1_;

View file

@ -0,0 +1,11 @@
--- a/net/minecraft/client/network/ServerPinger.java
+++ b/net/minecraft/client/network/ServerPinger.java
@@ -120,6 +120,8 @@
p_147224_1_.func_147407_a((String)null);
}
+ net.minecraftforge.fml.client.ClientHooks.processForgeListPingData(serverstatusresponse, p_147224_1_);
+
this.field_175092_e = Util.func_211177_b();
networkmanager.func_179290_a(new CPacketPing(this.field_175092_e));
this.field_147403_d = true;

View file

@ -1,6 +1,23 @@
--- a/net/minecraft/network/ServerStatusResponse.java
+++ b/net/minecraft/network/ServerStatusResponse.java
@@ -26,6 +26,7 @@
@@ -19,13 +19,24 @@
private ServerStatusResponse.Players field_151324_b;
private ServerStatusResponse.Version field_151325_c;
private String field_151323_d;
+ private net.minecraftforge.fml.network.FMLStatusPing forgeData;
+ public net.minecraftforge.fml.network.FMLStatusPing getForgeData() {
+ return this.forgeData;
+ }
+
+ public void setForgeData(net.minecraftforge.fml.network.FMLStatusPing data){
+ this.forgeData = data;
+ invalidateJson();
+ }
+
public ITextComponent func_151317_a() {
return this.field_151326_a;
}
public void func_151315_a(ITextComponent p_151315_1_) {
this.field_151326_a = p_151315_1_;
@ -8,7 +25,7 @@
}
public ServerStatusResponse.Players func_151318_b() {
@@ -34,6 +35,7 @@
@@ -34,6 +45,7 @@
public void func_151319_a(ServerStatusResponse.Players p_151319_1_) {
this.field_151324_b = p_151319_1_;
@ -16,7 +33,7 @@
}
public ServerStatusResponse.Version func_151322_c() {
@@ -42,16 +44,51 @@
@@ -42,16 +54,51 @@
public void func_151321_a(ServerStatusResponse.Version p_151321_1_) {
this.field_151325_c = p_151321_1_;
@ -68,3 +85,25 @@
public static class Players {
private final int field_151336_a;
private final int field_151334_b;
@@ -143,6 +190,10 @@
serverstatusresponse.func_151320_a(JsonUtils.func_151200_h(jsonobject, "favicon"));
}
+ if (jsonobject.has("forgeData")) {
+ serverstatusresponse.setForgeData(net.minecraftforge.fml.network.FMLStatusPing.Serializer.deserialize(JsonUtils.func_152754_s(jsonobject, "forgeData"), p_deserialize_3_));
+ }
+
return serverstatusresponse;
}
@@ -164,6 +215,10 @@
jsonobject.addProperty("favicon", p_serialize_1_.func_151316_d());
}
+ if(p_serialize_1_.getForgeData() != null){
+ jsonobject.add("forgeData", net.minecraftforge.fml.network.FMLStatusPing.Serializer.serialize(p_serialize_1_.getForgeData(), p_serialize_3_));
+ }
+
return jsonobject;
}
}

View file

@ -33,6 +33,7 @@ import net.minecraftforge.fml.loading.LoadingModList;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.network.FMLNetworkConstants;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
import net.minecraftforge.registries.GameData;
@ -190,6 +191,7 @@ public class ModLoader
dispatchAndHandleError(LifecycleEventProvider.PROCESS_IMC);
dispatchAndHandleError(LifecycleEventProvider.COMPLETE);
GameData.freezeData();
NetworkRegistry.lock();
}
public List<ModLoadingWarning> getWarnings()

View file

@ -20,16 +20,20 @@
package net.minecraftforge.fml.client;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.collect.*;
import net.minecraft.client.gui.*;
import net.minecraftforge.fml.ForgeI18n;
import net.minecraftforge.fml.network.FMLNetworkConstants;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.registries.RegistryManager;
import net.minecraftforge.versions.forge.ForgeVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
@ -38,19 +42,8 @@ import org.apache.logging.log4j.MarkerManager;
import com.google.common.base.CharMatcher;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.gson.JsonObject;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiConnecting;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiWorldSelection;
import net.minecraft.client.gui.ServerListEntryNormal;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.network.NetworkManager;
@ -62,7 +55,6 @@ import net.minecraft.resources.SimpleReloadableResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.WorldSummary;
import net.minecraftforge.fml.StartupQuery;
import net.minecraftforge.fml.client.gui.GuiAccessDenied;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.fml.packs.ModFileResourcePack;
@ -76,108 +68,78 @@ public class ClientHooks
private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
private static final CharMatcher DISALLOWED_CHAR_MATCHER = CharMatcher.anyOf(ALLOWED_CHARS).negate();
private static Map<ServerStatusResponse,JsonObject> extraServerListData;
private static Map<ServerData, ExtendedServerListData> serverDataTag;
public static void setupServerList()
{
extraServerListData = Collections.synchronizedMap(new HashMap<>());
serverDataTag = Collections.synchronizedMap(new HashMap<>());
}
public static void captureAdditionalData(ServerStatusResponse serverstatusresponse, JsonObject jsonobject)
{
if (jsonobject.has("modinfo"))
{
JsonObject fmlData = jsonobject.get("modinfo").getAsJsonObject();
extraServerListData.put(serverstatusresponse, fmlData);
}
}
public static void bindServerListData(ServerData data, ServerStatusResponse originalResponse)
{
/*
if (extraServerListData.containsKey(originalResponse))
{
JsonObject jsonData = extraServerListData.get(originalResponse);
String type = jsonData.get("type").getAsString();
JsonArray modDataArray = jsonData.get("modList").getAsJsonArray();
boolean moddedClientAllowed = !jsonData.has("clientModsAllowed") || jsonData.get("clientModsAllowed").getAsBoolean();
ImmutableMap.Builder<String, String> modListBldr = ImmutableMap.builder();
for (JsonElement obj : modDataArray)
{
JsonObject modObj = obj.getAsJsonObject();
modListBldr.put(modObj.get("modid").getAsString(), modObj.get("version").getAsString());
}
Map<String,String> modListMap = modListBldr.build();
String modRejections = FMLNetworkHandler.checkModList(modListMap, LogicalSide.SERVER);
serverDataTag.put(data, new ExtendedServerListData(type, modRejections == null, modListMap, !moddedClientAllowed));
}
else
{
String serverDescription = data.serverMOTD;
boolean moddedClientAllowed = true;
if (!Strings.isNullOrEmpty(serverDescription))
{
moddedClientAllowed = !serverDescription.endsWith(":NOFML§r");
}
serverDataTag.put(data, new ExtendedServerListData("VANILLA", false, ImmutableMap.of(), !moddedClientAllowed));
}
*/
startupConnectionData.countDown();
}
private static final ResourceLocation iconSheet = new ResourceLocation("fml:textures/gui/icons.png");
private static final CountDownLatch startupConnectionData = new CountDownLatch(1);
private static final ResourceLocation iconSheet = new ResourceLocation(ForgeVersion.MOD_ID, "textures/gui/icons.png");
@Nullable
public static String enhanceServerListEntry(ServerListEntryNormal serverListEntry, ServerData serverEntry, int x, int width, int y, int relativeMouseX, int relativeMouseY)
public static void processForgeListPingData(ServerStatusResponse packet, ServerData target)
{
String tooltip;
if(packet.getForgeData() != null){
int numberOfMods = packet.getForgeData().getNumberOfMods();
MapDifference<ResourceLocation, Integer> difference = Maps.difference(packet.getForgeData().getRegistryHashes(), RegistryManager.ACTIVE.computeRegistryHashes());
int fmlver = packet.getForgeData().getFMLNetworkVersion();
boolean b = NetworkRegistry.checkListPingCompatibilityForClient(packet.getForgeData().getPresentMods())
&& difference.areEqual()
&& fmlver == FMLNetworkConstants.FMLNETVERSION;
LOGGER.debug(CLIENTHOOKS, "Received FML ping data from server at {}: FMLNETVER={}, {} mods, channels: [{}] - compatible: {}", target.serverIP, fmlver, numberOfMods, packet.getForgeData().getPresentMods().entrySet(), b);
difference.entriesDiffering().forEach((k,vd)-> LOGGER.debug(CLIENTHOOKS, "Registry {}: Local: {}, Remote: {}", k, vd.rightValue(), vd.leftValue()));
difference.entriesOnlyOnLeft().forEach((k,vd)-> LOGGER.debug(CLIENTHOOKS, "Registry {} is only on server with hash {}", k, vd));
difference.entriesOnlyOnRight().forEach((k,vd)-> LOGGER.debug(CLIENTHOOKS, "Registry {} is missing on server with hash {}", k, vd));
difference.entriesInCommon().forEach((k,vd)-> LOGGER.debug(CLIENTHOOKS, "Registry {} is equal, hash={}", k, vd));
String extraReason = null;
if(fmlver<FMLNetworkConstants.FMLNETVERSION)
extraReason = "fml.menu.multiplayer.serveroutdated";
else if(fmlver > FMLNetworkConstants.FMLNETVERSION)
extraReason = "fml.menu.multiplayer.clientoutdated";
target.forgeData = new ExtendedServerListData("FML", b, packet.getForgeData().getPresentMods(), numberOfMods, extraReason);
}else{
target.forgeData = new ExtendedServerListData("VANILLA", NetworkRegistry.canConnectToVanillaServer(), Maps.newHashMap(), 0, null);
}
}
public static void drawForgePingInfo(GuiMultiplayer gui, ServerData target, int x, int y, int width, int relativeMouseX, int relativeMouseY){
int idx;
boolean blocked = false;
if (serverDataTag.containsKey(serverEntry))
{
ExtendedServerListData extendedData = serverDataTag.get(serverEntry);
if ("FML".equals(extendedData.type) && extendedData.isCompatible)
{
idx = 0;
tooltip = String.format("Compatible FML modded server\n%d mods present", extendedData.modData.size());
}
else if ("FML".equals(extendedData.type) && !extendedData.isCompatible)
{
idx = 16;
tooltip = String.format("Incompatible FML modded server\n%d mods present", extendedData.modData.size());
}
else if ("BUKKIT".equals(extendedData.type))
{
idx = 32;
tooltip = String.format("Bukkit modded server");
}
else if ("VANILLA".equals(extendedData.type))
{
idx = 48;
tooltip = String.format("Vanilla server");
}
else
{
String tooltip;
if(target.forgeData == null)
return;
switch (target.forgeData.type){
case "FML":
if (target.forgeData.isCompatible) {
idx = 0;
tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.compatible", target.forgeData.numberOfMods);
} else {
idx = 16;
if(target.forgeData.extraReason != null) {
String extraReason = ForgeI18n.parseMessage(target.forgeData.extraReason);
tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.incompatible.extra", extraReason);
} else {
tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.incompatible");
}
}
break;
case "VANILLA":
if(target.forgeData.isCompatible) {
idx = 48;
tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.vanilla");
} else {
idx = 80;
tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.vanilla.incompatible");
}
break;
default:
idx = 64;
tooltip = String.format("Unknown server data");
}
blocked = extendedData.isBlocked;
}
else
{
return null;
tooltip = ForgeI18n.parseMessage("fml.menu.multiplayer.unknown", target.forgeData.type);
}
Minecraft.getInstance().getTextureManager().bindTexture(iconSheet);
Gui.drawModalRectWithCustomSizedTexture(x + width - 18, y + 10, 0, (float)idx, 16, 16, 256.0f, 256.0f);
if (blocked)
{
Gui.drawModalRectWithCustomSizedTexture(x + width - 18, y + 10, 0, 80, 16, 16, 256.0f, 256.0f);
}
return relativeMouseX > width - 15 && relativeMouseX < width && relativeMouseY > 10 && relativeMouseY < 26 ? tooltip : null;
if(relativeMouseX > width - 15 && relativeMouseX < width && relativeMouseY > 10 && relativeMouseY < 26)
gui.setHoveringText(tooltip);
}
public static String fixDescription(String description)
@ -217,18 +179,6 @@ public class ClientHooks
}
}
public static void connectToServer(GuiScreen guiMultiplayer, ServerData serverEntry)
{
ExtendedServerListData extendedData = serverDataTag.get(serverEntry);
if (extendedData != null && extendedData.isBlocked)
{
Minecraft.getInstance().displayGuiScreen(new GuiAccessDenied(guiMultiplayer, serverEntry));
}
else
{
Minecraft.getInstance().displayGuiScreen(new GuiConnecting(guiMultiplayer, Minecraft.getInstance(), serverEntry));
}
}
public static String stripSpecialChars(String message)
{

View file

@ -19,19 +19,24 @@
package net.minecraftforge.fml.client;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Map;
public class ExtendedServerListData {
public final String type;
public final boolean isCompatible;
public final Map<String,String> modData;
public final boolean isBlocked;
public final Map<ResourceLocation, Pair<String, Boolean>> channelData;
public int numberOfMods;
public String extraReason;
public ExtendedServerListData(String type, boolean isCompatible, Map<String,String> modData, boolean isBlocked)
public ExtendedServerListData(String type, boolean isCompatible, Map<ResourceLocation, Pair<String, Boolean>> channelData, int num, String extraReason)
{
this.type = type;
this.isCompatible = isCompatible;
this.modData = modData;
this.isBlocked = isBlocked;
this.channelData = channelData;
this.numberOfMods = num;
this.extraReason = extraReason;
}
}

View file

@ -0,0 +1,123 @@
/*
* 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 com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gson.*;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.registries.RegistryManager;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Map;
public class FMLStatusPing {
private Map<ResourceLocation, Pair<String, Boolean>> channelVersions;
private int numberOfMods;
private int fmlNetworkVer;
private Map<ResourceLocation, Integer> registrySnapshots;
public FMLStatusPing(){
this.channelVersions = NetworkRegistry.buildChannelVersionsForListPing();
this.numberOfMods = ModList.get().size();
this.registrySnapshots = RegistryManager.ACTIVE.computeRegistryHashes();
this.fmlNetworkVer = FMLNetworkConstants.FMLNETVERSION;
}
private FMLStatusPing(Map<ResourceLocation, Pair<String, Boolean>> deserialized, Map<ResourceLocation, Integer> registryHashes, int nom, int fmlNetVer){
this.channelVersions = ImmutableMap.copyOf(deserialized);
this.numberOfMods = nom;
this.registrySnapshots = registryHashes;
this.fmlNetworkVer = fmlNetVer;
}
public static class Serializer {
public static FMLStatusPing deserialize(JsonObject forgeData, JsonDeserializationContext ctx) {
try {
JsonArray mods = JsonUtils.getJsonArray(forgeData, "mods");
Map<ResourceLocation, Pair<String, Boolean>> versions = Maps.newHashMap();
for(JsonElement el : mods){
JsonObject jo = el.getAsJsonObject();
ResourceLocation name = new ResourceLocation(JsonUtils.getString(jo, "namespace"), JsonUtils.getString(jo, "path"));
String version = JsonUtils.getString(jo, "version");
Boolean canBeAbsent = JsonUtils.getBoolean(jo, "mayBeAbsent");
versions.put(name, Pair.of(version, canBeAbsent));
}
JsonArray reghashes = JsonUtils.getJsonArray(forgeData, "registryKeys");
Map<ResourceLocation, Integer> registyData = Maps.newHashMap();
for(JsonElement el : reghashes){
JsonObject jo = el.getAsJsonObject();
ResourceLocation name = new ResourceLocation(JsonUtils.getString(jo, "namespace"), JsonUtils.getString(jo, "path"));
registyData.put(name, JsonUtils.getInt(jo, "hash"));
}
return new FMLStatusPing(versions, registyData, JsonUtils.getInt(forgeData, "numberOfMods"), JsonUtils.getInt(forgeData, "fmlNetworkVersion"));
}catch (Exception c){
return null;
}
}
public static JsonObject serialize(FMLStatusPing forgeData, JsonSerializationContext ctx){
JsonObject obj = new JsonObject();
JsonArray mods = new JsonArray();
forgeData.channelVersions.entrySet().stream().map(p -> {
JsonObject mi = new JsonObject();
mi.addProperty("namespace", p.getKey().getNamespace());
mi.addProperty("path", p.getKey().getPath());
mi.addProperty("version", p.getValue().getKey());
mi.addProperty("mayBeAbsent", p.getValue().getValue());
return mi;
}).forEach(mods::add);
obj.add("mods", mods);
JsonArray regdata = new JsonArray();
forgeData.registrySnapshots.entrySet().stream().map(p -> {
JsonObject mi = new JsonObject();
mi.addProperty("namespace", p.getKey().getNamespace());
mi.addProperty("path", p.getKey().getPath());
mi.addProperty("hash", p.getValue());
return mi;
}).forEach(regdata::add);
obj.add("registryKeys", regdata);
obj.addProperty("numberOfMods", forgeData.numberOfMods);
obj.addProperty("fmlNetworkVersion", forgeData.fmlNetworkVer);
return obj;
}
}
public Map<ResourceLocation, Pair<String, Boolean>> getPresentMods(){
return this.channelVersions;
}
public int getNumberOfMods(){
return numberOfMods;
}
public Map<ResourceLocation, Integer> getRegistryHashes(){
return registrySnapshots;
}
public int getFMLNetworkVersion(){
return fmlNetworkVer;
}
}

View file

@ -19,8 +19,6 @@
package net.minecraftforge.fml.network;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
@ -38,6 +36,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.HashSet;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
@ -63,13 +63,22 @@ public class NetworkRegistry
@SuppressWarnings("RedundantStringConstructorCall")
public static String ACCEPTVANILLA = new String("ALLOWVANILLA \uD83D\uDC93\uD83D\uDC93\uD83D\uDC93");
public static List<String> getNonVanillaNetworkMods()
public static List<String> getServerNonVanillaNetworkMods()
{
return listRejectedVanillaMods(NetworkInstance::tryClientVersionOnServer);
}
public static List<String> getClientNonVanillaNetworkMods()
{
return listRejectedVanillaMods(NetworkInstance::tryServerVersionOnClient);
}
public static boolean acceptsVanillaClientConnections() {
return instances.isEmpty() || getNonVanillaNetworkMods().isEmpty();
return instances.isEmpty() || getServerNonVanillaNetworkMods().isEmpty();
}
public static boolean canConnectToVanillaServer() {
return instances.isEmpty() || getClientNonVanillaNetworkMods().isEmpty();
}
@ -116,6 +125,10 @@ public class NetworkRegistry
*/
private static NetworkInstance createInstance(ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions)
{
if(lock && !name.getNamespace().equals("fml")){
LOGGER.error(NETREGISTRY, "Attempted to register channel {} even though registry phase is over", name);
throw new IllegalArgumentException("Registration of network channels is locked");
}
final NetworkInstance networkInstance = new NetworkInstance(name, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
if (instances.containsKey(name)) {
LOGGER.error(NETREGISTRY, "NetworkDirection channel {} already registered.", name);
@ -146,6 +159,19 @@ public class NetworkRegistry
return instances.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getNetworkProtocolVersion()));
}
/**
* Construct the Map representation of the channel list, for the client to check against during list ping
*
* @see FMLHandshakeMessages.S2CModList
* @see FMLHandshakeMessages.C2SModListReply
*/
static Map<ResourceLocation, Pair<String, Boolean>> buildChannelVersionsForListPing() {
return instances.entrySet().stream().
map( p -> Pair.of(p.getKey(), Pair.of(p.getValue().getNetworkProtocolVersion(), p.getValue().tryClientVersionOnServer(ABSENT)))).
filter(p -> !p.getLeft().getNamespace().equals("fml")).
collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
}
static List<String> listRejectedVanillaMods(BiFunction<NetworkInstance, String, Boolean> testFunction) {
final List<Pair<ResourceLocation, Boolean>> results = instances.values().stream().
map(ni -> {
@ -187,7 +213,7 @@ public class NetworkRegistry
/**
* Tests if the map matches with the supplied predicate tester
*
* @param channels An @{@link Map} of name->version pairs for testing
* @param incoming An @{@link Map} of name->version pairs for testing
* @param originName A label for use in logging (where the version pairs came from)
* @param testFunction The test function to use for testing
* @return true if all channels accept themselves
@ -225,6 +251,47 @@ public class NetworkRegistry
return gatheredPayloads;
}
public static boolean checkListPingCompatibilityForClient(Map<ResourceLocation, Pair<String, Boolean>> incoming) {
Set<ResourceLocation> handled = new HashSet<>();
final List<Pair<ResourceLocation, Boolean>> results = instances.values().stream().
filter(p -> !p.getChannelName().getNamespace().equals("fml")).
map(ni -> {
final Pair<String, Boolean> incomingVersion = incoming.getOrDefault(ni.getChannelName(), Pair.of(ABSENT, true));
final boolean test = ni.tryServerVersionOnClient(incomingVersion.getLeft());
handled.add(ni.getChannelName());
LOGGER.debug(NETREGISTRY, "Channel '{}' : Version test of '{}' during listping : {}", ni.getChannelName(), incomingVersion, test ? "ACCEPTED" : "REJECTED");
return Pair.of(ni.getChannelName(), test);
}).filter(p->!p.getRight()).collect(Collectors.toList());
final List<ResourceLocation> missingButRequired = incoming.entrySet().stream().
filter(p -> !p.getKey().getNamespace().equals("fml")).
filter(p -> !p.getValue().getRight()).
filter(p -> !handled.contains(p.getKey())).
map(Map.Entry::getKey).
collect(Collectors.toList());
if (!results.isEmpty()) {
LOGGER.error(NETREGISTRY, "Channels [{}] rejected their server side version number during listping",
results.stream().map(Pair::getLeft).map(Object::toString).collect(Collectors.joining(",")));
return false;
}
if(!missingButRequired.isEmpty()){
LOGGER.error(NETREGISTRY, "The server is likely to require channel [{}] to be present, yet we don't have it",
missingButRequired);
return false;
}
LOGGER.debug(NETREGISTRY, "Accepting channel list during listping");
return true;
}
private static boolean lock = false;
public boolean isLocked(){
return lock;
}
public static void lock() {
lock=true;
}
/**
* Tracks individual outbound messages for dispatch to clients during login handling. Gathered by dispatching
* {@link net.minecraftforge.fml.network.NetworkEvent.GatherLoginPayloadsEvent} during early connection handling.

View file

@ -24,6 +24,7 @@ import net.minecraftforge.fml.LogicalSidedProvider;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.ModLoadingWarning;
import net.minecraftforge.fml.SidedProvider;
import net.minecraftforge.fml.network.FMLStatusPing;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -50,5 +51,6 @@ public class ServerModLoader
LOGGER.warn(LOADING, "Mods loaded with {} warnings", warnings.size());
warnings.forEach(warning -> LOGGER.warn(LOADING, warning.formatToString()));
}
server.getServerStatusResponse().setForgeData(new FMLStatusPing()); //gathers NetworkRegistry data
}
}

View file

@ -159,4 +159,8 @@ public class RegistryManager
{
return new ArrayList<>(ACTIVE.registries.keySet());
}
public Map<ResourceLocation, Integer> computeRegistryHashes() {
return this.registries.entrySet().stream().map(p -> Pair.of(p.getKey(), p.getValue().getKeys().hashCode())).collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
}
}

View file

@ -15,6 +15,14 @@
"fml.menu.mods.info.childmods":"Child mods: {0}",
"fml.menu.mods.info.updateavailable":"Update available: {0}",
"fml.menu.mods.info.changelogheader":"Changelog:",
"fml.menu.multiplayer.compatible":"Compatible FML modded server, {0,choice,1#1 mod|1<{0} mods} present",
"fml.menu.multiplayer.incompatible":"Incompatible FML modded server",
"fml.menu.multiplayer.incompatible.extra":"Incompatible FML modded server - {}",
"fml.menu.multiplayer.vanilla":"Vanilla server",
"fml.menu.multiplayer.vanilla.incompatible":"Incompatible Vanilla server",
"fml.menu.multiplayer.unknown":"Unknown server {0}",
"fml.menu.multiplayer.serveroutdated":"Outdated server",
"fml.menu.multiplayer.clientoutdated":"Outdated client",
"fml.menu.loadingmods": "{0,choice,0#No mods|1#1 mod|1<{0} mods} loaded",
"fml.button.open.file": "Open {0}",
"fml.button.open.mods.folder": "Open Mods Folder",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB