Re-Add the list ping compatibility checker
Signed-off-by: JoJoDeveloping <jojohostert@gmail.com>
This commit is contained in:
parent
18400e8457
commit
4764136d47
13 changed files with 365 additions and 134 deletions
|
@ -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);
|
|
@ -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_;
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
123
src/main/java/net/minecraftforge/fml/network/FMLStatusPing.java
Normal file
123
src/main/java/net/minecraftforge/fml/network/FMLStatusPing.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 |
Loading…
Reference in a new issue