mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2025-01-14 04:02:01 +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>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<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>
|
<citizensapi.version>2.0.13-SNAPSHOT</citizensapi.version>
|
||||||
<vault.version>1.2.19-SNAPSHOT</vault.version>
|
<vault.version>1.2.19-SNAPSHOT</vault.version>
|
||||||
<powermock.version>1.4.12</powermock.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)
|
CommandSenderCreateNPCEvent event = sender instanceof Player ? new PlayerCreateNPCEvent((Player) sender, copy)
|
||||||
: new CommandSenderCreateNPCEvent(sender, copy);
|
: new CommandSenderCreateNPCEvent(sender, copy);
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
event.getNPC().destroy();
|
event.getNPC().destroy();
|
||||||
@ -343,7 +343,7 @@ public class NPCCommands {
|
|||||||
spawnLoc = args.getSenderLocation();
|
spawnLoc = args.getSenderLocation();
|
||||||
}
|
}
|
||||||
CommandSenderCreateNPCEvent event = sender instanceof Player ? new PlayerCreateNPCEvent((Player) sender, npc)
|
CommandSenderCreateNPCEvent event = sender instanceof Player ? new PlayerCreateNPCEvent((Player) sender, npc)
|
||||||
: new CommandSenderCreateNPCEvent(sender, npc);
|
: new CommandSenderCreateNPCEvent(sender, npc);
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
npc.destroy();
|
npc.destroy();
|
||||||
@ -1018,7 +1018,7 @@ public class NPCCommands {
|
|||||||
@Requirements(selected = true, ownership = true, types = { EntityType.CREEPER })
|
@Requirements(selected = true, ownership = true, types = { EntityType.CREEPER })
|
||||||
public void power(CommandContext args, CommandSender sender, NPC npc) {
|
public void power(CommandContext args, CommandSender sender, NPC npc) {
|
||||||
Messaging
|
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(
|
@Command(
|
||||||
@ -1170,6 +1170,32 @@ public class NPCCommands {
|
|||||||
Messaging.sendTr(sender, Messages.SKELETON_TYPE_SET, npc.getName(), type);
|
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(
|
@Command(
|
||||||
aliases = { "npc" },
|
aliases = { "npc" },
|
||||||
usage = "size [size]",
|
usage = "size [size]",
|
||||||
|
@ -115,8 +115,9 @@ public class CitizensNPC extends AbstractNPC {
|
|||||||
|
|
||||||
// Spawn the NPC
|
// Spawn the NPC
|
||||||
CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
|
CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
|
||||||
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null)
|
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null) {
|
||||||
spawn(spawnLocation.getLocation());
|
spawn(spawnLocation.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
navigator.load(root.getRelative("navigator"));
|
navigator.load(root.getRelative("navigator"));
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,35 @@
|
|||||||
package net.citizensnpcs.npc.entity;
|
package net.citizensnpcs.npc.entity;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import net.citizensnpcs.Settings.Setting;
|
import net.citizensnpcs.Settings.Setting;
|
||||||
import net.citizensnpcs.api.CitizensAPI;
|
import net.citizensnpcs.api.CitizensAPI;
|
||||||
|
import net.citizensnpcs.api.event.DespawnReason;
|
||||||
import net.citizensnpcs.api.npc.NPC;
|
import net.citizensnpcs.api.npc.NPC;
|
||||||
import net.citizensnpcs.api.util.Colorizer;
|
import net.citizensnpcs.api.util.Colorizer;
|
||||||
import net.citizensnpcs.npc.AbstractEntityController;
|
import net.citizensnpcs.npc.AbstractEntityController;
|
||||||
import net.citizensnpcs.util.NMS;
|
import net.citizensnpcs.util.NMS;
|
||||||
import net.minecraft.server.v1_7_R3.PlayerInteractManager;
|
import net.minecraft.server.v1_7_R3.PlayerInteractManager;
|
||||||
import net.minecraft.server.v1_7_R3.WorldServer;
|
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.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.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_7_R3.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_7_R3.CraftWorld;
|
import org.bukkit.craftbukkit.v1_7_R3.CraftWorld;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
public class HumanController extends AbstractEntityController {
|
public class HumanController extends AbstractEntityController {
|
||||||
public HumanController() {
|
public HumanController() {
|
||||||
super();
|
super();
|
||||||
@ -25,15 +37,22 @@ public class HumanController extends AbstractEntityController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Entity createEntity(final Location at, final NPC npc) {
|
protected Entity createEntity(final Location at, final NPC npc) {
|
||||||
WorldServer ws = ((CraftWorld) at.getWorld()).getHandle();
|
final WorldServer nmsWorld = ((CraftWorld) at.getWorld()).getHandle();
|
||||||
String parseColors = Colorizer.parseColors(npc.getFullName());
|
String coloredName = Colorizer.parseColors(npc.getFullName());
|
||||||
if (parseColors.length() > 16) {
|
if (coloredName.length() > 16) {
|
||||||
parseColors = parseColors.substring(0, 16);
|
coloredName = coloredName.substring(0, 16);
|
||||||
}
|
}
|
||||||
UUID uuid = UUID.randomUUID(); // clear version
|
|
||||||
uuid = new UUID(uuid.getMostSignificantBits() | 0x0000000000005000L, uuid.getLeastSignificantBits());
|
UUID uuid = UUID.randomUUID();
|
||||||
final EntityHumanNPC handle = new EntityHumanNPC(ws.getServer().getServer(), ws, new GameProfile(uuid,
|
if (uuid.version() == 4) { // clear version
|
||||||
parseColors), new PlayerInteractManager(ws), npc);
|
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());
|
handle.setPositionRotation(at.getX(), at.getY(), at.getZ(), at.getYaw(), at.getPitch());
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -51,4 +70,108 @@ public class HumanController extends AbstractEntityController {
|
|||||||
public Player getBukkitEntity() {
|
public Player getBukkitEntity() {
|
||||||
return (Player) super.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")
|
@SuppressWarnings("deprecation")
|
||||||
private static Constructor<?> getCustomEntityConstructor(Class<?> clazz, EntityType type) throws SecurityException,
|
private static Constructor<?> getCustomEntityConstructor(Class<?> clazz, EntityType type) throws SecurityException,
|
||||||
NoSuchMethodException {
|
NoSuchMethodException {
|
||||||
Constructor<?> constructor = ENTITY_CONSTRUCTOR_CACHE.get(clazz);
|
Constructor<?> constructor = ENTITY_CONSTRUCTOR_CACHE.get(clazz);
|
||||||
if (constructor == null) {
|
if (constructor == null) {
|
||||||
constructor = clazz.getConstructor(World.class);
|
constructor = clazz.getConstructor(World.class);
|
||||||
|
Loading…
Reference in New Issue
Block a user