SHOW:
|
|
- or go back to the newest paste.
1 | package somePackage; | |
2 | ||
3 | import java.lang.reflect.Field; | |
4 | import java.lang.reflect.Method; | |
5 | import java.util.ArrayList; | |
6 | import java.util.Arrays; | |
7 | import java.util.HashMap; | |
8 | import java.util.List; | |
9 | import java.util.Map; | |
10 | import java.util.UUID; | |
11 | ||
12 | import org.bukkit.Bukkit; | |
13 | import org.bukkit.entity.Player; | |
14 | import org.bukkit.scoreboard.DisplaySlot; | |
15 | import org.bukkit.scoreboard.Objective; | |
16 | import org.bukkit.scoreboard.Team; | |
17 | ||
18 | public class Scoreboard { | |
19 | ||
20 | static { | |
21 | manager = new ScoreboardManager(); | |
22 | } | |
23 | ||
24 | private static final ScoreboardManager manager; | |
25 | private static org.bukkit.scoreboard.Scoreboard board; | |
26 | private static Objective obj; | |
27 | ||
28 | /** | |
29 | * Returns the scoreboard manager | |
30 | * @return the scoreboard manager | |
31 | */ | |
32 | public static ScoreboardManager getManager() { | |
33 | return manager; | |
34 | } | |
35 | ||
36 | private final String name; | |
37 | private List<String> lastUpdate = new ArrayList<>(); | |
38 | private List<String> scores = new ArrayList<>(); | |
39 | private String header; | |
40 | ||
41 | private Scoreboard(Player p) { | |
42 | name = p == null ? null : p.getName(); | |
43 | if(p != null) { | |
44 | p.setScoreboard(board); | |
45 | header = p.getName(); | |
46 | } | |
47 | } | |
48 | ||
49 | /** | |
50 | * Returns the scoreboard header of the player | |
51 | * @return the scoreboard header | |
52 | */ | |
53 | public String getHeader() { | |
54 | return header; | |
55 | } | |
56 | ||
57 | /** | |
58 | * Sets the header | |
59 | * @param header the new header | |
60 | * @return the scoreboard instance, useful for chaining actions | |
61 | */ | |
62 | public Scoreboard setHeader(String header) { | |
63 | this.header = header; | |
64 | return this; | |
65 | } | |
66 | ||
67 | /** | |
68 | * Returns the scoreboard's player | |
69 | * @return the scoreboard's player | |
70 | */ | |
71 | @SuppressWarnings("deprecation") | |
72 | public Player getPlayer() { | |
73 | return Bukkit.getPlayer(name); | |
74 | } | |
75 | ||
76 | /** | |
77 | * Returns the scores | |
78 | * @return the scores, where the keys are the score names and the values their score value | |
79 | */ | |
80 | public List<String> getScores() { | |
81 | return scores; | |
82 | } | |
83 | ||
84 | /** | |
85 | * Sets the scoreboard scores in descending order | |
86 | * @param scores the score names | |
87 | * @return the scoreboard instance | |
88 | */ | |
89 | public Scoreboard setScores(List<String> scores) { | |
90 | - | this.scores = scores; |
90 | + | this.scores = new ArrayList<String>(scores); |
91 | return this; | |
92 | } | |
93 | ||
94 | /** | |
95 | * Sets the scoreboard scores in descending order, this method simply calls setScores(Arrays.asList(scores)) | |
96 | * @param scores the score names | |
97 | * @return the scoreboard instance | |
98 | */ | |
99 | public Scoreboard setScores(String... scores) { | |
100 | return setScores(Arrays.asList(scores)); | |
101 | } | |
102 | ||
103 | /** | |
104 | * Updates the scoreboard with the current scores and header | |
105 | * @return the scoreboard instance | |
106 | */ | |
107 | public Scoreboard update() { | |
108 | Player p = getPlayer(); | |
109 | if(p == null) return this; | |
110 | if(scores.size() == lastUpdate.size()) { | |
111 | int size = scores.size() - 1; | |
112 | for(int i=0;i<=size;i++) { | |
113 | String entry = scores.get(i); | |
114 | String last = lastUpdate.get(i); | |
115 | if(!entry.equals(last)) { | |
116 | Object remove = manager.getRemovePacket(obj, last); | |
117 | Object add = manager.getChangePacket(obj, entry, size - i); | |
118 | ReflectUtil.sendPacket(p, remove, add); | |
119 | } | |
120 | } | |
121 | } else { | |
122 | for(int i=0;i<lastUpdate.size();i++) { | |
123 | Object packet = manager.getRemovePacket(obj, lastUpdate.get(i)); | |
124 | ReflectUtil.sendPacket(p, packet); | |
125 | } | |
126 | int size = scores.size() - 1; | |
127 | for(int i=0;i<=size;i++) { | |
128 | Object packet = manager.getChangePacket(obj, scores.get(i), size-i); | |
129 | ReflectUtil.sendPacket(p, packet); | |
130 | } | |
131 | } | |
132 | try { | |
133 | Object packet = ReflectUtil.getNMSClass(NMSClass.PACKET_OBJECTIVE).newInstance(); | |
134 | NMSClass clazz = NMSClass.ENUM_HEALTH_DISPLAY; | |
135 | ||
136 | String[] keys = "a, b, c, d".split(", "); | |
137 | Object[] values; | |
138 | if(clazz.name != null) | |
139 | values = new Object[] { | |
140 | "Board", header, ReflectUtil.getField(ReflectUtil.getNMSClass(clazz), | |
141 | null, "INTEGER"), 2}; | |
142 | else values = new Object[] {"Board", header, 2}; | |
143 | - | for(int i=0;i<keys.length;i++) |
143 | + | for(int i=0;i<values.length;i++) |
144 | ReflectUtil.setField(packet.getClass(), packet, keys[i], values[i]); | |
145 | ReflectUtil.sendPacket(p, packet); | |
146 | } catch (Exception ex) { | |
147 | ex.printStackTrace(); | |
148 | } | |
149 | - | this.lastUpdate = scores; |
149 | + | this.lastUpdate = new ArrayList<String>(scores); |
150 | return this; | |
151 | } | |
152 | ||
153 | /** | |
154 | * Unregisters the scoreboard from the system | |
155 | */ | |
156 | public void unregister() { | |
157 | Player p = getPlayer(); | |
158 | if(p != null) { | |
159 | manager.boards.remove(p.getUniqueId()); | |
160 | p.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard()); | |
161 | } | |
162 | } | |
163 | ||
164 | public static class ScoreboardManager { | |
165 | ||
166 | private final Map<UUID, Scoreboard> boards; | |
167 | ||
168 | private ScoreboardManager() { | |
169 | boards = new HashMap<>(); | |
170 | } | |
171 | ||
172 | /** | |
173 | - | * Sets a scoreboard up for the player, this is called in a join listener, use getScoreboard(Player) to get a player's board |
173 | + | * Sets a scoreboard up for the player, this should be called in a join listener, use getScoreboard(Player) to get a player's board |
174 | * @param p the player to set the board for | |
175 | * @return the newly created scoreboard instance | |
176 | */ | |
177 | public Scoreboard setupBoard(Player p) { | |
178 | Scoreboard board = new Scoreboard(p); | |
179 | boards.put(p.getUniqueId(), board); | |
180 | return board; | |
181 | } | |
182 | ||
183 | /** | |
184 | * Returns the player's scoreboard | |
185 | * @param p the player's scoreboard | |
186 | * @return the scoreboard of the player | |
187 | */ | |
188 | public Scoreboard getScoreboard(Player p) { | |
189 | return boards.get(p.getUniqueId()); | |
190 | } | |
191 | ||
192 | /** | |
193 | * Gets the main scoreboard, use this to register/unregister/manipulate teams | |
194 | * @return | |
195 | */ | |
196 | public org.bukkit.scoreboard.Scoreboard getScoreboard() { | |
197 | return board; | |
198 | } | |
199 | ||
200 | /** | |
201 | * Resets all teams | |
202 | */ | |
203 | public void resetTeams() { | |
204 | //Converting it to an array because when unregistering a team, it gets removed from the board's teams list, | |
205 | //and even by calling Iterator#remove() it will throw a ConcurrentModificationException | |
206 | Team[] teams = board.getTeams().toArray(new Team[0]); | |
207 | for(Team team : teams) { | |
208 | team.unregister(); | |
209 | } | |
210 | } | |
211 | ||
212 | private Object getChangePacket(Objective obj, String entry, int score) { | |
213 | try { | |
214 | Object packet = ReflectUtil.getNMSClass(NMSClass.PACKET_SCORE).newInstance(); | |
215 | NMSClass clazz = NMSClass.ENUM_ACTION; | |
216 | String[] keys = "a, b, c, d".split(", "); | |
217 | Object[] values; | |
218 | if(clazz.name != null) | |
219 | values = new Object[] {entry, obj.getName(), score, | |
220 | ReflectUtil.getField(ReflectUtil.getNMSClass(clazz), | |
221 | null, "CHANGE")}; | |
222 | else values = new Object[] {entry, obj.getName(), score, 0}; | |
223 | for(int i=0;i<keys.length;i++) | |
224 | ReflectUtil.setField(packet.getClass(), packet, keys[i], values[i]); | |
225 | return packet; | |
226 | } catch (Exception ex) { | |
227 | ex.printStackTrace(); | |
228 | return null; | |
229 | } | |
230 | } | |
231 | ||
232 | private Object getRemovePacket(Objective obj, String entry) { | |
233 | Object handle = ReflectUtil.getHandle(obj); | |
234 | try { | |
235 | Class<?> objective = ReflectUtil.getNMSClassByName("ScoreboardObjective"); | |
236 | if(ReflectUtil.isAbove("v1_8_R1")) | |
237 | return ReflectUtil.getNMSClass(NMSClass.PACKET_SCORE) | |
238 | .getConstructor(String.class, objective) | |
239 | .newInstance(entry, handle); | |
240 | else { | |
241 | return ReflectUtil.getNMSClass(NMSClass.PACKET_SCORE) | |
242 | .getConstructor(String.class).newInstance(entry); | |
243 | } | |
244 | } catch (Exception ex) { | |
245 | ex.printStackTrace(); | |
246 | return null; | |
247 | } | |
248 | } | |
249 | ||
250 | public void start() { | |
251 | board = Bukkit.getScoreboardManager().getNewScoreboard(); | |
252 | obj = board.registerNewObjective("Board", "dummy"); | |
253 | obj.setDisplaySlot(DisplaySlot.SIDEBAR); | |
254 | } | |
255 | } | |
256 | ||
257 | private static enum NMSClass { | |
258 | ||
259 | PACKET_OBJECTIVE(new String[0], "PacketPlayOutScoreboardObjective"), | |
260 | ENUM_HEALTH_DISPLAY(new String[] {"v1_8_R1"}, null, "EnumScoreboardHealthDisplay"), | |
261 | PACKET_SCORE(new String[0], "PacketPlayOutScoreboardScore"), | |
262 | ENUM_ACTION(new String[] {"v1_8_R1"}, null, "EnumScoreboardAction"); | |
263 | ||
264 | String name; | |
265 | ||
266 | NMSClass(String[] versions, String... s) { | |
267 | if(versions.length == 0) | |
268 | name = s[0]; | |
269 | for(int i=versions.length - 1;i>=0;i--) { | |
270 | if(ReflectUtil.isAbove(versions[i])) | |
271 | name = s[i + 1]; | |
272 | } | |
273 | } | |
274 | /*PacketPlayOutScoreboardObjective | |
275 | EnumScoreboardHealthDisplay | |
276 | PacketPlayOutScoreboardScore | |
277 | EnumScoreboardAction*/ | |
278 | } | |
279 | ||
280 | private static class ReflectUtil { | |
281 | ||
282 | static final String CBK; | |
283 | static final boolean subclasses; | |
284 | ||
285 | static { | |
286 | String pkg = Bukkit.getServer().getClass().getPackage().getName(); | |
287 | CBK = pkg.substring(pkg.lastIndexOf('.') + 1); | |
288 | subclasses = isAbove("v1_8_R2"); | |
289 | } | |
290 | ||
291 | static boolean isAbove(String version) { | |
292 | String[] s0 = CBK.split("_"); | |
293 | int i0 = Integer.parseInt(s0[0].substring(1)); | |
294 | int j0 = Integer.parseInt(s0[1]); | |
295 | int k0 = Integer.parseInt(s0[2].substring(1)); | |
296 | ||
297 | String[] s1 = version.split("_"); | |
298 | ||
299 | int i1 = Integer.parseInt(s1[0].substring(1)); | |
300 | int j1 = Integer.parseInt(s1[1]); | |
301 | int k1 = Integer.parseInt(s1[2].substring(1)); | |
302 | return i0 > i1 ? true : i0 == i1 ? (j0 > j1 ? true : j0 == j1 ? k0 >= k1 : false) : false; | |
303 | } | |
304 | ||
305 | static Class<?> getNMSClassByName(String name) { | |
306 | if(subclasses){ | |
307 | //From v1_8_R2, some classes have become subclasses | |
308 | if(name.equals("EnumScoreboardAction")) | |
309 | name = "PacketPlayOutScoreboardScore$" + name; | |
310 | else if(name.equals("EnumScoreboardHealthDisplay")) | |
311 | name = "IScoreboardCriteria$" + name; | |
312 | } | |
313 | try { | |
314 | return Class.forName("net.minecraft.server." + CBK + "." + name); | |
315 | } catch (ClassNotFoundException ex) { | |
316 | ex.printStackTrace(); | |
317 | return null; | |
318 | } | |
319 | } | |
320 | ||
321 | static Class<?> getNMSClass(NMSClass clazz) { | |
322 | return getNMSClassByName(clazz.name); | |
323 | } | |
324 | ||
325 | static Object getField(Class<?> clazz, Object instance, String field) { | |
326 | try { | |
327 | Field f = clazz.getDeclaredField(field); | |
328 | f.setAccessible(true); | |
329 | return f.get(instance); | |
330 | } catch (Exception ex) { | |
331 | ex.printStackTrace(); | |
332 | return null; | |
333 | } | |
334 | } | |
335 | ||
336 | static void setField(Class<?> clazz, Object instance, String field, Object value) { | |
337 | try { | |
338 | Field f = clazz.getDeclaredField(field); | |
339 | f.setAccessible(true); | |
340 | f.set(instance, value); | |
341 | } catch (Exception ex) { | |
342 | ex.printStackTrace(); | |
343 | } | |
344 | } | |
345 | ||
346 | static Object getHandle(Object obj) { | |
347 | try { | |
348 | return obj.getClass().getMethod("getHandle").invoke(obj); | |
349 | } catch (Exception ex) { | |
350 | ex.printStackTrace(); | |
351 | return null; | |
352 | } | |
353 | } | |
354 | ||
355 | static void sendPacket(Player p, Object... packets) { | |
356 | try { | |
357 | Object handle = getHandle(p); | |
358 | Object connection = handle.getClass().getDeclaredField("playerConnection").get(handle); | |
359 | Method send = connection.getClass().getDeclaredMethod("sendPacket", getNMSClassByName("Packet")); | |
360 | for(Object packet : packets) | |
361 | send.invoke(connection, packet); | |
362 | } catch (Exception ex) { | |
363 | ex.printStackTrace(); | |
364 | } | |
365 | } | |
366 | } | |
367 | } |