mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2024-12-27 19:47:51 +01:00
Add skin handling and /npc skin (note: may not work in 1.8)
This commit is contained in:
parent
c1b5fd78c2
commit
0af5f8bee1
2
pom.xml
2
pom.xml
@ -11,7 +11,7 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<craftbukkit.version>1.7.8-R0.1-SNAPSHOT</craftbukkit.version>
|
||||
<craftbukkit.version>1.7.9-R0.1-SNAPSHOT</craftbukkit.version>
|
||||
<citizensapi.version>2.0.13-SNAPSHOT</citizensapi.version>
|
||||
<vault.version>1.2.19-SNAPSHOT</vault.version>
|
||||
<powermock.version>1.4.12</powermock.version>
|
||||
|
@ -267,7 +267,7 @@ public class NPCCommands {
|
||||
}
|
||||
|
||||
CommandSenderCreateNPCEvent event = sender instanceof Player ? new PlayerCreateNPCEvent((Player) sender, copy)
|
||||
: new CommandSenderCreateNPCEvent(sender, copy);
|
||||
: new CommandSenderCreateNPCEvent(sender, copy);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
event.getNPC().destroy();
|
||||
@ -343,7 +343,7 @@ public class NPCCommands {
|
||||
spawnLoc = args.getSenderLocation();
|
||||
}
|
||||
CommandSenderCreateNPCEvent event = sender instanceof Player ? new PlayerCreateNPCEvent((Player) sender, npc)
|
||||
: new CommandSenderCreateNPCEvent(sender, npc);
|
||||
: new CommandSenderCreateNPCEvent(sender, npc);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
npc.destroy();
|
||||
@ -1018,7 +1018,7 @@ public class NPCCommands {
|
||||
@Requirements(selected = true, ownership = true, types = { EntityType.CREEPER })
|
||||
public void power(CommandContext args, CommandSender sender, NPC npc) {
|
||||
Messaging
|
||||
.sendTr(sender, npc.getTrait(Powered.class).toggle() ? Messages.POWERED_SET : Messages.POWERED_STOPPED);
|
||||
.sendTr(sender, npc.getTrait(Powered.class).toggle() ? Messages.POWERED_SET : Messages.POWERED_STOPPED);
|
||||
}
|
||||
|
||||
@Command(
|
||||
@ -1170,6 +1170,32 @@ public class NPCCommands {
|
||||
Messaging.sendTr(sender, Messages.SKELETON_TYPE_SET, npc.getName(), type);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "npc" },
|
||||
usage = "skin (-c) [name]",
|
||||
desc = "Sets an NPC's skin name",
|
||||
modifiers = { "skin" },
|
||||
min = 1,
|
||||
max = 2,
|
||||
permission = "citizens.npc.skin")
|
||||
@Requirements(types = EntityType.PLAYER)
|
||||
public void skin(final CommandContext args, final CommandSender sender, final NPC npc) throws CommandException {
|
||||
String skinName = npc.getName();
|
||||
if (args.hasFlag('c')) {
|
||||
npc.data().remove(NPC.PLAYER_SKIN_UUID_METADATA);
|
||||
} else {
|
||||
if (args.argsLength() != 2)
|
||||
throw new CommandException();
|
||||
npc.data().setPersistent(NPC.PLAYER_SKIN_UUID_METADATA, args.getString(1));
|
||||
skinName = args.getString(1);
|
||||
}
|
||||
Messaging.sendTr(sender, Messages.SKIN_SET, npc.getName(), skinName);
|
||||
if (npc.isSpawned()) {
|
||||
npc.despawn(DespawnReason.PENDING_RESPAWN);
|
||||
npc.spawn(npc.getStoredLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "npc" },
|
||||
usage = "size [size]",
|
||||
|
@ -115,8 +115,9 @@ public class CitizensNPC extends AbstractNPC {
|
||||
|
||||
// Spawn the NPC
|
||||
CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
|
||||
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null)
|
||||
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null) {
|
||||
spawn(spawnLocation.getLocation());
|
||||
}
|
||||
|
||||
navigator.load(root.getRelative("navigator"));
|
||||
}
|
||||
|
@ -1,23 +1,35 @@
|
||||
package net.citizensnpcs.npc.entity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import net.citizensnpcs.Settings.Setting;
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.event.DespawnReason;
|
||||
import net.citizensnpcs.api.npc.NPC;
|
||||
import net.citizensnpcs.api.util.Colorizer;
|
||||
import net.citizensnpcs.npc.AbstractEntityController;
|
||||
import net.citizensnpcs.util.NMS;
|
||||
import net.minecraft.server.v1_7_R3.PlayerInteractManager;
|
||||
import net.minecraft.server.v1_7_R3.WorldServer;
|
||||
import net.minecraft.util.com.google.common.collect.Iterables;
|
||||
import net.minecraft.util.com.mojang.authlib.Agent;
|
||||
import net.minecraft.util.com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.util.com.mojang.authlib.GameProfileRepository;
|
||||
import net.minecraft.util.com.mojang.authlib.ProfileLookupCallback;
|
||||
import net.minecraft.util.com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import net.minecraft.util.com.mojang.authlib.properties.Property;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_7_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_7_R3.CraftWorld;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
public class HumanController extends AbstractEntityController {
|
||||
public HumanController() {
|
||||
super();
|
||||
@ -25,15 +37,22 @@ public class HumanController extends AbstractEntityController {
|
||||
|
||||
@Override
|
||||
protected Entity createEntity(final Location at, final NPC npc) {
|
||||
WorldServer ws = ((CraftWorld) at.getWorld()).getHandle();
|
||||
String parseColors = Colorizer.parseColors(npc.getFullName());
|
||||
if (parseColors.length() > 16) {
|
||||
parseColors = parseColors.substring(0, 16);
|
||||
final WorldServer nmsWorld = ((CraftWorld) at.getWorld()).getHandle();
|
||||
String coloredName = Colorizer.parseColors(npc.getFullName());
|
||||
if (coloredName.length() > 16) {
|
||||
coloredName = coloredName.substring(0, 16);
|
||||
}
|
||||
UUID uuid = UUID.randomUUID(); // clear version
|
||||
uuid = new UUID(uuid.getMostSignificantBits() | 0x0000000000005000L, uuid.getLeastSignificantBits());
|
||||
final EntityHumanNPC handle = new EntityHumanNPC(ws.getServer().getServer(), ws, new GameProfile(uuid,
|
||||
parseColors), new PlayerInteractManager(ws), npc);
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
if (uuid.version() == 4) { // clear version
|
||||
uuid = new UUID(uuid.getMostSignificantBits() | 0x0000000000005000L, uuid.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
GameProfile profile = new GameProfile(uuid, coloredName);
|
||||
updateSkin(npc, nmsWorld, profile);
|
||||
|
||||
final EntityHumanNPC handle = new EntityHumanNPC(nmsWorld.getServer().getServer(), nmsWorld, profile,
|
||||
new PlayerInteractManager(nmsWorld), npc);
|
||||
handle.setPositionRotation(at.getX(), at.getY(), at.getZ(), at.getYaw(), at.getPitch());
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
||||
@Override
|
||||
@ -51,4 +70,108 @@ public class HumanController extends AbstractEntityController {
|
||||
public Player getBukkitEntity() {
|
||||
return (Player) super.getBukkitEntity();
|
||||
}
|
||||
|
||||
private void updateSkin(final NPC npc, final WorldServer nmsWorld, GameProfile profile) {
|
||||
String skinUUID = npc.data().get(NPC.PLAYER_SKIN_UUID_METADATA);
|
||||
if (skinUUID == null) {
|
||||
skinUUID = npc.getName();
|
||||
}
|
||||
if (npc.data().has(CACHED_SKIN_UUID_METADATA) && npc.data().has(CACHED_SKIN_UUID_NAME_METADATA)
|
||||
&& skinUUID.equalsIgnoreCase(npc.data().<String> get(CACHED_SKIN_UUID_NAME_METADATA))) {
|
||||
skinUUID = npc.data().get(CACHED_SKIN_UUID_METADATA);
|
||||
}
|
||||
if (UUID_CACHE.containsKey(skinUUID)) {
|
||||
skinUUID = UUID_CACHE.get(skinUUID);
|
||||
}
|
||||
Property cached = TEXTURE_CACHE.get(skinUUID);
|
||||
if (cached != null) {
|
||||
profile.getProperties().put("textures", cached);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(CitizensAPI.getPlugin(),
|
||||
new SkinFetcher(new UUIDFetcher(skinUUID, npc), nmsWorld.getMinecraftServer().av(), npc));
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkinFetcher implements Runnable {
|
||||
private final NPC npc;
|
||||
private final MinecraftSessionService repo;
|
||||
private final Callable<String> uuid;
|
||||
|
||||
public SkinFetcher(Callable<String> uuid, MinecraftSessionService repo, NPC npc) {
|
||||
this.uuid = uuid;
|
||||
this.repo = repo;
|
||||
this.npc = npc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String realUUID;
|
||||
try {
|
||||
realUUID = uuid.call();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
GameProfile skinProfile = null;
|
||||
try {
|
||||
skinProfile = repo.fillProfileProperties(new GameProfile(UUID.fromString(realUUID), ""), true);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
if (skinProfile == null || !skinProfile.getProperties().containsKey("textures"))
|
||||
return;
|
||||
Property textures = Iterables.getFirst(skinProfile.getProperties().get("textures"), null);
|
||||
if (textures.getValue() != null && textures.getSignature() != null) {
|
||||
TEXTURE_CACHE.put(realUUID, new Property("textures", textures.getValue(), textures.getSignature()));
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (npc.isSpawned()) {
|
||||
npc.despawn(DespawnReason.PENDING_RESPAWN);
|
||||
npc.spawn(npc.getStoredLocation());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UUIDFetcher implements Callable<String> {
|
||||
private final NPC npc;
|
||||
private final String reportedUUID;
|
||||
|
||||
public UUIDFetcher(String reportedUUID, NPC npc) {
|
||||
this.reportedUUID = reportedUUID;
|
||||
this.npc = npc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
if (reportedUUID.contains("-")) {
|
||||
return reportedUUID;
|
||||
}
|
||||
final GameProfileRepository repo = ((CraftServer) Bukkit.getServer()).getServer()
|
||||
.getGameProfileRepository();
|
||||
repo.findProfilesByNames(new String[] { reportedUUID }, Agent.MINECRAFT, new ProfileLookupCallback() {
|
||||
@Override
|
||||
public void onProfileLookupFailed(GameProfile arg0, Exception arg1) {
|
||||
throw new RuntimeException(arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfileLookupSucceeded(final GameProfile profile) {
|
||||
UUID_CACHE.put(reportedUUID, profile.getId().toString());
|
||||
npc.data().setPersistent(CACHED_SKIN_UUID_METADATA, profile.getId().toString());
|
||||
npc.data().setPersistent(CACHED_SKIN_UUID_NAME_METADATA, profile.getName());
|
||||
}
|
||||
});
|
||||
return npc.data().get(CACHED_SKIN_UUID_METADATA);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String CACHED_SKIN_UUID_METADATA = "cached-skin-uuid";
|
||||
private static final String CACHED_SKIN_UUID_NAME_METADATA = "cached-skin-uuid-name";
|
||||
private static final Map<String, Property> TEXTURE_CACHE = Maps.newConcurrentMap();
|
||||
private static final Map<String, String> UUID_CACHE = Maps.newConcurrentMap();
|
||||
}
|
@ -176,7 +176,7 @@ public class NMS {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static Constructor<?> getCustomEntityConstructor(Class<?> clazz, EntityType type) throws SecurityException,
|
||||
NoSuchMethodException {
|
||||
NoSuchMethodException {
|
||||
Constructor<?> constructor = ENTITY_CONSTRUCTOR_CACHE.get(clazz);
|
||||
if (constructor == null) {
|
||||
constructor = clazz.getConstructor(World.class);
|
||||
|
Loading…
Reference in New Issue
Block a user