55package net .minecraftforge .bootstrap .dev ;
66
77import java .io .ByteArrayInputStream ;
8+ import java .io .File ;
89import java .io .IOException ;
910import java .lang .module .ModuleDescriptor ;
11+ import java .nio .file .FileSystem ;
12+ import java .nio .file .FileSystems ;
1013import java .nio .file .Files ;
1114import java .nio .file .Path ;
1215import java .util .ArrayList ;
1316import java .util .Collection ;
14- import java .util .jar . JarFile ;
17+ import java .util .List ;
1518import java .util .jar .Manifest ;
16- import java .util .jar .Attributes .Name ;
1719import java .util .zip .ZipEntry ;
1820import java .util .zip .ZipInputStream ;
21+ import java .util .jar .Attributes .Name ;
22+ import java .util .jar .JarFile ;
1923
2024class Util {
2125 private static final Name AUTOMATIC_MODULE_NAME = new Name ("Automatic-Module-Name" );
@@ -29,7 +33,7 @@ class Util {
2933
3034 static ModuleVersion findModuleName (Collection <Path > paths ) {
3135 for (var path : paths ) {
32- var ret = findModuleName (path , null );
36+ var ret = findModuleNameImpl (path , false );
3337 if (ret .name != null )
3438 return ret ;
3539 }
@@ -38,87 +42,31 @@ static ModuleVersion findModuleName(Collection<Path> paths) {
3842
3943 static ModuleVersion findModuleName (Path ... paths ) {
4044 for (var path : paths ) {
41- var ret = findModuleName (path , null );
45+ var ret = findModuleNameImpl (path , false );
4246 if (ret .name != null )
4347 return ret ;
4448 }
4549 return null ;
4650 }
4751
4852 record ModuleVersion (String name , String version , String layer ) {}
49- static ModuleVersion findModuleName (Path path , String _default ) {
53+ static ModuleVersion findModuleNameImpl (Path path , boolean slow ) {
5054 try {
51- Manifest mf = null ;
52- record InfoData (int version , byte [] data ) {}
53- var infos = new ArrayList <InfoData >();
54-
55+ Candidates data = null ;
5556 if (Files .isDirectory (path )) {
56- var meta_inf = findInsensitive (path , META_INF );
57- var manifest = findInsensitive (meta_inf , MANIFEST );
58-
59- if (manifest != null && Files .exists (manifest )) {
60- try (var is = Files .newInputStream (manifest )) {
61- mf = new Manifest (is );
62- }
63- }
64-
65- var module_info = path .resolve (MODULE_INFO );
66- if (Files .exists (module_info )) {
67- infos .add (new InfoData (0 , Files .readAllBytes (path )));
68- }
69-
70- if (isMultiRelease (mf )) {
71- var versions = findInsensitive (meta_inf , VERSIONS );
72- if (versions != null ) {
73- Files .list (versions )
74- .forEach (v -> {
75- try {
76- int version = Integer .parseInt (v .getFileName ().toString ());
77- var mod_info = v .resolve (MODULE_INFO );
78- if (version <= Runtime .version ().feature () && Files .exists (mod_info ))
79- infos .add (new InfoData (version , Files .readAllBytes (mod_info )));
80- } catch (NumberFormatException e ) {
81- // If its not a numerical directory we don't care
82- } catch (IOException e ) {
83- sneak (e );
84- }
85- });
86- }
87- }
57+ data = findCandidatesDirectory (path );
58+ } else if (slow ) {
59+ data = findCandidatesZip (path );
8860 } else {
89- // I would use JarFile here and let the JVM handle all this.
90- // but it only works on File objects not Paths
91- // Normal java gets around this by extracting all non-file paths
92- // to a temp directory and opening them as normal files.
93- // I do not want to do this. So this is what you get.
94- try (var zip = new ZipInputStream (Files .newInputStream (path ))) {
95- ZipEntry entry ;
96- while ((entry = zip .getNextEntry ()) != null ) {
97- var name = entry .getName ();
98- if (mf == null && JarFile .MANIFEST_NAME .equalsIgnoreCase (name )) {
99- mf = new Manifest (zip );
100- } else if (name .endsWith (MODULE_INFO )) {
101- int version = 0 ;
102- if (name .startsWith (VERSION_DIR )) {
103- if (!isMultiRelease (mf ))
104- continue ;
105-
106- int idx = name .indexOf ('/' , VERSION_DIR .length ());
107- var ver = name .substring (VERSION_DIR .length (), idx );
108- try {
109- version = Integer .parseInt (ver );
110- } catch (NumberFormatException e ) {
111- // If its not a numerical directory we don't care
112- version = Integer .MAX_VALUE ;
113- }
114- }
115- if (version <= Runtime .version ().feature ())
116- infos .add (new InfoData (version , zip .readAllBytes ()));
117- }
118- }
61+ try (FileSystem jarFS = FileSystems .newFileSystem (path )) {
62+ var root = jarFS .getPath ("/" );
63+ data = findCandidatesDirectory (root );
11964 }
12065 }
12166
67+ var mf = data .mf ;
68+ var infos = data .modules ;
69+
12270 InfoData info = null ;
12371 if (infos .size () == 1 ) {
12472 info = infos .get (0 );
@@ -129,7 +77,7 @@ record InfoData(int version, byte[] data) {}
12977 .orElse (null );
13078 }
13179
132- String name = _default ;
80+ String name = null ;
13381 String version = null ;
13482 String layer = null ;
13583
@@ -150,6 +98,89 @@ record InfoData(int version, byte[] data) {}
15098 }
15199 }
152100
101+ record InfoData (int version , byte [] data ) {}
102+ record Candidates (Manifest mf , List <InfoData > modules ) {}
103+ private static Candidates findCandidatesDirectory (Path path ) {
104+ try {
105+ var infos = new ArrayList <InfoData >();
106+ var meta_inf = findInsensitive (path , META_INF );
107+ var manifest = findInsensitive (meta_inf , MANIFEST );
108+ Manifest mf = null ;
109+
110+ if (manifest != null && Files .exists (manifest )) {
111+ try (var is = Files .newInputStream (manifest )) {
112+ mf = new Manifest (is );
113+ }
114+ }
115+
116+ var module_info = path .resolve (MODULE_INFO );
117+ if (Files .exists (module_info )) {
118+ infos .add (new InfoData (0 , Files .readAllBytes (module_info )));
119+ }
120+
121+ if (isMultiRelease (mf )) {
122+ var versions = findInsensitive (meta_inf , VERSIONS );
123+ if (versions != null ) {
124+ Files .list (versions )
125+ .forEach (v -> {
126+ try {
127+ int version = Integer .parseInt (v .getFileName ().toString ());
128+ var mod_info = v .resolve (MODULE_INFO );
129+ if (version <= Runtime .version ().feature () && Files .exists (mod_info ))
130+ infos .add (new InfoData (version , Files .readAllBytes (mod_info )));
131+ } catch (NumberFormatException e ) {
132+ // If its not a numerical directory we don't care
133+ } catch (IOException e ) {
134+ sneak (e );
135+ }
136+ });
137+ }
138+ }
139+ return new Candidates (mf , infos );
140+ } catch (IOException e ) {
141+ return sneak (e );
142+ }
143+ }
144+
145+ private static Candidates findCandidatesZip (Path path ) {
146+ // I would use JarFile here and let the JVM handle all this.
147+ // but it only works on File objects not Paths
148+ // Normal java gets around this by extracting all non-file paths
149+ // to a temp directory and opening them as normal files.
150+ // I do not want to do this. So this is what you get.
151+ try (var zip = new ZipInputStream (Files .newInputStream (path ))) {
152+ var infos = new ArrayList <InfoData >();
153+ Manifest mf = null ;
154+ ZipEntry entry ;
155+ while ((entry = zip .getNextEntry ()) != null ) {
156+ var name = entry .getName ();
157+ if (mf == null && JarFile .MANIFEST_NAME .equalsIgnoreCase (name )) {
158+ mf = new Manifest (zip );
159+ } else if (name .endsWith (MODULE_INFO )) {
160+ int version = 0 ;
161+ if (name .startsWith (VERSION_DIR )) {
162+ if (!isMultiRelease (mf ))
163+ continue ;
164+
165+ int idx = name .indexOf ('/' , VERSION_DIR .length ());
166+ var ver = name .substring (VERSION_DIR .length (), idx );
167+ try {
168+ version = Integer .parseInt (ver );
169+ } catch (NumberFormatException e ) {
170+ // If its not a numerical directory we don't care
171+ version = Integer .MAX_VALUE ;
172+ }
173+ }
174+ if (version <= Runtime .version ().feature ())
175+ infos .add (new InfoData (version , zip .readAllBytes ()));
176+ }
177+ }
178+ return new Candidates (mf , infos );
179+ } catch (IOException e ) {
180+ return sneak (e );
181+ }
182+ }
183+
153184 private static Path findInsensitive (Path root , String name ) {
154185 if (root == null )
155186 return null ;
@@ -178,4 +209,57 @@ private static boolean isMultiRelease(Manifest mf) {
178209 static <E extends Throwable , R > R sneak (Exception exception ) throws E {
179210 throw (E )exception ;
180211 }
212+
213+ // Only here for testing/profiling because I don't want to setup a whole benchmark sub-project
214+ // Intentionally left in so others can reproduce and I dont have to write this later.
215+ public static void main (String [] args ) {
216+ var runs = 100 ;
217+ var slow = false ; // Wither or not to force the slow code path
218+ var forgeWorkspace = Path .of (args .length == 1 ? args [0 ] : "Z:/Projects/Forge_1214" );
219+ if (Files .exists (forgeWorkspace )) {
220+ var exploded = forgeWorkspace .resolve ("projects/forge/bin/main" );
221+ if (Files .exists (exploded )) {
222+ for (int x = 0 ; x < runs ; x ++) {
223+ var mod = findModuleNameImpl (exploded , slow );
224+ if (mod == null || !"net.minecraftforge.forge" .equals (mod .name ()))
225+ throw new IllegalStateException ("Failed to find correct module name from exploded: " + mod );
226+ }
227+ }
228+ var jared = forgeWorkspace .resolve ("projects/mcp/build/mcp/downloadServer/server.jar" );
229+ if (Files .exists (jared )) {
230+ for (int x = 0 ; x < runs ; x ++) {
231+ var mod = findModuleNameImpl (jared , slow );
232+ if (mod != null && mod .name () != null )
233+ throw new IllegalStateException ("Expected null module from server jar but was " + mod );
234+ }
235+ }
236+ }
237+
238+ var paths = findAllClassPathEntries ();
239+
240+ for (int x = 0 ; x < runs ; x ++) {
241+ for (var path : paths ) {
242+ var mod = findModuleNameImpl (path , slow );
243+ System .out .println (mod );
244+ }
245+ }
246+ }
247+
248+ private static List <Path > findAllClassPathEntries () {
249+ try {
250+ var parts = System .getProperty ("java.class.path" ).split (File .pathSeparator );
251+ var paths = new ArrayList <Path >();
252+ for (var part : parts ) {
253+ var path = new File (part ).getCanonicalFile ().toPath ();
254+ if (!Files .exists (path ))
255+ continue ;
256+ if (Files .isDirectory (path ) && Files .list (path ).findAny ().isEmpty ())
257+ continue ;
258+ paths .add (path );
259+ }
260+ return paths ;
261+ } catch (IOException e ) {
262+ return sneak (e );
263+ }
264+ }
181265}
0 commit comments