1+ package com .loohp .interactivechat .Bungee ;
2+
3+ import java .io .IOException ;
4+ import java .net .SocketAddress ;
5+ import java .nio .charset .StandardCharsets ;
6+ import java .util .Collection ;
7+ import java .util .LinkedList ;
8+ import java .util .List ;
9+ import java .util .Random ;
10+ import java .util .Timer ;
11+ import java .util .TimerTask ;
12+ import java .util .concurrent .CompletableFuture ;
13+ import java .util .concurrent .ExecutionException ;
14+ import java .util .concurrent .atomic .AtomicLong ;
15+
16+ import com .google .common .io .ByteArrayDataOutput ;
17+ import com .google .common .io .ByteStreams ;
18+ import com .loohp .interactivechat .Bungee .Metrics .Charts ;
19+ import com .loohp .interactivechat .Bungee .Metrics .Metrics ;
20+ import com .loohp .interactivechat .Utils .CompressionUtils ;
21+ import com .loohp .interactivechat .Utils .CustomArrayUtils ;
22+ import com .loohp .interactivechat .Utils .DataTypeIO ;
23+
24+ import net .md_5 .bungee .api .Callback ;
25+ import net .md_5 .bungee .api .ChatColor ;
26+ import net .md_5 .bungee .api .ProxyServer ;
27+ import net .md_5 .bungee .api .ServerPing ;
28+ import net .md_5 .bungee .api .config .ServerInfo ;
29+ import net .md_5 .bungee .api .connection .ProxiedPlayer ;
30+ import net .md_5 .bungee .api .event .PlayerDisconnectEvent ;
31+ import net .md_5 .bungee .api .event .PluginMessageEvent ;
32+ import net .md_5 .bungee .api .event .PostLoginEvent ;
33+ import net .md_5 .bungee .api .plugin .Listener ;
34+ import net .md_5 .bungee .api .plugin .Plugin ;
35+ import net .md_5 .bungee .event .EventHandler ;
36+
37+ public class InteractiveChatBungee extends Plugin implements Listener {
38+
39+ public static Plugin plugin ;
40+ public static Metrics metrics ;
41+ private static Random random = new Random ();
42+ public static AtomicLong pluginMessagesCounter = new AtomicLong (0 );
43+
44+ @ Override
45+ public void onEnable () {
46+ plugin = this ;
47+
48+ getProxy ().registerChannel ("interchat:main" );
49+ getProxy ().getPluginManager ().registerListener (this , this );
50+ getLogger ().info (ChatColor .GREEN + "[InteractiveChat] Registered Plugin Messaging Channels!" );
51+
52+ metrics = new Metrics (plugin , 8839 );
53+ Charts .setup (metrics );
54+
55+ run ();
56+
57+ getLogger ().info (ChatColor .GREEN + "[InteractiveChat] InteractiveChatBungee has been enabled!" );
58+ }
59+
60+ @ Override
61+ public void onDisable () {
62+ getLogger ().info (ChatColor .RED + "[InteractiveChat] InteractiveChatBungee has been disabled!" );
63+ }
64+
65+ private void run () {
66+ new Timer ().schedule (new TimerTask () {
67+ @ Override
68+ public void run () {
69+ try {
70+ sendPlayerListData ();
71+ sendDelay ();
72+ } catch (IOException e ) {
73+ e .printStackTrace ();
74+ }
75+ }
76+ }, 0 , 10000 );
77+ }
78+
79+ @ EventHandler
80+ public void onReceive (PluginMessageEvent event ) {
81+ if (!event .getTag ().equals ("interchat:main" )) {
82+ return ;
83+ }
84+
85+ SocketAddress senderServer = event .getSender ().getSocketAddress ();
86+
87+ for (ServerInfo server : getProxy ().getServers ().values ()) {
88+ if (!server .getSocketAddress ().equals (senderServer ) && server .getPlayers ().size () > 0 ) {
89+ server .sendData ("interchat:main" , event .getData ());
90+ }
91+ }
92+ }
93+
94+ @ EventHandler
95+ public void onJoin (PostLoginEvent event ) {
96+ new Timer ().schedule (new TimerTask () {
97+ @ Override
98+ public void run () {
99+ try {
100+ sendPlayerListData ();
101+ } catch (IOException e ) {
102+ e .printStackTrace ();
103+ }
104+ }
105+ }, 1000 );
106+ }
107+
108+ @ EventHandler
109+ public void onLeave (PlayerDisconnectEvent event ) {
110+ new Timer ().schedule (new TimerTask () {
111+ @ Override
112+ public void run () {
113+ try {
114+ sendPlayerListData ();
115+ } catch (IOException e ) {
116+ e .printStackTrace ();
117+ }
118+ }
119+ }, 1000 );
120+ }
121+
122+ private void sendPlayerListData () throws IOException {
123+ ByteArrayDataOutput output = ByteStreams .newDataOutput ();
124+ Collection <ProxiedPlayer > players = ProxyServer .getInstance ().getPlayers ();
125+ output .writeInt (players .size ());
126+ for (ProxiedPlayer player : players ) {
127+ DataTypeIO .writeUUID (output , player .getUniqueId ());
128+ DataTypeIO .writeString (output , player .getDisplayName (), StandardCharsets .UTF_8 );
129+ }
130+
131+ int packetNumber = random .nextInt ();
132+ int packetId = 0x00 ;
133+ byte [] data = output .toByteArray ();
134+
135+ byte [][] dataArray = CustomArrayUtils .divideArray (CompressionUtils .compress (data ), 32700 );
136+
137+ for (int i = 0 ; i < dataArray .length ; i ++) {
138+ byte [] chunk = dataArray [i ];
139+
140+ ByteArrayDataOutput out = ByteStreams .newDataOutput ();
141+ out .writeInt (packetNumber );
142+
143+ out .writeShort (packetId );
144+ out .writeBoolean (i == (dataArray .length - 1 ));
145+
146+ out .write (chunk );
147+
148+ for (ServerInfo server : getProxy ().getServers ().values ()) {
149+ server .sendData ("interchat:main" , out .toByteArray ());
150+ }
151+ }
152+ }
153+
154+ private void sendDelay () throws IOException {
155+ ByteArrayDataOutput output = ByteStreams .newDataOutput ();
156+
157+ List <CompletableFuture <Integer >> futures = new LinkedList <>();
158+
159+ for (ServerInfo server : getProxy ().getServers ().values ()) {
160+ futures .add (getPing (server ));
161+ }
162+ int highestPing = futures .stream ().mapToInt (each -> {
163+ try {
164+ return each .get ();
165+ } catch (InterruptedException | ExecutionException e ) {
166+ return 0 ;
167+ }
168+ }).max ().orElse (0 );
169+
170+ output .writeInt (highestPing * 2 + 200 );
171+
172+ int packetNumber = random .nextInt ();
173+ int packetId = 0x01 ;
174+ byte [] data = output .toByteArray ();
175+
176+ byte [][] dataArray = CustomArrayUtils .divideArray (CompressionUtils .compress (data ), 32700 );
177+
178+ for (int i = 0 ; i < dataArray .length ; i ++) {
179+ byte [] chunk = dataArray [i ];
180+
181+ ByteArrayDataOutput out = ByteStreams .newDataOutput ();
182+ out .writeInt (packetNumber );
183+
184+ out .writeShort (packetId );
185+ out .writeBoolean (i == (dataArray .length - 1 ));
186+
187+ out .write (chunk );
188+
189+ for (ServerInfo server : getProxy ().getServers ().values ()) {
190+ server .sendData ("interchat:main" , out .toByteArray ());
191+ }
192+ }
193+ }
194+
195+ private CompletableFuture <Integer > getPing (ServerInfo server ) {
196+ CompletableFuture <Integer > future = new CompletableFuture <>();
197+ long start = System .currentTimeMillis ();
198+ Callback <ServerPing > callback = new Callback <ServerPing >() {
199+ @ Override
200+ public void done (ServerPing result , Throwable error ) {
201+ if (error == null ) {
202+ future .complete ((int ) (System .currentTimeMillis () - start ));
203+ } else {
204+ future .complete (0 );
205+ }
206+ }
207+ };
208+ server .ping (callback );
209+ return future ;
210+ }
211+ }
0 commit comments