Skip to content

Commit

Permalink
add skin mirroring to NPCs
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Mar 16, 2019
1 parent bcb39fa commit bb47fe3
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 15 deletions.
Expand Up @@ -9,12 +9,15 @@
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 {

protected static final Map<UUID, PlayerProfile> fakeProfiles = new HashMap<UUID, PlayerProfile>();
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());
Expand Down
1 change: 1 addition & 0 deletions plugin/src/main/java/net/aufdemrand/denizen/Denizen.java
Expand Up @@ -534,6 +534,7 @@ public void onEnable() {
CitizensAPI.getTraitFactory().registerTrait(TraitInfo.create(SneakingTrait.class).withName("sneaking"));
CitizensAPI.getTraitFactory().registerTrait(TraitInfo.create(InvisibleTrait.class).withName("invisible"));
CitizensAPI.getTraitFactory().registerTrait(TraitInfo.create(MobproxTrait.class).withName("mobprox"));
CitizensAPI.getTraitFactory().registerTrait(TraitInfo.create(MirrorTrait.class).withName("mirror"));

// Register Speech AI
CitizensAPI.getSpeechFactory().register(DenizenChat.class, "denizen_chat");
Expand Down
35 changes: 35 additions & 0 deletions plugin/src/main/java/net/aufdemrand/denizen/NPCCommandHandler.java
Expand Up @@ -587,10 +587,45 @@ public void sneaking(CommandContext args, CommandSender sender, NPC npc) throws
SneakingTrait trait = npc.getTrait(SneakingTrait.class);

if (trait.isSneaking()) {
trait.stand();
Messaging.sendError(sender, npc.getName() + " is already sneaking!");
}
else {
trait.sneak();
Messaging.send(sender, npc.getName() + " is now sneaking.");
}

}

/*
* Mirror
*/
@Command(
aliases = {"npc"}, usage = "mirror",
desc = "Makes the NPC mirror the skin of the player looking at it.", flags = "", modifiers = {"mirror"},
min = 1, max = 3, permission = "denizen.npc.mirror")
@Requirements(selected = true, ownership = true)
public void mirror(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
if (npc.getEntity().getType() != EntityType.PLAYER) {
Messaging.sendError(sender, npc.getName() + " needs to be a Player type NPC to be a mirror!");
return;
}

if (!npc.hasTrait(MirrorTrait.class)) {
npc.addTrait(MirrorTrait.class);
npc.getTrait(MirrorTrait.class).enableMirror();
Messaging.send(sender, npc.getName() + " is now mirroring player skins.");
return;
}
MirrorTrait trait = npc.getTrait(MirrorTrait.class);

if (trait.mirror) {
trait.disableMirror();
Messaging.send(sender, npc.getName() + " is no longer mirroring player skins.");
}
else {
trait.enableMirror();
Messaging.send(sender, npc.getName() + " is now mirroring player skins.");
}

}
Expand Down
@@ -0,0 +1,92 @@
package net.aufdemrand.denizen.npc.traits;

import net.aufdemrand.denizen.nms.abstracts.ProfileEditor;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.citizensnpcs.api.event.DespawnReason;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import org.bukkit.Bukkit;
import org.bukkit.Location;

import java.util.UUID;

public class MirrorTrait extends Trait {

@Persist("")
public boolean mirror = true;

public MirrorTrait() {
super("mirror");
}

public static UUID getUUID(NPC npc) {
UUID uuid = npc.getUniqueId();
if (uuid.version() == 4) { // clear version
long msb = uuid.getMostSignificantBits();
msb &= ~0x0000000000004000L;
msb |= 0x0000000000002000L;
uuid = new UUID(msb, uuid.getLeastSignificantBits());
}
return uuid;
}

public static void respawn(NPC npc) {
Bukkit.getScheduler().scheduleSyncDelayedTask(DenizenAPI.getCurrentInstance(), () -> {
if (npc.isSpawned()) {
Location loc = npc.getEntity().getLocation();
npc.despawn(DespawnReason.PENDING_RESPAWN);
Bukkit.getScheduler().scheduleSyncDelayedTask(DenizenAPI.getCurrentInstance(), () -> {
npc.spawn(loc);
});
}
});
}

public static void mirrorOn(NPC npc) {
UUID uuid = getUUID(npc);
if (!ProfileEditor.mirrorUUIDs.contains(uuid)) {
ProfileEditor.mirrorUUIDs.add(uuid);
respawn(npc);
}
}

public static void mirrorOff(NPC npc) {
UUID uuid = getUUID(npc);
if (ProfileEditor.mirrorUUIDs.contains(uuid)) {
ProfileEditor.mirrorUUIDs.remove(uuid);
respawn(npc);
}
}

public void enableMirror() {
mirror = true;
mirrorOn(npc);
}

public void disableMirror() {
mirror = false;
mirrorOff(npc);
}

@Override
public void onSpawn() {
if (mirror) {
mirrorOn(npc);
}
}

@Override
public void onRemove() {
if (mirror) {
mirrorOff(npc);
}
}

@Override
public void onAttach() {
if (mirror) {
mirrorOn(npc);
}
}
}
34 changes: 34 additions & 0 deletions plugin/src/main/java/net/aufdemrand/denizen/objects/dEntity.java
Expand Up @@ -3,8 +3,10 @@
import net.aufdemrand.denizen.flags.FlagManager;
import net.aufdemrand.denizen.nms.NMSHandler;
import net.aufdemrand.denizen.nms.NMSVersion;
import net.aufdemrand.denizen.nms.abstracts.ProfileEditor;
import net.aufdemrand.denizen.nms.interfaces.EntityHelper;
import net.aufdemrand.denizen.nms.interfaces.FakePlayer;
import net.aufdemrand.denizen.npc.traits.MirrorTrait;
import net.aufdemrand.denizen.objects.properties.entity.EntityAge;
import net.aufdemrand.denizen.objects.properties.entity.EntityColor;
import net.aufdemrand.denizen.objects.properties.entity.EntityTame;
Expand Down Expand Up @@ -3095,6 +3097,38 @@ && getBukkitEntity() instanceof Sheep) {
NMSHandler.getInstance().getEntityHelper().hideEntity(null, getBukkitEntity(), false);
}

// <--[mechanism]
// @object dEntity
// @name mirror_player
// @input Element(Boolean)
// @description
// Makes the player-like entity have the same skin as the player looking at it.
// For NPCs, this will add the Mirror trait.
// -->
if (mechanism.matches("mirror_player") && mechanism.requireBoolean()) {
if (isNPC()) {
NPC npc = getDenizenNPC().getCitizen();
if (!npc.hasTrait(MirrorTrait.class)) {
npc.addTrait(MirrorTrait.class);
}
MirrorTrait mirror = npc.getTrait(MirrorTrait.class);
if (mechanism.getValue().asBoolean()) {
mirror.enableMirror();
}
else {
mirror.disableMirror();
}
}
else {
if (mechanism.getValue().asBoolean()) {
ProfileEditor.mirrorUUIDs.add(getUUID());
}
else {
ProfileEditor.mirrorUUIDs.remove(getUUID());
}
}
}

// <--[mechanism]
// @object dEntity
// @name swimming
Expand Down
5 changes: 2 additions & 3 deletions plugin/src/main/resources/plugin.yml
@@ -1,5 +1,5 @@
name: Denizen
authors: ['aufdemrand', 'mcmonkey', 'Morphan1']
authors: ['mcmonkey', 'Morphan1', 'aufdemrand']
version: ${project.version} (build ${BUILD_NUMBER}-${BUILD_CLASS})
main: net.aufdemrand.denizen.Denizen
softdepend: [Citizens, Vault]
Expand All @@ -20,7 +20,6 @@ commands:
usage: Use /ex in front of a normal script entry.
permission: denizen.ex


permissions:
denizen.*:
description: Gives access to Denizen commands
Expand Down Expand Up @@ -63,4 +62,4 @@ permissions:
denizen.npc.assign: true
denizen.npc.constants: true
denizen.npc.pushable: true

denizen.npc.mirror: true
Expand Up @@ -2,9 +2,12 @@

import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.aufdemrand.denizen.nms.NMSHandler;
import net.aufdemrand.denizen.nms.abstracts.ProfileEditor;
import net.aufdemrand.denizen.nms.helpers.PacketHelper_v1_13_R2;
import net.aufdemrand.denizen.nms.impl.packets.handlers.DenizenNetworkManager_v1_13_R2;
import net.aufdemrand.denizen.nms.util.PlayerProfile;
import net.aufdemrand.denizen.nms.util.ReflectionHelper;
import net.aufdemrand.denizencore.utilities.CoreUtilities;
Expand All @@ -19,6 +22,7 @@
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;
import java.util.UUID;
Expand Down Expand Up @@ -57,6 +61,58 @@ public void run() {
}.runTaskLater(NMSHandler.getJavaPlugin(), 5);
}

public static boolean handleMirrorProfiles(PacketPlayOutPlayerInfo packet, DenizenNetworkManager_v1_13_R2 manager,
GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
if (ProfileEditor.mirrorUUIDs.isEmpty()) {
return true;
}
PacketPlayOutPlayerInfo.EnumPlayerInfoAction action = ReflectionHelper.getFieldValue(PacketPlayOutPlayerInfo.class, "a", packet);
if (action != PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER) {
return true;
}
List dataList = ReflectionHelper.getFieldValue(PacketPlayOutPlayerInfo.class, "b", packet);
if (dataList == null) {
return true;
}
try {
boolean any = false;
for (Object data : dataList) {
GameProfile gameProfile = (GameProfile) playerInfoData_gameProfile.get(data);
if (ProfileEditor.mirrorUUIDs.contains(gameProfile.getId())) {
any = true;
}
}
if (!any) {
return true;
}
GameProfile ownProfile = manager.player.getProfile();
for (Object data : dataList) {
GameProfile gameProfile = (GameProfile) playerInfoData_gameProfile.get(data);
if (!ProfileEditor.mirrorUUIDs.contains(gameProfile.getId())) {
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER);
List newPacketDataList = ReflectionHelper.getFieldValue(PacketPlayOutPlayerInfo.class, "b", newPacket);
newPacketDataList.add(data);
manager.oldManager.sendPacket(newPacket, genericfuturelistener);
}
else {
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER);
List newPacketDataList = ReflectionHelper.getFieldValue(PacketPlayOutPlayerInfo.class, "b", newPacket);
GameProfile patchedProfile = new GameProfile(gameProfile.getId(), gameProfile.getName());
patchedProfile.getProperties().putAll(ownProfile.getProperties());
Object newData = playerInfoData_construct.newInstance(newPacket, patchedProfile,
playerInfoData_latency.getInt(data), playerInfoData_gamemode.get(data), playerInfoData_displayName.get(data));
newPacketDataList.add(newData);
manager.oldManager.sendPacket(newPacket, genericfuturelistener);
}
}
return false;
}
catch (Exception e) {
dB.echoError(e);
return true;
}
}

public static void updatePlayerProfiles(PacketPlayOutPlayerInfo packet) {
PacketPlayOutPlayerInfo.EnumPlayerInfoAction action = ReflectionHelper.getFieldValue(PacketPlayOutPlayerInfo.class, "a", packet);
if (action != PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER) {
Expand Down Expand Up @@ -85,22 +141,45 @@ private static GameProfile getGameProfile(PlayerProfile playerProfile) {
return gameProfile;
}

private static final Field playerInfoData_gameProfile;
public static final Class playerInfoData;

public static final Field playerInfoData_latency,
playerInfoData_gamemode,
playerInfoData_gameProfile,
playerInfoData_displayName;

public static final Constructor playerInfoData_construct;

static {
Field pidGameProfile = null;
Class pid = null;
Field pidLatency = null, pidGamemode = null, pidGameProfile = null, pidDisplayName = null;
Constructor pidConstruct = null;
try {
for (Class clzz : PacketPlayOutPlayerInfo.class.getDeclaredClasses()) {
if (CoreUtilities.toLowerCase(clzz.getName()).contains("infodata")) {
pidGameProfile = clzz.getDeclaredField("d"); // PlayerInfoData.
if (CoreUtilities.toLowerCase(clzz.getName()).contains("infodata")) { // PlayerInfoData.
pid = clzz;
pidLatency = clzz.getDeclaredField("b");
pidLatency.setAccessible(true);
pidGamemode = clzz.getDeclaredField("c");
pidGamemode.setAccessible(true);
pidGameProfile = clzz.getDeclaredField("d");
pidGameProfile.setAccessible(true);
pidDisplayName = clzz.getDeclaredField("e");
pidDisplayName.setAccessible(true);
pidConstruct = pid.getDeclaredConstructors()[0];
pidConstruct.setAccessible(true);
break;
}
}
}
catch (Exception e) {
dB.echoError(e);
e.printStackTrace();
}
playerInfoData = pid;
playerInfoData_latency = pidLatency;
playerInfoData_gamemode = pidGamemode;
playerInfoData_gameProfile = pidGameProfile;
playerInfoData_displayName = pidDisplayName;
playerInfoData_construct = pidConstruct;
}
}
Expand Up @@ -31,10 +31,10 @@

public class DenizenNetworkManager_v1_13_R2 extends NetworkManager {

private final NetworkManager oldManager;
private final DenizenPacketListener_v1_13_R2 packetListener;
private final EntityPlayer player;
private final PacketHandler packetHandler;
public final NetworkManager oldManager;
public final DenizenPacketListener_v1_13_R2 packetListener;
public final EntityPlayer player;
public final PacketHandler packetHandler;

public DenizenNetworkManager_v1_13_R2(EntityPlayer entityPlayer, NetworkManager oldManager, PacketHandler packetHandler) {
super(getProtocolDirection(oldManager));
Expand Down Expand Up @@ -295,8 +295,10 @@ public void run() {
}
else if (packet instanceof PacketPlayOutPlayerInfo) {
PacketPlayOutPlayerInfo playerInfo = (PacketPlayOutPlayerInfo) packet;
ProfileEditor_v1_13_R2.updatePlayerProfiles(playerInfo);
oldManager.sendPacket(playerInfo);
if (ProfileEditor_v1_13_R2.handleMirrorProfiles(playerInfo, this, genericfuturelistener)) {
ProfileEditor_v1_13_R2.updatePlayerProfiles(playerInfo);
oldManager.sendPacket(playerInfo);
}
}
else if (packet instanceof PacketPlayOutEntityMetadata) {
if (!packetHandler.sendPacket(player.getBukkitEntity(), new PacketOutEntityMetadata_v1_13_R2((PacketPlayOutEntityMetadata) packet))) {
Expand Down

0 comments on commit bb47fe3

Please sign in to comment.