View difference between Paste ID: tbaxsq8F and FSAGRD0p
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
}