/
ProfileEditor.java
124 lines (110 loc) · 6.31 KB
/
ProfileEditor.java
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package com.denizenscript.denizen.nms.abstracts;
import com.denizenscript.denizen.nms.NMSHandler;
import com.denizenscript.denizen.nms.util.PlayerProfile;
import com.denizenscript.denizen.utilities.packets.NetworkInterceptHelper;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
public abstract class ProfileEditor {
public final static Map<UUID, PlayerProfile> fakeProfiles = new HashMap<>();
public final static HashSet<UUID> mirrorUUIDs = new HashSet<>();
public ProfileEditor() {
Bukkit.getServer().getPluginManager().registerEvents(new PlayerProfileEditorListener(), NMSHandler.getJavaPlugin());
}
public void setPlayerName(Player player, String name) {
NetworkInterceptHelper.enable();
PlayerProfile profile = getFakeProfile(player, true);
profile.setName(name);
updatePlayer(player, false);
}
public String getPlayerName(Player player) {
return getFakeProfile(player, false).getName();
}
public void setPlayerSkin(Player player, String name) {
NetworkInterceptHelper.enable();
PlayerProfile profile = getFakeProfile(player, true);
PlayerProfile skinProfile = NMSHandler.instance.fillPlayerProfile(new PlayerProfile(name, null));
if (skinProfile == null) {
return;
}
if (skinProfile.getTexture() != null) {
profile.setTexture(skinProfile.getTexture());
profile.setTextureSignature(skinProfile.getTextureSignature());
updatePlayer(player, true);
}
}
// <--[language]
// @name Player Entity Skins (Skin Blobs)
// @group Minecraft Logic
// @description
// Player entities (that is, both real players and player-type NPCs), in addition to Player_Head items,
// use something called a "skin blob" to determine what skin to show.
// A skin blob consists of an identifier (usually the player's UUID, sometimes the name), a "texture", and a "signature".
//
// The "texture" portion, despite the name, does not contain actual texture data - it contains a Base64 encoding of a JSON section that contains outlinks to real skin data.
// That might be a bit confusing, so here's an example:
//
// The raw "texture" data for "mcmonkey4eva"'s skin is:
// eyJ0aW1lc3RhbXAiOjE1NjU1NjUyNDA4NTMsInByb2ZpbGVJZCI6IjQ2MGU5NmI5N2EwZTQxNmRiMmMzNDUwODE2NGI4YjFiIiwicHJvZmlsZU5hbWUiOiJtY21vbmtleTRldmEiLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dH
// VyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2ZkMzRiM2UyN2EzZmU1MzgyN2IzN2FkNTk1NmFjY2EwOGYyODYzYzY5MjZjYzk3MTE2ZGRhMzM0ODY5N2E1YTkifX19
//
// This is base64 encoding, which is just a way of controlling the format of binary data. When passed through a Base64 decoder, that says:
// {"timestamp":1565565240853,"profileId":"460e96b97a0e416db2c34508164b8b1b","profileName":"mcmonkey4eva","signatureRequired":true,
// "textures":{"SKIN":{"url":"http://textures.minecraft.net/texture/fd34b3e27a3fe53827b37ad5956acca08f2863c6926cc97116dda3348697a5a9"}}}
//
// As you can see, it contains: the timestamp of when the skin was added, the UUID and name, and a link to the actual skin file on Mojang's servers.
//
// The "signature" portion is a digitally encrypted signature that is used to verify a skin really was generated by Mojang.
// It is also represented as Base64, but cannot be decoded to anything readable.
// The "signature" is required for Player entity skins, but can generally be left off from Player_Head items (meaning, you can generate your own Player_Head items by base64 encoding your own JSON).
//
// The website <@link url https://mineskin.org/> can be used to generate full texture+signature data from any skin file
// (it does this by automatically uploading the skin image to Mojang's servers for processing and signing, using a donated Minecraft account).
//
// In terms of general actual usage, skin blobs are generally meant to be read from tags and applied with mechanisms, never directly written out or read by a human, due to their complexity.
//
// A skin_blob always represents a single actual skin, as opposed to a player name/uuid, where the account owner might choose to change their skin.
//
// It should be noted that any time a skin is applied to a Player, NPC, Or Player_Head item using just a name or UUID, the server must contact Mojang's servers to requst the skin blob for that given name/uuid.
// With a skin blob, however, the server does not need to make any remote calls, and can apply the skin directly (However note that the client will still download the image from Mojang's servers).
// -->
public void setPlayerSkinBlob(Player player, String blob) {
NetworkInterceptHelper.enable();
PlayerProfile profile = getFakeProfile(player, true);
String[] split = blob.split(";");
profile.setTexture(split[0]);
profile.setTextureSignature(split.length > 1 ? split[1] : null);
updatePlayer(player, true);
}
public String getPlayerSkinBlob(Player player) {
PlayerProfile prof = getFakeProfile(player, false);
return prof.getTexture() + ";" + prof.getTextureSignature();
}
protected abstract void updatePlayer(Player player, boolean isSkinChanging);
private PlayerProfile getFakeProfile(Player player, boolean createCache) {
UUID uuid = player.getUniqueId();
if (fakeProfiles.containsKey(uuid)) {
return fakeProfiles.get(uuid);
}
else {
PlayerProfile fakeProfile = NMSHandler.instance.getPlayerProfile(player);
if (createCache) {
fakeProfiles.put(uuid, fakeProfile);
}
return fakeProfile;
}
}
private static class PlayerProfileEditorListener implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
fakeProfiles.remove(uuid);
}
}
}