2022-01-10 17:45:26 +00:00
/ *
* Copyright ( c ) 2019 - 2022 GeyserMC . http : //geysermc.org
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*
* @author GeyserMC
* @link https : //github.com/GeyserMC/Geyser
* /
package org.geysermc.geyser.extension ;
import org.geysermc.geyser.GeyserImpl ;
2022-01-10 19:01:36 +00:00
import org.geysermc.geyser.api.extension.ExtensionDescription ;
import org.geysermc.geyser.api.extension.GeyserExtension ;
2022-01-10 17:45:26 +00:00
import java.io.File ;
import java.lang.reflect.Constructor ;
import java.util.* ;
import java.util.regex.Pattern ;
2022-01-10 19:01:36 +00:00
public class GeyserExtensionManager {
2022-01-12 12:50:54 +00:00
protected Map < String , GeyserExtension > extensions = new LinkedHashMap < > ( ) ;
2022-01-10 19:01:36 +00:00
protected Map < Pattern , GeyserExtensionLoader > fileAssociations = new HashMap < > ( ) ;
2022-01-10 17:45:26 +00:00
2022-01-12 15:40:51 +00:00
public void init ( ) {
2022-01-10 17:45:26 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . info ( " Loading extensions... " ) ;
2022-01-12 12:50:54 +00:00
2022-01-12 15:40:51 +00:00
this . registerInterface ( GeyserExtensionLoader . class ) ;
this . loadExtensions ( new File ( " extensions " ) ) ;
2022-01-10 17:45:26 +00:00
2022-01-12 15:40:51 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . info ( " Loaded " + this . extensions . size ( ) + " extension(s) " ) ;
2022-01-10 17:45:26 +00:00
}
2022-01-12 12:50:54 +00:00
public GeyserExtension getExtension ( String name ) {
2022-01-10 17:45:26 +00:00
if ( this . extensions . containsKey ( name ) ) {
return this . extensions . get ( name ) ;
}
return null ;
}
2022-01-12 12:50:54 +00:00
public Map < String , GeyserExtension > getExtensions ( ) {
2022-01-10 17:45:26 +00:00
return this . extensions ;
}
2022-01-10 19:01:36 +00:00
public void registerInterface ( Class < ? extends GeyserExtensionLoader > loader ) {
GeyserExtensionLoader instance ;
2022-01-10 17:45:26 +00:00
2022-01-10 19:01:36 +00:00
if ( GeyserExtensionLoader . class . isAssignableFrom ( loader ) ) {
Constructor < ? extends GeyserExtensionLoader > constructor ;
2022-01-10 17:45:26 +00:00
try {
constructor = loader . getConstructor ( ) ;
instance = constructor . newInstance ( ) ;
} catch ( NoSuchMethodException ex ) { // This should never happen
String className = loader . getName ( ) ;
throw new IllegalArgumentException ( " Class " + className + " does not have a public constructor " , ex ) ;
} catch ( Exception ex ) { // This should never happen
throw new IllegalArgumentException ( " Unexpected exception " + ex . getClass ( ) . getName ( ) + " while attempting to construct a new instance of " + loader . getName ( ) , ex ) ;
}
} else {
throw new IllegalArgumentException ( " Class " + loader . getName ( ) + " does not implement interface ExtensionLoader " ) ;
}
2022-01-10 19:01:36 +00:00
Pattern [ ] patterns = instance . extensionFilters ( ) ;
2022-01-10 17:45:26 +00:00
synchronized ( this ) {
for ( Pattern pattern : patterns ) {
fileAssociations . put ( pattern , instance ) ;
}
}
}
2022-01-10 19:01:36 +00:00
public GeyserExtension loadExtension ( File file , Map < Pattern , GeyserExtensionLoader > loaders ) {
for ( GeyserExtensionLoader loader : ( loaders = = null ? this . fileAssociations : loaders ) . values ( ) ) {
for ( Pattern pattern : loader . extensionFilters ( ) ) {
2022-01-10 17:45:26 +00:00
if ( pattern . matcher ( file . getName ( ) ) . matches ( ) ) {
try {
2022-01-10 19:01:36 +00:00
ExtensionDescription description = loader . extensionDescription ( file ) ;
2022-01-10 17:45:26 +00:00
if ( description ! = null ) {
GeyserExtension extension = loader . loadExtension ( file ) ;
if ( extension ! = null ) {
2022-01-10 19:01:36 +00:00
this . extensions . put ( extension . description ( ) . name ( ) , extension ) ;
2022-01-10 17:45:26 +00:00
return extension ;
}
}
} catch ( Exception e ) {
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Could not load extension " , e ) ;
return null ;
}
}
}
}
return null ;
}
2022-01-12 12:50:54 +00:00
public Map < String , GeyserExtension > loadExtensions ( File dictionary ) {
if ( GeyserImpl . VERSION . equalsIgnoreCase ( " dev " ) ) { // If your IDE says this is always true, ignore it, it isn't.
2022-01-10 17:45:26 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Cannot load extensions in a development environment, aborting extension loading " ) ;
return new HashMap < > ( ) ;
}
if ( ! GeyserImpl . VERSION . contains ( " . " ) ) {
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Something went wrong with the Geyser version number, aborting extension loading " ) ;
return new HashMap < > ( ) ;
}
2022-01-12 12:50:54 +00:00
String [ ] apiVersion = GeyserImpl . VERSION . split ( " \\ . " ) ;
2022-01-10 17:45:26 +00:00
if ( ! dictionary . exists ( ) ) {
dictionary . mkdir ( ) ;
}
if ( ! dictionary . isDirectory ( ) ) {
return new HashMap < > ( ) ;
}
Map < String , File > extensions = new LinkedHashMap < > ( ) ;
2022-01-12 12:50:54 +00:00
Map < String , GeyserExtension > loadedExtensions = new LinkedHashMap < > ( ) ;
2022-01-10 17:45:26 +00:00
2022-01-10 19:01:36 +00:00
for ( final GeyserExtensionLoader loader : this . fileAssociations . values ( ) ) {
2022-01-10 17:45:26 +00:00
for ( File file : dictionary . listFiles ( ( dir , name ) - > {
2022-01-10 19:01:36 +00:00
for ( Pattern pattern : loader . extensionFilters ( ) ) {
2022-01-10 17:45:26 +00:00
if ( pattern . matcher ( name ) . matches ( ) ) {
return true ;
}
}
return false ;
} ) ) {
if ( file . isDirectory ( ) ) {
continue ;
}
try {
2022-01-10 19:01:36 +00:00
ExtensionDescription description = loader . extensionDescription ( file ) ;
2022-01-10 17:45:26 +00:00
if ( description ! = null ) {
2022-01-10 19:01:36 +00:00
String name = description . name ( ) ;
2022-01-10 17:45:26 +00:00
if ( extensions . containsKey ( name ) | | this . getExtension ( name ) ! = null ) {
GeyserImpl . getInstance ( ) . getLogger ( ) . warning ( " Found duplicate extension ' " + name + " ', ignoring ' " + file . getName ( ) + " ' " ) ;
continue ;
}
2022-01-12 12:50:54 +00:00
try {
//Check the format: majorVersion.minorVersion.patch
2022-01-12 14:31:28 +00:00
if ( ! Pattern . matches ( " ^[0-9]+ \\ .[0-9]+ \\ .[0-9]+$ " , description . apiVersion ( ) ) ) {
2022-01-12 12:50:54 +00:00
throw new IllegalArgumentException ( ) ;
2022-01-10 17:45:26 +00:00
}
2022-01-12 12:50:54 +00:00
} catch ( NullPointerException | IllegalArgumentException e ) {
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Couldn't load extension " + name + " : Wrong API version format, should be 'majorVersion.minorVersion.patch', current version: " + apiVersion [ 0 ] + " . " + apiVersion [ 1 ] ) ;
continue ;
}
2022-01-10 17:45:26 +00:00
2022-01-12 14:31:28 +00:00
String [ ] versionArray = description . apiVersion ( ) . split ( " \\ . " ) ;
2022-01-10 17:45:26 +00:00
2022-01-12 12:50:54 +00:00
//Completely different API version
if ( ! Objects . equals ( Integer . valueOf ( versionArray [ 0 ] ) , Integer . valueOf ( apiVersion [ 0 ] ) ) ) {
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Couldn't load extension " + name + " : Wrong API version, current version: " + apiVersion [ 0 ] + " . " + apiVersion [ 1 ] ) ;
continue ;
2022-01-10 17:45:26 +00:00
}
2022-01-12 12:50:54 +00:00
//If the extension requires new API features, being backwards compatible
if ( Integer . parseInt ( versionArray [ 1 ] ) > Integer . parseInt ( apiVersion [ 1 ] ) ) {
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Couldn't load extension " + name + " : Wrong API version, current version: " + apiVersion [ 0 ] + " . " + apiVersion [ 1 ] ) ;
continue ;
2022-01-10 17:45:26 +00:00
}
extensions . put ( name , file ) ;
loadedExtensions . put ( name , this . loadExtension ( file , this . fileAssociations ) ) ;
}
} catch ( Exception e ) {
2022-01-12 12:50:54 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Couldn't load " + file . getName ( ) + " in folder " + dictionary . getAbsolutePath ( ) + " : " , e ) ;
2022-01-10 17:45:26 +00:00
}
}
}
return loadedExtensions ;
}
2022-01-12 12:50:54 +00:00
public void enableExtension ( GeyserExtension extension ) {
2022-01-10 17:45:26 +00:00
if ( ! extension . isEnabled ( ) ) {
try {
2022-01-10 19:01:36 +00:00
extension . extensionLoader ( ) . enableExtension ( extension ) ;
2022-01-10 17:45:26 +00:00
} catch ( Exception e ) {
2022-01-10 19:01:36 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Error enabling extension " + extension . name ( ) + " : " , e ) ;
2022-01-10 17:45:26 +00:00
this . disableExtension ( extension ) ;
}
}
}
2022-01-12 12:50:54 +00:00
public void disableExtension ( GeyserExtension extension ) {
2022-01-10 17:45:26 +00:00
if ( extension . isEnabled ( ) ) {
try {
2022-01-10 19:01:36 +00:00
extension . extensionLoader ( ) . disableExtension ( extension ) ;
2022-01-10 17:45:26 +00:00
} catch ( Exception e ) {
2022-01-10 19:01:36 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Error disabling extension " + extension . name ( ) + " : " , e ) ;
2022-01-10 17:45:26 +00:00
}
}
}
2022-01-12 12:50:54 +00:00
public void enableExtensions ( ) {
for ( GeyserExtension extension : this . getExtensions ( ) . values ( ) ) {
this . enableExtension ( extension ) ;
}
}
2022-01-10 17:45:26 +00:00
public void disableExtensions ( ) {
2022-01-12 12:50:54 +00:00
for ( GeyserExtension extension : this . getExtensions ( ) . values ( ) ) {
2022-01-10 17:45:26 +00:00
this . disableExtension ( extension ) ;
}
}
}