5
5
package net .minecraftforge .bootstrap .dev ;
6
6
7
7
import java .io .ByteArrayInputStream ;
8
+ import java .io .File ;
8
9
import java .io .IOException ;
9
10
import java .lang .module .ModuleDescriptor ;
11
+ import java .nio .file .FileSystem ;
12
+ import java .nio .file .FileSystems ;
10
13
import java .nio .file .Files ;
11
14
import java .nio .file .Path ;
12
15
import java .util .ArrayList ;
13
16
import java .util .Collection ;
14
- import java .util .jar . JarFile ;
17
+ import java .util .List ;
15
18
import java .util .jar .Manifest ;
16
- import java .util .jar .Attributes .Name ;
17
19
import java .util .zip .ZipEntry ;
18
20
import java .util .zip .ZipInputStream ;
21
+ import java .util .jar .Attributes .Name ;
22
+ import java .util .jar .JarFile ;
19
23
20
24
class Util {
21
25
private static final Name AUTOMATIC_MODULE_NAME = new Name ("Automatic-Module-Name" );
@@ -29,7 +33,7 @@ class Util {
29
33
30
34
static ModuleVersion findModuleName (Collection <Path > paths ) {
31
35
for (var path : paths ) {
32
- var ret = findModuleName (path , null );
36
+ var ret = findModuleNameImpl (path , false );
33
37
if (ret .name != null )
34
38
return ret ;
35
39
}
@@ -38,87 +42,31 @@ static ModuleVersion findModuleName(Collection<Path> paths) {
38
42
39
43
static ModuleVersion findModuleName (Path ... paths ) {
40
44
for (var path : paths ) {
41
- var ret = findModuleName (path , null );
45
+ var ret = findModuleNameImpl (path , false );
42
46
if (ret .name != null )
43
47
return ret ;
44
48
}
45
49
return null ;
46
50
}
47
51
48
52
record ModuleVersion (String name , String version , String layer ) {}
49
- static ModuleVersion findModuleName (Path path , String _default ) {
53
+ static ModuleVersion findModuleNameImpl (Path path , boolean slow ) {
50
54
try {
51
- Manifest mf = null ;
52
- record InfoData (int version , byte [] data ) {}
53
- var infos = new ArrayList <InfoData >();
54
-
55
+ Candidates data = null ;
55
56
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 );
88
60
} 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 );
119
64
}
120
65
}
121
66
67
+ var mf = data .mf ;
68
+ var infos = data .modules ;
69
+
122
70
InfoData info = null ;
123
71
if (infos .size () == 1 ) {
124
72
info = infos .get (0 );
@@ -129,7 +77,7 @@ record InfoData(int version, byte[] data) {}
129
77
.orElse (null );
130
78
}
131
79
132
- String name = _default ;
80
+ String name = null ;
133
81
String version = null ;
134
82
String layer = null ;
135
83
@@ -150,6 +98,89 @@ record InfoData(int version, byte[] data) {}
150
98
}
151
99
}
152
100
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
+
153
184
private static Path findInsensitive (Path root , String name ) {
154
185
if (root == null )
155
186
return null ;
@@ -178,4 +209,57 @@ private static boolean isMultiRelease(Manifest mf) {
178
209
static <E extends Throwable , R > R sneak (Exception exception ) throws E {
179
210
throw (E )exception ;
180
211
}
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
+ }
181
265
}
0 commit comments