├── .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 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 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 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 entities, Color color, Collection 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 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 | --------------------------------------------------------------------------------