├── .github
├── FUNDING.yml
├── stale.yml
└── workflows
│ └── maven.yml
├── .gitignore
├── .travis.yml
├── ISSUE_TEMPLATE.md
├── LICENSE
├── jitpack.yml
├── pom.xml
├── renovate.json
├── resources
└── plugin.yml
├── settings.xml
└── src
└── org
└── inventivetalent
└── glow
├── GlowAPI.java
├── GlowData.java
└── GlowPlugin.java
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: InventivetalentDev
2 | patreon: inventivetalent
3 | custom: ["https://www.paypal.me/inventivetalent", "https://donation.inventivetalent.org"]
4 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/InventivetalentDev/GlowAPI/c9d3f4d06b3db74e9ff7d68a96bfb21cb80c6707/.github/stale.yml
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Build with Maven
2 |
3 | on: push
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v3
11 | - name: Set up JDK 17
12 | uses: actions/setup-java@v3
13 | with:
14 | java-version: 17
15 | distribution: 'temurin'
16 | cache: maven
17 | - name: Build with Maven
18 | run: mvn install
19 | - name: Upload Artifact to GitHub Action
20 | uses: actions/upload-artifact@v3
21 | with:
22 | name: GlowAPI
23 | path: target/GlowAPI*.jar
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | bin/
3 | .classpath
4 | .project
5 | .settings/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - openjdk8
4 |
5 | script: "mvn deploy --settings settings.xml"
6 | env:
7 | global:
8 | - secure: "X+kVpmQ1mPIviUYUtmn9NsSMYr4ysZUoJ5co3oAM6aE/DbIhbuTsS4mN2SmTIHf6+olvJGr8e4NRvrRgzixCs7ZpraflYC1XHjkxx5fP+F1IA8sIOy+zwTQwiez+v6wrrnqP7+9wJmQpF2qV6bYqC0Fzcl8riBNXzAJ2wFsFujUQ8kdh2+vBDHK4n7oW3YLuqISfSLB5uboOYvQ4RRNT/hyfutdan49/HJIcZn1iH3LbsCfnNtvrja6jLFOloa7D+WNPF3/sQSqLgt6/FrNdOBM+6wvot3D/nsYG9mono56iI2PcUjq6AirKfnpnR1XFcVgYMUjDwzaMtepSY0+H4jVbcGJsfISaumDJh/+q/C3MvsiDluxVj9e7xB4vn4ti+iJS8NZpR9cizhtMnY3WnONOc/yLHoujxy0i/XKMqk7pKX3WkPA8jAVGCXnlr6OLROjVILVbuMrKG4DLqFqiQBnfRZDCLWK1Ka3ymjNXWcf3nyvQQHBPfvrZk1CrRxIfQOLlUBM4/34XjKtH+XBj6xeY0KL0XYzvO7d6cYCV1fVX/fJm1OWjUPc+YtkQgT6kdKra9BJQDqEi2zQI9xGNGwsqO4lhpiaH2hH1pq21aCWqpTrnEYpceZ9bU2Ut7Ej2/woAEK2ubD9tGLwETSsVbu7shmrg/pdRLRredzRLsRM="
9 | - secure: "P0x0Fwaeb+BvBw54rGQGWRHES5qdfWl9PYqLIgZSwepSm4C0oe6+Np4eoSbf2i/9ioP1B41tGZO/WFf+iZiFeyq9FkktIlvDYE5pe5gwUi1aJ9kmFemSPr5YXeGrM5yktxtmVgqm4DiOw37Zq6ZRXUvsnwtrk7bL3fwlScdMY+y0U4lCk0kxoiUajfnSv3/R3J5QI6kGR2LVfK694uzOI1nk05k3SAc3xbflzePzicavjY0w9/fCjV917WeVhclwXkPxhhmoQNNpdqfuXUj3HXXXK3P+zcg+Ez7eBQqwke9Rg2jzH1tKCXqQF6OaNOvOm2EV+cNDyhMDdg4QiID1XVJ8GXSusOIQV8T+bMLkx6HmH7OJomIIfKbFi3FcjyKCXwHUI8eBClANt9BGmIWM8OXQvIdHKDD92yz5IdQJzXVP+zLDj32hE4DeCsRnDYGBWzKeujV/LUKUBeeYdeMbefo4S0M31S7VAleQP0qYgIA3CZmWvHCBcRLGPQmehYRN8r46rV3rGL6nTqqFBz8Xod1JUxYRBKImTA8JOTVfv8FLduYZ4fJv6ksG1PMJoBx2FcPTKTYgWfSkDmaBm9FAkpvp+VLD2WnlsmC7M3OXvf15bWhNWSIwJ1kMhfq62GVZG5qqo0GfiaDpFOVdWiiuHJCteraB/qePzk2ECEX9VzI="
10 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## What steps will reproduce the problem?
2 | 1.
3 | 2.
4 | 3.
5 |
6 | ## What were you expecting to happen? What happened instead?
7 |
8 | ## What version of the plugin are you using? *Type /version <Plugin Name>*
9 |
10 | ## What Spigot version are you using? *Type /version*
11 |
12 | ## What plugins ae you using? *Type /plugins*
13 |
14 | ## Do you have an error log? Use [pastebin.com](http://pastebin.com). *If you're not sure, upload your whole server log*
15 |
16 | ## Did your client crash? *Upload errors in .minecraft/logs/latest.log as well*
17 |
18 | ## Additional information? *(Are you using Bungeecord? Did it work in previous versions? etc.)*
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Haylee Schäfer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk17
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.inventivetalent
8 | glowapi
9 | 1.5.7-SNAPSHOT
10 | GlowAPI
11 |
12 |
13 | 11
14 | 11
15 |
16 |
17 | GlowAPI_v${project.version}
18 | src
19 |
20 |
21 | src
22 |
23 | **/*.java
24 |
25 |
26 |
27 | resources
28 | true
29 |
30 | plugin.yml
31 | config.yml
32 |
33 |
34 |
35 |
36 |
37 | maven-compiler-plugin
38 | 3.8.1
39 |
40 | 16
41 | 16
42 |
43 |
44 |
45 | org.apache.maven.plugins
46 | maven-shade-plugin
47 | 3.3.0
48 |
49 |
50 | package
51 |
52 | shade
53 |
54 |
55 |
56 |
57 | org.bstats:*
58 | org.inventivetalent:glowapi**
59 | org.inventivetalent:reflectionhelper**
60 | org.inventivetalent:apimanager**
61 |
62 |
63 |
64 |
65 | org.bstats
66 | org.inventivetalent.glowapi
67 |
68 |
69 | org.inventivetalent.reflection
70 | org.inventivetalent.glow.reflection
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | sonatype-nexus-releases
82 | https://repo.inventivetalent.org/repository/maven-releases/
83 |
84 |
85 | sonatype-nexus-snapshots
86 | https://repo.inventivetalent.org/repository/maven-snapshots/
87 |
88 |
89 |
90 |
91 | io.papermc.paper
92 | paper-api
93 | 1.17.1-R0.1-SNAPSHOT
94 | provided
95 |
96 |
97 | org.inventivetalent.packetlistenerapi
98 | api
99 | 3.9.9-SNAPSHOT
100 |
101 |
102 | org.inventivetalent
103 | reflectionhelper
104 | 1.18.11-SNAPSHOT
105 |
106 |
107 | org.bstats
108 | bstats-bukkit-lite
109 | 1.7
110 |
111 |
112 |
113 |
114 |
115 | inventive-repo
116 | https://repo.inventivetalent.org/repository/public/
117 |
118 |
119 | jitpack.io
120 | https://jitpack.io
121 |
122 |
123 | md_5-repo
124 | http://repo.md-5.net/content/repositories/public/
125 |
126 |
127 | sonatype
128 | https://oss.sonatype.org/content/groups/public/
129 |
130 |
131 | spigot-repo
132 | https://hub.spigotmc.org/nexus/content/groups/public/
133 |
134 |
135 | CodeMC
136 | https://repo.codemc.org/repository/maven-public
137 |
138 |
139 | papermc
140 | https://papermc.io/repo/repository/maven-public/
141 |
142 |
143 |
144 |
145 |
146 | maven-snapshots
147 | https://repository.apache.org/content/repositories/snapshots/
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "maven": {
6 | "enabled": true
7 | },
8 | "ignoreUnstable": false,
9 | "hostRules": [{
10 | "hostType": "maven",
11 | "endpoint": "https://repo.inventivetalent.org/content/groups/public/"
12 | }]
13 | }
14 |
--------------------------------------------------------------------------------
/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | name: GlowAPI
2 | main: org.inventivetalent.glow.GlowPlugin
3 | author: inventivetalent
4 | version: ${project.version}
5 | api-version: 1.13
6 |
7 | softdepend: [PacketListenerApi]
8 |
--------------------------------------------------------------------------------
/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | sonatype-nexus-releases
5 | ${env.CI_DEPLOY_USERNAME}
6 | ${env.CI_DEPLOY_PASSWORD}
7 |
8 |
9 | sonatype-nexus-snapshots
10 | ${env.CI_DEPLOY_USERNAME}
11 | ${env.CI_DEPLOY_PASSWORD}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/glow/GlowAPI.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.glow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.google.common.collect.Lists;
5 | import org.bukkit.Bukkit;
6 | import org.bukkit.OfflinePlayer;
7 | import org.bukkit.World;
8 | import org.bukkit.entity.Entity;
9 | import org.bukkit.entity.ItemFrame;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.event.EventHandler;
12 | import org.bukkit.event.Listener;
13 | import org.bukkit.event.player.PlayerJoinEvent;
14 | import org.bukkit.event.player.PlayerQuitEvent;
15 | import org.bukkit.plugin.Plugin;
16 | import org.inventivetalent.packetlistener.handler.PacketHandler;
17 | import org.inventivetalent.packetlistener.handler.PacketOptions;
18 | import org.inventivetalent.packetlistener.handler.ReceivedPacket;
19 | import org.inventivetalent.packetlistener.handler.SentPacket;
20 | import org.inventivetalent.reflection.minecraft.Minecraft;
21 | import org.inventivetalent.reflection.minecraft.MinecraftVersion;
22 | import org.inventivetalent.reflection.resolver.ConstructorResolver;
23 | import org.inventivetalent.reflection.resolver.FieldResolver;
24 | import org.inventivetalent.reflection.resolver.MethodResolver;
25 | import org.inventivetalent.reflection.resolver.ResolverQuery;
26 | import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
27 | import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
28 |
29 | import java.lang.reflect.InvocationTargetException;
30 | import java.lang.reflect.Method;
31 | import java.util.*;
32 | import java.util.concurrent.ConcurrentHashMap;
33 |
34 | public class GlowAPI extends PacketHandler implements Listener {
35 |
36 | private static final Map dataMap = new HashMap<>();
37 | private final static Map entityById = new ConcurrentHashMap<>();
38 |
39 | private static final NMSClassResolver NMS_CLASS_RESOLVER = new NMSClassResolver();
40 |
41 | //Metadata
42 | private static Class> PacketPlayOutEntityMetadata;
43 | static Class> DataWatcher;
44 | static Class> DataWatcherItem;
45 | private static Class> Entity;
46 |
47 | private static FieldResolver PacketPlayOutMetadataFieldResolver;
48 | private static FieldResolver EntityFieldResolver;
49 | private static FieldResolver DataWatcherFieldResolver;
50 | static FieldResolver DataWatcherItemFieldResolver;
51 | private static FieldResolver DataWatcherItemAccessorFieldResolver; // >= 1.19
52 |
53 | private static ConstructorResolver PacketPlayOutMetadataResolver;
54 | private static ConstructorResolver DataWatcherItemConstructorResolver;
55 |
56 | private static MethodResolver DataWatcherMethodResolver;
57 | static MethodResolver DataWatcherItemMethodResolver;
58 | private static MethodResolver EntityMethodResolver;
59 |
60 | //Scoreboard
61 | private static Object nms$Scoreboard;
62 | private static ArrayList scoreboardTeamEntityList;
63 |
64 | private static Class> Scoreboard; // >= 1.17
65 | private static Class> ScoreboardTeam; // >= 1.17
66 | private static Class> PacketPlayOutScoreboardTeam;
67 | private static Class> PacketPlayOutScoreboardTeam$info; // >= 1.17
68 |
69 | private static FieldResolver PacketScoreboardTeamFieldResolver;
70 |
71 | private static MethodResolver ScoreboardTeamMethodResolver; // >= 1.17
72 |
73 | private static ConstructorResolver ScoreboardResolver; // >= 1.17
74 | private static ConstructorResolver ScoreboardTeamResolver; // >= 1.17
75 |
76 | private static ConstructorResolver PacketScoreboardTeamResolver; // >= 1.17
77 | private static ConstructorResolver PacketScoreboardTeamInfoResolver; // >= 1.17
78 |
79 | private static MethodResolver EnumNameTagVisibilityResolver; // >= 1.17
80 | private static MethodResolver EnumTeamPushResolver; // >= 1.17
81 |
82 | private static ConstructorResolver ChatComponentTextResolver;
83 | private static MethodResolver EnumChatFormatResolver;
84 | private static MethodResolver IChatBaseComponentMethodResolver;
85 |
86 | //Packets
87 | private static FieldResolver EntityPlayerFieldResolver;
88 | private static MethodResolver PlayerConnectionMethodResolver;
89 |
90 | static boolean isPaper = false;
91 |
92 | static {
93 | try {
94 | Class.forName("com.destroystokyo.paper.PaperConfig");
95 | isPaper = true;
96 | } catch (Exception ignored) {
97 | isPaper = false;
98 | }
99 | }
100 |
101 | //Options
102 | /**
103 | * Default name-tag visibility (always, hideForOtherTeams, hideForOwnTeam, never)
104 | */
105 | public static String TEAM_TAG_VISIBILITY = "always";
106 | /**
107 | * Default push behaviour (always, pushOtherTeams, pushOwnTeam, never)
108 | */
109 | public static String TEAM_PUSH = "always";
110 |
111 | /**
112 | * Set the glowing-color of an entity
113 | *
114 | * @param entity {@link Entity} to update
115 | * @param color {@link org.inventivetalent.glow.GlowAPI.Color} of the glow, or null
to stop glowing
116 | * @param tagVisibility visibility of the name-tag (always, hideForOtherTeams, hideForOwnTeam, never)
117 | * @param push push behaviour (always, pushOtherTeams, pushOwnTeam, never)
118 | * @param receiver {@link Player} that will see the update
119 | */
120 | public static void setGlowing(Entity entity, Color color, String tagVisibility, String push, Player receiver) {
121 | if (receiver == null) return;
122 |
123 | boolean glowing = color != null;
124 | if (entity == null) {
125 | glowing = false;
126 | }
127 | if (entity instanceof OfflinePlayer) {
128 | if (!((OfflinePlayer) entity).isOnline()) {
129 | glowing = false;
130 | }
131 | }
132 |
133 | boolean wasGlowing = dataMap.containsKey(entity != null ? entity.getUniqueId() : null);
134 | GlowData glowData;
135 | if (wasGlowing && entity != null) {glowData = dataMap.get(entity.getUniqueId());} else {glowData = new GlowData();}
136 |
137 | Color oldColor = wasGlowing ? glowData.colorMap.get(receiver.getUniqueId()) : null;
138 |
139 | if (glowing) {
140 | glowData.colorMap.put(receiver.getUniqueId(), color);
141 | entityById.put(entity.getEntityId(), entity.getUniqueId());
142 | } else {
143 | glowData.colorMap.remove(receiver.getUniqueId());
144 | }
145 | if (glowData.colorMap.isEmpty()) {
146 | dataMap.remove(entity != null ? entity.getUniqueId() : null);
147 | if (entity != null) entityById.remove(entity.getEntityId());
148 | } else {
149 | if (entity != null) {
150 | dataMap.put(entity.getUniqueId(), glowData);
151 | }
152 | }
153 |
154 | if (color != null && oldColor == color) return;
155 | if (entity == null) return;
156 | if (entity instanceof OfflinePlayer) {
157 | if (!((OfflinePlayer) entity).isOnline()) return;
158 | }
159 | if (!receiver.isOnline()) return;
160 |
161 | sendGlowPacket(entity, wasGlowing, glowing, receiver);
162 | if (oldColor != null && oldColor != Color.NONE/*We never add to NONE, so no need to remove*/) {
163 | sendTeamPacket(entity, oldColor/*use the old color to remove the player from its team*/, false, false, tagVisibility, push, receiver);
164 | }
165 | if (glowing) {
166 | sendTeamPacket(entity, color, false, color != Color.NONE, tagVisibility, push, receiver);
167 | }
168 | }
169 |
170 | /**
171 | * Set the glowing-color of an entity
172 | *
173 | * @param entity {@link Entity} to update
174 | * @param color {@link org.inventivetalent.glow.GlowAPI.Color} of the glow, or null
to stop glowing
175 | * @param receiver {@link Player} that will see the update
176 | */
177 | public static void setGlowing(Entity entity, Color color, Player receiver) {
178 | setGlowing(entity, color, "always", "always", receiver);
179 | }
180 |
181 | /**
182 | * Set the glowing-color of an entity
183 | *
184 | * @param entity {@link Entity} to update
185 | * @param glowing whether the entity is glowing or not
186 | * @param receiver {@link Player} that will see the update
187 | * @see #setGlowing(Entity, Color, Player)
188 | */
189 | public static void setGlowing(Entity entity, boolean glowing, Player receiver) {
190 | setGlowing(entity, glowing ? Color.NONE : null, receiver);
191 | }
192 |
193 | /**
194 | * Set the glowing-color of an entity
195 | *
196 | * @param entity {@link Entity} to update
197 | * @param glowing whether the entity is glowing or not
198 | * @param receivers Collection of {@link Player}s that will see the update
199 | * @see #setGlowing(Entity, Color, Player)
200 | */
201 | public static void setGlowing(Entity entity, boolean glowing, Collection extends Player> receivers) {
202 | for (Player receiver : receivers) {
203 | setGlowing(entity, glowing, receiver);
204 | }
205 | }
206 |
207 | /**
208 | * Set the glowing-color of an entity
209 | *
210 | * @param entity {@link Entity} to update
211 | * @param color {@link org.inventivetalent.glow.GlowAPI.Color} of the glow, or null
to stop glowing
212 | * @param receivers Collection of {@link Player}s that will see the update
213 | */
214 | public static void setGlowing(Entity entity, Color color, Collection extends Player> receivers) {
215 | for (Player receiver : receivers) {
216 | setGlowing(entity, color, receiver);
217 | }
218 | }
219 |
220 | /**
221 | * Set the glowing-color of an entity
222 | *
223 | * @param entities Collection of {@link Entity} to update
224 | * @param color {@link org.inventivetalent.glow.GlowAPI.Color} of the glow, or null
to stop glowing
225 | * @param receiver {@link Player} that will see the update
226 | */
227 | public static void setGlowing(Collection extends Entity> entities, Color color, Player receiver) {
228 | for (Entity entity : entities) {
229 | setGlowing(entity, color, receiver);
230 | }
231 | }
232 |
233 | /**
234 | * Set the glowing-color of an entity
235 | *
236 | * @param entities Collection of {@link Entity} to update
237 | * @param color {@link org.inventivetalent.glow.GlowAPI.Color} of the glow, or null
to stop glowing
238 | * @param receivers Collection of {@link Player}s that will see the update
239 | */
240 | public static void setGlowing(Collection extends Entity> entities, Color color, Collection extends Player> receivers) {
241 | for (Entity entity : entities) {
242 | setGlowing(entity, color, receivers);
243 | }
244 | }
245 |
246 | /**
247 | * Check if an entity is glowing
248 | *
249 | * @param entity {@link Entity} to check
250 | * @param receiver {@link Player} receiver to check (as used in the setGlowing methods)
251 | * @return true
if the entity appears glowing to the player
252 | */
253 | public static boolean isGlowing(Entity entity, Player receiver) {
254 | return getGlowColor(entity, receiver) != null;
255 | }
256 |
257 | private static boolean isGlowing(UUID entity, Player receiver) {
258 | return getGlowColor(entity, receiver) != null;
259 | }
260 |
261 | /**
262 | * Checks if an entity is glowing
263 | *
264 | * @param entity {@link Entity} to check
265 | * @param receivers Collection of {@link Player} receivers to check
266 | * @param checkAll if true
, this only returns true
if the entity is glowing for all receivers; if false
this returns true
if the entity is glowing for any of the receivers
267 | * @return true
if the entity appears glowing to the players
268 | */
269 | public static boolean isGlowing(Entity entity, Collection extends Player> receivers, boolean checkAll) {
270 | if (checkAll) {
271 | boolean glowing = true;
272 | for (Player receiver : receivers) {
273 | if (!isGlowing(entity, receiver)) {
274 | glowing = false;
275 | }
276 | }
277 | return glowing;
278 | } else {
279 | for (Player receiver : receivers) {
280 | if (isGlowing(entity, receiver)) return true;
281 | }
282 | }
283 | return false;
284 | }
285 |
286 | /**
287 | * Get the glow-color of an entity
288 | *
289 | * @param entity {@link Entity} to get the color for
290 | * @param receiver {@link Player} receiver of the color (as used in the setGlowing methods)
291 | * @return the {@link org.inventivetalent.glow.GlowAPI.Color}, or null
if the entity doesn't appear glowing to the player
292 | */
293 | public static Color getGlowColor(Entity entity, Player receiver) {
294 | return getGlowColor(entity.getUniqueId(), receiver);
295 | }
296 |
297 | private static Color getGlowColor(UUID entityUniqueId, Player receiver) {
298 | if (!dataMap.containsKey(entityUniqueId)) return null;
299 | GlowData data = dataMap.get(entityUniqueId);
300 | return data.colorMap.get(receiver.getUniqueId());
301 | }
302 |
303 | protected static void sendGlowPacket(Entity entity, boolean wasGlowing, boolean glowing, Player receiver) {
304 | try {
305 | if (PacketPlayOutEntityMetadata == null) {
306 | PacketPlayOutEntityMetadata = NMS_CLASS_RESOLVER.resolve("network.protocol.game.PacketPlayOutEntityMetadata");
307 | }
308 | if (DataWatcher == null) {
309 | DataWatcher = NMS_CLASS_RESOLVER.resolve("network.syncher.DataWatcher");
310 | }
311 | if (DataWatcherItem == null) {
312 | DataWatcherItem = NMS_CLASS_RESOLVER.resolve("network.syncher.DataWatcher$Item");
313 | }
314 | if (Entity == null) {
315 | Entity = NMS_CLASS_RESOLVER.resolve("world.entity.Entity");
316 | }
317 | if (PacketPlayOutMetadataFieldResolver == null) {
318 | PacketPlayOutMetadataFieldResolver = new FieldResolver(PacketPlayOutEntityMetadata);
319 | }
320 | if (PacketPlayOutMetadataResolver == null) {
321 | PacketPlayOutMetadataResolver = new ConstructorResolver(PacketPlayOutEntityMetadata);
322 | }
323 | if (DataWatcherItemConstructorResolver == null) {
324 | DataWatcherItemConstructorResolver = new ConstructorResolver(DataWatcherItem);
325 | }
326 | if (EntityFieldResolver == null) {
327 | EntityFieldResolver = new FieldResolver(Entity);
328 | }
329 | if (DataWatcherMethodResolver == null) {
330 | DataWatcherMethodResolver = new MethodResolver(DataWatcher);
331 | }
332 | if (DataWatcherItemMethodResolver == null) {
333 | DataWatcherItemMethodResolver = new MethodResolver(DataWatcherItem);
334 | }
335 | if (EntityMethodResolver == null) {
336 | EntityMethodResolver = new MethodResolver(Entity);
337 | }
338 | if (DataWatcherFieldResolver == null) {
339 | DataWatcherFieldResolver = new FieldResolver(DataWatcher);
340 | }
341 |
342 | List list = new ArrayList();
343 |
344 | //Existing values
345 | Object dataWatcher = EntityMethodResolver.resolve("getDataWatcher", "ai").invoke(Minecraft.getHandle(entity));
346 | Class dataWatcherItemsType;
347 | if (isPaper || MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_18_R1)) {
348 | dataWatcherItemsType = Class.forName("it.unimi.dsi.fastutil.ints.Int2ObjectMap");
349 | } else {
350 | dataWatcherItemsType = Class.forName("org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.Int2ObjectMap");
351 | }
352 | Map dataWatcherItems = (Map) DataWatcherFieldResolver.resolveByLastType(dataWatcherItemsType).get(dataWatcher);
353 |
354 | Object dataWatcherObject = org.inventivetalent.reflection.minecraft.DataWatcher.V1_9.ValueType.ENTITY_SHARED_FLAGS.getType();
355 | byte prev = (byte) (dataWatcherItems.isEmpty() ? 0 : DataWatcherItemMethodResolver.resolve("b").invoke(dataWatcherItems.get(0)));
356 | byte b = (byte) (glowing ? (prev | 1 << 6) : (prev & ~(1 << 6)));//6 = glowing index
357 | Object dataWatcherItem = DataWatcherItemConstructorResolver.resolveFirstConstructor().newInstance(dataWatcherObject, b);
358 |
359 | //The glowing item
360 | list.add(dataWatcherItem);
361 |
362 | Object packetMetadata = PacketPlayOutMetadataResolver
363 | .resolve(new Class[]{int.class, DataWatcher, boolean.class})
364 | .newInstance(-entity.getEntityId(), dataWatcher, true);
365 | List dataWatcherList = (List) PacketPlayOutMetadataFieldResolver.resolve("b").get(packetMetadata);
366 | dataWatcherList.clear();
367 | dataWatcherList.addAll(list);
368 |
369 | sendPacket(packetMetadata, receiver);
370 | } catch (ReflectiveOperationException e) {
371 | throw new RuntimeException(e);
372 | }
373 | }
374 |
375 | /**
376 | * Initializes the teams for a player
377 | *
378 | * @param receiver {@link Player} receiver
379 | * @param tagVisibility visibility of the name-tag (always, hideForOtherTeams, hideForOwnTeam, never)
380 | * @param push push behaviour (always, pushOtherTeams, pushOwnTeam, never)
381 | */
382 | public static void initTeam(Player receiver, String tagVisibility, String push) {
383 | for (GlowAPI.Color color : GlowAPI.Color.values()) {
384 | GlowAPI.sendTeamPacket(null, color, true, false, tagVisibility, push, receiver);
385 | }
386 | }
387 |
388 | /**
389 | * Initializes the teams for a player
390 | *
391 | * @param receiver {@link Player} receiver
392 | */
393 | public static void initTeam(Player receiver) {
394 | initTeam(receiver, TEAM_TAG_VISIBILITY, TEAM_PUSH);
395 | }
396 |
397 | protected static void sendTeamPacket(Entity entity, Color color, boolean createNewTeam/*If true, we don't add any entities*/, boolean addEntity/*true->add the entity, false->remove the entity*/, String tagVisibility, String push, Player receiver) {
398 | try {
399 | if (PacketPlayOutScoreboardTeam == null) {
400 | PacketPlayOutScoreboardTeam = NMS_CLASS_RESOLVER.resolve("network.protocol.game.PacketPlayOutScoreboardTeam");
401 | }
402 | if (PacketScoreboardTeamResolver == null) {
403 | PacketScoreboardTeamResolver = new ConstructorResolver(PacketPlayOutScoreboardTeam);
404 | }
405 | if (PacketScoreboardTeamFieldResolver == null) {
406 | PacketScoreboardTeamFieldResolver = new FieldResolver(PacketPlayOutScoreboardTeam);
407 | }
408 | if (Scoreboard == null) {
409 | Scoreboard = NMS_CLASS_RESOLVER.resolve("world.scores.Scoreboard");
410 | }
411 | if (ScoreboardResolver == null) {
412 | ScoreboardResolver = new ConstructorResolver(Scoreboard);
413 | }
414 | if (ScoreboardTeam == null) {
415 | ScoreboardTeam = NMS_CLASS_RESOLVER.resolve("world.scores.ScoreboardTeam");
416 | }
417 | if (ScoreboardTeamResolver == null) {
418 | ScoreboardTeamResolver = new ConstructorResolver(ScoreboardTeam);
419 | }
420 | if (ScoreboardTeamMethodResolver == null) {
421 | ScoreboardTeamMethodResolver = new MethodResolver(ScoreboardTeam);
422 | }
423 | if (ChatComponentTextResolver == null && MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_19_R1)) {
424 | ChatComponentTextResolver = new ConstructorResolver(NMS_CLASS_RESOLVER.resolve("network.chat.ChatComponentText"));
425 | }
426 | if (IChatBaseComponentMethodResolver == null && MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_19_R1)) {
427 | IChatBaseComponentMethodResolver = new MethodResolver(NMS_CLASS_RESOLVER.resolve("network.chat.IChatBaseComponent"));
428 | }
429 |
430 | final int mode = (createNewTeam ? 0 : addEntity ? 3 : 4); //Mode (0 = create, 3 = add entity, 4 = remove entity)
431 |
432 | Object nms$ScoreboardTeam = null;
433 | Object packetScoreboardTeam = null;
434 |
435 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
436 | if (nms$Scoreboard == null) {
437 | nms$Scoreboard = ScoreboardResolver.resolveFirstConstructor().newInstance();
438 | }
439 | nms$ScoreboardTeam = ScoreboardTeamResolver.resolveFirstConstructor().newInstance(nms$Scoreboard, color.getTeamName());
440 | } else {
441 | packetScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor().newInstance();
442 | PacketScoreboardTeamFieldResolver.resolve("i").set(packetScoreboardTeam, mode);//Mode
443 | PacketScoreboardTeamFieldResolver.resolve("a").set(packetScoreboardTeam, color.getTeamName());//Name
444 | PacketScoreboardTeamFieldResolver.resolve("e").set(packetScoreboardTeam, tagVisibility);//NameTag visibility
445 | PacketScoreboardTeamFieldResolver.resolve("f").set(packetScoreboardTeam, push);//Team-push
446 | }
447 |
448 | if (createNewTeam) {
449 | Object prefix;
450 | Object displayName;
451 | Object suffix;
452 |
453 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_19_R1)) {
454 | Method aMethod = IChatBaseComponentMethodResolver.resolve(new ResolverQuery("b", String.class));
455 | prefix = aMethod.invoke(null, "§" + color.colorCode);
456 | displayName = aMethod.invoke(null, color.getTeamName());
457 | suffix = aMethod.invoke(null, "");
458 | } else {
459 | prefix = ChatComponentTextResolver.resolveFirstConstructor().newInstance("§" + color.colorCode);
460 | displayName = ChatComponentTextResolver.resolveFirstConstructor().newInstance(color.getTeamName());
461 | suffix = ChatComponentTextResolver.resolveFirstConstructor().newInstance("");
462 | }
463 |
464 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
465 | if (PacketPlayOutScoreboardTeam$info == null) {
466 | PacketPlayOutScoreboardTeam$info = NMS_CLASS_RESOLVER.resolve("network.protocol.game.PacketPlayOutScoreboardTeam$b", "network.protocol.game.PacketPlayOutScoreboardTeam$Parameters");
467 | }
468 | if (PacketScoreboardTeamInfoResolver == null) {
469 | PacketScoreboardTeamInfoResolver = new ConstructorResolver(PacketPlayOutScoreboardTeam$info);
470 | }
471 | if (EnumNameTagVisibilityResolver == null) {
472 | EnumNameTagVisibilityResolver = new MethodResolver(NMS_CLASS_RESOLVER.resolve(
473 | "world.scores.ScoreboardTeamBase$EnumNameTagVisibility"));
474 | }
475 | if (EnumTeamPushResolver == null) {
476 | EnumTeamPushResolver = new MethodResolver(NMS_CLASS_RESOLVER.resolve(
477 | "world.scores.ScoreboardTeamBase$EnumTeamPush"));
478 | }
479 |
480 | Object visibilityObj = EnumNameTagVisibilityResolver
481 | .resolve(new ResolverQuery("a", String.class), new ResolverQuery("valueOf", String.class))
482 | .invoke(null, tagVisibility);
483 | Object pushObj = EnumTeamPushResolver
484 | .resolve(new ResolverQuery("a", String.class), new ResolverQuery("valueOf", String.class))
485 | .invoke(null, push);
486 |
487 | ScoreboardTeamMethodResolver.resolve(
488 | new ResolverQuery("setDisplayName"),
489 | new ResolverQuery("a", NMS_CLASS_RESOLVER.resolve("network.chat.IChatBaseComponent")))
490 | .invoke(nms$ScoreboardTeam, displayName);
491 | ScoreboardTeamMethodResolver.resolve(
492 | new ResolverQuery("setPrefix"),
493 | new ResolverQuery("b", NMS_CLASS_RESOLVER.resolve("network.chat.IChatBaseComponent")))
494 | .invoke(nms$ScoreboardTeam, prefix);
495 | ScoreboardTeamMethodResolver.resolve(
496 | new ResolverQuery("setSuffix"),
497 | new ResolverQuery("c", NMS_CLASS_RESOLVER.resolve("network.chat.IChatBaseComponent")))
498 | .invoke(nms$ScoreboardTeam, suffix);
499 | ScoreboardTeamMethodResolver.resolve(
500 | new ResolverQuery("setColor"),
501 | new ResolverQuery("a", NMS_CLASS_RESOLVER.resolve("EnumChatFormat")))
502 | .invoke(nms$ScoreboardTeam, color.packetValue);
503 | ScoreboardTeamMethodResolver.resolve(
504 | new ResolverQuery("setNameTagVisibilityRule"),
505 | new ResolverQuery("a", NMS_CLASS_RESOLVER.resolve("world.scores.ScoreboardTeamBase$EnumNameTagVisibility")))
506 | .invoke(nms$ScoreboardTeam, visibilityObj);
507 | ScoreboardTeamMethodResolver.resolve(
508 | new ResolverQuery("setCollisionRule"),
509 | new ResolverQuery("a", NMS_CLASS_RESOLVER.resolve("world.scores.ScoreboardTeamBase$EnumTeamPush")))
510 | .invoke(nms$ScoreboardTeam, pushObj);
511 |
512 | Object packetScoreboardTeamInfo = PacketScoreboardTeamInfoResolver.resolveFirstConstructor().newInstance(nms$ScoreboardTeam);
513 | packetScoreboardTeam = PacketScoreboardTeamResolver.resolve(
514 | new Class[]{String.class, int.class, Optional.class, Collection.class})
515 | .newInstance(color.getTeamName(), mode, Optional.of(packetScoreboardTeamInfo), ImmutableList.of());
516 | } else {
517 | PacketScoreboardTeamFieldResolver.resolve("g").set(packetScoreboardTeam, color.packetValue);//Color -> this is what we care about
518 |
519 | PacketScoreboardTeamFieldResolver.resolve("c").set(packetScoreboardTeam, prefix);//prefix - for some reason this controls the color, even though there's the extra color value...
520 | PacketScoreboardTeamFieldResolver.resolve("b").set(packetScoreboardTeam, displayName);//Display name
521 | PacketScoreboardTeamFieldResolver.resolve("d").set(packetScoreboardTeam, suffix);//suffix
522 | PacketScoreboardTeamFieldResolver.resolve("j").set(packetScoreboardTeam, 0);//Options - let's just ignore them for now
523 | }
524 | } else {
525 | /* Add/remove players */
526 |
527 | Collection entitiesList;
528 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
529 | if (scoreboardTeamEntityList == null) {
530 | scoreboardTeamEntityList = Lists.newArrayList();
531 | }
532 | scoreboardTeamEntityList.clear();
533 | entitiesList = scoreboardTeamEntityList;
534 | } else {
535 | entitiesList = ((Collection) PacketScoreboardTeamFieldResolver.resolve("h").get(packetScoreboardTeam));
536 | }
537 |
538 | if (entity instanceof OfflinePlayer) {//Players still use the name...
539 | entitiesList.add(entity.getName());
540 | } else {
541 | entitiesList.add(entity.getUniqueId().toString());
542 | }
543 |
544 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
545 | packetScoreboardTeam = PacketScoreboardTeamResolver.resolve(new Class[]{String.class, int.class, Optional.class, Collection.class}).newInstance(color.getTeamName(), mode, Optional.empty(), entitiesList);
546 | }
547 | }
548 |
549 | sendPacket(packetScoreboardTeam, receiver);
550 | } catch (ReflectiveOperationException e) {
551 | throw new RuntimeException(e);
552 | }
553 | }
554 |
555 | protected static void sendPacket(Object packet, Player p) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
556 | if (EntityPlayerFieldResolver == null) {
557 | EntityPlayerFieldResolver = new FieldResolver(NMS_CLASS_RESOLVER.resolve("server.level.EntityPlayer"));
558 | }
559 | if (PlayerConnectionMethodResolver == null) {
560 | PlayerConnectionMethodResolver = new MethodResolver(NMS_CLASS_RESOLVER.resolve("server.network.PlayerConnection"));
561 | }
562 |
563 | try {
564 | Object handle = Minecraft.getHandle(p);
565 | final Object connection;
566 |
567 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) { // even playerConnection got changed!
568 | connection = EntityPlayerFieldResolver.resolve("b").get(handle);
569 | } else {
570 | connection = EntityPlayerFieldResolver.resolve("playerConnection").get(handle);
571 | }
572 |
573 | PlayerConnectionMethodResolver.resolve(new ResolverQuery[]{
574 | new ResolverQuery("sendPacket"),
575 | new ResolverQuery("a", NMS_CLASS_RESOLVER.resolve("network.protocol.Packet"))
576 | }).invoke(connection, packet);
577 | } catch (ReflectiveOperationException e) {
578 | throw new RuntimeException(e);
579 | }
580 | }
581 |
582 | /**
583 | * Team Colors
584 | */
585 | public enum Color {
586 |
587 | BLACK(0, "0"),
588 | DARK_BLUE(1, "1"),
589 | DARK_GREEN(2, "2"),
590 | DARK_AQUA(3, "3"),
591 | DARK_RED(4, "4"),
592 | DARK_PURPLE(5, "5"),
593 | GOLD(6, "6"),
594 | GRAY(7, "7"),
595 | DARK_GRAY(8, "8"),
596 | BLUE(9, "9"),
597 | GREEN(10, "a"),
598 | AQUA(11, "b"),
599 | RED(12, "c"),
600 | PURPLE(13, "d"),
601 | YELLOW(14, "e"),
602 | WHITE(15, "f"),
603 | NONE(-1, "");
604 |
605 | Object packetValue;
606 | String colorCode;
607 |
608 | Color(int packetValue, String colorCode) {
609 | try {
610 | if (EnumChatFormatResolver == null) {
611 | EnumChatFormatResolver = new MethodResolver(NMS_CLASS_RESOLVER.resolve("EnumChatFormat"));
612 | }
613 |
614 | this.packetValue = EnumChatFormatResolver.resolve(new ResolverQuery("a", int.class)).invoke(null, packetValue);
615 | } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException |
616 | ClassNotFoundException e) {
617 | e.printStackTrace();
618 | }
619 |
620 | this.colorCode = colorCode;
621 | }
622 |
623 | String getTeamName() {
624 | String name = String.format("GAPI#%s", name());
625 | if (name.length() > 16) {
626 | name = name.substring(0, 16);
627 | }
628 | return name;
629 | }
630 | }
631 |
632 | @EventHandler
633 | public void onJoin(final PlayerJoinEvent event) {
634 | //Initialize the teams
635 | GlowAPI.initTeam(event.getPlayer());
636 | }
637 |
638 | @EventHandler
639 | public void onQuit(final PlayerQuitEvent event) {
640 | for (Player receiver : Bukkit.getOnlinePlayers()) {
641 | if (GlowAPI.isGlowing(event.getPlayer(), receiver)) {
642 | GlowAPI.setGlowing(event.getPlayer(), null, receiver);
643 | }
644 | }
645 | entityById.remove(event.getPlayer().getEntityId());
646 | }
647 |
648 | @PacketOptions(forcePlayer = true)
649 | @Override
650 | public void onSend(SentPacket sentPacket) {
651 | if (!"PacketPlayOutEntityMetadata".equals(sentPacket.getPacketName())) return;
652 | if (PacketPlayOutEntityMetadata == null) return;
653 | if (PacketPlayOutMetadataFieldResolver == null) return;
654 | if (DataWatcherItem != null && DataWatcherItemFieldResolver == null) {
655 | DataWatcherItemFieldResolver = new FieldResolver(GlowAPI.DataWatcherItem);
656 | }
657 |
658 | Object rawPacket = sentPacket.getPacket();
659 |
660 | try {
661 | int a = (int) sentPacket.getPacketValue("a");
662 | if (a < 0) {//Our packet
663 | //Reset the ID and let it through
664 | sentPacket.setPacketValue("a", -a);
665 | return;
666 | }
667 |
668 | List dataWatcherItemsList = (List) PacketPlayOutMetadataFieldResolver.resolve("b").get(rawPacket);
669 | if (dataWatcherItemsList.size() <= 0) return;
670 |
671 | Object dataWatcherItem = dataWatcherItemsList.get(0);
672 | if (dataWatcherItem == null) return;
673 |
674 | Object dataWatcherItemAccessor = DataWatcherItemMethodResolver.resolve("a").invoke(dataWatcherItem);
675 | if (dataWatcherItemAccessor == null) return;
676 |
677 | if (DataWatcherItemAccessorFieldResolver == null) {
678 | DataWatcherItemAccessorFieldResolver = new FieldResolver(nmsClassResolver.resolve("network.syncher.DataWatcherObject"));
679 | }
680 |
681 | int id = (Integer) DataWatcherItemAccessorFieldResolver.resolve("a").get(dataWatcherItemAccessor);
682 | if (id != 0) return;
683 |
684 | int targetEntityId = (Integer) PacketPlayOutMetadataFieldResolver.resolve("a").get(rawPacket);
685 | UUID entityUniqueId = entityById.get(targetEntityId);
686 | if (entityUniqueId == null) return;
687 |
688 | byte dataWatcherItemValue = (byte) DataWatcherItemMethodResolver.resolve("b").invoke(dataWatcherItem);
689 |
690 | boolean internalGlowFlagValue = (dataWatcherItemValue & (1 << 6)) == (1 << 6);
691 | boolean currentGlowFlagValue = isGlowing(entityUniqueId, sentPacket.getPlayer());
692 | if (internalGlowFlagValue == currentGlowFlagValue) return;
693 |
694 | byte newDataWatcherItemValue = (byte) (currentGlowFlagValue ? dataWatcherItemValue | (1 << 6) : dataWatcherItemValue & ~(1 << 6));
695 | DataWatcherItemMethodResolver.resolve(
696 | new ResolverQuery("a", Object.class)
697 | ).invoke(dataWatcherItem, newDataWatcherItemValue);
698 | } catch (Exception e) {
699 | throw new RuntimeException(e);
700 | }
701 | }
702 |
703 | @Override
704 | public void onReceive(ReceivedPacket receivedPacket) {
705 | }
706 |
707 | protected static NMSClassResolver nmsClassResolver = new NMSClassResolver();
708 | protected static OBCClassResolver obcClassResolver = new OBCClassResolver();
709 |
710 | private static FieldResolver CraftWorldFieldResolver;
711 | private static FieldResolver WorldFieldResolver;
712 | private static FieldResolver WorldServerFieldResolver;
713 | private static MethodResolver IntHashMapMethodResolver;
714 | private static MethodResolver WorldServerMethodResolver;
715 |
716 | public static Entity getEntityById(World world, int entityId) {
717 | try {
718 | if (CraftWorldFieldResolver == null) {
719 | CraftWorldFieldResolver = new FieldResolver(obcClassResolver.resolve("CraftWorld"));
720 | }
721 | if (WorldFieldResolver == null) {
722 | WorldFieldResolver = new FieldResolver(nmsClassResolver.resolve("world.level.World"));
723 | }
724 | if (WorldServerFieldResolver == null) {
725 | WorldServerFieldResolver = new FieldResolver(nmsClassResolver.resolve("server.level.WorldServer"));
726 | }
727 | if (EntityMethodResolver == null) {
728 | EntityMethodResolver = new MethodResolver(nmsClassResolver.resolve("world.entity.Entity"));
729 | }
730 | if (WorldServerMethodResolver == null) {
731 | WorldServerMethodResolver = new MethodResolver(nmsClassResolver.resolve("server.level.WorldServer"));
732 | }
733 |
734 | Object nmsWorld = CraftWorldFieldResolver.resolve("world").get(world);
735 | Object entity;
736 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_18_R1)) {
737 | entity = world.getEntitiesByClass(ItemFrame.class).stream().filter(i -> i.getEntityId() == entityId).findFirst().orElse(null);
738 | if (entity != null) {
739 | entity = Minecraft.getHandle(entity);
740 | }
741 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
742 | // no more entitiesById in 1.17+
743 | entity = WorldServerMethodResolver.resolve(new ResolverQuery[]{
744 | new ResolverQuery("getEntity", int.class),
745 | new ResolverQuery("a", int.class)
746 | }).invoke(nmsWorld, entityId);
747 | } else {
748 | Object entitiesById;
749 | // NOTE: this check can be false, if the v1_14_R1 doesn't exist (stupid java), i.e. in old ReflectionHelper versions
750 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_8_R1)
751 | && MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_14_R1)) { /* seriously?! between 1.8 and 1.14 entitiesyId was moved to World */
752 | entitiesById = WorldFieldResolver.resolveAccessor("entitiesById").get(nmsWorld);
753 | } else {
754 | entitiesById = WorldServerFieldResolver.resolveAccessor("entitiesById").get(nmsWorld);
755 | }
756 |
757 | if (MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_14_R1)) {// < 1.14 uses IntHashMap
758 | if (IntHashMapMethodResolver == null) {
759 | IntHashMapMethodResolver = new MethodResolver(NMS_CLASS_RESOLVER.resolve("IntHashMap"));
760 | }
761 |
762 | entity = IntHashMapMethodResolver.resolve(new ResolverQuery("get", int.class)).invoke(entitiesById, entityId);
763 | } else {// > 1.14 uses Int2ObjectMap which implements Map
764 | entity = ((Map) entitiesById).get(entityId);
765 | }
766 | }
767 | if (entity == null) return null;
768 | return (Entity) EntityMethodResolver.resolve("getBukkitEntity").invoke(entity);
769 | } catch (Exception e) {
770 | throw new RuntimeException(e);
771 | }
772 | }
773 |
774 | public GlowAPI(Plugin plugin) {
775 | super(plugin);
776 | }
777 |
778 | }
779 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/glow/GlowData.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.glow;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.Objects;
6 | import java.util.UUID;
7 |
8 | public class GlowData {
9 |
10 | //Maps player-UUID to Color
11 | public Map colorMap = new HashMap<>();
12 |
13 | @Override
14 | public boolean equals(Object o) {
15 | if (this == o) {return true;}
16 | if (o == null || getClass() != o.getClass()) return false;
17 |
18 | GlowData glowData = (GlowData) o;
19 |
20 | return Objects.equals(colorMap, glowData.colorMap);
21 |
22 | }
23 |
24 | @Override
25 | public int hashCode() {
26 | return colorMap != null ? colorMap.hashCode() : 0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/glow/GlowPlugin.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.glow;
2 |
3 | import org.bstats.bukkit.MetricsLite;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.configuration.file.FileConfiguration;
6 | import org.bukkit.plugin.java.JavaPlugin;
7 | import org.inventivetalent.packetlistener.PacketListenerAPI;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.util.Set;
12 |
13 | public class GlowPlugin extends JavaPlugin {
14 |
15 | static final Set VALID_VISIBILITY = Set.of("always", "never", "hideForOtherTeams", "hideForOwnTeam");
16 | static final Set VALID_COLLISION = Set.of("always", "never", "pushOtherTeams", "pushOwnTeam");
17 |
18 | static GlowPlugin instance;
19 | GlowAPI glowAPI;
20 |
21 | @Override
22 | public void onLoad() {
23 | instance = this;
24 | glowAPI = new GlowAPI(this);
25 | loadDefaults();
26 | }
27 |
28 | @Override
29 | public void onEnable() {
30 | Bukkit.getPluginManager().registerEvents(glowAPI, this);
31 | PacketListenerAPI.addPacketHandler(glowAPI);
32 |
33 | new MetricsLite(this, 2190);
34 | }
35 |
36 | @Override
37 | public void onDisable() {
38 | PacketListenerAPI.removePacketHandler(glowAPI);
39 | }
40 |
41 | private void loadDefaults() {
42 | FileConfiguration configuration;
43 | File dataFolder = getDataFolder();
44 |
45 | if (!dataFolder.exists()) {
46 | dataFolder.mkdirs();
47 |
48 | // Fill the configuration with current values
49 | configuration = getConfig();
50 | configuration.set("nameTagVisibility", GlowAPI.TEAM_TAG_VISIBILITY);
51 | configuration.set("collision", GlowAPI.TEAM_PUSH);
52 |
53 | try {
54 | configuration.save(new File(dataFolder, "config.yml"));
55 | } catch (IOException e) {
56 | throw new RuntimeException("Failed to save default config", e);
57 | }
58 | } else {
59 | configuration = getConfig();
60 | }
61 |
62 | String visibility = configuration.getString("nameTagVisibility");
63 | if (visibility != null) {
64 | if (!VALID_VISIBILITY.contains(visibility)) {
65 | getLogger().warning("Ignored unknown nameTagVisibility default value: '" + visibility + "'");
66 | } else {
67 | GlowAPI.TEAM_TAG_VISIBILITY = visibility;
68 | }
69 | }
70 |
71 | String push = configuration.getString("collision");
72 | if (push != null) {
73 | if (!VALID_COLLISION.contains(push)) {
74 | getLogger().warning("Ignored unknown collision default value: '" + push + "'");
75 | } else {
76 | GlowAPI.TEAM_PUSH = push;
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------