16
16
import java .util .Set ;
17
17
import java .util .UUID ;
18
18
import java .util .concurrent .Callable ;
19
+ import java .util .stream .Collectors ;
20
+
19
21
import lombok .extern .slf4j .Slf4j ;
20
22
import me .itzg .helpers .errors .GenericException ;
21
23
import me .itzg .helpers .errors .InvalidParameterException ;
26
28
import me .itzg .helpers .json .ObjectMappers ;
27
29
import me .itzg .helpers .users .model .JavaOp ;
28
30
import me .itzg .helpers .users .model .JavaUser ;
31
+ import me .itzg .helpers .users .model .UserDef ;
29
32
30
33
import org .apache .commons .codec .digest .DigestUtils ;
31
34
import org .apache .maven .artifact .versioning .ComparableVersion ;
@@ -114,8 +117,9 @@ public Integer call() throws Exception {
114
117
}
115
118
116
119
private void processJavaUserIdList (SharedFetch sharedFetch , List <String > inputs ) throws IOException {
120
+ List <UserDef > userDefs = inputs .stream ().map (input -> new UserDef (input )).collect (Collectors .toList ());
117
121
if (usesTextUserList ()) {
118
- verifyNotUuids (inputs );
122
+ verifyNotUuids (userDefs );
119
123
120
124
final Path resultFile = outputDirectory .resolve (
121
125
type == Type .JAVA_OPS ? "ops.txt" : "white-list.txt"
@@ -126,8 +130,9 @@ private void processJavaUserIdList(SharedFetch sharedFetch, List<String> inputs)
126
130
}
127
131
128
132
final Set <String > users = loadExistingTextUserList (resultFile );
129
-
130
- users .addAll (inputs );
133
+ for (final UserDef user : userDefs ) {
134
+ users .add (user .getName ());
135
+ }
131
136
132
137
log .debug ("Writing users list to {}: {}" , resultFile , users );
133
138
Files .write (resultFile , users );
@@ -142,7 +147,7 @@ private void processJavaUserIdList(SharedFetch sharedFetch, List<String> inputs)
142
147
}
143
148
144
149
objectMapper .writeValue (resultFile .toFile (),
145
- reconcile (sharedFetch , inputs ,
150
+ reconcile (sharedFetch , userDefs ,
146
151
loadExistingJavaJson (resultFile )
147
152
)
148
153
);
@@ -158,7 +163,7 @@ private boolean handleSkipExistingFile(Path resultFile) {
158
163
return false ;
159
164
}
160
165
161
- private List <? extends JavaUser > reconcile (SharedFetch sharedFetch , List <String > inputs , List <? extends JavaUser > existing ) {
166
+ private List <? extends JavaUser > reconcile (SharedFetch sharedFetch , List <UserDef > userDefs , List <? extends JavaUser > existing ) {
162
167
163
168
final List <JavaUser > reconciled ;
164
169
if (existingFileBehavior == ExistingFileBehavior .MERGE ) {
@@ -168,8 +173,8 @@ private List<? extends JavaUser> reconcile(SharedFetch sharedFetch, List<String>
168
173
reconciled = new ArrayList <>(inputs .size ());
169
174
}
170
175
171
- for (final String input : inputs ) {
172
- final JavaUser resolvedUser = resolveJavaUserId (sharedFetch , existing , input . trim () );
176
+ for (final UserDef userDef : userDefs ) {
177
+ final JavaUser resolvedUser = resolveJavaUserId (sharedFetch , existing , userDef );
173
178
174
179
if (existingFileBehavior == ExistingFileBehavior .SYNCHRONIZE
175
180
|| !containsUserByUuid (reconciled , resolvedUser .getUuid ())) {
@@ -203,18 +208,18 @@ private boolean containsUserByUuid(List<JavaUser> users, String uuid) {
203
208
return false ;
204
209
}
205
210
206
- private JavaUser resolveJavaUserId (SharedFetch sharedFetch , List <? extends JavaUser > existing , String input ) {
211
+ private JavaUser resolveJavaUserId (SharedFetch sharedFetch , List <? extends JavaUser > existing , UserDef user ) {
207
212
208
- return UuidQuirks .ifIdOrUuid (input )
213
+ return UuidQuirks .ifIdOrUuid (user . getName () )
209
214
.map (uuid -> {
210
215
for (final JavaUser existingUser : existing ) {
211
216
if (existingUser .getUuid ().equalsIgnoreCase (uuid )) {
212
- log .debug ("Resolved '{}' from existing user entry by UUID: {}" , input , existingUser );
217
+ log .debug ("Resolved '{}' from existing user entry by UUID: {}" , user . getName () , existingUser );
213
218
return existingUser ;
214
219
}
215
220
}
216
221
217
- log .debug ("Resolved '{}' into new user entry" , input );
222
+ log .debug ("Resolved '{}' into new user entry" , user . getName () );
218
223
return JavaUser .builder ()
219
224
.uuid (uuid )
220
225
// username needs to be present, but content doesn't matter
@@ -223,34 +228,46 @@ private JavaUser resolveJavaUserId(SharedFetch sharedFetch, List<? extends JavaU
223
228
224
229
})
225
230
.orElseGet (() -> {
231
+ // User to be used
232
+ JavaUser finalUser = null ;
226
233
227
- // ...or username
234
+ // Try to find user in existing users list
228
235
for (final JavaUser existingUser : existing ) {
229
- if (existingUser .getName ().equalsIgnoreCase (input )) {
230
- log .debug ("Resolved '{}' from existing user entry by name: {}" , input , existingUser );
231
- return existingUser ;
236
+ if (existingUser .getName ().equalsIgnoreCase (user . getName () )) {
237
+ log .debug ("Resolved '{}' from existing user entry by name: {}" , user . getName () , existingUser );
238
+ finalUser = existingUser ;
232
239
}
233
240
}
234
241
242
+ // If existing user is not found, build a new one
243
+ if (finalUser == null ) {
244
+ finalUser = JavaUser .builder ().name (user .getName ()).build ();
245
+ }
246
+
247
+ // User is not online, generating offline UUID
248
+ if (offline && user .getFlags ().contains ("offline" )) {
249
+ log .debug ("Resolved '{}' as offline user" , user .getName ());
250
+ // update UUID keeping the other fields in case of existing user
251
+ return finalUser .setUuid (getOfflineUUID (user .getName ()));
252
+ }
253
+
235
254
final Path userCacheFile = outputDirectory .resolve ("usercache.json" );
236
255
if (Files .exists (userCacheFile )) {
237
256
try {
238
257
final List <JavaUser > userCache = objectMapper .readValue (userCacheFile .toFile (), LIST_OF_JAVA_USER );
239
258
for (final JavaUser existingUser : userCache ) {
240
- if (existingUser .getName ().equalsIgnoreCase (input )) {
241
- log .debug ("Resolved '{}' from user cache by name: {}" , input , existingUser );
242
- return existingUser ;
259
+ if (existingUser .getName ().equalsIgnoreCase (user .getName ())) {
260
+ log .debug ("Resolved '{}' from user cache by name: {}" , user .getName (), existingUser );
261
+ // UUID from usercache.jsona are safe to use regardless of the user type
262
+ // if a UUID is present here, user joined successfully with that UUID
263
+ return finalUser .setUuid (existingUser .getUuid ());
243
264
}
244
265
}
245
266
} catch (IOException e ) {
246
267
log .error ("Failed to parse usercache.json" , e );
247
268
}
248
269
}
249
270
250
- if (offline ) {
251
- return getOfflineUUID (input );
252
- }
253
-
254
271
final UserApi userApi ;
255
272
switch (userApiProvider ) {
256
273
case mojang :
@@ -262,8 +279,14 @@ private JavaUser resolveJavaUserId(SharedFetch sharedFetch, List<? extends JavaU
262
279
default :
263
280
throw new GenericException ("User API provider was not specified" );
264
281
}
265
- return userApi .resolveUser (input );
282
+ JavaUser apiUser = userApi .resolveUser (user . getName () );
266
283
284
+ if (finalUser != null ) {
285
+ return finalUser .setUuid (apiUser .getUuid ());
286
+ }
287
+ else {
288
+ return apiUser ;
289
+ }
267
290
});
268
291
269
292
}
@@ -293,10 +316,10 @@ private Set<String> loadExistingTextUserList(Path resultFile) throws IOException
293
316
return new HashSet <>();
294
317
}
295
318
296
- private void verifyNotUuids (List <String > inputs ) {
297
- for (final String input : inputs ) {
298
- if (UuidQuirks .isIdOrUuid (input )) {
299
- throw new InvalidParameterException ("UUID cannot be provided: " + input );
319
+ private void verifyNotUuids (List <UserDef > userDefs ) {
320
+ for (final UserDef user : userDefs ) {
321
+ if (UuidQuirks .isIdOrUuid (user . getName () )) {
322
+ throw new InvalidParameterException ("UUID cannot be provided: " + user . getName () );
300
323
}
301
324
}
302
325
}
@@ -341,8 +364,8 @@ private boolean usesTextUserList() {
341
364
return version != null && new ComparableVersion (version ).compareTo (MIN_VERSION_USES_JSON ) < 0 ;
342
365
}
343
366
344
- private static JavaUser getOfflineUUID (String username ) {
345
- byte [] bytes = DigestUtils .md5 ("OfflinePlayer:" + username );
367
+ private static String getOfflineUUID (String username ) {
368
+ byte [] bytes = DigestUtils .md5 ("OfflinePlayer:" + username );
346
369
347
370
// Force version = 3 (bits 12-15 of time_hi_and_version)
348
371
bytes [6 ] &= 0x0F ;
@@ -363,9 +386,6 @@ private static JavaUser getOfflineUUID(String username) {
363
386
lsb = (lsb << 8 ) | (bytes [i ] & 0xFF );
364
387
}
365
388
366
- return JavaUser .builder ()
367
- .name (username )
368
- .uuid (new UUID (msb , lsb ).toString ())
369
- .build ();
389
+ return new UUID (msb , lsb ).toString ();
370
390
}
371
391
}
0 commit comments