2018-03-25 15:59:43 +00:00
/ *
* Minecraft Forge
2020-07-02 17:49:11 +00:00
* Copyright ( c ) 2016 - 2020 .
2018-03-25 15:59:43 +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.loading.moddiscovery ;
2019-10-24 01:30:17 +00:00
import cpw.mods.gross.Java9ClassLoaderUtil ;
2019-09-01 18:40:39 +00:00
import cpw.mods.modlauncher.Launcher ;
2018-03-25 15:59:43 +00:00
import cpw.mods.modlauncher.ServiceLoaderStreamUtils ;
2019-10-24 01:30:17 +00:00
import cpw.mods.modlauncher.api.LamdbaExceptionUtils ;
import net.minecraftforge.fml.loading.FMLEnvironment ;
2018-04-07 15:48:43 +00:00
import net.minecraftforge.fml.loading.FMLLoader ;
2018-06-15 19:03:35 +00:00
import net.minecraftforge.fml.loading.LoadingModList ;
2019-10-24 01:30:17 +00:00
import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer ;
2018-06-06 15:37:56 +00:00
import net.minecraftforge.fml.loading.ModSorter ;
2019-10-05 21:02:54 +00:00
import net.minecraftforge.fml.loading.progress.StartupMessageManager ;
2019-09-01 18:40:39 +00:00
import net.minecraftforge.forgespi.Environment ;
2019-09-01 15:31:09 +00:00
import net.minecraftforge.forgespi.locating.IModFile ;
import net.minecraftforge.forgespi.locating.IModLocator ;
2018-08-27 17:10:07 +00:00
import org.apache.logging.log4j.LogManager ;
import org.apache.logging.log4j.Logger ;
2018-03-25 15:59:43 +00:00
2019-08-05 00:16:01 +00:00
import java.io.IOException ;
import java.net.URI ;
import java.net.URISyntaxException ;
2019-10-24 01:30:17 +00:00
import java.net.URL ;
import java.net.URLClassLoader ;
2019-08-05 00:16:01 +00:00
import java.nio.file.FileSystem ;
import java.nio.file.FileSystems ;
import java.nio.file.Files ;
2018-12-31 21:33:54 +00:00
import java.nio.file.Path ;
2019-08-05 00:16:01 +00:00
import java.nio.file.Paths ;
2019-02-13 23:33:50 +00:00
import java.util.ArrayList ;
2019-08-05 00:16:01 +00:00
import java.util.Arrays ;
2018-03-25 15:59:43 +00:00
import java.util.Collection ;
2018-09-24 04:07:33 +00:00
import java.util.Collections ;
2019-08-05 00:16:01 +00:00
import java.util.HashMap ;
2018-06-06 15:37:56 +00:00
import java.util.Iterator ;
2018-03-25 15:59:43 +00:00
import java.util.List ;
import java.util.Map ;
2019-08-05 00:16:01 +00:00
import java.util.Objects ;
import java.util.Optional ;
2018-03-25 15:59:43 +00:00
import java.util.ServiceLoader ;
2019-08-05 00:16:01 +00:00
import java.util.function.Consumer ;
import java.util.jar.Manifest ;
2018-03-25 15:59:43 +00:00
import java.util.stream.Collectors ;
2019-08-05 00:16:01 +00:00
import java.util.stream.Stream ;
import java.util.zip.ZipError ;
2018-03-25 15:59:43 +00:00
2019-03-16 03:19:28 +00:00
import static net.minecraftforge.fml.loading.LogMarkers.CORE ;
2018-12-31 21:33:54 +00:00
import static net.minecraftforge.fml.loading.LogMarkers.SCAN ;
2018-03-25 15:59:43 +00:00
public class ModDiscoverer {
2018-08-27 17:10:07 +00:00
private static final Logger LOGGER = LogManager . getLogger ( ) ;
2018-03-25 15:59:43 +00:00
private final ServiceLoader < IModLocator > locators ;
private final List < IModLocator > locatorList ;
2019-10-24 01:30:17 +00:00
private final LocatorClassLoader locatorClassLoader ;
2018-03-25 15:59:43 +00:00
2018-12-31 21:33:54 +00:00
public ModDiscoverer ( Map < String , ? > arguments ) {
2019-09-01 18:40:39 +00:00
Launcher . INSTANCE . environment ( ) . computePropertyIfAbsent ( Environment . Keys . MODFOLDERFACTORY . get ( ) , v - > ModsFolderLocator : : new ) ;
2019-11-03 17:33:03 +00:00
Launcher . INSTANCE . environment ( ) . computePropertyIfAbsent ( Environment . Keys . MODDIRECTORYFACTORY . get ( ) , v - > ModsFolderLocator : : new ) ;
2019-10-05 21:02:54 +00:00
Launcher . INSTANCE . environment ( ) . computePropertyIfAbsent ( Environment . Keys . PROGRESSMESSAGE . get ( ) , v - > StartupMessageManager . locatorConsumer ( ) . orElseGet ( ( ) - > s - > { } ) ) ;
2019-10-24 01:30:17 +00:00
locatorClassLoader = new LocatorClassLoader ( ) ;
Launcher . INSTANCE . environment ( ) . computePropertyIfAbsent ( FMLEnvironment . Keys . LOCATORCLASSLOADER . get ( ) , v - > locatorClassLoader ) ;
ModDirTransformerDiscoverer . getExtraLocators ( )
. stream ( )
. map ( LamdbaExceptionUtils . rethrowFunction ( p - > p . toUri ( ) . toURL ( ) ) )
. forEach ( locatorClassLoader : : addURL ) ;
locators = ServiceLoader . load ( IModLocator . class , locatorClassLoader ) ;
2018-03-25 15:59:43 +00:00
locatorList = ServiceLoaderStreamUtils . toList ( this . locators ) ;
2018-12-31 21:33:54 +00:00
locatorList . forEach ( l - > l . initArguments ( arguments ) ) ;
2019-08-05 00:16:01 +00:00
locatorList . add ( new MinecraftLocator ( ) ) ;
2019-03-16 03:19:28 +00:00
LOGGER . debug ( CORE , " Found Mod Locators : {} " , ( ) - > locatorList . stream ( ) . map ( iModLocator - > " ( " + iModLocator . name ( ) + " : " + iModLocator . getClass ( ) . getPackage ( ) . getImplementationVersion ( ) + " ) " ) . collect ( Collectors . joining ( " , " ) ) ) ;
2018-03-25 15:59:43 +00:00
}
ModDiscoverer ( List < IModLocator > locatorList ) {
this . locatorList = locatorList ;
2019-10-24 01:30:17 +00:00
this . locatorClassLoader = null ;
2018-03-25 15:59:43 +00:00
this . locators = null ;
}
public BackgroundScanHandler discoverMods ( ) {
2018-08-27 17:10:07 +00:00
LOGGER . debug ( SCAN , " Scanning for mods and other resources to load. We know {} ways to find mods " , locatorList . size ( ) ) ;
2019-09-01 15:31:09 +00:00
final Map < IModFile . Type , List < ModFile > > modFiles = locatorList . stream ( )
2018-08-27 17:10:07 +00:00
. peek ( loc - > LOGGER . debug ( SCAN , " Trying locator {} " , loc ) )
2018-03-25 15:59:43 +00:00
. map ( IModLocator : : scanMods )
. flatMap ( Collection : : stream )
2018-08-27 17:10:07 +00:00
. peek ( mf - > LOGGER . debug ( SCAN , " Found mod file {} of type {} with locator {} " , mf . getFileName ( ) , mf . getType ( ) , mf . getLocator ( ) ) )
2019-10-05 21:02:54 +00:00
. peek ( mf - > StartupMessageManager . modLoaderConsumer ( ) . ifPresent ( c - > c . accept ( " Found mod file " + mf . getFileName ( ) + " of type " + mf . getType ( ) ) ) )
2019-09-01 15:31:09 +00:00
. map ( ModFile . class : : cast )
. collect ( Collectors . groupingBy ( IModFile : : getType ) ) ;
2018-03-25 15:59:43 +00:00
2019-09-01 15:31:09 +00:00
FMLLoader . getLanguageLoadingProvider ( ) . addAdditionalLanguages ( modFiles . get ( IModFile . Type . LANGPROVIDER ) ) ;
2019-09-27 21:42:24 +00:00
BackgroundScanHandler backgroundScanHandler = new BackgroundScanHandler ( modFiles ) ;
2019-09-01 15:31:09 +00:00
final List < ModFile > mods = modFiles . getOrDefault ( IModFile . Type . MOD , Collections . emptyList ( ) ) ;
2019-02-13 23:33:50 +00:00
final List < ModFile > brokenFiles = new ArrayList < > ( ) ;
2018-06-06 15:37:56 +00:00
for ( Iterator < ModFile > iterator = mods . iterator ( ) ; iterator . hasNext ( ) ; )
{
ModFile mod = iterator . next ( ) ;
2019-02-13 23:33:50 +00:00
if ( ! mod . getLocator ( ) . isValid ( mod ) | | ! mod . identifyMods ( ) ) {
LOGGER . warn ( SCAN , " File {} has been ignored - it is invalid " , mod . getFilePath ( ) ) ;
2018-06-06 15:37:56 +00:00
iterator . remove ( ) ;
2019-02-13 23:33:50 +00:00
brokenFiles . add ( mod ) ;
2018-06-06 15:37:56 +00:00
}
}
2018-08-27 17:10:07 +00:00
LOGGER . debug ( SCAN , " Found {} mod files with {} mods " , mods : : size , ( ) - > mods . stream ( ) . mapToInt ( mf - > mf . getModInfos ( ) . size ( ) ) . sum ( ) ) ;
2019-10-05 21:02:54 +00:00
StartupMessageManager . modLoaderConsumer ( ) . ifPresent ( c - > c . accept ( " Found " + mods . size ( ) + " modfiles to load " ) ) ;
2018-06-15 19:03:35 +00:00
final LoadingModList loadingModList = ModSorter . sort ( mods ) ;
loadingModList . addCoreMods ( ) ;
loadingModList . addAccessTransformers ( ) ;
loadingModList . addForScanning ( backgroundScanHandler ) ;
2019-02-13 23:33:50 +00:00
loadingModList . setBrokenFiles ( brokenFiles ) ;
2018-03-25 15:59:43 +00:00
return backgroundScanHandler ;
}
2018-12-31 21:33:54 +00:00
2019-10-24 01:30:17 +00:00
private static class LocatorClassLoader extends URLClassLoader {
LocatorClassLoader ( ) {
super ( Java9ClassLoaderUtil . getSystemClassPathURLs ( ) , getSystemClassLoader ( ) ) ;
}
@Override
protected void addURL ( final URL url ) {
super . addURL ( url ) ;
}
}
2019-08-05 00:16:01 +00:00
private static class MinecraftLocator implements IModLocator {
private final Path mcJar = FMLLoader . getMCPaths ( ) [ 0 ] ;
private final FileSystem fileSystem ;
2018-12-31 21:33:54 +00:00
2019-08-05 00:16:01 +00:00
MinecraftLocator ( ) {
if ( ! Files . isDirectory ( mcJar ) ) {
try {
fileSystem = FileSystems . newFileSystem ( mcJar , getClass ( ) . getClassLoader ( ) ) ;
} catch ( ZipError | IOException e ) {
LOGGER . fatal ( SCAN , " Invalid Minecraft JAR file - no filesystem created " ) ;
throw new RuntimeException ( e ) ;
}
} else {
fileSystem = null ;
}
}
@Override
2019-09-01 15:31:09 +00:00
public List < IModFile > scanMods ( ) {
2020-06-24 02:12:39 +00:00
return Collections . singletonList ( ModFile . newFMLInstance ( mcJar , this ) ) ;
2019-08-05 00:16:01 +00:00
}
@Override
public String name ( ) {
return " minecraft " ;
}
@Override
2019-09-01 15:31:09 +00:00
public Path findPath ( final IModFile modFile , final String . . . path ) {
2019-08-05 00:16:01 +00:00
String [ ] newPath = Arrays . copyOf ( path , path . length ) ;
if ( path . length = = 2 & & Objects . equals ( path [ 1 ] , " mods.toml " ) ) {
final URI jarFileURI ;
try {
jarFileURI = getClass ( ) . getClassLoader ( ) . getResource ( " minecraftmod.toml " ) . toURI ( ) ;
if ( Objects . equals ( jarFileURI . getScheme ( ) , " jar " ) ) {
// Initialize the filesystem for the forge jar, because otherwise this barfs?
FileSystems . newFileSystem ( jarFileURI , new HashMap < > ( ) ) ;
}
} catch ( URISyntaxException | IOException e ) {
LOGGER . fatal ( SCAN , " Unable to read minecraft default mod " ) ;
throw new RuntimeException ( e ) ;
}
return Paths . get ( jarFileURI ) ;
}
if ( Files . isDirectory ( mcJar ) ) return findPathDirectory ( modFile , path ) ;
return findPathJar ( modFile , path ) ;
}
2019-09-01 15:31:09 +00:00
private Path findPathDirectory ( final IModFile modFile , final String . . . path ) {
2019-08-05 00:16:01 +00:00
if ( path . length < 1 ) {
throw new IllegalArgumentException ( " Missing path " ) ;
}
final Path target = Paths . get ( path [ 0 ] , Arrays . copyOfRange ( path , 1 , path . length ) ) ;
// try right path first (resources)
return mcJar . resolve ( target ) ;
}
2019-09-01 15:31:09 +00:00
private Path findPathJar ( final IModFile modFile , final String . . . path ) {
2019-08-05 00:16:01 +00:00
return fileSystem . getPath ( path [ 0 ] , Arrays . copyOfRange ( path , 1 , path . length ) ) ;
}
@Override
2019-09-01 15:31:09 +00:00
public void scanFile ( final IModFile modFile , final Consumer < Path > pathConsumer ) {
2019-08-05 00:16:01 +00:00
LOGGER . debug ( SCAN , " Scan started: {} " , modFile ) ;
Path path ;
if ( Files . isDirectory ( mcJar ) )
path = mcJar ;
else
path = fileSystem . getPath ( " / " ) ;
try ( Stream < Path > files = Files . find ( path , Integer . MAX_VALUE , ( p , a ) - > p . getNameCount ( ) > 0 & & p . getFileName ( ) . toString ( ) . endsWith ( " .class " ) ) ) {
files . forEach ( pathConsumer ) ;
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
LOGGER . debug ( SCAN , " Scan finished: {} " , modFile ) ;
}
@Override
public Optional < Manifest > findManifest ( final Path file ) {
return Optional . empty ( ) ;
}
@Override
public void initArguments ( final Map < String , ? > arguments ) {
// no op
}
@Override
2019-09-01 15:31:09 +00:00
public boolean isValid ( final IModFile modFile ) {
2019-08-05 00:16:01 +00:00
return true ;
}
2018-12-31 21:33:54 +00:00
}
2018-03-25 15:59:43 +00:00
}