由于业务需要,研究一下如何将离线版(盗版)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目录路径>
即可,效果如下图:
截图