我的世界服务器玩家UUID生成规则

由于业务需要,研究一下如何将离线版(盗版)minecraft服务器的玩家数据迁移到在线版我的世界服务器上去。

查看minecraft的服务器代码(反编译),如果是在线模式就连接用户验证服务器去获取玩家的uuid,否则就根据规则自动生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//代码片断from: net.minecraft.server.NameReferencingFileConverter

if (minecraftserver.getOnlineMode() || org.spigotmc.SpigotConfig.bungee) {
// Spigot: bungee = online mode, for now.
minecraftserver.getGameProfileRepository().findProfilesByNames(astring, Agent.MINECRAFT, profilelookupcallback);
} else {
String[] astring1 = astring;
int i = astring.length;

for (int j = 0; j < i; ++j) {
String s = astring1[j];
UUID uuid = EntityHuman.a(new GameProfile((UUID) null, s));
GameProfile gameprofile = new GameProfile(uuid, s);

profilelookupcallback.onProfileLookupSucceeded(gameprofile);
}
}

//from:net.minecraft.server.EntityHuman
public static UUID a(GameProfile gameprofile) {
UUID uuid = gameprofile.getId();

if (uuid == null) {
uuid = b(gameprofile.getName());
}

return uuid;
}

public static UUID b(String s) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + s).getBytes(Charsets.UTF_8));
}

生成算法是在用户昵称前面加上“OfflinePlayer:”后使用java函数nameUUIDFromBytes来生成。

nameUUIDFromBytes函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// from: java.util.UUID.java
public static UUID nameUUIDFromBytes(byte[] name) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
throw new InternalError("MD5 not supported");
}
byte[] md5Bytes = md.digest(name);
md5Bytes[6] &= 0x0f; /* clear version */
md5Bytes[6] |= 0x30; /* set to version 3 */
md5Bytes[8] &= 0x3f; /* clear variant */
md5Bytes[8] |= 0x80; /* set to IETF variant */
return new UUID(md5Bytes);
}

1、计算出MD5

OfflinePlayer:k1988 -> MD5: 08D699BB6400555E581B678C9441FA75

2、MD5的值和生成的uuid文件名对比

1
2
08d699bb-6400-555e-581b-678c9441fa75
08d699bb-6400-355e-981b-678c9441fa75

== 解析法 ==

还有一种方法就是枚举minecraft服务器中的world\playerdata目录,这个目录下每个文件对应着一个玩家,文件名(不含后缀)就是一个玩家的uuid,文件使用NBT方式储存着一些用户信息,包括用户上一次使用的昵称(可见,在线版服务器是方便更改昵称的),及其它信息。

我fork了一个NBT解析的python代码,并在里面添加了一个python脚本用来枚举playerdata下的所有文件,fork后的代码在https://github.com/k1988/NBT ,脚本在examples/player_print.py,使用时直接player_print.py <playerdata目录路径>即可,效果如下图:
截图