Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 38 additions & 43 deletions src/main/java/me/itzg/helpers/users/ManageUsersCommand.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package me.itzg.helpers.users;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
Expand All @@ -17,7 +21,6 @@
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;
import me.itzg.helpers.errors.GenericException;
import me.itzg.helpers.errors.InvalidParameterException;
Expand All @@ -29,10 +32,7 @@
import me.itzg.helpers.users.model.JavaOp;
import me.itzg.helpers.users.model.JavaUser;
import me.itzg.helpers.users.model.UserDef;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.maven.artifact.versioning.ComparableVersion;

import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.ExitCode;
Expand All @@ -53,7 +53,7 @@ public class ManageUsersCommand implements Callable<Integer> {
@Option(names = {"--help", "-h"}, usageHelp = true)
boolean help;

@Option(names = {"--offline"}, required = false, description = "Use for offline server, UUIDs are generated")
@Option(names = {"--offline"}, description = "Use for offline server, UUIDs are generated")
boolean offline;

@Option(names = "--output-directory", defaultValue = ".")
Expand Down Expand Up @@ -117,7 +117,7 @@ public Integer call() throws Exception {
}

private void processJavaUserIdList(SharedFetch sharedFetch, List<String> inputs) throws IOException {
List<UserDef> userDefs = inputs.stream().map(input -> new UserDef(input)).collect(Collectors.toList());
List<UserDef> userDefs = inputs.stream().map(UserDef::new).collect(Collectors.toList());
if (usesTextUserList()) {
verifyNotUuids(userDefs);

Expand Down Expand Up @@ -268,18 +268,7 @@ private JavaUser resolveJavaUserId(SharedFetch sharedFetch, List<? extends JavaU
}
}

final UserApi userApi;
switch (userApiProvider) {
case mojang:
userApi = new MojangUserApi(sharedFetch, mojangApiBaseUrl);
break;
case playerdb:
userApi = new PlayerdbUserApi(sharedFetch, playerdbApiBaseUrl);
break;
default:
throw new GenericException("User API provider was not specified");
}
JavaUser apiUser = userApi.resolveUser(user.getName());
final JavaUser apiUser = getJavaUser(sharedFetch, user);

if (finalUser != null) {
return finalUser.setUuid(apiUser.getUuid());
Expand All @@ -291,16 +280,35 @@ private JavaUser resolveJavaUserId(SharedFetch sharedFetch, List<? extends JavaU

}

private JavaUser getJavaUser(SharedFetch sharedFetch, UserDef user) {
final UserApi userApi;
switch (userApiProvider) {
case mojang:
userApi = new MojangUserApi(sharedFetch, mojangApiBaseUrl);
break;
case playerdb:
userApi = new PlayerdbUserApi(sharedFetch, playerdbApiBaseUrl);
break;
default:
throw new GenericException("User API provider was not specified");
}
return userApi.resolveUser(user.getName());
}

private List<? extends JavaUser> loadExistingJavaJson(Path userFile) throws IOException {
if (!Files.exists(userFile)) {
return Collections.emptyList();
}

if (type == Type.JAVA_OPS) {
return objectMapper.readValue(userFile.toFile(), LIST_OF_JAVA_OP);
}
else {
return objectMapper.readValue(userFile.toFile(), LIST_OF_JAVA_USER);
try {
if (type == Type.JAVA_OPS) {
return objectMapper.readValue(userFile.toFile(), LIST_OF_JAVA_OP);
}
else {
return objectMapper.readValue(userFile.toFile(), LIST_OF_JAVA_USER);
}
} catch (JsonProcessingException e) {
throw new GenericException("Failed to parse existing file " + userFile + ", fix or remove it", e);
}
}

Expand Down Expand Up @@ -365,27 +373,14 @@ private boolean usesTextUserList() {
}

private static String getOfflineUUID(String username) {
byte[] bytes = DigestUtils.md5("OfflinePlayer:" + username);

// Force version = 3 (bits 12-15 of time_hi_and_version)
bytes[6] &= 0x0F;
bytes[6] |= 0x30;

// Force variant = 2 (bits 6-7 of clock_seq_hi_and_reserved)
bytes[8] &= 0x3F;
bytes[8] |= 0x80;

long msb = 0;
long lsb = 0;

for (int i = 0; i < 8; i++) {
msb = (msb << 8) | (bytes[i] & 0xFF);
}

for (int i = 8; i < 16; i++) {
lsb = (lsb << 8) | (bytes[i] & 0xFF);
final MessageDigest digester;
try {
digester = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new GenericException("Failed to create MD5 digester to generate offline player UUID", e);
}
final byte[] bytes = digester.digest(("OfflinePlayer:"+username).getBytes(StandardCharsets.UTF_8));

return new UUID(msb, lsb).toString();
return UUID.nameUUIDFromBytes(bytes).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ class ManageUsersCommandTest {

private static final String USER1_ID = "3f5f20286a85445fa7b46100e70c2b3a";
private static final String USER1_UUID = "3f5f2028-6a85-445f-a7b4-6100e70c2b3a";
private static final String USER1_OFFLINE_UUID = "fb4cdad9-642b-358f-8f6f-717981c9f42b";
private static final String USER1_OFFLINE_UUID = "95825a72-847d-3e1f-983a-e144b90ec1c9";
private static final String USER2_ID = "5e5a1b2294b14f5892466062597e4c91";
private static final String USER2_UUID = "5e5a1b22-94b1-4f58-9246-6062597e4c91";
private static final String USER2_OFFLINE_UUID = "6e7d9aa0-0da2-390c-ab6a-377df9d77518";
private static final String USER2_OFFLINE_UUID = "357f8b0a-3577-32e9-aafd-6602bb4b6cbf";

@TempDir
Path tempDir;
Expand Down