77import io .github .syst3ms .skriptparser .registration .SkriptAddon ;
88import io .github .syst3ms .skriptparser .registration .SkriptRegistration ;
99import io .github .syst3ms .skriptparser .util .ConsoleColors ;
10- import io .github .syst3ms .skriptparser .util .FileUtils ;
10+ import org .jetbrains .annotations .Nullable ;
11+ import org .reflections .Reflections ;
12+ import org .reflections .scanners .Scanners ;
1113
1214import java .io .IOException ;
1315import java .lang .reflect .InvocationTargetException ;
2325import java .util .Calendar ;
2426import java .util .List ;
2527import java .util .jar .JarFile ;
26- import java .util .jar .Manifest ;
2728
2829public class Parser {
30+
2931 public static final String CONSOLE_FORMAT = "[%tT] %s: %s%n" ;
30- private static SkriptRegistration registration ;
3132
33+ private static SkriptRegistration registration ;
3234 private static List <LogEntry > logs ;
3335
3436 public static void main (String [] args ) {
@@ -69,49 +71,72 @@ public static void main(String[] args) {
6971 * @param standalone whether the parser tries to load addons (standalone) or not (library)
7072 */
7173 public static void init (String [] mainPackages , String [] subPackages , String [] programArgs , boolean standalone ) {
74+ init (mainPackages , subPackages , programArgs , standalone , null );
75+ }
76+
77+ /**
78+ * Starts the parser.
79+ * @param mainPackages packages inside which all subpackages containing classes to load may be present. Doesn't need
80+ * to contain Skript's own main packages.
81+ * @param subPackages the subpackages inside which classes to load may be present. Doesn't need to contain Skript's
82+ * own subpackages.
83+ * @param programArgs any other program arguments (typically from the command line)
84+ * @param standalone whether the parser tries to load addons (standalone) or not (library)
85+ * @param parserPath the main path to the parser, will be used to get the addons folder. If null, the path will be inferred from the parser's location.
86+ */
87+ public static void init (String [] mainPackages , String [] subPackages , String [] programArgs , boolean standalone , @ Nullable Path parserPath ) {
7288 Skript skript = new Skript (programArgs );
7389 registration = new SkriptRegistration (skript );
7490 DefaultRegistration .register ();
75- // Make sure Skript loads properly no matter what
91+
92+ // Ensure Skript loads first
7693 mainPackages = Arrays .copyOf (mainPackages , mainPackages .length + 1 );
7794 mainPackages [mainPackages .length - 1 ] = "io.github.syst3ms.skriptparser" ;
95+
96+ // Combine main and sub-packages
7897 List <String > sub = new ArrayList <>();
7998 sub .addAll (Arrays .asList (subPackages ));
8099 sub .addAll (Arrays .asList ("expressions" , "effects" , "event" , "lang" , "sections" , "tags" ));
81100 subPackages = sub .toArray (new String [0 ]);
82- try {
83- for (String mainPackage : mainPackages ) {
84- FileUtils .loadClasses (FileUtils .getJarFile (Parser .class ), mainPackage , subPackages );
101+ List <String > allPackages = new ArrayList <>(List .of (mainPackages ));
102+ for (String subPackage : subPackages ) {
103+ for (String main : mainPackages ) {
104+ allPackages .add (main + "." + subPackage );
85105 }
106+ }
107+
108+ try {
109+ // Load all classes in the specified packages
110+ new Reflections (allPackages .toArray (new String [0 ]), Scanners .SubTypes .filterResultsBy (s -> true ))
111+ .getSubTypesOf (Object .class )
112+ .forEach (clazz -> {
113+ try {
114+ Class .forName (clazz .getName (), true , Parser .class .getClassLoader ());
115+ } catch (ClassNotFoundException e ) {
116+ e .printStackTrace ();
117+ }
118+ });
119+
120+ // Load addons if standalone mode is enabled
86121 if (standalone ) {
87- Path parserPath = Paths .get (Parser .class
88- .getProtectionDomain ()
89- .getCodeSource ()
90- .getLocation ()
91- .toURI ()
92- );
93- Path addonFolderPath = Paths .get (parserPath .getParent ().toString (), "addons" );
122+ if (parserPath == null ) {
123+ parserPath = Paths .get (Parser .class .getProtectionDomain ().getCodeSource ().getLocation ().toURI ()).getParent ();
124+ }
125+ Path addonFolderPath = parserPath .getParent ().resolve ("addons" );
94126 if (Files .isDirectory (addonFolderPath )) {
95127 Files .walk (addonFolderPath )
96128 .filter (Files ::isRegularFile )
97- .filter ((filePath ) -> filePath .toString ().endsWith (".jar" ))
98- .forEach ((Path addonPath ) -> {
99- try {
100- URLClassLoader child = new URLClassLoader (
101- new URL []{addonPath .toUri ().toURL ()},
102- Parser .class .getClassLoader ()
103- );
104- JarFile jar = new JarFile (addonPath .toString ());
105- Manifest manifest = jar .getManifest ();
106- String main = manifest .getMainAttributes ().getValue ("Main-Class" );
107- if (main != null ) {
108- Class <?> mainClass = Class .forName (main , true , child );
129+ .filter (filePath -> filePath .toString ().endsWith (".jar" ))
130+ .forEach (addonPath -> {
131+ try (JarFile jar = new JarFile (addonPath .toString ())) {
132+ URLClassLoader child = new URLClassLoader (new URL []{addonPath .toUri ().toURL ()}, Parser .class .getClassLoader ());
133+ String mainClassName = jar .getManifest ().getMainAttributes ().getValue ("Main-Class" );
134+ if (mainClassName != null ) {
109135 try {
136+ Class <?> mainClass = Class .forName (mainClassName , true , child );
110137 Method init = mainClass .getDeclaredMethod ("initAddon" );
111138 init .invoke (null );
112139 } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored ) {
113- } finally {
114- jar .close ();
115140 }
116141 }
117142 } catch (IOException | ClassNotFoundException e ) {
@@ -125,6 +150,8 @@ public static void init(String[] mainPackages, String[] subPackages, String[] pr
125150 System .err .println ("Error while loading classes:" );
126151 e .printStackTrace ();
127152 }
153+
154+ // Log registration results
128155 Calendar time = Calendar .getInstance ();
129156 logs = registration .register ();
130157 if (!logs .isEmpty ()) {
0 commit comments