Add ability to use texture url in /skull (#5120)

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
This commit is contained in:
Luke Chambers 2023-08-08 16:10:26 -04:00 committed by GitHub
parent dfa22969c9
commit 0a4cf27342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 8 deletions

View File

@ -5,13 +5,20 @@ import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.google.common.collect.Lists;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.profile.PlayerProfile;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
@ -19,20 +26,53 @@ import static com.earth2me.essentials.I18n.tl;
public class Commandskull extends EssentialsCommand {
private static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
private static final Pattern URL_VALUE_PATTERN = Pattern.compile("^[0-9a-fA-F]{64}$");
private static final Pattern BASE_64_PATTERN = Pattern.compile("^[A-Za-z0-9+/=]{180}$");
private static final Material SKULL_ITEM = EnumUtil.getMaterial("PLAYER_HEAD", "SKULL_ITEM");
private final boolean playerProfileSupported;
public Commandskull() {
super("skull");
// The player profile API is only available in newer versions of Spigot 1.18.1 and above
boolean playerProfileSupported = true;
try {
Class.forName("org.bukkit.profile.PlayerProfile");
} catch (final ClassNotFoundException e) {
playerProfileSupported = false;
}
this.playerProfileSupported = playerProfileSupported;
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
final String owner;
if (args.length > 0 && user.isAuthorized("essentials.skull.others")) {
if (!NAME_PATTERN.matcher(args[0]).matches()) {
if (BASE_64_PATTERN.matcher(args[0]).matches()) {
try {
final String decoded = new String(Base64.getDecoder().decode(args[0]));
final JsonObject jsonObject = JsonParser.parseString(decoded).getAsJsonObject();
final String url = jsonObject
.getAsJsonObject("textures")
.getAsJsonObject("SKIN")
.get("url")
.getAsString();
owner = url.substring(url.lastIndexOf("/") + 1);
} catch (final Exception e) {
// Any exception that can realistically happen here is caused by an invalid texture value
throw new IllegalArgumentException(tl("skullInvalidBase64"));
}
if (!URL_VALUE_PATTERN.matcher(owner).matches()) {
throw new IllegalArgumentException(tl("skullInvalidBase64"));
}
} else if (!NAME_PATTERN.matcher(args[0]).matches()) {
throw new IllegalArgumentException(tl("alphaNames"));
} else {
owner = args[0];
}
owner = args[0];
} else {
owner = user.getName();
}
@ -60,18 +100,43 @@ public class Commandskull extends EssentialsCommand {
private void editSkull(final User user, final ItemStack stack, final SkullMeta skullMeta, final String owner, final boolean spawn) {
ess.runTaskAsynchronously(() -> {
//Run this stuff async because SkullMeta#setOwner causes a http request.
skullMeta.setDisplayName("§fSkull of " + owner);
//noinspection deprecation
skullMeta.setOwner(owner);
// Run this stuff async because it causes an HTTP request
final String shortOwnerName;
if (URL_VALUE_PATTERN.matcher(owner).matches()) {
if (!playerProfileSupported) {
user.sendMessage(tl("unsupportedFeature"));
return;
}
final URL url;
try {
url = new URL("https://textures.minecraft.net/texture/" + owner);
} catch (final MalformedURLException e) {
// The URL should never be malformed
throw new RuntimeException(e);
}
final PlayerProfile profile = ess.getServer().createPlayerProfile(UUID.randomUUID());
profile.getTextures().setSkin(url);
skullMeta.setOwnerProfile(profile);
shortOwnerName = owner.substring(0, 7);
} else {
//noinspection deprecation
skullMeta.setOwner(owner);
shortOwnerName = owner;
}
skullMeta.setDisplayName("§fSkull of " + shortOwnerName);
ess.scheduleSyncDelayedTask(() -> {
stack.setItemMeta(skullMeta);
if (spawn) {
Inventories.addItem(user.getBase(), stack);
user.sendMessage(tl("givenSkull", owner));
user.sendMessage(tl("givenSkull", shortOwnerName));
return;
}
user.sendMessage(tl("skullChanged", owner));
user.sendMessage(tl("skullChanged", shortOwnerName));
});
});
}

View File

@ -1208,6 +1208,9 @@ skullCommandUsage1=/<command>
skullCommandUsage1Description=Gets your own skull
skullCommandUsage2=/<command> <player>
skullCommandUsage2Description=Gets the skull of the specified player
skullCommandUsage3=/<command> <texture>
skullCommandUsage3Description=Gets a skull with the specified texture (either the hash from a texture URL or a Base64 texture value)
skullInvalidBase64=\u00a74The texture value is invalid.
slimeMalformedSize=\u00a74Malformed size.
smithingtableCommandDescription=Opens up a smithing table.
smithingtableCommandUsage=/<command>