11package me .itzg .helpers .users ;
22
3+ import com .fasterxml .jackson .core .JsonProcessingException ;
34import com .fasterxml .jackson .core .type .TypeReference ;
45import com .fasterxml .jackson .databind .ObjectMapper ;
56import java .io .IOException ;
67import java .net .URI ;
78import java .net .URISyntaxException ;
9+ import java .nio .charset .StandardCharsets ;
810import java .nio .file .Files ;
911import java .nio .file .Path ;
1012import java .nio .file .Paths ;
1113import java .nio .file .StandardCopyOption ;
14+ import java .security .MessageDigest ;
15+ import java .security .NoSuchAlgorithmException ;
1216import java .util .ArrayList ;
1317import java .util .Collections ;
1418import java .util .HashSet ;
1721import java .util .UUID ;
1822import java .util .concurrent .Callable ;
1923import java .util .stream .Collectors ;
20-
2124import lombok .extern .slf4j .Slf4j ;
2225import me .itzg .helpers .errors .GenericException ;
2326import me .itzg .helpers .errors .InvalidParameterException ;
2932import me .itzg .helpers .users .model .JavaOp ;
3033import me .itzg .helpers .users .model .JavaUser ;
3134import me .itzg .helpers .users .model .UserDef ;
32-
33- import org .apache .commons .codec .digest .DigestUtils ;
3435import org .apache .maven .artifact .versioning .ComparableVersion ;
35-
3636import picocli .CommandLine .ArgGroup ;
3737import picocli .CommandLine .Command ;
3838import picocli .CommandLine .ExitCode ;
@@ -53,7 +53,7 @@ public class ManageUsersCommand implements Callable<Integer> {
5353 @ Option (names = {"--help" , "-h" }, usageHelp = true )
5454 boolean help ;
5555
56- @ Option (names = {"--offline" }, required = false , description = "Use for offline server, UUIDs are generated" )
56+ @ Option (names = {"--offline" }, description = "Use for offline server, UUIDs are generated" )
5757 boolean offline ;
5858
5959 @ Option (names = "--output-directory" , defaultValue = "." )
@@ -117,7 +117,7 @@ public Integer call() throws Exception {
117117 }
118118
119119 private void processJavaUserIdList (SharedFetch sharedFetch , List <String > inputs ) throws IOException {
120- List <UserDef > userDefs = inputs .stream ().map (input -> new UserDef ( input ) ).collect (Collectors .toList ());
120+ List <UserDef > userDefs = inputs .stream ().map (UserDef :: new ).collect (Collectors .toList ());
121121 if (usesTextUserList ()) {
122122 verifyNotUuids (userDefs );
123123
@@ -268,18 +268,7 @@ private JavaUser resolveJavaUserId(SharedFetch sharedFetch, List<? extends JavaU
268268 }
269269 }
270270
271- final UserApi userApi ;
272- switch (userApiProvider ) {
273- case mojang :
274- userApi = new MojangUserApi (sharedFetch , mojangApiBaseUrl );
275- break ;
276- case playerdb :
277- userApi = new PlayerdbUserApi (sharedFetch , playerdbApiBaseUrl );
278- break ;
279- default :
280- throw new GenericException ("User API provider was not specified" );
281- }
282- JavaUser apiUser = userApi .resolveUser (user .getName ());
271+ final JavaUser apiUser = getJavaUser (sharedFetch , user );
283272
284273 if (finalUser != null ) {
285274 return finalUser .setUuid (apiUser .getUuid ());
@@ -291,16 +280,35 @@ private JavaUser resolveJavaUserId(SharedFetch sharedFetch, List<? extends JavaU
291280
292281 }
293282
283+ private JavaUser getJavaUser (SharedFetch sharedFetch , UserDef user ) {
284+ final UserApi userApi ;
285+ switch (userApiProvider ) {
286+ case mojang :
287+ userApi = new MojangUserApi (sharedFetch , mojangApiBaseUrl );
288+ break ;
289+ case playerdb :
290+ userApi = new PlayerdbUserApi (sharedFetch , playerdbApiBaseUrl );
291+ break ;
292+ default :
293+ throw new GenericException ("User API provider was not specified" );
294+ }
295+ return userApi .resolveUser (user .getName ());
296+ }
297+
294298 private List <? extends JavaUser > loadExistingJavaJson (Path userFile ) throws IOException {
295299 if (!Files .exists (userFile )) {
296300 return Collections .emptyList ();
297301 }
298302
299- if (type == Type .JAVA_OPS ) {
300- return objectMapper .readValue (userFile .toFile (), LIST_OF_JAVA_OP );
301- }
302- else {
303- return objectMapper .readValue (userFile .toFile (), LIST_OF_JAVA_USER );
303+ try {
304+ if (type == Type .JAVA_OPS ) {
305+ return objectMapper .readValue (userFile .toFile (), LIST_OF_JAVA_OP );
306+ }
307+ else {
308+ return objectMapper .readValue (userFile .toFile (), LIST_OF_JAVA_USER );
309+ }
310+ } catch (JsonProcessingException e ) {
311+ throw new GenericException ("Failed to parse existing file " + userFile + ", fix or remove it" , e );
304312 }
305313 }
306314
@@ -365,27 +373,14 @@ private boolean usesTextUserList() {
365373 }
366374
367375 private static String getOfflineUUID (String username ) {
368- byte [] bytes = DigestUtils .md5 ("OfflinePlayer:" + username );
369-
370- // Force version = 3 (bits 12-15 of time_hi_and_version)
371- bytes [6 ] &= 0x0F ;
372- bytes [6 ] |= 0x30 ;
373-
374- // Force variant = 2 (bits 6-7 of clock_seq_hi_and_reserved)
375- bytes [8 ] &= 0x3F ;
376- bytes [8 ] |= 0x80 ;
377-
378- long msb = 0 ;
379- long lsb = 0 ;
380-
381- for (int i = 0 ; i < 8 ; i ++) {
382- msb = (msb << 8 ) | (bytes [i ] & 0xFF );
383- }
384-
385- for (int i = 8 ; i < 16 ; i ++) {
386- lsb = (lsb << 8 ) | (bytes [i ] & 0xFF );
376+ final MessageDigest digester ;
377+ try {
378+ digester = MessageDigest .getInstance ("MD5" );
379+ } catch (NoSuchAlgorithmException e ) {
380+ throw new GenericException ("Failed to create MD5 digester to generate offline player UUID" , e );
387381 }
382+ final byte [] bytes = digester .digest (("OfflinePlayer:" +username ).getBytes (StandardCharsets .UTF_8 ));
388383
389- return new UUID ( msb , lsb ).toString ();
384+ return UUID . nameUUIDFromBytes ( bytes ).toString ();
390385 }
391386}
0 commit comments