├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── io └── github └── danielthedev └── npc └── NPC.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | .idea/ 3 | *.iml 4 | *.iws 5 | 6 | # Mac 7 | .DS_Store 8 | 9 | # Maven 10 | log/ 11 | target/ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.danielthedev/npc/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.github.danielthedev/npc) 2 | 3 | ## NPC Util 1.17 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | io.github.danielthedev 7 | npc 8 | 1.2 9 | 10 | 11 | 12 | org.apache.maven.plugins 13 | maven-compiler-plugin 14 | 15 | 16 16 | 16 17 | 18 | 19 | 20 | org.sonatype.plugins 21 | nexus-staging-maven-plugin 22 | 1.6.7 23 | true 24 | 25 | ossrh 26 | https://s01.oss.sonatype.org/ 27 | true 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-source-plugin 33 | 2.2.1 34 | 35 | 36 | attach-sources 37 | 38 | jar-no-fork 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-javadoc-plugin 46 | 2.9.1 47 | 48 | 49 | attach-javadocs 50 | 51 | jar 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-gpg-plugin 59 | 1.5 60 | 61 | 62 | sign-artifacts 63 | verify 64 | 65 | sign 66 | 67 | 68 | 69 | 70 | 71 | org.sonatype.plugins 72 | nexus-staging-maven-plugin 73 | 1.6.8 74 | true 75 | 76 | ossrh 77 | https://s01.oss.sonatype.org/ 78 | true 79 | 80 | 81 | 82 | 83 | jar 84 | NPC 85 | NPC util for minecraft craftbukkit 86 | https://github.com/DanielTheDev/NPC 87 | 88 | 89 | Apache License, Version 2.0 90 | http://www.apache.org/licenses/LICENSE-2.0.txt 91 | repo 92 | 93 | 94 | 95 | 96 | ossrh 97 | https://s01.oss.sonatype.org/content/repositories/snapshots 98 | 99 | 100 | ossrh 101 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 102 | 103 | 104 | 105 | 106 | DanielTheDev 107 | danielkoopmans.nl@gmail.com 108 | 109 | 110 | 111 | scm:git:git://github.com/DanielTheDev/NPC.git 112 | scm:git:ssh://github.com:DanielTheDev/NPC.git 113 | http://github.com/DanielTheDev/NPC/tree/master 114 | 115 | 116 | 16 117 | 16 118 | 119 | 120 | 121 | 122 | org.spigotmc 123 | spigot 124 | 1.17-R0.1-SNAPSHOT 125 | provided 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/main/java/io/github/danielthedev/npc/NPC.java: -------------------------------------------------------------------------------- 1 | package io.github.danielthedev.npc; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import com.mojang.authlib.properties.Property; 5 | import com.mojang.datafixers.util.Pair; 6 | import io.netty.buffer.Unpooled; 7 | import net.minecraft.core.BlockPosition; 8 | import net.minecraft.nbt.NBTTagCompound; 9 | import net.minecraft.network.PacketDataSerializer; 10 | import net.minecraft.network.chat.IChatBaseComponent; 11 | import net.minecraft.network.protocol.Packet; 12 | import net.minecraft.network.protocol.game.*; 13 | import net.minecraft.network.syncher.DataWatcher; 14 | import net.minecraft.network.syncher.DataWatcherObject; 15 | import net.minecraft.network.syncher.DataWatcherRegistry; 16 | import net.minecraft.network.syncher.DataWatcherSerializer; 17 | import net.minecraft.world.entity.Entity; 18 | import net.minecraft.world.entity.EntityPose; 19 | import net.minecraft.world.entity.EntityTypes; 20 | import net.minecraft.world.entity.EnumItemSlot; 21 | import net.minecraft.world.entity.animal.EntityParrot; 22 | import net.minecraft.world.item.ItemStack; 23 | import net.minecraft.world.level.EnumGamemode; 24 | import net.minecraft.world.scores.Scoreboard; 25 | import net.minecraft.world.scores.ScoreboardTeam; 26 | import net.minecraft.world.scores.ScoreboardTeamBase; 27 | import org.bukkit.Bukkit; 28 | import org.bukkit.Location; 29 | import org.bukkit.World; 30 | import org.bukkit.craftbukkit.v1_17_R1.CraftServer; 31 | import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; 32 | import org.bukkit.craftbukkit.v1_17_R1.entity.CraftParrot; 33 | import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; 34 | import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; 35 | import org.bukkit.craftbukkit.v1_17_R1.util.CraftChatMessage; 36 | import org.bukkit.entity.Parrot; 37 | import org.bukkit.entity.Player; 38 | import org.bukkit.plugin.Plugin; 39 | import org.bukkit.scheduler.BukkitRunnable; 40 | import org.json.simple.JSONArray; 41 | import org.json.simple.JSONObject; 42 | import org.json.simple.parser.JSONParser; 43 | import org.json.simple.parser.ParseException; 44 | 45 | import java.io.IOException; 46 | import java.lang.reflect.Field; 47 | import java.lang.reflect.Method; 48 | import java.net.HttpURLConnection; 49 | import java.net.URI; 50 | import java.net.URISyntaxException; 51 | import java.net.http.HttpClient; 52 | import java.net.http.HttpRequest; 53 | import java.net.http.HttpResponse; 54 | import java.time.Duration; 55 | import java.util.*; 56 | import java.util.concurrent.CompletableFuture; 57 | import java.util.concurrent.ThreadLocalRandom; 58 | import java.util.concurrent.atomic.AtomicInteger; 59 | import java.util.function.BiConsumer; 60 | import java.util.function.Consumer; 61 | 62 | public class NPC { 63 | 64 | private static AtomicInteger atomicInteger; 65 | private final String hideTeam; 66 | private final int entityID; //unique entityID the server holds to find/modify existing entities. Be careful when assigning values that they do not overlap 67 | private GameProfile profile; 68 | private final NPCMetaData metadata = new NPCMetaData(); 69 | private final Location location; 70 | private Ping ping = Ping.FIVE_BARS; 71 | private Gamemode gamemode = Gamemode.CREATIVE; 72 | private String displayName; 73 | 74 | static { 75 | try { 76 | Field field = Entity.class.getDeclaredField("b"); 77 | field.setAccessible(true); 78 | atomicInteger = (AtomicInteger) field.get(null); 79 | } catch (NoSuchFieldException | IllegalAccessException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | 84 | public NPC(UUID uuid, Location location, String displayName) { 85 | this.entityID = atomicInteger.incrementAndGet(); 86 | 87 | this.profile = new GameProfile(uuid, displayName); 88 | this.location = location; 89 | this.displayName = displayName; 90 | this.hideTeam = "hide-" + Integer.toHexString(ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE)); 91 | } 92 | 93 | public NPC(Location location, String displayName) { 94 | this(UUID.randomUUID(), location, displayName); 95 | } 96 | 97 | public void spawnNPC(Collection players) { 98 | players.forEach(this::spawnNPC); 99 | } 100 | 101 | public void spawnNPC(Player player) { 102 | this.addToTabList(player); 103 | this.sendPacket(player, this.getEntitySpawnPacket()); 104 | this.updateMetadata(player); 105 | } 106 | 107 | public void destroyNPC(Collection players) { 108 | players.forEach(this::destroyNPC); 109 | } 110 | 111 | public void destroyNPC(Player player) { 112 | this.sendPacket(player, this.getPlayerInfoPacket(PlayerInfo.REMOVE_PLAYER)); 113 | this.sendPacket(player, this.getEntityDestroyPacket()); 114 | } 115 | 116 | public void reloadNPC(Collection players) { 117 | players.forEach(this::reloadNPC); 118 | } 119 | 120 | public void reloadNPC(Player player) { 121 | this.destroyNPC(player); 122 | this.spawnNPC(player); 123 | } 124 | 125 | public void teleportNPC(Collection players, Location location, boolean onGround) { 126 | players.forEach(p->this.teleportNPC(p, location, onGround)); 127 | } 128 | 129 | public void teleportNPC(Player player, Location location, boolean onGround) { 130 | this.location.setX(location.getX()); 131 | this.location.setY(location.getY()); 132 | this.location.setZ(location.getZ()); 133 | this.location.setPitch(location.getPitch()); 134 | this.location.setYaw(location.getYaw()); 135 | this.rotateHead(player, location.getPitch(), location.getYaw()); 136 | this.sendPacket(player, this.getEntityTeleportPacket(onGround)); 137 | } 138 | 139 | public void updateMetadata(Collection players) { 140 | players.forEach(this::updateMetadata); 141 | } 142 | 143 | public void updateMetadata(Player player) { 144 | this.sendPacket(player, this.getEntityMetadataPacket()); 145 | } 146 | 147 | public void updateGameMode(Collection players) { 148 | players.forEach(this::updateGameMode); 149 | } 150 | 151 | public void updateGameMode(Player player) { 152 | this.sendPacket(player, this.getPlayerInfoPacket(PlayerInfo.UPDATE_GAME_MODE)); 153 | } 154 | 155 | public void updatePing(Collection players) { 156 | players.forEach(this::updatePing); 157 | } 158 | 159 | public void updatePing(Player player) { 160 | this.sendPacket(player, this.getPlayerInfoPacket(PlayerInfo.UPDATE_LATENCY)); 161 | } 162 | 163 | public void updateTabListName(Collection players) { 164 | players.forEach(this::updateTabListName); 165 | } 166 | 167 | public void updateTabListName(Player player) { 168 | this.sendPacket(player, this.getPlayerInfoPacket(PlayerInfo.UPDATE_DISPLAY_NAME)); 169 | } 170 | 171 | public void removeFromTabList(Collection players) { 172 | players.forEach(this::removeFromTabList); 173 | } 174 | 175 | public void removeFromTabList(Player player) { 176 | this.sendPacket(player, this.getPlayerInfoPacket(PlayerInfo.REMOVE_PLAYER)); 177 | } 178 | 179 | public void addToTabList(Collection players) { 180 | players.forEach(this::addToTabList); 181 | } 182 | 183 | public void addToTabList(Player player) { 184 | this.sendPacket(player, this.getPlayerInfoPacket(PlayerInfo.ADD_PLAYER)); 185 | } 186 | 187 | public void playAnimation(Collection players, Animation animation) { 188 | players.forEach(p->this.playAnimation(p, animation)); 189 | } 190 | 191 | public void playAnimation(Player player, Animation animation) { 192 | this.sendPacket(player, this.getEntityAnimationPacket(animation)); 193 | } 194 | 195 | public void lookAtPlayer(Collection players, Player target) { 196 | players.forEach(p->this.lookAtPlayer(p, target)); 197 | } 198 | 199 | public void lookAtPlayer(Player player, Player target) { 200 | this.lookAtPoint(player, target.getEyeLocation()); 201 | } 202 | 203 | public void lookAtPoint(Collection players, Location location) { 204 | players.forEach(p->this.lookAtPoint(p, location)); 205 | } 206 | 207 | public void lookAtPoint(Player player, Location location) { 208 | Location eyeLocation = this.getEyeLocation(); 209 | float yaw = (float) Math.toDegrees(Math.atan2(location.getZ() - eyeLocation.getZ(), location.getX()-eyeLocation.getX())) - 90; 210 | yaw = (float) (yaw + Math.ceil( -yaw / 360 ) * 360); 211 | 212 | float deltaXZ = (float) Math.sqrt(Math.pow(eyeLocation.getX()-location.getX(), 2) + Math.pow(eyeLocation.getZ()-location.getZ(), 2)); 213 | float pitch = (float) Math.toDegrees(Math.atan2(deltaXZ, location.getY()-eyeLocation.getY())) - 90; 214 | 215 | pitch = (float) (pitch + Math.ceil( -pitch / 360 ) * 360); 216 | 217 | this.rotateHead(player, pitch, yaw); 218 | } 219 | 220 | public void rotateHead(Collection players, float pitch, float yaw) { 221 | players.forEach(p->this.rotateHead(p, pitch, yaw)); 222 | } 223 | 224 | public void rotateHead(Player player, float pitch, float yaw) { 225 | this.location.setPitch(pitch); 226 | this.location.setYaw(yaw); 227 | this.sendPacket(player, this.getEntityLookPacket()); 228 | this.sendPacket(player, this.getEntityHeadRotatePacket()); 229 | } 230 | 231 | public void setTabListName(String name) { 232 | this.displayName = name; 233 | } 234 | 235 | public void setEquipment(Collection players, ItemSlot slot, org.bukkit.inventory.ItemStack itemStack) { 236 | players.forEach(p->this.setEquipment(p, slot, itemStack)); 237 | } 238 | 239 | public void setEquipment(Player player, ItemSlot slot, org.bukkit.inventory.ItemStack itemStack) { 240 | this.sendPacket(player, this.getEntityEquipmentPacket(slot.getSlot(), CraftItemStack.asNMSCopy(itemStack))); 241 | } 242 | 243 | public void setPassenger(Collection players, int... entityIDs) { 244 | players.forEach(p->this.setPassenger(p, entityIDs)); 245 | } 246 | 247 | public void setPassenger(Player player, int... entityIDs) { 248 | this.sendPacket(player, getEntityAttachPacket(entityIDs)); 249 | } 250 | 251 | private void sendPacket(Player player, Packet packet) { 252 | ((CraftPlayer)(player)).getHandle().b.sendPacket(packet); 253 | } 254 | 255 | public void setNameTagVisibility(Collection players, boolean show) { 256 | players.forEach(p->this.setNameTagVisibility(p, show)); 257 | } 258 | 259 | public void setNameTagVisibility(Player player, boolean show) { 260 | ScoreboardTeam team = new ScoreboardTeam(new Scoreboard(), this.hideTeam); 261 | if(show) { 262 | PacketPlayOutScoreboardTeam leavePacket = PacketPlayOutScoreboardTeam.a(team, this.profile.getName(), PacketPlayOutScoreboardTeam.a.b); 263 | this.sendPacket(player, leavePacket); 264 | } else { 265 | team.setNameTagVisibility(ScoreboardTeamBase.EnumNameTagVisibility.b); 266 | PacketPlayOutScoreboardTeam createPacket = PacketPlayOutScoreboardTeam.a(team, true); 267 | PacketPlayOutScoreboardTeam joinPacket = PacketPlayOutScoreboardTeam.a(team, this.profile.getName(), PacketPlayOutScoreboardTeam.a.a); 268 | this.sendPacket(player, createPacket); 269 | this.sendPacket(player, joinPacket); 270 | } 271 | } 272 | 273 | private PacketPlayOutMount getEntityAttachPacket(int[] entityIDs) { 274 | return this.createDataSerializer(data->{ 275 | data.d(this.entityID); 276 | data.a(entityIDs); 277 | return new PacketPlayOutMount(data); 278 | }); 279 | } 280 | 281 | private PacketPlayOutEntity.PacketPlayOutEntityLook getEntityLookPacket() { 282 | return new PacketPlayOutEntity.PacketPlayOutEntityLook(this.entityID, (byte)((int)(this.location.getYaw() * 256.0F / 360.0F)), (byte)((int)(this.location.getPitch() * 256.0F / 360.0F)), true); 283 | } 284 | 285 | private PacketPlayOutEntityTeleport getEntityTeleportPacket(boolean onGround) { 286 | return this.createDataSerializer(data->{ 287 | data.d(this.entityID); 288 | data.writeDouble(this.location.getX()); 289 | data.writeDouble(this.location.getY()); 290 | data.writeDouble(this.location.getZ()); 291 | data.writeByte((byte)((int)(this.location.getYaw() * 256.0F / 360.0F))); 292 | data.writeByte((byte)((int)(this.location.getPitch() * 256.0F / 360.0F))); 293 | data.writeBoolean(onGround); 294 | return new PacketPlayOutEntityTeleport(data); 295 | }); 296 | } 297 | 298 | private PacketPlayOutEntityHeadRotation getEntityHeadRotatePacket() { 299 | return this.createDataSerializer(data->{ 300 | data.d(this.entityID); 301 | data.writeByte((byte)((int)(this.location.getYaw() * 256.0F / 360.0F))); 302 | return new PacketPlayOutEntityHeadRotation(data); 303 | }); 304 | } 305 | 306 | private PacketPlayOutEntityEquipment getEntityEquipmentPacket(EnumItemSlot slot, ItemStack itemStack) { 307 | return new PacketPlayOutEntityEquipment(this.entityID, Arrays.asList(new Pair(slot, itemStack))); 308 | } 309 | 310 | private PacketPlayOutAnimation getEntityAnimationPacket(Animation animation) { 311 | return this.createDataSerializer((data)->{ 312 | data.d(this.entityID); 313 | data.writeByte((byte)animation.getType()); 314 | return new PacketPlayOutAnimation(data); 315 | }); 316 | } 317 | 318 | private PacketPlayOutEntityDestroy getEntityDestroyPacket(){ 319 | return new PacketPlayOutEntityDestroy(this.entityID); 320 | } 321 | 322 | private PacketPlayOutEntityMetadata getEntityMetadataPacket() { 323 | return this.createDataSerializer((data)->{ 324 | data.d(this.entityID); 325 | DataWatcher.a(this.metadata.getList(), data); 326 | return new PacketPlayOutEntityMetadata(data); 327 | }); 328 | } 329 | 330 | private PacketPlayOutNamedEntitySpawn getEntitySpawnPacket() { 331 | return this.createDataSerializer((data)->{ 332 | data.d(this.entityID); 333 | data.a(this.profile.getId()); 334 | data.writeDouble(this.location.getX()); 335 | data.writeDouble(this.location.getY()); 336 | data.writeDouble(this.location.getZ()); 337 | data.writeByte((byte)((int)(this.location.getYaw() * 256.0F / 360.0F))); 338 | data.writeByte((byte)((int)(this.location.getPitch() * 256.0F / 360.0F))); 339 | return new PacketPlayOutNamedEntitySpawn(data); 340 | }); 341 | } 342 | 343 | public PacketPlayOutPlayerInfo getPlayerInfoPacket(PlayerInfo playerInfo) { 344 | return this.createDataSerializer((data)->{ 345 | PacketPlayOutPlayerInfo.EnumPlayerInfoAction action = playerInfo.getPlayerInfo(); 346 | PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData = new PacketPlayOutPlayerInfo.PlayerInfoData(this.profile, this.ping.getMilliseconds(), this.gamemode.getGamemode(), CraftChatMessage.fromString(this.displayName)[0]); 347 | List list = Arrays.asList(playerInfoData); 348 | data.a(playerInfo.getPlayerInfo()); 349 | Method method = playerInfo.getPlayerInfo().getDeclaringClass().getDeclaredMethod("a", PacketDataSerializer.class, PacketPlayOutPlayerInfo.PlayerInfoData.class); 350 | method.setAccessible(true); 351 | data.a(list, (a,b)->this.unsafe(()->method.invoke(action, a, b))); 352 | return new PacketPlayOutPlayerInfo(data); 353 | }); 354 | } 355 | 356 | public int getEntityID() { 357 | return entityID; 358 | } 359 | 360 | public GameProfile getProfile() { 361 | return profile; 362 | } 363 | 364 | public NPCMetaData getMetadata() { 365 | return metadata; 366 | } 367 | 368 | public Location getLocation() { 369 | return location; 370 | } 371 | 372 | public Location getEyeLocation() { 373 | return this.location.clone().add(0, EntityTypes.bi.m().b * 0.85F, 0); 374 | } 375 | 376 | public Ping getPing() { 377 | return ping; 378 | } 379 | 380 | public Gamemode getGameMode() { 381 | return gamemode; 382 | } 383 | 384 | public String getDisplayName() { 385 | return displayName; 386 | } 387 | 388 | public void setSkin(SkinTextures skinTextures) { 389 | this.profile.getProperties().put("textures", new Property("textures", skinTextures.getTexture(), skinTextures.getSignature())); 390 | } 391 | 392 | public void setASyncSkinByUsername(Plugin plugin, Collection players, String username) { 393 | this.setASyncSkinByUsername(plugin, players, username, null); 394 | } 395 | 396 | public void setASyncSkinByUsername(Plugin plugin, Player player, String username) { 397 | this.setASyncSkinByUsername(plugin, player, username, null); 398 | } 399 | 400 | public void setASyncSkinByUUID(Plugin plugin, Collection players, UUID uuid) { 401 | this.setASyncSkinByUUID(plugin, players, uuid, null); 402 | } 403 | 404 | public void setASyncSkinByUUID(Plugin plugin, Player player, UUID uuid) { 405 | this.setASyncSkinByUUID(plugin, player, uuid, null); 406 | } 407 | 408 | public void setASyncSkinByUsername(Plugin plugin, Player player, String username, BiConsumer callback) { 409 | SkinTextures.getByUsername(plugin, username, (success, skin)->setASyncSkin(success, skin, player, callback)); 410 | } 411 | 412 | public void setASyncSkinByUsername(Plugin plugin, Collection players, String username, BiConsumer callback) { 413 | SkinTextures.getByUsername(plugin, username, (success, skin)->setASyncSkin(success, skin, players, callback)); 414 | } 415 | 416 | public void setASyncSkinByUUID(Plugin plugin, Player player, UUID uuid, BiConsumer callback) { 417 | SkinTextures.getByUUID(plugin, uuid, (success, skin)->setASyncSkin(success, skin, player, callback)); 418 | } 419 | 420 | public void setASyncSkinByUUID(Plugin plugin, Collection players, UUID uuid, BiConsumer callback) { 421 | SkinTextures.getByUUID(plugin, uuid, (success, skin)->setASyncSkin(success, skin, players, callback)); 422 | } 423 | 424 | private void setASyncSkin(boolean success, SkinTextures skin, Collection players, BiConsumer callback) { 425 | if(success) { 426 | this.setSkin(skin); 427 | this.reloadNPC(players); 428 | } 429 | callback.accept(success, this); 430 | } 431 | 432 | private void setASyncSkin(boolean success, SkinTextures skin, Player player, BiConsumer callback) { 433 | this.setASyncSkin(success, skin, Arrays.asList(player), callback); 434 | } 435 | 436 | public void setPing(Ping ping) { 437 | this.ping = ping; 438 | } 439 | 440 | public void setGameMode(Gamemode gamemode) { 441 | this.gamemode = gamemode; 442 | } 443 | 444 | public void setDisplayName(String displayName) { 445 | this.displayName = displayName; 446 | GameProfile swapProfile = new GameProfile(this.profile.getId(), displayName); 447 | swapProfile.getProperties().putAll(this.profile.getProperties()); 448 | this.profile = swapProfile; 449 | } 450 | 451 | public class NPCMetaData { 452 | 453 | //Entity metadata 454 | private final DataWatcher.Item entityState = a(0, (byte)EntityState.createMask(EntityState.DEFAULT)); 455 | private final DataWatcher.Item airTicks = a(1, 300); 456 | private final DataWatcher.Item> customName = a(2, Optional.empty(), DataWatcherRegistry.f); 457 | private final DataWatcher.Item customNameVisible = a(3, false); 458 | private final DataWatcher.Item silent = a(4, false); 459 | private final DataWatcher.Item gravity = a(5, false); 460 | private final DataWatcher.Item pose = a(6, Pose.STANDING.getPose()); 461 | private final DataWatcher.Item frozenTicks = a(7, 0); //shaking at tick 140 462 | 463 | //LivingEntity metadata 464 | private final DataWatcher.Item handStatus = a(8, (byte)HandStatus.createMask(HandStatus.MAIN_HAND)); 465 | private final DataWatcher.Item health = a(9, 1.0F); 466 | private final DataWatcher.Item potionEffectColor = a(10, 0); 467 | private final DataWatcher.Item isPotionEffectAmbient = a(11, false); 468 | private final DataWatcher.Item arrowsInEntity = a(12, 0); 469 | private final DataWatcher.Item absorptionHealth = a(13, 0); 470 | private final DataWatcher.Item> sleepingBedLocation = a(14, Optional.empty(), DataWatcherRegistry.m); 471 | 472 | //Player metadata 473 | private final DataWatcher.Item additionalHearts = a(15, 0.0F); 474 | private final DataWatcher.Item score = a(16, 0); 475 | private final DataWatcher.Item skinStatus = a(17, (byte)SkinStatus.createMask(SkinStatus.ALL_ENABLED)); 476 | private final DataWatcher.Item hand = a(18, (byte)Hand.RIGHT.getType()); 477 | private final DataWatcher.Item leftShoulder = a(19, new NBTTagCompound()); 478 | private final DataWatcher.Item rightShoulder = a(20, new NBTTagCompound()); 479 | 480 | private final List> list; 481 | 482 | public NPCMetaData() { 483 | this.list = new ArrayList<>(Arrays.asList( 484 | this.entityState, 485 | this.airTicks, 486 | this.customName, 487 | this.customNameVisible, 488 | this.silent, 489 | this.gravity, 490 | this.pose, 491 | this.frozenTicks, 492 | this.handStatus, 493 | this.health, 494 | this.potionEffectColor, 495 | this.isPotionEffectAmbient, 496 | this.arrowsInEntity, 497 | this.absorptionHealth, 498 | this.sleepingBedLocation, 499 | this.additionalHearts, 500 | this.score, 501 | this.skinStatus, 502 | this.hand, 503 | this.leftShoulder, 504 | this.rightShoulder)); 505 | } 506 | 507 | public EntityState[] getEntityState() { 508 | return EntityState.fromMask(entityState.b()); 509 | } 510 | 511 | public Integer getAirTicks() { 512 | return airTicks.b(); 513 | } 514 | 515 | public Optional getCustomName() { 516 | return customName.b(); 517 | } 518 | 519 | public Boolean isCustomNameVisible() { 520 | return customNameVisible.b(); 521 | } 522 | 523 | public Boolean isSilent() { 524 | return silent.b(); 525 | } 526 | 527 | public Boolean hasGravity() { 528 | return gravity.b(); 529 | } 530 | 531 | public Pose getPose() { 532 | return Pose.fromPose(pose.b()); 533 | } 534 | 535 | public Integer getFrozenTicks() { 536 | return frozenTicks.b(); 537 | } 538 | 539 | public HandStatus[] getHandStatus() { 540 | return HandStatus.fromMask(handStatus.b()); 541 | } 542 | 543 | public Float getHealth() { 544 | return health.b(); 545 | } 546 | 547 | public Integer getPotionEffectColor() { 548 | return potionEffectColor.b(); 549 | } 550 | 551 | public Boolean isPotionEffectAmbient() { 552 | return isPotionEffectAmbient.b(); 553 | } 554 | 555 | public Integer getArrowsInEntity() { 556 | return arrowsInEntity.b(); 557 | } 558 | 559 | public Integer getAbsorptionHealth() { 560 | return absorptionHealth.b(); 561 | } 562 | 563 | public Optional getSleepingBedLocation() { 564 | return sleepingBedLocation.b(); 565 | } 566 | 567 | public Float getAdditionalHearts() { 568 | return additionalHearts.b(); 569 | } 570 | 571 | public Integer getScore() { 572 | return score.b(); 573 | } 574 | 575 | public SkinStatus[] getSkinStatus() { 576 | return SkinStatus.fromMask(skinStatus.b()); 577 | } 578 | 579 | public Hand getHand() { 580 | return Hand.fromByte(hand.b()); 581 | } 582 | 583 | public NBTTagCompound getLeftShoulder() { 584 | return leftShoulder.b(); 585 | } 586 | 587 | public NBTTagCompound getRightShoulder() { 588 | return rightShoulder.b(); 589 | } 590 | 591 | public List> getList() { 592 | return list; 593 | } 594 | 595 | public void setEntityState(EntityState... entityState) { 596 | this.entityState.a((byte) EntityState.createMask(entityState)); 597 | } 598 | 599 | public void setAirTicks(Integer airTicks) { 600 | this.airTicks.a(airTicks); 601 | } 602 | 603 | public void setCustomName(String customName) { 604 | this.customName.a(Optional.ofNullable(IChatBaseComponent.a(customName))); 605 | } 606 | 607 | public void setCustomNameVisible(Boolean customNameVisible) { 608 | this.customNameVisible.a(customNameVisible); 609 | } 610 | 611 | public void setSilent(Boolean silent) { 612 | this.silent.a(silent); 613 | } 614 | 615 | public void setGravity(Boolean gravity) { 616 | this.gravity.a(gravity); 617 | } 618 | 619 | public void setPose(Pose pose) { 620 | this.pose.a(pose.getPose()); 621 | } 622 | 623 | public void setFrozenTicks(Integer frozenTicks) { 624 | this.frozenTicks.a(frozenTicks); 625 | } 626 | 627 | public void setShaking() { 628 | this.setFrozenTicks(140); 629 | } 630 | 631 | public void setHandStatus(HandStatus handStatus) { 632 | this.handStatus.a((byte) HandStatus.createMask(handStatus)); 633 | } 634 | 635 | public void setHealth(Float health) { 636 | this.health.a(health); 637 | } 638 | 639 | public void setPotionEffectColor(Integer potionEffectColor) { 640 | this.potionEffectColor.a(potionEffectColor); 641 | } 642 | 643 | public void setIsPotionEffectAmbient(Boolean isPotionEffectAmbient) { 644 | this.isPotionEffectAmbient.a(isPotionEffectAmbient); 645 | } 646 | 647 | public void setArrowsInEntity(Integer arrowsInEntity) { 648 | this.arrowsInEntity.a(arrowsInEntity); 649 | } 650 | 651 | public void setAbsorptionHealth(Integer absorptionHealth) { 652 | this.absorptionHealth.a(absorptionHealth); 653 | } 654 | 655 | public void setSleepingBedLocation(BlockPosition sleepingBedLocation) { 656 | this.sleepingBedLocation.a(Optional.ofNullable(sleepingBedLocation)); 657 | } 658 | 659 | public void setAdditionalHearts(Float additionalHearts) { 660 | this.additionalHearts.a(additionalHearts); 661 | } 662 | 663 | public void setScore(Integer score) { 664 | this.score.a(score); 665 | } 666 | 667 | public void setSkinStatus(SkinStatus... skinStatus) { 668 | this.skinStatus.a((byte) SkinStatus.createMask(skinStatus)); 669 | } 670 | 671 | public void setHand(Hand hand) { 672 | this.hand.a((byte) hand.getType()); 673 | } 674 | 675 | public NBTTagCompound createParrot(Consumer callback, World world) { 676 | EntityParrot entityParrot = new EntityParrot(EntityTypes.al, ((CraftWorld)world).getHandle()); 677 | CraftParrot parrot = new CraftParrot((CraftServer) Bukkit.getServer(), entityParrot); 678 | callback.accept(parrot); 679 | NBTTagCompound nbtTagCompound = new NBTTagCompound(); 680 | entityParrot.d(nbtTagCompound); 681 | return nbtTagCompound; 682 | } 683 | 684 | public void setParrotLeftShoulder(Consumer callback, World world) { 685 | this.setLeftShoulder(this.createParrot(callback, world)); 686 | } 687 | 688 | public void setParrotRightShoulder(Consumer callback, World world) { 689 | this.setRightShoulder(this.createParrot(callback, world)); 690 | } 691 | 692 | public void setLeftShoulder(NBTTagCompound leftShoulder) { 693 | this.leftShoulder.a(leftShoulder); 694 | } 695 | 696 | public void setRightShoulder(NBTTagCompound rightShoulder) { 697 | this.rightShoulder.a(rightShoulder); 698 | } 699 | 700 | private static DataWatcher.Item a(int index, T value) { 701 | DataWatcherSerializer serializer = null; 702 | 703 | if(value instanceof Byte) { 704 | serializer = DataWatcherRegistry.a; 705 | } else if(value instanceof Float) { 706 | serializer = DataWatcherRegistry.c; 707 | } else if(value instanceof Integer) { 708 | serializer = DataWatcherRegistry.b; 709 | } else if(value instanceof String) { 710 | serializer = DataWatcherRegistry.d; 711 | } else if(value instanceof Boolean) { 712 | serializer = DataWatcherRegistry.i; 713 | } else if(value instanceof NBTTagCompound) { 714 | serializer = DataWatcherRegistry.p; 715 | } else if(value instanceof BlockPosition) { 716 | serializer = DataWatcherRegistry.m; 717 | } else if(value instanceof IChatBaseComponent) { 718 | serializer = DataWatcherRegistry.e; 719 | } else if(value instanceof EntityPose) { 720 | serializer = DataWatcherRegistry.s; 721 | } 722 | return a(index, value, (DataWatcherSerializer)serializer); 723 | } 724 | 725 | private static DataWatcher.Item a(int index, T value, DataWatcherSerializer serializer) { 726 | return new DataWatcher.Item(new DataWatcherObject(index, serializer), value); 727 | } 728 | 729 | } 730 | 731 | public enum ItemSlot { 732 | 733 | MAIN_HAND(EnumItemSlot.a), 734 | OFF_HAND(EnumItemSlot.b), 735 | BOOTS(EnumItemSlot.c), 736 | LEGGINGS(EnumItemSlot.d), 737 | CHESTPLATE(EnumItemSlot.e), 738 | HELMET(EnumItemSlot.f); 739 | 740 | private final EnumItemSlot slot; 741 | 742 | ItemSlot(EnumItemSlot slot) { 743 | this.slot = slot; 744 | } 745 | 746 | public EnumItemSlot getSlot() { 747 | return slot; 748 | } 749 | } 750 | 751 | public enum Hand { 752 | 753 | LEFT(0), 754 | RIGHT(1); 755 | 756 | private final int type; 757 | 758 | Hand(int type) { 759 | this.type = type; 760 | } 761 | 762 | public int getType() { 763 | return type; 764 | } 765 | 766 | public static Hand fromByte(byte type) { 767 | for(Hand hand : values()) { 768 | if(type == hand.type) { 769 | return hand; 770 | } 771 | } 772 | return null; 773 | } 774 | } 775 | 776 | public enum Pose { 777 | 778 | STANDING(EntityPose.a), 779 | FALL_FLYING(EntityPose.b), 780 | SLEEPING(EntityPose.c), 781 | SWIMMING(EntityPose.d), 782 | SPIN_ATTACK(EntityPose.e), 783 | CROUCHING(EntityPose.f), 784 | LONG_JUMPING(EntityPose.g), 785 | DYING(EntityPose.h); 786 | 787 | private final EntityPose pose; 788 | 789 | Pose(EntityPose pose) { 790 | this.pose = pose; 791 | } 792 | 793 | public EntityPose getPose() { 794 | return pose; 795 | } 796 | 797 | public static Pose fromPose(EntityPose entityPose) { 798 | for(Pose pose : values()) { 799 | if(entityPose == pose.pose) { 800 | return pose; 801 | } 802 | } 803 | return null; 804 | } 805 | } 806 | 807 | public enum SkinStatus { 808 | 809 | CAPE_ENABLED(0x01), 810 | JACKET_ENABLED(0x02), 811 | LEFT_SLEEVE_ENABLED(0x04), 812 | RIGHT_SLEEVE_ENABLED(0x08), 813 | LEFT_PANTS_LEG_ENABLED(0x10), 814 | RIGHT_PANTS_LEG_ENABLED(0x20), 815 | HAT_ENABLED(0x40), 816 | @Deprecated UNUSED(0x80), 817 | ALL_ENABLED(0xFF); 818 | 819 | private final int mask; 820 | 821 | SkinStatus(int mask) { 822 | this.mask = mask; 823 | } 824 | 825 | public int getMask() { 826 | return mask; 827 | } 828 | 829 | public static int createMask(SkinStatus... skinStatuses) { 830 | int mask = 0; 831 | for(SkinStatus handStatus : skinStatuses) { 832 | mask |= handStatus.mask; 833 | } 834 | return mask; 835 | } 836 | 837 | public static SkinStatus[] fromMask(int mask) { 838 | List list = new ArrayList<>(); 839 | for(SkinStatus skinStatus : values()) { 840 | if((skinStatus.mask & mask) == skinStatus.mask) { 841 | list.add(skinStatus); 842 | } 843 | } 844 | return list.toArray(new SkinStatus[list.size()]); 845 | } 846 | } 847 | 848 | public enum HandStatus { 849 | 850 | MAIN_HAND(0x00), 851 | HAND_ACTIVE(0x01), 852 | OFF_HAND(0x02), 853 | RIPTIDE_SPIN_ATTACK(0x04), 854 | ALL(0x07); 855 | 856 | private final int mask; 857 | 858 | HandStatus(int mask) { 859 | this.mask = mask; 860 | } 861 | 862 | public int getMask() { 863 | return mask; 864 | } 865 | 866 | public static int createMask(HandStatus... handStatuses) { 867 | int mask = 0; 868 | for(HandStatus handStatus : handStatuses) { 869 | mask |= handStatus.mask; 870 | } 871 | return mask; 872 | } 873 | 874 | public static HandStatus[] fromMask(int mask) { 875 | List list = new ArrayList<>(); 876 | for(HandStatus handStatus : values()) { 877 | if((handStatus.mask & mask) == handStatus.mask) { 878 | list.add(handStatus); 879 | } 880 | } 881 | return list.toArray(new HandStatus[list.size()]); 882 | } 883 | } 884 | 885 | public enum Animation { 886 | 887 | SWING_MAIN_HAND(0), 888 | TAKE_DAMAGE(1), 889 | LEAVE_BED(2), 890 | SWING_OFFHAND(3), 891 | CRITICAL_EFFECT(4), 892 | MAGIC_CRITICAL_EFFECT(5); 893 | 894 | private final int type; 895 | 896 | Animation(int type) { 897 | this.type = type; 898 | } 899 | 900 | public int getType() { 901 | return type; 902 | } 903 | 904 | } 905 | 906 | public enum EntityState { 907 | 908 | DEFAULT(0x00), 909 | ON_FIRE(0x01), 910 | @Deprecated CROUCHING(0x02), 911 | @Deprecated UNUSED(0x04), 912 | SPRINTING(0x08), 913 | SWIMMING(0x10), 914 | INVISIBLE(0x20), 915 | GLOWING(0x40), 916 | FLYING(0x80), 917 | ALL(0xFF); 918 | 919 | private final int mask; 920 | 921 | EntityState(int mask) { 922 | this.mask = mask; 923 | } 924 | 925 | public int getMask() { 926 | return mask; 927 | } 928 | 929 | public static int createMask(EntityState... entityStates) { 930 | int mask = 0; 931 | for(EntityState entityState : entityStates) { 932 | mask |= entityState.mask; 933 | } 934 | return mask; 935 | } 936 | 937 | public static EntityState[] fromMask(int mask) { 938 | List list = new ArrayList<>(); 939 | for(EntityState entityState : values()) { 940 | if((entityState.mask & mask) == entityState.mask) { 941 | list.add(entityState); 942 | } 943 | } 944 | return list.toArray(new EntityState[list.size()]); 945 | } 946 | } 947 | 948 | public enum Gamemode { 949 | 950 | SURVIVAL(EnumGamemode.a), 951 | CREATIVE(EnumGamemode.b), 952 | ADVENTURE(EnumGamemode.c), 953 | SPECTATOR(EnumGamemode.d); 954 | 955 | private final EnumGamemode gamemode; 956 | 957 | Gamemode(EnumGamemode gamemode) { 958 | this.gamemode = gamemode; 959 | } 960 | 961 | public EnumGamemode getGamemode() { 962 | return gamemode; 963 | } 964 | } 965 | 966 | public enum PlayerInfo { 967 | 968 | ADD_PLAYER(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a), 969 | UPDATE_GAME_MODE(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.b), 970 | UPDATE_LATENCY(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.c), 971 | UPDATE_DISPLAY_NAME(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.d), 972 | REMOVE_PLAYER(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e); 973 | 974 | private final PacketPlayOutPlayerInfo.EnumPlayerInfoAction playerInfo; 975 | 976 | PlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction a) { 977 | this.playerInfo = a; 978 | } 979 | 980 | public PacketPlayOutPlayerInfo.EnumPlayerInfoAction getPlayerInfo() { 981 | return playerInfo; 982 | } 983 | } 984 | 985 | public enum Ping { 986 | 987 | NO_CONNECTION(-1), 988 | ONE_BAR(1000), 989 | TWO_BARS(999), 990 | THREE_BARS(599), 991 | FOUR_BARS(299), 992 | FIVE_BARS(149); 993 | 994 | private final int milliseconds; 995 | 996 | Ping(int milliseconds) { 997 | this.milliseconds = milliseconds; 998 | } 999 | 1000 | public int getMilliseconds() { 1001 | return milliseconds; 1002 | } 1003 | } 1004 | 1005 | public static class SkinTextures { 1006 | 1007 | private static final String TEXTURE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/%s?unsigned=false"; 1008 | private static final String UUID_URL = "https://api.mojang.com/profiles/minecraft"; 1009 | 1010 | private final String texture; 1011 | private final String signature; 1012 | 1013 | public SkinTextures(String textures, String signature) { 1014 | this.texture = textures; 1015 | this.signature = signature; 1016 | } 1017 | 1018 | public String getTexture() { 1019 | return texture; 1020 | } 1021 | 1022 | public String getSignature() { 1023 | return signature; 1024 | } 1025 | 1026 | public static void getByUsername(Plugin plugin, String username, BiConsumer callback) { 1027 | new BukkitRunnable(){ 1028 | @Override 1029 | public void run() { 1030 | JSONArray array = new JSONArray(); 1031 | array.add(username); 1032 | UUID result = null; 1033 | 1034 | try { 1035 | HttpRequest request = HttpRequest.newBuilder(new URI(UUID_URL)) 1036 | .setHeader("Content-Type", "application/json") 1037 | .POST(HttpRequest.BodyPublishers.ofString(array.toString())) 1038 | .timeout(Duration.ofSeconds(5)) 1039 | .build(); 1040 | 1041 | HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); 1042 | if(response.statusCode() == HttpURLConnection.HTTP_OK) { 1043 | JSONArray uuidArray = (JSONArray) new JSONParser().parse(response.body()); 1044 | if(uuidArray.size() > 0) { 1045 | String uuidStr = (String) ((JSONObject) uuidArray.get(0)).get("id"); 1046 | result = UUID.fromString(uuidStr.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); 1047 | } 1048 | } 1049 | 1050 | } catch (URISyntaxException | InterruptedException | IOException | ParseException e) { 1051 | e.printStackTrace(); 1052 | } 1053 | 1054 | if(result == null) { 1055 | new BukkitRunnable(){ 1056 | @Override 1057 | public void run() { 1058 | callback.accept(false, null); 1059 | } 1060 | }.runTask(plugin); 1061 | } else { 1062 | SkinTextures.getByUUID(plugin, result, callback); 1063 | } 1064 | } 1065 | }.runTaskAsynchronously(plugin); 1066 | } 1067 | 1068 | public static void getByUUID(Plugin plugin, UUID uuid, BiConsumer callback) { 1069 | new BukkitRunnable(){ 1070 | @Override 1071 | public void run() { 1072 | SkinTextures result = null; 1073 | 1074 | try { 1075 | HttpRequest request = HttpRequest.newBuilder(new URI(String.format(TEXTURE_URL, uuid.toString().replace("-", "")))) 1076 | .timeout(Duration.ofSeconds(5)) 1077 | .GET() 1078 | .build(); 1079 | 1080 | HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); 1081 | 1082 | if(response.statusCode() == HttpURLConnection.HTTP_OK) { 1083 | JSONArray properties = (JSONArray) ((JSONObject) new JSONParser().parse(response.body())).get("properties"); 1084 | for(int t = 0; t < properties.size(); t++) { 1085 | JSONObject obj = (JSONObject) properties.get(t); 1086 | if(obj.containsKey("name") && obj.get("name").equals("textures")) { 1087 | result = new SkinTextures((String)obj.get("value"), (String)obj.get("signature")); 1088 | } 1089 | } 1090 | } 1091 | 1092 | } catch (URISyntaxException | InterruptedException | IOException | ParseException e) { 1093 | e.printStackTrace(); 1094 | } 1095 | 1096 | final SkinTextures skinTextures = result; 1097 | 1098 | new BukkitRunnable(){ 1099 | @Override 1100 | public void run() { 1101 | if(skinTextures == null) { 1102 | callback.accept(false, null); 1103 | } else { 1104 | callback.accept(true, skinTextures); 1105 | } 1106 | } 1107 | }.runTask(plugin); 1108 | } 1109 | }.runTaskAsynchronously(plugin); 1110 | } 1111 | 1112 | public static CompletableFuture getByUsername(String username) { 1113 | return CompletableFuture.supplyAsync(()->{ 1114 | JSONArray array = new JSONArray(); 1115 | array.add(username); 1116 | UUID result = null; 1117 | try { 1118 | HttpRequest request = HttpRequest.newBuilder(new URI(UUID_URL)) 1119 | .setHeader("Content-Type", "application/json") 1120 | .POST(HttpRequest.BodyPublishers.ofString(array.toString())) 1121 | .timeout(Duration.ofSeconds(5)) 1122 | .build(); 1123 | 1124 | HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); 1125 | if (response.statusCode() == HttpURLConnection.HTTP_OK) { 1126 | JSONArray uuidArray = (JSONArray) new JSONParser().parse(response.body()); 1127 | if (uuidArray.size() > 0) { 1128 | String uuidStr = (String) ((JSONObject) uuidArray.get(0)).get("id"); 1129 | result = UUID.fromString(uuidStr.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); 1130 | } 1131 | } 1132 | } catch (URISyntaxException | InterruptedException | IOException | ParseException e) { 1133 | e.printStackTrace(); 1134 | } 1135 | if(result == null) 1136 | throw new IllegalArgumentException("SkinTextures not found by username"); 1137 | else { 1138 | return getByUUID(result).join(); 1139 | } 1140 | }); 1141 | } 1142 | 1143 | public static CompletableFuture getByUUID(UUID uuid) { 1144 | return CompletableFuture.supplyAsync(()->{ 1145 | SkinTextures result = null; 1146 | try { 1147 | HttpRequest request = HttpRequest.newBuilder(new URI(String.format(TEXTURE_URL, uuid.toString().replace("-", "")))) 1148 | .timeout(Duration.ofSeconds(5)) 1149 | .GET().build(); 1150 | 1151 | HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); 1152 | if (response.statusCode() == HttpURLConnection.HTTP_OK) { 1153 | JSONArray properties = (JSONArray) ((JSONObject) new JSONParser().parse(response.body())).get("properties"); 1154 | for (int t = 0; t < properties.size(); t++) { 1155 | JSONObject obj = (JSONObject) properties.get(t); 1156 | if (obj.containsKey("name") && obj.get("name").equals("textures")) { 1157 | result = new SkinTextures((String) obj.get("value"), (String) obj.get("signature")); 1158 | } 1159 | } 1160 | } 1161 | } catch (URISyntaxException | InterruptedException | IOException | ParseException e) { 1162 | e.printStackTrace(); 1163 | } 1164 | if(result==null) 1165 | throw new IllegalArgumentException("SkinTextures not found by uuid"); 1166 | else 1167 | return result; 1168 | }); 1169 | } 1170 | } 1171 | 1172 | private void unsafe(UnsafeRunnable run) { 1173 | try { 1174 | run.run(); 1175 | } catch (Exception e) { 1176 | e.printStackTrace(); 1177 | } 1178 | } 1179 | 1180 | private T createDataSerializer(UnsafeFunction callback) { 1181 | PacketDataSerializer data = new PacketDataSerializer(Unpooled.buffer()); 1182 | T result = null; 1183 | try { 1184 | result = callback.apply(data); 1185 | } catch (Exception e) { 1186 | e.printStackTrace(); 1187 | } finally { 1188 | data.release(); 1189 | } 1190 | return result; 1191 | } 1192 | 1193 | @FunctionalInterface 1194 | private interface UnsafeRunnable { 1195 | void run() throws Exception; 1196 | } 1197 | 1198 | @FunctionalInterface 1199 | private interface UnsafeFunction { 1200 | T apply(K k) throws Exception; 1201 | } 1202 | } 1203 | --------------------------------------------------------------------------------