2018-06-21 19:37:32 +00:00
/ *
* Minecraft Forge
2020-07-02 17:49:11 +00:00
* Copyright ( c ) 2016 - 2020 .
2018-06-21 19:37:32 +00:00
*
* 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.client ;
2018-12-21 22:45:35 +00:00
import java.io.File ;
2020-06-19 14:58:45 +00:00
import java.util.* ;
2019-03-31 20:36:49 +00:00
import java.util.concurrent.atomic.AtomicBoolean ;
2018-12-21 22:45:35 +00:00
import java.util.stream.Collectors ;
import javax.annotation.Nullable ;
2019-03-31 20:36:49 +00:00
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 ;
2020-06-19 14:58:45 +00:00
import com.mojang.blaze3d.matrix.MatrixStack ;
2019-08-31 17:53:43 +00:00
import net.minecraft.client.entity.player.ClientPlayerEntity ;
2019-05-23 23:02:15 +00:00
import net.minecraft.client.gui.AbstractGui ;
import net.minecraft.client.gui.screen.MultiplayerScreen ;
2019-08-31 17:53:43 +00:00
import net.minecraft.client.multiplayer.PlayerController ;
2020-06-19 14:58:45 +00:00
import net.minecraft.util.text.StringTextComponent ;
2019-08-31 17:53:43 +00:00
import net.minecraftforge.client.event.ClientPlayerNetworkEvent ;
import net.minecraftforge.common.MinecraftForge ;
2019-03-31 20:36:49 +00:00
import net.minecraftforge.fml.ExtensionPoint ;
2019-03-17 23:01:23 +00:00
import net.minecraftforge.fml.ForgeI18n ;
2019-03-31 20:36:49 +00:00
import net.minecraftforge.fml.ModList ;
2019-03-17 23:01:23 +00:00
import net.minecraftforge.fml.network.FMLNetworkConstants ;
import net.minecraftforge.fml.network.NetworkRegistry ;
import net.minecraftforge.versions.forge.ForgeVersion ;
2019-03-31 20:36:49 +00:00
import org.apache.commons.lang3.tuple.Pair ;
2018-12-21 22:45:35 +00:00
import org.apache.logging.log4j.LogManager ;
import org.apache.logging.log4j.Logger ;
import org.apache.logging.log4j.Marker ;
import org.apache.logging.log4j.MarkerManager ;
import com.google.common.base.MoreObjects ;
2018-06-21 19:37:32 +00:00
import com.google.common.base.Strings ;
2018-12-21 22:45:35 +00:00
2018-06-21 19:37:32 +00:00
import net.minecraft.client.Minecraft ;
import net.minecraft.client.multiplayer.ServerData ;
2019-05-23 23:02:15 +00:00
import net.minecraft.client.world.ClientWorld ;
2018-06-21 19:37:32 +00:00
import net.minecraft.network.NetworkManager ;
import net.minecraft.network.ServerStatusResponse ;
2019-05-23 23:02:15 +00:00
import net.minecraft.resources.ResourcePack ;
2018-12-21 22:45:35 +00:00
import net.minecraft.resources.FallbackResourceManager ;
import net.minecraft.resources.IResourcePack ;
2019-02-07 04:46:21 +00:00
import net.minecraft.resources.SimpleReloadableResourceManager ;
2018-06-21 19:37:32 +00:00
import net.minecraft.util.ResourceLocation ;
2019-02-07 04:46:21 +00:00
import net.minecraftforge.fml.common.ObfuscationReflectionHelper ;
2019-01-14 03:51:36 +00:00
import net.minecraftforge.forgespi.language.IModInfo ;
2018-12-21 22:45:35 +00:00
import net.minecraftforge.fml.packs.ModFileResourcePack ;
2018-06-21 19:37:32 +00:00
import net.minecraftforge.registries.GameData ;
public class ClientHooks
{
2018-08-27 17:10:07 +00:00
private static final Logger LOGGER = LogManager . getLogger ( ) ;
2018-06-21 19:37:32 +00:00
private static final Marker CLIENTHOOKS = MarkerManager . getMarker ( " CLIENTHOOKS " ) ;
2019-03-17 23:01:23 +00:00
private static final ResourceLocation iconSheet = new ResourceLocation ( ForgeVersion . MOD_ID , " textures/gui/icons.png " ) ;
@Nullable
2019-03-31 20:36:49 +00:00
2019-03-17 23:01:23 +00:00
public static void processForgeListPingData ( ServerStatusResponse packet , ServerData target )
2018-06-21 19:37:32 +00:00
{
2019-03-31 20:36:49 +00:00
if ( packet . getForgeData ( ) ! = null ) {
final Map < String , String > mods = packet . getForgeData ( ) . getRemoteModData ( ) ;
final Map < ResourceLocation , Pair < String , Boolean > > remoteChannels = packet . getForgeData ( ) . getRemoteChannels ( ) ;
final int fmlver = packet . getForgeData ( ) . getFMLNetworkVersion ( ) ;
boolean fmlNetMatches = fmlver = = FMLNetworkConstants . FMLNETVERSION ;
boolean channelsMatch = NetworkRegistry . checkListPingCompatibilityForClient ( remoteChannels ) ;
AtomicBoolean result = new AtomicBoolean ( true ) ;
2020-08-21 18:15:44 +00:00
final List < String > extraClientMods = new ArrayList < > ( ) ;
ModList . get ( ) . forEachModContainer ( ( modid , mc ) - >
mc . getCustomExtension ( ExtensionPoint . DISPLAYTEST ) . ifPresent ( ext - > {
boolean foundModOnServer = ext . getRight ( ) . test ( mods . get ( modid ) , true ) ;
result . compareAndSet ( true , foundModOnServer ) ;
if ( ! foundModOnServer ) {
extraClientMods . add ( modid ) ;
}
} )
) ;
2019-03-31 20:36:49 +00:00
boolean modsMatch = result . get ( ) ;
2019-03-17 23:01:23 +00:00
2019-03-31 20:36:49 +00:00
final Map < String , String > extraServerMods = mods . entrySet ( ) . stream ( ) .
filter ( e - > ! Objects . equals ( FMLNetworkConstants . IGNORESERVERONLY , e . getValue ( ) ) ) .
filter ( e - > ! ModList . get ( ) . isLoaded ( e . getKey ( ) ) ) .
collect ( Collectors . toMap ( Map . Entry : : getKey , Map . Entry : : getValue ) ) ;
2019-03-17 23:01:23 +00:00
2019-03-31 20:36:49 +00:00
LOGGER . debug ( CLIENTHOOKS , " Received FML ping data from server at {}: FMLNETVER={}, mod list is compatible : {}, channel list is compatible: {}, extra server mods: {} " , target . serverIP , fmlver , modsMatch , channelsMatch , extraServerMods ) ;
2019-03-17 23:01:23 +00:00
String extraReason = null ;
2019-03-31 20:36:49 +00:00
if ( ! extraServerMods . isEmpty ( ) ) {
extraReason = " fml.menu.multiplayer.extraservermods " ;
2020-08-21 18:15:44 +00:00
LOGGER . info ( CLIENTHOOKS , ForgeI18n . parseMessage ( extraReason ) + " : {} " , extraServerMods . entrySet ( ) . stream ( )
. map ( e - > e . getKey ( ) + " @ " + e . getValue ( ) )
. collect ( Collectors . joining ( " , " ) ) ) ;
2019-03-31 20:36:49 +00:00
}
if ( ! modsMatch ) {
extraReason = " fml.menu.multiplayer.modsincompatible " ;
2020-08-21 18:15:44 +00:00
LOGGER . info ( CLIENTHOOKS , " Client has mods that are missing on server: {} " , extraClientMods ) ;
2019-03-31 20:36:49 +00:00
}
if ( ! channelsMatch ) {
extraReason = " fml.menu.multiplayer.networkincompatible " ;
}
if ( fmlver < FMLNetworkConstants . FMLNETVERSION ) {
2019-03-17 23:01:23 +00:00
extraReason = " fml.menu.multiplayer.serveroutdated " ;
2019-03-31 20:36:49 +00:00
}
if ( fmlver > FMLNetworkConstants . FMLNETVERSION ) {
2019-03-17 23:01:23 +00:00
extraReason = " fml.menu.multiplayer.clientoutdated " ;
2019-03-31 20:36:49 +00:00
}
2022-01-20 20:24:44 +00:00
if ( ! packet . getForgeData ( ) . isPatchAdvertised ( ) ) {
extraReason = " fml.menu.multiplayer.serverunpatched " ;
}
2019-03-31 20:36:49 +00:00
target . forgeData = new ExtendedServerListData ( " FML " , extraServerMods . isEmpty ( ) & & fmlNetMatches & & channelsMatch & & modsMatch , mods . size ( ) , extraReason ) ;
} else {
target . forgeData = new ExtendedServerListData ( " VANILLA " , NetworkRegistry . canConnectToVanillaServer ( ) , 0 , null ) ;
2018-06-21 19:37:32 +00:00
}
2019-03-17 23:01:23 +00:00
}
2018-06-21 19:37:32 +00:00
2020-06-19 14:58:45 +00:00
public static void drawForgePingInfo ( MultiplayerScreen gui , ServerData target , MatrixStack mStack , int x , int y , int width , int relativeMouseX , int relativeMouseY ) {
2018-06-21 19:37:32 +00:00
int idx ;
2019-03-17 23:01:23 +00:00
String tooltip ;
2019-03-31 20:36:49 +00:00
if ( target . forgeData = = null )
2019-03-17 23:01:23 +00:00
return ;
2019-03-31 20:36:49 +00:00
switch ( target . forgeData . type ) {
2019-03-17 23:01:23 +00:00
case " FML " :
if ( target . forgeData . isCompatible ) {
2022-01-20 20:24:44 +00:00
// HACK: Allow connections to unpatched servers, but show a warning
if ( target . forgeData . extraReason ! = null & & target . forgeData . extraReason . equals ( " fml.menu.multiplayer.serverunpatched " ) ) {
idx = 96 ;
tooltip = ForgeI18n . parseMessage ( " fml.menu.multiplayer.incompatible.extra " , ForgeI18n . parseMessage ( target . forgeData . extraReason ) ) ;
} else {
idx = 0 ;
tooltip = ForgeI18n . parseMessage ( " fml.menu.multiplayer.compatible " , target . forgeData . numberOfMods ) ;
}
2019-03-17 23:01:23 +00:00
} else {
idx = 16 ;
if ( target . forgeData . extraReason ! = null ) {
2022-01-20 20:24:44 +00:00
if ( target . forgeData . extraReason . equals ( " fml.menu.multiplayer.serverunpatched " ) )
idx = 96 ;
2019-03-17 23:01:23 +00:00
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 " :
2019-03-31 20:36:49 +00:00
if ( target . forgeData . isCompatible ) {
2019-03-17 23:01:23 +00:00
idx = 48 ;
tooltip = ForgeI18n . parseMessage ( " fml.menu.multiplayer.vanilla " ) ;
} else {
idx = 80 ;
tooltip = ForgeI18n . parseMessage ( " fml.menu.multiplayer.vanilla.incompatible " ) ;
}
break ;
default :
2018-06-21 19:37:32 +00:00
idx = 64 ;
2019-03-17 23:01:23 +00:00
tooltip = ForgeI18n . parseMessage ( " fml.menu.multiplayer.unknown " , target . forgeData . type ) ;
2018-06-21 19:37:32 +00:00
}
2019-03-17 23:01:23 +00:00
2018-09-22 10:40:22 +00:00
Minecraft . getInstance ( ) . getTextureManager ( ) . bindTexture ( iconSheet ) ;
2020-10-29 00:11:57 +00:00
AbstractGui . blit ( mStack , x + width - 18 , y + 10 , 16 , 16 , 0 , idx , 16 , 16 , 256 , 256 ) ;
2018-06-21 19:37:32 +00:00
2019-03-17 23:01:23 +00:00
if ( relativeMouseX > width - 15 & & relativeMouseX < width & & relativeMouseY > 10 & & relativeMouseY < 26 )
2020-06-19 14:58:45 +00:00
//TODO using StringTextComponent here is a hack, we should be using TranslationTextComponents.
gui . func_238854_b_ ( Collections . singletonList ( new StringTextComponent ( tooltip ) ) ) ;
2019-03-17 23:01:23 +00:00
2018-06-21 19:37:32 +00:00
}
public static String fixDescription ( String description )
{
return description . endsWith ( " :NOFML§r " ) ? description . substring ( 0 , description . length ( ) - 8 ) + " §r " : description ;
}
2020-09-10 19:01:34 +00:00
@SuppressWarnings ( " resource " )
2018-06-21 19:37:32 +00:00
static File getSavesDir ( )
{
2018-09-22 10:40:22 +00:00
return new File ( Minecraft . getInstance ( ) . gameDir , " saves " ) ;
2018-06-21 19:37:32 +00:00
}
private static NetworkManager getClientToServerNetworkManager ( )
{
2018-09-22 10:40:22 +00:00
return Minecraft . getInstance ( ) . getConnection ( ) ! = null ? Minecraft . getInstance ( ) . getConnection ( ) . getNetworkManager ( ) : null ;
2018-06-21 19:37:32 +00:00
}
2019-05-23 23:02:15 +00:00
public static void handleClientWorldClosing ( ClientWorld world )
2018-06-21 19:37:32 +00:00
{
NetworkManager client = getClientToServerNetworkManager ( ) ;
// ONLY revert a non-local connection
if ( client ! = null & & ! client . isLocalChannel ( ) )
{
GameData . revertToFrozen ( ) ;
}
}
2018-06-23 02:45:01 +00:00
2018-12-21 22:45:35 +00:00
private static SetMultimap < String , ResourceLocation > missingTextures = HashMultimap . create ( ) ;
private static Set < String > badTextureDomains = Sets . newHashSet ( ) ;
private static Table < String , String , Set < ResourceLocation > > brokenTextures = HashBasedTable . create ( ) ;
public static void trackMissingTexture ( ResourceLocation resourceLocation )
{
badTextureDomains . add ( resourceLocation . getNamespace ( ) ) ;
missingTextures . put ( resourceLocation . getNamespace ( ) , resourceLocation ) ;
}
public static void trackBrokenTexture ( ResourceLocation resourceLocation , String error )
{
badTextureDomains . add ( resourceLocation . getNamespace ( ) ) ;
Set < ResourceLocation > badType = brokenTextures . get ( resourceLocation . getNamespace ( ) , error ) ;
if ( badType = = null )
{
badType = Sets . newHashSet ( ) ;
brokenTextures . put ( resourceLocation . getNamespace ( ) , MoreObjects . firstNonNull ( error , " Unknown error " ) , badType ) ;
}
badType . add ( resourceLocation ) ;
}
public static void logMissingTextureErrors ( )
{
if ( missingTextures . isEmpty ( ) & & brokenTextures . isEmpty ( ) )
{
return ;
}
Logger logger = LogManager . getLogger ( " FML.TEXTURE_ERRORS " ) ;
logger . error ( Strings . repeat ( " += " , 25 ) ) ;
logger . error ( " The following texture errors were found. " ) ;
2019-02-07 04:46:21 +00:00
Map < String , FallbackResourceManager > resManagers = ObfuscationReflectionHelper . getPrivateValue ( SimpleReloadableResourceManager . class , ( SimpleReloadableResourceManager ) Minecraft . getInstance ( ) . getResourceManager ( ) , " field_199014 " + " _c " ) ;
2018-12-21 22:45:35 +00:00
for ( String resourceDomain : badTextureDomains )
{
Set < ResourceLocation > missing = missingTextures . get ( resourceDomain ) ;
logger . error ( Strings . repeat ( " = " , 50 ) ) ;
logger . error ( " DOMAIN {} " , resourceDomain ) ;
logger . error ( Strings . repeat ( " - " , 50 ) ) ;
logger . error ( " domain {} is missing {} texture{} " , resourceDomain , missing . size ( ) , missing . size ( ) ! = 1 ? " s " : " " ) ;
FallbackResourceManager fallbackResourceManager = resManagers . get ( resourceDomain ) ;
if ( fallbackResourceManager = = null )
{
logger . error ( " domain {} is missing a resource manager - it is probably a side-effect of automatic texture processing " , resourceDomain ) ;
}
else
{
2019-02-07 04:46:21 +00:00
List < IResourcePack > resPacks = fallbackResourceManager . resourcePacks ;
2018-12-21 22:45:35 +00:00
logger . error ( " domain {} has {} location{}: " , resourceDomain , resPacks . size ( ) , resPacks . size ( ) ! = 1 ? " s " : " " ) ;
for ( IResourcePack resPack : resPacks )
{
if ( resPack instanceof ModFileResourcePack ) {
ModFileResourcePack modRP = ( ModFileResourcePack ) resPack ;
List < IModInfo > mods = modRP . getModFile ( ) . getModInfos ( ) ;
logger . error ( " mod(s) {} resources at {} " , mods . stream ( ) . map ( IModInfo : : getDisplayName ) . collect ( Collectors . toList ( ) ) , modRP . getModFile ( ) . getFilePath ( ) ) ;
}
2019-05-23 23:02:15 +00:00
else if ( resPack instanceof ResourcePack )
2018-12-21 22:45:35 +00:00
{
2019-05-23 23:02:15 +00:00
logger . error ( " resource pack at path {} " , ( ( ResourcePack ) resPack ) . file . getPath ( ) ) ;
2018-12-21 22:45:35 +00:00
}
else
{
logger . error ( " unknown resourcepack type {} : {} " , resPack . getClass ( ) . getName ( ) , resPack . getName ( ) ) ;
}
}
}
logger . error ( Strings . repeat ( " - " , 25 ) ) ;
if ( missingTextures . containsKey ( resourceDomain ) ) {
logger . error ( " The missing resources for domain {} are: " , resourceDomain ) ;
for ( ResourceLocation rl : missing ) {
logger . error ( " {} " , rl . getPath ( ) ) ;
}
logger . error ( Strings . repeat ( " - " , 25 ) ) ;
}
if ( ! brokenTextures . containsRow ( resourceDomain ) )
{
logger . error ( " No other errors exist for domain {} " , resourceDomain ) ;
}
else
{
logger . error ( " The following other errors were reported for domain {}: " , resourceDomain ) ;
Map < String , Set < ResourceLocation > > resourceErrs = brokenTextures . row ( resourceDomain ) ;
for ( String error : resourceErrs . keySet ( ) )
{
logger . error ( Strings . repeat ( " - " , 25 ) ) ;
logger . error ( " Problem: {} " , error ) ;
for ( ResourceLocation rl : resourceErrs . get ( error ) )
{
logger . error ( " {} " , rl . getPath ( ) ) ;
}
}
}
logger . error ( Strings . repeat ( " = " , 50 ) ) ;
}
logger . error ( Strings . repeat ( " += " , 25 ) ) ;
}
2019-08-31 17:53:43 +00:00
public static void firePlayerLogin ( PlayerController pc , ClientPlayerEntity player , NetworkManager networkManager ) {
MinecraftForge . EVENT_BUS . post ( new ClientPlayerNetworkEvent . LoggedInEvent ( pc , player , networkManager ) ) ;
}
public static void firePlayerLogout ( PlayerController pc , ClientPlayerEntity player ) {
MinecraftForge . EVENT_BUS . post ( new ClientPlayerNetworkEvent . LoggedOutEvent ( pc , player , player ! = null ? player . connection ! = null ? player . connection . getNetworkManager ( ) : null : null ) ) ;
}
public static void firePlayerRespawn ( PlayerController pc , ClientPlayerEntity oldPlayer , ClientPlayerEntity newPlayer , NetworkManager networkManager ) {
MinecraftForge . EVENT_BUS . post ( new ClientPlayerNetworkEvent . RespawnEvent ( pc , oldPlayer , newPlayer , networkManager ) ) ;
}
2018-06-21 19:37:32 +00:00
}