diff --git a/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java b/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java index 5b07444..1e2e20c 100644 --- a/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java +++ b/core/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java @@ -16,6 +16,7 @@ import java.io.InputStreamReader; import java.net.URL; import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; public class MojangAPI { public static final String URL_NAME = "https://api.mojang.com/users/profiles/minecraft/{name}"; @@ -29,6 +30,7 @@ public class MojangAPI { }; private final LoadingCache> cache = CacheBuilder .newBuilder() + .expireAfterWrite(120, TimeUnit.MINUTES) .build(loader); public Optional getSkin(String uuid) throws IOException, ExecutionException { diff --git a/pom.xml b/pom.xml index 08ef469..bc5ab6e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ dist v1_18_R2 v1_18_R1 + v1_19_R1 diff --git a/v1_19_R1/pom.xml b/v1_19_R1/pom.xml new file mode 100644 index 0000000..dc4803f --- /dev/null +++ b/v1_19_R1/pom.xml @@ -0,0 +1,28 @@ + + + + nicko-parent + net.artelnatif + 1.0-SNAPSHOT + + 4.0.0 + + nicko-v1_19_R1 + + + + org.spigotmc + spigot + 1.19.2-R0.1-SNAPSHOT + provided + + + net.artelnatif + nicko-core + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/v1_19_R1/src/main/java/net/artelnatif/nicko/impl/v1_19_R1.java b/v1_19_R1/src/main/java/net/artelnatif/nicko/impl/v1_19_R1.java new file mode 100644 index 0000000..b3b76fd --- /dev/null +++ b/v1_19_R1/src/main/java/net/artelnatif/nicko/impl/v1_19_R1.java @@ -0,0 +1,134 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.level.EnumGamemode; +import net.minecraft.world.level.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; + +import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +public class v1_19_R1 implements Internals { + @Override + public void updateSelf(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final ResourceKey levelResourceKey = entityPlayer.x().ab(); + final CraftWorld world = entityPlayer.s.getWorld(); + // last boolean is: "has death location" attribute, if true, the optional contains the death dimension and positon. + // with the boolean being false, we don't need to provide a value and thus we return an empty optional. + final PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(entityPlayer.x().Z(), + levelResourceKey, world.getSeed(), + entityPlayer.d.b(), entityPlayer.d.c(), + false, + false, + false, + Optional.empty()); + + final boolean wasFlying = player.isFlying(); + final ItemStack itemOnCursor = player.getItemOnCursor(); + entityPlayer.b.a(respawn); + player.setFlying(wasFlying); + player.setItemOnCursor(itemOnCursor); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); + final PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(entityPlayer.getBukkitEntity().getEntityId()); + final PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(entityPlayer); + + /* + BIT MASKS: + 0x01 Cape enabled + 0x02 Jacket enabled + 0x04 Left sleeve enabled + 0x08 Right sleeve enabled + 0x10 Left pants leg enabled + 0x20 Right pants leg enabled + 0x40 Hat enabled + */ + final DataWatcher dataWatcher = entityPlayer.ai(); + final DataWatcherObject displayedSkinPartDataWatcher = new DataWatcherObject<>(17, DataWatcherRegistry.a); + dataWatcher.b(displayedSkinPartDataWatcher, (byte) 0x7f); // 127, all masks combined + final PacketPlayOutEntityMetadata entityMetadata = new PacketPlayOutEntityMetadata(entityPlayer.getBukkitEntity().getEntityId(), dataWatcher, true); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + if (onlineEntityPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineEntityPlayer.b.a(destroy); + onlineEntityPlayer.b.a(spawn); + onlineEntityPlayer.b.a(entityMetadata); + } + }); + } + + @Override + public void updateProfile(Player player, NickoProfile profile, boolean skinChange) { + final CraftPlayer craftPlayer = (CraftPlayer) player; + final EntityPlayer entityPlayer = craftPlayer.getHandle(); + Optional skin; + + final PacketPlayOutPlayerInfo remove = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e, entityPlayer); + entityPlayer.b.a(remove); + + final PacketPlayOutPlayerInfo add = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profile.getName()); + + if (skinChange) { + try { + final Optional uuid = NickoBukkit.getInstance().getMojangAPI().getUUID(profile.getSkin()); + if (uuid.isPresent()) { + skin = NickoBukkit.getInstance().getMojangAPI().getSkin(uuid.get()); + if (skin.isPresent()) { + final PropertyMap properties = gameProfile.getProperties(); + properties.put("textures", new Property("textures", skin.get().value(), skin.get().signature())); + updateSelf(player); + } else { + player.sendMessage(NickoBukkit.getInstance().getNickoConfig().getPrefix() + "§cFailed to get skin from Mojang."); + } + } else { + player.sendMessage(NickoBukkit.getInstance().getNickoConfig().getPrefix() + "§cFailed to get username from Mojang. Does the user exists?"); + } + } catch (IOException e) { + player.sendMessage(NickoBukkit.getInstance().getNickoConfig().getPrefix() + "§cAn error occurred."); + } catch (ExecutionException e) { + player.sendMessage(NickoBukkit.getInstance().getNickoConfig().getPrefix() + "§cFailed to get skin from cache."); + } + } + + add.b().clear(); + add.b().add(new PacketPlayOutPlayerInfo.PlayerInfoData(gameProfile, + player.getPing(), + EnumGamemode.a(player.getGameMode().ordinal()), + IChatBaseComponent.a(profile.getName()), + entityPlayer.fz().b())); + entityPlayer.b.a(add); + + Bukkit.getOnlinePlayers().forEach(online -> { + EntityPlayer onlineEntityPlayer = ((CraftPlayer) online).getHandle(); + onlineEntityPlayer.b.a(remove); + onlineEntityPlayer.b.a(add); + }); + updateOthers(player); + } +}