├── .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 | [](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 |
--------------------------------------------------------------------------------