295 Commits

Author SHA1 Message Date
md_5
db5510cc4e Only interrupt tab completion if we have things to complete! 2013-09-17 10:28:51 +10:00
md_5
5ed5c71aea Move AbstractReconnectManager to the API and rename to AbstractReconnectHandler. 2013-09-16 08:21:53 +10:00
md_5
38a8469ab4 Cap command completion to one argument 2013-09-15 15:14:47 +10:00
md_5
9538dcf4d4 Properly tab complete 2013-09-15 14:04:51 +10:00
md_5
33f654ce6f *unused imports 2013-09-15 07:44:13 +10:00
md_5
c108e4e1ce Server command completion 2013-09-15 07:43:50 +10:00
md_5
e998faeec1 Add tab completion for find command. Also change api a bit. 2013-09-15 07:37:20 +10:00
md_5
d67acd7bc9 Add functionality to replicate #336 2013-09-15 07:29:22 +10:00
md_5
702f434db1 Add API to support #468 - force setting of reconnect server 2013-09-15 07:12:58 +10:00
md_5
47b5631562 Not part of the contract 2013-09-15 06:54:58 +10:00
Robin Lambertz
80e23d6646 Allow removal of listeners / commands by plugin 2013-09-15 06:52:46 +10:00
md_5
1dca12cffb Use boolean not binary and 2013-09-15 06:48:06 +10:00
md_5
29c897c9cf Add Tab Completion loosely based on @TheUnnamedDude's work. 2013-09-15 06:46:10 +10:00
md_5
042f47cbb9 Wrapped buffers are not thread safe. 2013-09-10 21:33:44 +10:00
md_5
422e97f495 Don't let pingbuf be released 2013-09-10 21:26:59 +10:00
md_5
08789d8f9f Write down a supported message type (ByteBuf) when using the ping handler. 2013-09-10 20:56:40 +10:00
md_5
96444ae304 Fix a message consisting only of a space causing the player to be kicked. 2013-09-10 16:23:05 +10:00
md_5
af58db8a67 Simpler, unit tested throttle to close #613 2013-09-10 12:02:29 +10:00
md_5
49cffebd9b Dynamic build dates - see #526 2013-09-10 11:37:48 +10:00
md_5
ffdb917f2c Use translation - closes #578 2013-09-09 14:58:56 +10:00
md_5
9c9657e36d Update links 2013-09-09 14:41:58 +10:00
md_5
1342baed47 Use fluid bytes instead of enum + format. 2013-09-09 14:36:48 +10:00
Dabo Ross
e3a7490bcd Fix formatting error - not sure how that happened 2013-09-09 14:27:23 +10:00
Dabo Ross
3e8693793c Add multiple listeners to EventPriorityTest 2013-09-09 14:27:23 +10:00
Dabo Ross
bb47aba682 Add UnregisteringListenerTest 2013-09-09 14:27:23 +10:00
Dabo Ross
d0e5ee4e09 Don't try to bake handlers when there are no more handlers. Remove them instead! 2013-09-09 14:27:23 +10:00
Dabo Ross
07e330b005 Create test for event priorities 2013-09-09 14:27:23 +10:00
Dabo Ross
024288e587 Added a Map<Class<?>, EventHandlerMethod[]> and implemented using it. I believe this should have a positive performance effect. 2013-09-09 14:27:23 +10:00
Dabo Ross
53a6bb1dee Added EventHandlerMethod wrapper for Listener and Method 2013-09-09 14:27:23 +10:00
Dabo Ross
0f06b2c4e0 Implement usage of EventPriority in EventBus 2013-09-09 14:27:23 +10:00
Dabo Ross
d3c1acce83 Add EventPriority method to EventHandler 2013-09-09 14:27:23 +10:00
Dabo Ross
eaea090d37 Add EventPriority 2013-09-09 14:27:22 +10:00
md_5
7384e797fc Bump date 2013-09-08 11:49:27 +10:00
md_5
d4d93ddbb9 Bump to Netty 4.0.9 2013-09-08 08:45:37 +10:00
md_5
ccdf2a89d8 Close #518 - use csv method for perms command 2013-09-07 12:22:43 +10:00
md_5
89edb00c05 Properly cancel tasks! 2013-09-05 19:52:41 +10:00
md_5
00a0277a13 Just call our own logger, screw jdk logger parenting 2013-09-03 11:36:00 +10:00
md_5
68f11e46f7 Depend updates. 2013-08-27 12:30:00 +10:00
Ammar Askar
c352e854ee Catch exceptions when disabling plugins 2013-08-25 10:27:25 +10:00
md_5
d8c92cd311 Add ConnectOther channel for moving other players from a plugin 2013-08-25 10:23:17 +10:00
md_5
99f361ca77 Instead of storing packets about to be passed on as a byte array, store them as a Netty buffer, which is likely to be pooled, direct and manually memory managed leading to increased performance and less GC strain. In order to ensure no resources are leaked, we free them at the end of each handle cycle if they have not been passed to a channel for writing. In initial profiles this now causes encryption to be one of the most intensive parts of BungeeCord, however in depth profiling snapshots may provide further routes for optimization. 2013-08-20 19:29:43 +10:00
md_5
738ed99d54 Code format. 2013-08-20 19:28:09 +10:00
md_5
ad0da59267 Really need to automatically do this. Add a few weeks to expire time 2013-08-20 18:50:52 +10:00
md_5
1dcc8d6a4b Close #572 - kick event message 2013-08-20 11:18:51 +10:00
md_5
0840a77153 Dem dates :( 2013-08-15 07:44:33 +10:00
md_5
61a93a54a9 ammar2 missed a spot 2013-08-14 22:14:32 +10:00
md_5
da0281508e Oi! Get back inside of that if statement. NOW! 2013-08-13 18:53:16 +10:00
Ammar Askar
51e92de2dd Only save to reconnectHandler if we have a listener that isn't forcing to the default server 2013-08-13 18:50:19 +10:00
md_5
f948acd634 Don't loop registering of listeners 2013-08-12 20:31:51 +10:00
md_5
773ce089c1 Fix http client 2013-08-10 07:30:41 +10:00
md_5
a07eba7965 Netty 4.0.7.Final - fixes memory leak 2013-08-09 19:50:48 +10:00
md_5
b68b6a76c7 Recover from broken yaml 2013-08-09 17:23:16 +10:00
md_5
332033bb02 Disable resource leak detector for ~15% cpu reduction 2013-08-09 16:58:14 +10:00
md_5
172b8bc75b Update to Netty 4.0.6-Final 2013-08-09 16:56:09 +10:00
md_5
db5a147491 Revert changes to SeverConnectedEvent 2013-08-06 11:14:54 +10:00
md_5
f083e27649 More translations! 2013-08-05 17:29:47 +10:00
md_5
b64a7be19b Bump date to the 9th 2013-08-04 21:34:56 +10:00
md_5
c4d60a8fa9 Hold player for ServerSwitchEvent - see #539 2013-08-04 21:28:10 +10:00
md_5
f07cfe0cf7 Make the ServerConnectedEvent async to allow stalling it. Closes #538 by @BjoernAkAManf. 2013-08-04 20:58:17 +10:00
md_5
4463b0c1b2 Use Java 7 API to make classloader thread safe. Closes #516 2013-08-04 20:23:31 +10:00
mrapple
ee8f33c196 Add State to ServerKickEvent 2013-08-04 18:57:13 +10:00
md_5
14ac2dd308 Allow setting whether to bind to the local address. 2013-08-04 18:56:48 +10:00
md_5
fb94612315 Fix throttle to 1) Work, 2) Not throttle outbound connections 2013-08-02 19:31:46 +10:00
md_5
4c96880580 Lets just silence java.util.NoSuchElementException: decrypt - PEOPLE DON'T UNDERSTAND ITS NOT AN ERROR. 2013-08-02 19:11:16 +10:00
md_5
4c4cdd51a1 Downgrade to Netty CR9 2013-08-02 07:32:42 +10:00
md_5
1f38152530 [URGENT] Add connection throttle. 2013-08-01 13:37:32 +10:00
md_5
911f08d52c Disable packet grouping in an attempt to increase reliability. 2013-07-31 20:18:54 +10:00
md_5
8f961c9d4e Put colours in default motd to try and force quoting in the dumped yaml 2013-07-27 12:12:12 +10:00
md_5
8a5d8a57f7 Don't infinite loop on tasks with no period 2013-07-24 17:38:46 +10:00
md_5
c54553d0f9 How kind of @lazertester to test the new scheduler! 2013-07-24 17:32:08 +10:00
md_5
600a1b4ff5 Update expire date 2013-07-24 17:02:37 +10:00
md_5
09e592295f Update to Netty 4.0.4-Final 2013-07-24 15:58:02 +10:00
md_5
fa0ee02beb Update Netty - if you guys could verify the leak has been fixed, that would be great, #523 2013-07-22 19:40:14 +10:00
md_5
b23b54d9e4 Bump Netty minor version 2013-07-19 19:31:46 +10:00
md_5
b3e8feb4cb Update POMs to 1.6.2 2013-07-18 20:13:07 +10:00
md_5
d0d1562155 Hi, I'm Mojang and I make hundreds of millions of dollars a year. I still like to think I am an Indy company, so I randomly remove existing game features and don't provide replacements.
Removes Texture Pack setting
2013-07-18 20:10:45 +10:00
md_5
f510ab2a0b Update to netty 4 final, exciting! 2013-07-17 16:47:49 +10:00
md_5
fb1cab499d Always use eventloop since we cannot be sure about plugins, thanks @MonsieurApple for the report. 2013-07-12 09:29:53 +10:00
md_5
58ca63e2b1 Use a defensive copy for shorter lock time 2013-07-11 11:14:12 +10:00
md_5
499337c98e Optimized yaml reconnect locations! Ding dong sqlite is dead.. 2013-07-11 11:12:43 +10:00
md_5
526137be7b Remove our packet queuing as it may be contributing to high CPU usage and/or memory leaks. 2013-07-11 10:14:51 +10:00
md_5
47839cb11c writeStringUTF8WithoutLengthHeaderBecause @Dinnerbone StuffedUpTheMCBrandPacket 2013-07-10 23:42:50 +10:00
md_5
55a6cc56ef Recycle messagelist when channel goes inactive (reverted from commit daa58ffe58) 2013-07-10 23:41:37 +10:00
md_5
8c2bea5be2 Fix possible race condition with regards to abandoning servers 2013-07-10 13:02:25 +10:00
md_5
daa58ffe58 Recycle messagelist when channel goes inactive 2013-07-10 09:18:58 +10:00
md_5
0189ad9c17 Add disabled commands 2013-07-09 14:55:27 +10:00
md_5
9adcb05d45 Fix 1.6.2 support 2013-07-08 22:37:59 +10:00
md_5
10e81041b2 Update to 1.6.2 2013-07-08 21:29:09 +10:00
md_5
0c56945ffd Implement upcoming Minecraft API to get the server brand - thanks @Grum for the hint. 2013-07-08 21:29:09 +10:00
md_5
0a36cbd5bc Fix compilation -> add silly catch block 2013-07-08 08:41:11 +10:00
md_5
61b4777177 Use more threads for IO to eliminate resource starvation possibly leading to high CPU usage. This brings the count back in line with what we had pre 1.6 / late 1.5 2013-07-08 08:35:12 +10:00
md_5
7d1904584b Back to Java cipher, they are both the same speed 2013-07-08 08:33:25 +10:00
md_5
475571986c Make sure packets are written before closing 2013-07-07 13:08:25 +10:00
md_5
55c2bcd634 Undo recent SQLite changes - might need to seek *another* DB engine - whats H2 like for concurrency? 2013-07-06 08:30:38 +10:00
md_5
db4abfe486 Expand streams a little bit 2013-07-05 19:17:25 +10:00
md_5
9424bdedca Implement basic MC stream 2013-07-05 19:06:46 +10:00
md_5
52b3c6b77c Dont need to force use of eventloop anymore 2013-07-05 09:32:20 +10:00
md_5
be29799f5a [Beta] Implement own HTTP client for online mode checks, instead of asynchttpclient 2013-07-05 09:29:28 +10:00
md_5
c0d581d41f Rework SQLite again to use thread local connections - closes #492 2013-07-05 08:23:29 +10:00
md_5
6b50c7c599 Move HTTP client stuffs into bungee-proxy 2013-07-04 21:59:38 +10:00
hyperring
b4101874cc Fixed forced_hosts MOTD
Noticed a tiny bug in creating a ServerPing response. The response was still using the old listener.getMotd() when it should be using the new motd variable (to take advantage of the new forced_hosts MOTD methods).
2013-07-04 21:39:33 +10:00
md_5
66de4c95ef Implement BouncyCastle as the cipher engine. 2013-07-04 21:13:10 +10:00
md_5
927a295add Add SSL support 2013-07-04 11:48:09 +10:00
md_5
2cbea83c02 HTTP is working, still need to do HTTPS though 2013-07-04 11:32:36 +10:00
md_5
87884ad084 Downgrade maven compiler - new one is still derp 2013-07-04 11:12:25 +10:00
md_5
94cc2412e7 Flush pending messages when manipulating pipeline - fixes forge support 2013-07-04 11:11:57 +10:00
md_5
924b90e325 Add statr of work on HTTP client. 2013-07-04 10:52:54 +10:00
md_5
3f476a30b4 Get depend name right :p 2013-07-04 10:31:00 +10:00
md_5
f579b31bca Add framework for HTTP api 2013-07-04 10:29:37 +10:00
md_5
cac35116c3 Import cleanup 2013-07-04 10:03:41 +10:00
md_5
bd42fb23a0 Update to Netty CR9 2013-07-04 09:58:29 +10:00
md_5
ffbebaff69 Remove old @Subscribe event handling 2013-07-04 09:43:32 +10:00
md_5
b741722e5d Close #489 - disable resource leak detector for performance reasons 2013-07-04 09:22:27 +10:00
md_5
07288c722c Update maven compiler version 2013-07-02 21:04:10 +10:00
md_5
85e82a2e34 Update POMs to 1.6.1 2013-07-02 20:59:04 +10:00
md_5
3aef35ccbb Warn about non existant fallback 2013-07-02 19:43:48 +10:00
md_5
d1760dad93 Custom outdated messages, tick! 2013-07-02 19:26:21 +10:00
md_5
d3d11cf283 Update to netty CR8 2013-07-02 15:31:20 +10:00
md_5
d3bada58d4 Close #469 - chat event setMessage 2013-07-02 10:45:37 +10:00
md-5
23517a9a97 Merge pull request #476 from vemacs/patch-1
Supposedly fix walk speeds
2013-07-01 14:10:43 -07:00
vemacs
fdc87e88f5 Supposedly fix walk speeds 2013-07-01 15:34:17 -04:00
md_5
12941ffe62 Close #471 - sync sqlite operations to guard against deadlocks 2013-07-01 21:25:39 +10:00
md_5
06e732d8c7 Close #474 - 1.5 ping to 1.6 bungee 2013-07-01 21:20:18 +10:00
md_5
5c4ea3c7a0 Solve long standing issue of creating too many TCP packets. This fix works very effectively. 2013-07-01 17:38:50 +10:00
md_5
632fa8bd94 Partially support forced_hosts MOTD without SRV records - gonna think how best to put this in the config, for now its server: motd:, might remove listener motd later on 2013-07-01 14:05:57 +10:00
md_5
8732904bfd Add stream helper to PacketFA 2013-07-01 13:45:36 +10:00
md_5
788b96dc0a knohacks - thanks @ammaraskar 2013-07-01 13:37:03 +10:00
md_5
1296783d9b Update to Minecraft 1.6.1 2013-07-01 13:19:18 +10:00
md_5
a9603a6372 Bump Javassist version 2013-06-29 15:04:13 +10:00
md_5
b15ed87ad5 Netty CR7 m8 2013-06-29 15:02:38 +10:00
md_5
3e816f628b Update to Netty CR6 2013-06-27 16:22:06 +10:00
md_5
7bfc4bf819 Remove outbound boss for now 2013-06-27 10:16:23 +10:00
David Marby
f8d15f4c88 Fix bad packet ID with bows 2013-06-27 10:14:34 +10:00
md_5
a73b06eee3 Close #462 - shutdown gracefully 2013-06-26 20:48:13 +10:00
md_5
2069679140 Silence JLine errors 2013-06-25 11:29:47 +10:00
md_5
9a173968f1 Update to Netty CR3 but include workaround for (bug?) present in it. Feedback is welcome, #448 is related. 2013-06-23 10:40:27 +10:00
AgentK
13f1fa7443 Reject clients on other protocol versions. 2013-06-21 16:36:37 +10:00
md_5
0f7da279ef Close #450 - errors from our backport 2013-06-19 13:30:43 +10:00
md_5
a6ba661a32 Back to CR1 we go. Deal with the issues. 2013-06-19 07:36:40 +10:00
md_5
22133bc8d2 Close #445 - error when clients use forge 2013-06-18 21:10:16 +10:00
md_5
f9c9517958 Why can no one ever provide helpful information to attempt to diagnose a bug, it is ridiculous that you can expect my help when you don't even provide a version number.
I am seriously just tired of this and need a break.
2013-06-18 20:52:18 +10:00
md_5
7a79bd0816 Update to Netty CR5, boasts very nice performance and should hopefully fix many of the issues we have seen. 2013-06-18 17:14:34 +10:00
md_5
6a60376033 If #438 does not go away, then BungeeCord users are derps, since this class has no been reverted to the exact same state before the so called issue inducing commit 2013-06-17 19:10:38 +10:00
md_5
4ce0eee232 #438 please just go away. 2013-06-17 17:25:20 +10:00
md_5
72f3a79759 Do what we can about Jline not being installed 2013-06-17 17:05:02 +10:00
md_5
dbb6aebf58 #3 windows fix 2013-06-17 16:29:21 +10:00
md_5
54040ec48d Windows fix #2 2013-06-17 16:17:50 +10:00
md_5
8c4ddf458c Fix #1 for windows. 2013-06-17 16:13:27 +10:00
md_5
07fb6490f8 Close issue #440 - players remaining after logout 2013-06-17 14:19:16 +10:00
Robin Lambertz
d9eb8c66b8 Change order of boolean so the latch is decremented all the time 2013-06-17 14:16:54 +10:00
md_5
7fab3ba372 Try twice to init jline 2013-06-16 21:27:15 +10:00
md_5
92c3ef1989 Fix custom tab API to allow using as soon as constructed 2013-06-16 15:40:31 +10:00
md_5
fbf2d8969e Exception caught should rely on channel activity state, not OUR close flag 2013-06-16 11:30:03 +10:00
md_5
1881507712 Move scoreboard stuff to the sscore package in preparation for refactor 2013-06-16 09:10:25 +10:00
md_5
fd2a72477f Move tab list stuff to the 'tab' package 2013-06-16 09:08:48 +10:00
md_5
d4cbac1bdf Add tab list getter to api 2013-06-16 07:56:38 +10:00
md_5
fa0671ab2a Finish up TabApi impl. 2013-06-16 07:55:15 +10:00
md_5
184154a8b3 Close issue #437 2013-06-16 07:26:29 +10:00
md_5
cbec4e836a Harsher reload warning 2013-06-15 21:12:15 +10:00
md_5
3ce7982778 Clean up pipeline flow. 2013-06-15 21:08:49 +10:00
md_5
b55944e2fb Dont spam the console with too many exceptions 2013-06-14 07:31:58 +10:00
md_5
12cba14657 Tweak our channel promise to be a bit more hellpful on errors 2013-06-13 20:53:35 +10:00
md_5
78e67283cc Roblabla feels listeners should be at the top 2013-06-11 20:23:48 +10:00
md_5
f0f1e71c93 Implement super sexy console to close #315 2013-06-11 18:55:15 +10:00
md_5
3c1a5aabfd Add translation + fix spelling for mojang servers down 2013-06-11 10:29:19 +10:00
md_5
f0d4e8c24a I feel like the time for change is here 2013-06-10 14:55:57 +10:00
md_5
ba8bd7faf0 Try to cut off packet race conditions when moving servers. 2013-06-10 08:54:52 +10:00
md_5
787692070e Set a row limit to stop at, we should shrink this after a while. Meh api is good enough for me 2013-06-08 15:48:26 +10:00
md_5
523e991018 Doesnt allow ALL possible variations of a string, but its good enough for now 2013-06-08 15:34:30 +10:00
md_5
7733fbfb28 Make tab list work! 2013-06-08 14:43:03 +10:00
md_5
44ac36941f Use 1 based index 2013-06-08 14:10:15 +10:00
md_5
0235c4a01e Make sure to init the tab list 2013-06-08 14:08:48 +10:00
md_5
b4220e9229 Refactor all the tab APIs 2013-06-08 14:06:09 +10:00
md_5
9b9addfccd Add interfaces for custom TabAPI, just need to add the hooks now, and of course a pretty example. As always, not tested yet. 2013-06-08 13:51:23 +10:00
md_5
b75a2b5060 [Breaking] Close #423 by making tab list per listener. 2013-06-08 13:13:17 +10:00
md_5
b5aecd5dcc Stab at fixing forge and wecui support when combined. F**** I hate mods. 2013-06-08 12:35:50 +10:00
md_5
4d51d16512 Fix mods like wecui not working after switching servers.
#364
2013-06-08 09:45:45 +10:00
md_5
05a9342854 Ramp up warning in preparation for breaking commit 2013-06-06 20:23:50 +10:00
md_5
483fede234 Work around windows bugs 2013-06-06 18:13:56 +10:00
md_5
ce8f1b44b6 Musical versions 2013-06-05 20:30:34 +10:00
md_5
b1e3f6a75b And people think dependancy loading is easy. Close #381 2013-06-05 18:24:33 +10:00
DerFlash
33d315b719 Tone down PingHandler too 2013-06-04 07:42:13 +10:00
md_5
d11e130d61 Close #365 - prettier error when mc.net is down 2013-06-03 19:54:41 +10:00
md_5
45a93c8cfc Close #417: whois -> find 2013-06-03 19:49:35 +10:00
md_5
fd411edddb Tone down logging for surprisingly large reduction in CPU usage - Closes #401 2013-06-03 19:48:21 +10:00
md_5
ac5e8dbaff Fix the bad packets! Naughty naughty packets! 2013-06-03 19:35:38 +10:00
md_5
340d82812a Reorder checks to prevent malformed packets throwing errors 2013-06-02 22:19:49 +10:00
md_5
eaf99cf4a6 Yo dawg. Lets start populating the seen row 2013-06-02 17:11:44 +10:00
md_5
4baae5a230 Add space to whois 2013-06-02 14:39:51 +10:00
md_5
aa1a871967 Actually use our threadLocal. Do'h 2013-06-02 14:33:47 +10:00
md_5
18f5ed3102 Close #410 - forge crashes 2013-06-02 10:30:44 +10:00
md_5
d1dd7379b1 Use cleaner equals check and actually set member field 2013-06-02 10:23:21 +10:00
md_5
0b0d09427d Proper case sensitivity + dont reverse lookup forced hosts 2013-06-02 09:23:05 +10:00
md_5
dce0f6b408 Missed some getters 2013-06-01 18:01:04 +10:00
md_5
c5307c4451 Dont use getters in team packet and add null check to team name 2013-06-01 18:00:17 +10:00
md_5
4f2b98188e Don't allow stupid users to connect bungee to themselves 2013-06-01 17:55:51 +10:00
md_5
d5eb37c7a6 Add debug to tryFailure 2013-06-01 17:46:11 +10:00
md_5
2a421cdd8d Close #306 use SQLite for reconnect locations 2013-06-01 17:29:14 +10:00
md_5
757f8f0cb9 Fix comparisons of objects in case insensitive hashmap 2013-06-01 16:07:17 +10:00
md_5
388d2620db Fix forge support with new protocol - closes #407 2013-06-01 12:55:02 +10:00
md_5
3ba52cb98b Might need a better way to do this...... oh well, add all the netbeans code style files 2013-06-01 11:12:03 +10:00
md_5
e652214071 Close issue #406 - tab list 2013-06-01 11:10:56 +10:00
md_5
0821404f92 Add netbeans config file 2013-06-01 11:09:46 +10:00
md-5
11b90b91b7 Update ServerConnection.java 2013-05-31 20:45:00 +10:00
md-5
aa3989db19 damn web editor 2013-05-31 19:26:15 +11:00
md-5
76c914db14 Update InitialHandler.java 2013-05-31 18:22:19 +10:00
md_5
639e5f3c1d Add 'unsafe' api for things like packet sending that may be implementation specific or break at any time 2013-05-31 17:02:45 +10:00
md_5
9fd69068ae [TESTING] Merge protocol api into master. This should provide slightly better performance and allow more flexibility for plugin developers. 2013-05-31 16:54:19 +10:00
md_5
9c35cad824 Will do final tests tomorrow, but all seems to work. Yay for efficiency and options for plugin developers! 2013-05-30 19:36:43 +10:00
md_5
d82b29e15a Finish up protocol API - we now compile again. Extensive testing is required, but that is for another day. 2013-05-30 19:11:05 +10:00
md_5
9b0c827c37 Now just to implement the necessary constructors and constants 2013-05-30 18:29:59 +10:00
md_5
125d3f07f7 Fix up failing test 2013-05-30 18:15:10 +10:00
md_5
2f45f0d578 Rework protocol system 2013-05-30 18:09:46 +10:00
md_5
ad4c143ce4 Finish and create passing unit tests for the integrity of all packet classes. 2013-05-30 17:34:56 +10:00
md_5
835e4e332c Start work on more efficient, publically accessable packet API 2013-05-30 16:38:53 +10:00
md_5
0578f94522 Rework shutdown sequence to close #391 2013-05-30 16:23:02 +10:00
md_5
0cd4c9030c Close #396 - broken API spec for "ALL" server 2013-05-29 12:03:41 +10:00
md_5
0d666168f0 Close #398 by printing debug so we can identify the issue if it arises again 2013-05-29 12:02:06 +10:00
md_5
cfb823f077 Close #395 - work around trove quirks 2013-05-29 12:00:57 +10:00
md_5
36a5e89ff9 I told you so 2013-05-27 19:28:50 +10:00
md_5
bb4e8e29a5 Update Netty version and remove our workaround - if this breaks, @mibby keeps the pieces. 2013-05-27 18:22:59 +10:00
md_5
8e34e038d6 Remove chat event firing when we get a message from server to client, as Mojang has decided to completely break this in the next major Minecraft release. 2013-05-26 21:24:32 +10:00
md_5
d1950389cc Just leave field as map... 2013-05-26 12:26:26 +10:00
md_5
9d841bb91a Store config in a case insensitive map 2013-05-26 12:25:46 +10:00
zSwayz
828cebcc4b Sexified
Pls add D:
2013-05-26 09:28:12 +10:00
md_5
12fec2fcdd Add some not null checks to API methods 2013-05-25 17:26:54 +10:00
md_5
8b6b134662 Maybe one day we will want to set this null - it can start null, so it can become null 2013-05-25 17:24:37 +10:00
md_5
538beb33a6 Remove now redundant field from InitialHandler 2013-05-25 17:22:43 +10:00
md_5
97338cbfad Its impossible unless you have a creative server and no mobs and no nothing, but we must allow 0 as an entity ID 2013-05-25 17:18:03 +10:00
md_5
3e28decef2 Remove getServer from the api - long depreceated 2013-05-25 17:09:29 +10:00
md_5
f93b647df3 Move protocol version declaration 2013-05-25 17:03:00 +10:00
md_5
775ffdc998 Optimize online count and broadcast methods 2013-05-25 17:01:39 +10:00
md_5
80c22027de Slightly more optimized getChannels 2013-05-25 16:52:41 +10:00
md_5
122987dd83 No space for lost connection translation 2013-05-25 16:50:39 +10:00
md_5
ac4bab2425 More case insensitive tests and read write lock for connections 2013-05-25 16:50:04 +10:00
md_5
a51ffb1f4c Use our own promise to work around @netty pipeline issues 2013-05-25 11:55:54 +10:00
md_5
77e0dcc7f8 Dont throw exceptions on missing translations 2013-05-25 11:54:17 +10:00
md_5
ddb93fd988 That was an easy test - just had time to write 2013-05-24 14:45:33 +10:00
md_5
7eac22d362 Make perms case insensitive - need to write unit test still 2013-05-24 14:44:40 +10:00
md_5
185dc97ca6 *chatcolor import 2013-05-24 14:41:15 +10:00
md_5
e0d19cf305 Show current server in server command 2013-05-24 14:37:40 +10:00
md_5
0e9002091b Add whois command 2013-05-24 14:35:27 +10:00
md_5
9fdcded97f Close #376 - case insensitive servers and maps 2013-05-24 14:31:31 +10:00
md_5
32fdc83841 Close #383 - swallow exceptions once and for all 2013-05-24 14:16:43 +10:00
md_5
1bf126d4f8 Close #384 - reset locations.yml on error 2013-05-24 14:12:50 +10:00
md_5
56533c6259 Close issue #374 - take a lock when checking channel state / writing 2013-05-23 13:49:58 +10:00
md-5
4cb46c6e5c Merge pull request #372 from roblabla/patch-2
Add global PlayerCount if target is "ALL"
2013-05-22 05:01:48 -07:00
md_5
6decf860c9 Update warning 2013-05-22 21:01:52 +10:00
md_5
29f22f9be9 Just swallow the error because thats what we did before. 2013-05-22 18:27:53 +10:00
md_5
98860ffd02 SLightly more atomic locations.yml saving to guard against ctrl+c'ing users 2013-05-22 17:07:46 +10:00
md_5
2c225a05e7 Add atomic close tracking. Closes #370. 2013-05-22 09:24:55 +10:00
Robin Lambertz
c1dfd0fb7b Add global PlayerCount if target is "ALL"
This allows bukkit servers to get the global bungeecord player count.
2013-05-21 22:14:00 +02:00
md_5
9be44d51a6 Update to netty CR3 2013-05-21 11:30:05 +10:00
md_5
2a2c2717d5 Connect via bound address - closes #337.
Blame JacobiCarter if this breaks
2013-05-19 18:14:59 +10:00
md_5
3f994a1c4c Downgrade to @netty CR1 2013-05-19 18:02:01 +10:00
md_5
9a0da50e6c Fix formatting 2013-05-16 16:49:14 +10:00
md_5
67fdc830c2 Protected access please 2013-05-16 16:47:21 +10:00
md_5
64e8a79551 Close #348 - translation key 2013-05-16 06:40:01 +10:00
md_5
afc387ce0d Set local address to listener address, closes #337 (reverse-merged from commit 57793e93f0) 2013-05-16 06:39:29 +10:00
md_5
8a70af5293 Clean up code style surrounding bootstrap creation 2013-05-15 19:08:14 +10:00
md_5
57793e93f0 Set local address to listener address, closes #337 2013-05-15 19:05:38 +10:00
md_5
a48ef137bd Make connect event implement cancellable, closes #338 2013-05-15 19:04:22 +10:00
md_5
ff32d29e09 Gracefully shutdown event loop, closes #346 2013-05-15 19:02:10 +10:00
md_5
9f3359f8fa Thanks Lex! Closes #319 2013-05-15 18:59:13 +10:00
Jacobi Carter
539fccb873 The client handles the server sending the same score multiple times to overwrite the previous entry. 2013-05-14 18:52:12 +10:00
md_5
b25c81daf3 Update to latest netty, fix event bus bug, comment and rework PacketDecoder to new netty for better performance 2013-05-14 18:32:30 +10:00
md_5
21a354fa75 Add home grown event bus 2013-05-14 11:38:39 +10:00
mickare
aefe3333a9 Add per plugin loggers 2013-05-14 11:19:01 +10:00
md_5
0afefa8f61 Allow nested event dispatch. Yet another thing which I should one day try and PR to Guava 2013-05-13 18:39:45 +10:00
md_5
834ac24b38 Add EventBus test, which fails, now to fix! 2013-05-13 18:36:12 +10:00
md_5
c465eca03b Just escape utf chars 2013-05-12 22:01:42 +10:00
md_5
beb0bf9836 Fu*** offline mode users 2013-05-12 16:09:21 +10:00
md_5
688c42219c Actually translate message 2013-05-12 15:55:51 +10:00
md_5
1ea53f01aa Add a series of new translations 2013-05-12 13:40:43 +10:00
md_5
202dab5c98 Add texture pack API 2013-05-12 09:28:36 +10:00
md_5
49ea7f908f Add server switch event 2013-05-12 09:15:17 +10:00
Harry
9d3bddedb6 Return if command should not be executed to avoid exceptions and unnecessary messages to the player. 2013-05-06 07:22:56 +10:00
md_5
332bdaaec0 Refactor forge support - closes #318 2013-05-05 08:31:44 +10:00
md_5
904a1bfaa3 *register channels. This fixes plugins being broke 2013-05-04 10:20:53 +10:00
md_5
5eb7a6eba7 Fix forge support - closes #312 2013-05-04 09:40:10 +10:00
md_5
8e262cf428 Close issue #311 - exception feedback on server connector 2013-05-04 09:28:28 +10:00
md_5
125df5c22d Add SQLite driver for future use 2013-05-03 21:25:47 +10:00
md_5
7b631092f5 Add experimental Forge support. This may cause issues when using Vanilla clients etc, so caution is advised. Please visit GitHub to report any issues you encounter. Thanks @LexManos for providing the basis for this implementation. 2013-05-03 21:21:55 +10:00
md_5
d3c1339cc9 Make sure we write out custom login packets 2013-05-03 20:36:55 +10:00
md_5
679bf2fca9 Synchronize on pending packet queue, add forge/no forge constructor argument to login packet, and don't send channel registers twice 2013-05-03 20:29:36 +10:00
md_5
7436621481 Refactor encryption to be two step like vanilla. Thanks @LexManos for pointing this out. 2013-05-03 19:35:00 +10:00
md_5
6236cff658 Refactor encrypt util class in preparation for forge support. 2013-05-03 19:10:54 +10:00
md_5
6b504d9160 Use faster collections for the various tab lists. 2013-05-03 18:20:10 +10:00
md_5
d1124ca70b Cleanup imports 2013-05-03 14:39:25 +10:00
md_5
779582d441 Use multimap in scheduler 2013-05-03 14:33:04 +10:00
md_5
b91564f77a Fix eclipse gitignore, I think - don't use eclipse 2013-05-03 14:24:35 +10:00
Zach Bruggeman
30b2e5008b Add ResourceBundle localization 2013-05-03 14:22:12 +10:00
md_5
140830efe0 Close #300 - cleaner disconnects when server is full 2013-05-03 14:16:48 +10:00
md_5
5f8e76c61c Revert "ConcurrentHashMap is junk - lets stick to standard unless issues arise."
This reverts commit 5d1a2c59a7 and closes #304
2013-05-03 14:15:23 +10:00
md_5
b7511abfda Update to 1.5.2, closes #302 2013-05-02 07:32:45 +10:00
175 changed files with 5146 additions and 2575 deletions

5
.gitignore vendored
View File

@@ -1,12 +1,11 @@
# Eclipse stuff
.classpath/
.project/
.classpath
.project
.settings/
# netbeans
nbproject/
nbactions.xml
nb-configuration.xml
# we use maven!
build.xml

31
api/nb-configuration.xml Normal file
View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -6,13 +6,13 @@
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-API</name>
@@ -26,9 +26,15 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.7.14</version>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@@ -1,111 +0,0 @@
/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.eventbus;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.UncheckedExecutionException;
import java.lang.reflect.Method;
import java.util.Set;
/**
* A {@link HandlerFindingStrategy} for collecting all event handler methods that are marked with
* the {@link Subscribe} annotation.
*
* @author Cliff Biffle
* @author Louis Wasserman
*/
class AnnotatedHandlerFinder implements HandlerFindingStrategy {
/**
* A thread-safe cache that contains the mapping from each class to all methods in that class and
* all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all
* instances of this class; this greatly improves performance if multiple EventBus instances are
* created and objects of the same class are registered on all of them.
*/
private static final LoadingCache<Class<?>, ImmutableList<Method>> handlerMethodsCache =
CacheBuilder.newBuilder()
.weakKeys()
.build(new CacheLoader<Class<?>, ImmutableList<Method>>() {
@Override
public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {
return getAnnotatedMethodsInternal(concreteClass);
}
});
/**
* {@inheritDoc}
*
* This implementation finds all methods marked with a {@link Subscribe} annotation.
*/
@Override
public Multimap<Class<?>, EventHandler> findAllHandlers(Object listener) {
Multimap<Class<?>, EventHandler> methodsInListener = HashMultimap.create();
Class<?> clazz = listener.getClass();
for (Method method : getAnnotatedMethods(clazz)) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> eventType = parameterTypes[0];
EventHandler handler = new EventHandler(listener, method);
methodsInListener.put(eventType, handler);
}
return methodsInListener;
}
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
try {
return handlerMethodsCache.getUnchecked(clazz);
} catch (UncheckedExecutionException e) {
throw Throwables.propagate(e.getCause());
}
}
private static ImmutableList<Method> getAnnotatedMethodsInternal(Class<?> clazz) {
Set<? extends Class<?>> supers = TypeToken.of(clazz).getTypes().rawTypes();
ImmutableList.Builder<Method> result = ImmutableList.builder();
for (Method method : clazz.getMethods()) {
/*
* Iterate over each distinct method of {@code clazz}, checking if it is annotated with
* @Subscribe by any of the superclasses or superinterfaces that declare it.
*/
for (Class<?> c : supers) {
try {
Method m = c.getMethod(method.getName(), method.getParameterTypes());
if (m.isAnnotationPresent(Subscribe.class)) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new IllegalArgumentException("Method " + method
+ " has @Subscribe annotation, but requires " + parameterTypes.length
+ " arguments. Event handler methods must require a single argument.");
}
Class<?> eventType = parameterTypes[0];
result.add(method);
break;
}
} catch (NoSuchMethodException ignored) {
// Move on.
}
}
}
return result.build();
}
}

View File

@@ -0,0 +1,46 @@
package net.md_5.bungee.api;
import com.google.common.base.Preconditions;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public abstract class AbstractReconnectHandler implements ReconnectHandler
{
@Override
public ServerInfo getServer(ProxiedPlayer player)
{
ServerInfo server = getForcedHost( player.getPendingConnection() );
if ( server == null )
{
server = getStoredServer( player );
if ( server == null )
{
server = ProxyServer.getInstance().getServerInfo( player.getPendingConnection().getListener().getDefaultServer() );
}
Preconditions.checkState( server != null, "Default server not defined" );
}
return server;
}
public static ServerInfo getForcedHost(PendingConnection con)
{
if ( con.getVirtualHost() == null )
{
return null;
}
String forced = con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() );
if ( forced == null && con.getListener().isForceDefault() )
{
forced = con.getListener().getDefaultServer();
}
return ProxyServer.getInstance().getServerInfo( forced );
}
protected abstract ServerInfo getStoredServer(ProxiedPlayer player);
}

View File

@@ -2,7 +2,6 @@ package net.md_5.bungee.api;
import net.md_5.bungee.api.plugin.PluginManager;
import com.google.common.base.Preconditions;
import com.ning.http.client.AsyncHttpClient;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.Collection;
@@ -12,9 +11,9 @@ import lombok.Getter;
import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.api.tab.CustomTabList;
public abstract class ProxyServer
{
@@ -49,6 +48,13 @@ public abstract class ProxyServer
*/
public abstract String getVersion();
/**
* Gets a localized string from the .properties file.
*
* @return the localized string
*/
public abstract String getTranslation(String name);
/**
* Gets the main logger which can be used as a suitable replacement for
* {@link System#out} and {@link System#err}.
@@ -72,20 +78,6 @@ public abstract class ProxyServer
*/
public abstract ProxiedPlayer getPlayer(String name);
/**
* Get a server by its name. The instance returned will be taken from a
* player currently on that server to facilitate abstract proxy -> server
* actions.
*
* @param name the name to lookup
* @return the associated server
* @deprecated in most cases the {@link #getServerInfo(java.lang.String)}
* method should be used, as it will return a server even when no players
* are online.
*/
@Deprecated
public abstract Server getServer(String name);
/**
* Return all servers registered to this proxy, keyed by name. Unlike the
* methods in {@link ConfigurationAdapter#getServers()}, this will not
@@ -127,21 +119,6 @@ public abstract class ProxyServer
*/
public abstract void setConfigurationAdapter(ConfigurationAdapter adapter);
/**
* Get the currently in use tab list handle.
*
* @return the tab list handler
*/
public abstract TabListHandler getTabListHandler();
/**
* Set the used tab list handler, should not be changed once players have
* connected
*
* @param handler the tab list handler to set
*/
public abstract void setTabListHandler(TabListHandler handler);
/**
* Get the currently in use reconnect handler.
*
@@ -211,10 +188,11 @@ public abstract class ProxyServer
*
* @param name name of the server
* @param address connectable Minecraft address + port of the server
* @param motd the motd when used as a forced server
* @param restricted whether the server info restricted property will be set
* @return the constructed instance
*/
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted);
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, String motd, boolean restricted);
/**
* Returns the console overlord for this proxy. Being the console, this
@@ -240,11 +218,33 @@ public abstract class ProxyServer
public abstract TaskScheduler getScheduler();
/**
* Gets the the web client used by this proxy to facilitate making web
* requests. Care should be taken to ensure that all operations are non
* blocking where applicable.
* Get the current number of connected users. The default implementation is
* more efficient than {@link #getPlayers()} as it does not take a lock or
* make a copy.
*
* @return the server's {@link AsyncHttpClient} instance
* @return the current number of connected players
*/
public abstract AsyncHttpClient getHttpClient();
public abstract int getOnlineCount();
/**
* Send the specified message to the console and all connected players.
*
* @param message the message to broadcast
*/
public abstract void broadcast(String message);
/**
* Gets a new instance of this proxies custom tab list.
*
* @param player the player to generate this list in the context of
* @return a new {@link CustomTabList} instance
*/
public abstract CustomTabList customTabList(ProxiedPlayer player);
/**
* Gets the commands which are disabled and will not be run on this proxy.
*
* @return the set of disabled commands
*/
public abstract Collection<String> getDisabledCommands();
}

View File

@@ -12,7 +12,7 @@ public interface ReconnectHandler
* @param player the connecting player
* @return the server to connect to
*/
public ServerInfo getServer(ProxiedPlayer player);
ServerInfo getServer(ProxiedPlayer player);
/**
* Save the server of this player before they disconnect so it can be
@@ -20,12 +20,20 @@ public interface ReconnectHandler
*
* @param player the player to save
*/
public void setServer(ProxiedPlayer player);
void setServer(ProxiedPlayer player); // TOOD: String + String arguments?
/**
* Save all pending reconnect locations. Whilst not used for database
* connections, this method will be called at a predefined interval to allow
* the saving of reconnect files.
*/
public void save();
void save();
/**
* Close all connections indicating that the proxy is about to shutdown and
* all data should be saved. No new requests will be made after this method
* has been called.
*
*/
void close();
}

View File

@@ -43,6 +43,15 @@ public interface ConfigurationAdapter
*/
public boolean getBoolean(String path, boolean def);
/**
* Get a list from the specified path.
*
* @param path the path to retrieve the list form.
* @param def the default value
* @return the retrieved list
*/
public Collection<?> getList(String path, Collection<?> def);
/**
* Get the configuration all servers which may be accessible via the proxy.
*

View File

@@ -3,6 +3,7 @@ package net.md_5.bungee.api.config;
import java.net.InetSocketAddress;
import java.util.Map;
import lombok.Data;
import net.md_5.bungee.api.tab.TabListHandler;
/**
* Class representing the configuration of a server listener. Used for allowing
@@ -48,8 +49,16 @@ public class ListenerInfo
*/
private final Map<String, String> forcedHosts;
/**
* Get the texture pack used for servers connected to this proxy. May be
* null.
* Class used to build tab lists for this player.
*/
private final TexturePackInfo texturePack;
private final Class<? extends TabListHandler> tabList;
/**
* Whether to set the local address when connecting to servers.
*/
private final boolean setLocalAddress;
/**
* Whether to pass the ping through when we can reliably get the target
* server (force default server).
*/
private final boolean pingPassthrough;
}

View File

@@ -36,6 +36,13 @@ public interface ServerInfo
*/
Collection<ProxiedPlayer> getPlayers();
/**
* Returns the MOTD which should be used when this server is a forced host.
*
* @return the motd
*/
String getMotd();
/**
* Whether the player can access this server. It will only return false when
* the player has no permission and this server is restricted.

View File

@@ -1,17 +0,0 @@
package net.md_5.bungee.api.config;
import lombok.Data;
@Data
public class TexturePackInfo
{
/**
* The URL of the texture pack.
*/
private final String url;
/**
* The square dimension of this texture pack.
*/
private final int size;
}

View File

@@ -1,6 +1,7 @@
package net.md_5.bungee.api.connection;
import java.net.InetSocketAddress;
import net.md_5.bungee.protocol.packet.DefinedPacket;
/**
* A proxy connection is defined as a connection directly connected to a socket.
@@ -15,7 +16,7 @@ public interface Connection
*
* @return the remote address
*/
public InetSocketAddress getAddress();
InetSocketAddress getAddress();
/**
* Disconnects this end of the connection for the specified reason. If this
@@ -25,5 +26,23 @@ public interface Connection
* @param reason the reason shown to the player / sent to the server on
* disconnect
*/
public void disconnect(String reason);
void disconnect(String reason);
/**
* Get the unsafe methods of this class.
*
* @return the unsafe method interface
*/
Unsafe unsafe();
interface Unsafe
{
/**
* Send a packet to this connection.
*
* @param packet the packet to send
*/
void sendPacket(DefinedPacket packet);
}
}

View File

@@ -2,6 +2,7 @@ package net.md_5.bungee.api.connection;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.tab.TabListHandler;
/**
* Represents a player who's connection is being connected to somewhere else,
@@ -15,7 +16,7 @@ public interface ProxiedPlayer extends Connection, CommandSender
*
* @return the players current display name
*/
public String getDisplayName();
String getDisplayName();
/**
* Sets this players display name to be used as their nametag and tab list
@@ -23,7 +24,7 @@ public interface ProxiedPlayer extends Connection, CommandSender
*
* @param name the name to set
*/
public void setDisplayName(String name);
void setDisplayName(String name);
/**
* Connects / transfers this user to the specified connection, gracefully
@@ -32,21 +33,21 @@ public interface ProxiedPlayer extends Connection, CommandSender
*
* @param target the new server to connect to
*/
public void connect(ServerInfo target);
void connect(ServerInfo target);
/**
* Gets the server this player is connected to.
*
* @return the server this player is connected to
*/
public Server getServer();
Server getServer();
/**
* Gets the ping time between the proxy and this connection.
*
* @return the current ping time
*/
public int getPing();
int getPing();
/**
* Send a plugin message to this player.
@@ -54,19 +55,48 @@ public interface ProxiedPlayer extends Connection, CommandSender
* @param channel the channel to send this data via
* @param data the data to send
*/
public void sendData(String channel, byte[] data);
void sendData(String channel, byte[] data);
/**
* Get the pending connection that belongs to this player.
*
* @return the pending connection that this player used
*/
public PendingConnection getPendingConnection();
PendingConnection getPendingConnection();
/**
* Make this player chat (say something), to the server he is currently on.
*
* @param message the message to say
*/
public void chat(String message);
void chat(String message);
/**
* Sets the new tab list for the user. At this stage it is not advisable to
* change after the user has logged in!
*
* @param list the new list
*/
void setTabList(TabListHandler list);
/**
* Get the current tab list.
*
* @return the tab list in use by this user
*/
TabListHandler getTabList();
/**
* Get the server which this player will be sent to next time the log in.
*
* @return the server, or null if default
*/
ServerInfo getReconnectServer();
/**
* Set the server which this player will be sent to next time the log in.
*
* @param server the server to set
*/
void setReconnectServer(ServerInfo server);
}

View File

@@ -67,7 +67,7 @@ public class AsyncEvent<T> extends Event
{
Preconditions.checkState( intents.contains( plugin ), "Plugin %s has not registered intent for event %s", plugin, this );
intents.remove( plugin );
if ( latch.decrementAndGet() == 0 )
if ( latch.decrementAndGet() == 0 && fired.get() )
{
done.done( (T) this, null );
}

View File

@@ -1,18 +1,17 @@
package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
@Data
@AllArgsConstructor
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class ServerConnectEvent extends Event
public class ServerConnectEvent extends Event implements Cancellable
{
/**
@@ -23,4 +22,14 @@ public class ServerConnectEvent extends Event
* Server the player will be connected to.
*/
private ServerInfo target;
/**
* Cancelled state.
*/
private boolean cancelled;
public ServerConnectEvent(ProxiedPlayer player, ServerInfo target)
{
this.player = player;
this.target = target;
}
}

View File

@@ -33,11 +33,27 @@ public class ServerKickEvent extends Event implements Cancellable
* Server to send player to if this event is cancelled.
*/
private ServerInfo cancelServer;
/**
* State in which the kick occured.
*/
private State state;
public enum State
{
CONNECTING, CONNECTED, UNKNOWN;
}
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer)
{
this( player, kickReason, cancelServer, State.UNKNOWN );
}
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer, State state)
{
this.player = player;
this.kickReason = kickReason;
this.cancelServer = cancelServer;
this.state = state;
}
}

View File

@@ -0,0 +1,22 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
/**
* Called when a player has changed servers.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class ServerSwitchEvent extends Event
{
/**
* Player whom the server is for.
*/
private final ProxiedPlayer player;
}

View File

@@ -2,6 +2,7 @@ package net.md_5.bungee.api.plugin;
import java.io.File;
import java.io.InputStream;
import java.util.logging.Logger;
import lombok.Getter;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ConfigurationAdapter;
@@ -19,6 +20,8 @@ public class Plugin
private ProxyServer proxy;
@Getter
private File file;
@Getter
private Logger logger;
/**
* Called when the plugin has just been loaded. Most of the proxy will not
@@ -73,10 +76,11 @@ public class Plugin
* @param description the description that describes this plugin
* @param jarfile this plugins jar or container
*/
final void init(ProxyServer proxy, PluginDescription description, File file)
final void init(ProxyServer proxy, PluginDescription description)
{
this.proxy = proxy;
this.description = description;
this.file = file;
this.file = description.getFile();
this.logger = new PluginLogger( this );
}
}

View File

@@ -2,13 +2,18 @@ package net.md_5.bungee.api.plugin;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public class PluginClassloader extends URLClassLoader
{
private static final Set<PluginClassloader> allLoaders = new HashSet<>();
private static final Set<PluginClassloader> allLoaders = new CopyOnWriteArraySet<>();
static
{
ClassLoader.registerAsParallelCapable();
}
public PluginClassloader(URL[] urls)
{

View File

@@ -1,5 +1,6 @@
package net.md_5.bungee.api.plugin;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import lombok.AllArgsConstructor;
@@ -35,4 +36,8 @@ public class PluginDescription
* Plugin hard dependencies.
*/
private Set<String> depends = new HashSet<>();
/**
* File we were loaded from.
*/
private File file = null;
}

View File

@@ -0,0 +1,24 @@
package net.md_5.bungee.api.plugin;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import net.md_5.bungee.api.ProxyServer;
public class PluginLogger extends Logger
{
private String pluginName;
protected PluginLogger(Plugin plugin)
{
super( plugin.getClass().getCanonicalName(), null );
pluginName = "[" + plugin.getDescription().getName() + "] ";
}
@Override
public void log(LogRecord logRecord)
{
logRecord.setMessage( pluginName + logRecord.getMessage() );
ProxyServer.getInstance().getLogger().log( logRecord );
}
}

View File

@@ -1,15 +1,20 @@
package net.md_5.bungee.api.plugin;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.EventBus;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.eventbus.Subscribe;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.jar.JarEntry;
@@ -20,7 +25,8 @@ import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.event.EventBus;
import net.md_5.bungee.event.EventHandler;
import org.yaml.snakeyaml.Yaml;
/**
@@ -36,9 +42,19 @@ public class PluginManager
private final ProxyServer proxy;
/*========================================================================*/
private final Yaml yaml = new Yaml();
private final EventBus eventBus = new EventBus();
private final Map<String, Plugin> plugins = new HashMap<>();
private final EventBus eventBus;
private final Map<String, Plugin> plugins = new LinkedHashMap<>();
private final Map<String, Command> commandMap = new HashMap<>();
private Map<String, PluginDescription> toLoad = new HashMap<>();
private Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create();
private Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create();
@SuppressWarnings("unchecked")
public PluginManager(ProxyServer proxy)
{
this.proxy = proxy;
eventBus = new EventBus( proxy.getLogger() );
}
/**
* Register a command so that it may be executed.
@@ -53,6 +69,7 @@ public class PluginManager
{
commandMap.put( alias.toLowerCase(), command );
}
commandsByPlugin.put( plugin, command );
}
/**
@@ -63,6 +80,26 @@ public class PluginManager
public void unregisterCommand(Command command)
{
commandMap.values().remove( command );
commandsByPlugin.values().remove( command );
}
/**
* Unregister all commands owned by a {@link Plugin}
*
* @param plugin the plugin to register the commands of
*/
public void unregisterCommands(Plugin plugin)
{
for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); )
{
commandMap.values().remove( it.next() );
it.remove();
}
}
public boolean dispatchCommand(CommandSender sender, String commandLine)
{
return dispatchCommand( sender, commandLine, null );
}
/**
@@ -73,10 +110,21 @@ public class PluginManager
* arguments
* @return whether the command was handled
*/
public boolean dispatchCommand(CommandSender sender, String commandLine)
public boolean dispatchCommand(CommandSender sender, String commandLine, List<String> tabResults)
{
String[] split = argsSplit.split( commandLine );
Command command = commandMap.get( split[0].toLowerCase() );
// Check for chat that only contains " "
if ( split.length == 0 )
{
return false;
}
String commandName = split[0].toLowerCase();
if ( proxy.getDisabledCommands().contains( commandName ) )
{
return false;
}
Command command = commandMap.get( commandName );
if ( command == null )
{
return false;
@@ -85,14 +133,23 @@ public class PluginManager
String permission = command.getPermission();
if ( permission != null && !permission.isEmpty() && !sender.hasPermission( permission ) )
{
sender.sendMessage( ChatColor.RED + "You do not have permission to execute this command!" );
sender.sendMessage( proxy.getTranslation( "no_permission" ) );
return true;
}
String[] args = Arrays.copyOfRange( split, 1, split.length );
try
{
command.execute( sender, args );
if ( tabResults == null )
{
command.execute( sender, args );
} else if ( command instanceof TabExecutor )
{
for ( String s : ( (TabExecutor) command ).onTabComplete( sender, args ) )
{
tabResults.add( s );
}
}
} catch ( Exception ex )
{
sender.sendMessage( ChatColor.RED + "An internal error occurred whilst executing this command, please check the console log for details." );
@@ -122,23 +179,37 @@ public class PluginManager
return plugins.get( name );
}
/**
* Enable all plugins by calling the {@link Plugin#onEnable()} method.
*/
public void enablePlugins()
public void loadAndEnablePlugins()
{
Map<Plugin, Boolean> pluginStatuses = new HashMap<>();
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() )
Map<PluginDescription, Boolean> pluginStatuses = new HashMap<>();
for ( Map.Entry<String, PluginDescription> entry : toLoad.entrySet() )
{
Plugin plugin = entry.getValue();
if ( !this.enablePlugin( pluginStatuses, new Stack<Plugin>(), plugin ) )
PluginDescription plugin = entry.getValue();
if ( !enablePlugin( pluginStatuses, new Stack<PluginDescription>(), plugin ) )
{
ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() );
}
}
toLoad.clear();
toLoad = null;
for ( Plugin plugin : plugins.values() )
{
try
{
plugin.onEnable();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
} );
} catch ( Throwable t )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
}
}
}
private boolean enablePlugin(Map<Plugin, Boolean> pluginStatuses, Stack<Plugin> dependStack, Plugin plugin)
private boolean enablePlugin(Map<PluginDescription, Boolean> pluginStatuses, Stack<PluginDescription> dependStack, PluginDescription plugin)
{
if ( pluginStatuses.containsKey( plugin ) )
{
@@ -149,21 +220,21 @@ public class PluginManager
boolean status = true;
// try to load dependencies first
for ( String dependName : plugin.getDescription().getDepends() )
for ( String dependName : plugin.getDepends() )
{
Plugin depend = this.plugins.get( dependName );
Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE;
PluginDescription depend = toLoad.get( dependName );
Boolean dependStatus = ( depend != null ) ? pluginStatuses.get( depend ) : Boolean.FALSE;
if ( dependStatus == null )
{
if ( dependStack.contains( depend ) )
{
StringBuilder dependencyGraph = new StringBuilder();
for ( Plugin element : dependStack )
for ( PluginDescription element : dependStack )
{
dependencyGraph.append( element.getDescription().getName() ).append( " -> " );
dependencyGraph.append( element.getName() ).append( " -> " );
}
dependencyGraph.append( plugin.getDescription().getName() ).append( " -> " ).append( dependName );
dependencyGraph.append( plugin.getName() ).append( " -> " ).append( dependName );
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph );
status = false;
} else
@@ -178,7 +249,7 @@ public class PluginManager
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
{
depend.getDescription().getName(), plugin.getDescription().getName()
String.valueOf( depend.getName() ), plugin.getName()
} );
status = false;
}
@@ -194,15 +265,23 @@ public class PluginManager
{
try
{
plugin.onEnable();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
URLClassLoader loader = new PluginClassloader( new URL[]
{
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
plugin.getFile().toURI().toURL()
} );
Class<?> main = loader.loadClass( plugin.getMain() );
Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance();
clazz.init( proxy, plugin );
plugins.put( plugin.getName(), clazz );
clazz.onLoad();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
plugin.getName(), plugin.getVersion(), plugin.getAuthor()
} );
} catch ( Throwable t )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
status = false;
proxy.getLogger().log( Level.WARNING, "Error enabling plugin " + plugin.getName(), t );
}
}
@@ -210,51 +289,12 @@ public class PluginManager
return status;
}
/**
* Load a plugin from the specified file. This file must be in jar format.
* This will not enable plugins, {@link #enablePlugins()} must be called.
*
* @param file the file to load from
* @throws Exception Any exceptions encountered when loading a plugin from
* this file.
*/
public void loadPlugin(File file) throws Exception
{
Preconditions.checkNotNull( file, "file" );
Preconditions.checkArgument( file.isFile(), "Must load from file" );
try ( JarFile jar = new JarFile( file ) )
{
JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
try ( InputStream in = jar.getInputStream( pdf ) )
{
PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
URLClassLoader loader = new PluginClassloader( new URL[]
{
file.toURI().toURL()
} );
Class<?> main = loader.loadClass( desc.getMain() );
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
plugin.init( proxy, desc, file );
plugins.put( desc.getName(), plugin );
plugin.onLoad();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
desc.getName(), desc.getVersion(), desc.getAuthor()
} );
}
}
}
/**
* Load all plugins from the specified folder.
*
* @param folder the folder to search for plugins in
*/
public void loadPlugins(File folder)
public void detectPlugins(File folder)
{
Preconditions.checkNotNull( folder, "folder" );
Preconditions.checkArgument( folder.isDirectory(), "Must load from a directory" );
@@ -263,9 +303,17 @@ public class PluginManager
{
if ( file.isFile() && file.getName().endsWith( ".jar" ) )
{
try
try ( JarFile jar = new JarFile( file ) )
{
loadPlugin( file );
JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
try ( InputStream in = jar.getInputStream( pdf ) )
{
PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
desc.setFile( file );
toLoad.put( desc.getName(), desc );
}
} catch ( Exception ex )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load plugin from file " + file, ex );
@@ -304,13 +352,44 @@ public class PluginManager
/**
* Register a {@link Listener} for receiving called events. Methods in this
* Object which wish to receive events must be annotated with the
* {@link Subscribe} annotation.
* {@link EventHandler} annotation.
*
* @param plugin the owning plugin
* @param listener the listener to register events for
*/
public void registerListener(Plugin plugin, Listener listener)
{
for ( Method method : listener.getClass().getDeclaredMethods() )
{
Preconditions.checkArgument( !method.isAnnotationPresent( Subscribe.class ),
"Listener %s has registered using deprecated subscribe annotation! Please update to @EventHandler.", listener );
}
eventBus.register( listener );
listenersByPlugin.put( plugin, listener );
}
/**
* Unregister a {@link Listener} so that the events do not reach it anymore.
*
* @param listener the listener to unregister
*/
public void unregisterListener(Listener listener)
{
eventBus.unregister( listener );
listenersByPlugin.values().remove( listener );
}
/**
* Unregister all of a Plugin's listener.
*
* @param plugin
*/
public void unregisterListeners(Plugin plugin)
{
for ( Iterator<Listener> it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); )
{
eventBus.unregister( it.next() );
it.remove();
}
}
}

View File

@@ -0,0 +1,10 @@
package net.md_5.bungee.api.plugin;
import net.md_5.bungee.api.CommandSender;
public interface TabExecutor
{
public Iterable<String> onTabComplete(CommandSender sender, String[] args);
}

View File

@@ -1,6 +1,5 @@
package net.md_5.bungee.api.scheduler;
import java.util.concurrent.TimeUnit;
import net.md_5.bungee.api.plugin.Plugin;
/**
@@ -31,11 +30,7 @@ public interface ScheduledTask
Runnable getTask();
/**
* Get the delay in the specified unit before this task will next be
* executed.
*
* @param unit the unit to get the delay in
* @return the time before the next execution of this task
* Cancel this task to suppress subsequent executions.
*/
long getDelay(TimeUnit unit);
void cancel();
}

View File

@@ -1,4 +1,4 @@
package net.md_5.bungee.api.scoreboard;
package net.md_5.bungee.api.score;
import lombok.Data;

View File

@@ -1,4 +1,4 @@
package net.md_5.bungee.api.scoreboard;
package net.md_5.bungee.api.score;
/**
* Represents locations for a scoreboard to be displayed.

View File

@@ -1,4 +1,4 @@
package net.md_5.bungee.api.scoreboard;
package net.md_5.bungee.api.score;
import lombok.Data;

View File

@@ -1,4 +1,4 @@
package net.md_5.bungee.api.scoreboard;
package net.md_5.bungee.api.score;
import com.google.common.base.Preconditions;
import java.util.Collection;
@@ -59,7 +59,6 @@ public class Scoreboard
public void addScore(Score score)
{
Preconditions.checkNotNull( score, "score" );
Preconditions.checkArgument( !scores.containsKey( score.getItemName() ), "Score %s already exists in this scoreboard", score.getItemName() );
scores.put( score.getItemName(), score );
}

View File

@@ -1,20 +1,22 @@
package net.md_5.bungee.api.scoreboard;
package net.md_5.bungee.api.score;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
import lombok.NonNull;
@Data
public class Team
{
@NonNull
private final String name;
private String displayName;
private String prefix;
private String suffix;
private byte friendlyMode;
private boolean friendlyFire;
private Set<String> players = new HashSet<>();
public Collection<String> getPlayers()

View File

@@ -0,0 +1,60 @@
package net.md_5.bungee.api.tab;
/**
* Represents a custom tab list, which may have slots manipulated.
*/
public interface CustomTabList extends TabListHandler
{
/**
* Blank out this tab list and update immediately.
*/
void clear();
/**
* Gets the columns in this list.
*
* @return the width of this list
*/
int getColumns();
/**
* Gets the rows in this list.
*
* @return the height of this list
*/
int getRows();
/**
* Get the total size of this list.
*
* @return {@link #getRows()} * {@link #getColumns()}
*/
int getSize();
/**
* Set the text in the specified slot and update immediately.
*
* @param row the row to set
* @param column the column to set
* @param text the text to set
* @return the padded text
*/
String setSlot(int row, int column, String text);
/**
* Set the text in the specified slot.
*
* @param row the row to set
* @param column the column to set
* @param text the text to set
* @param update whether or not to invoke {@link #update()} upon completion
* @return the padded text
*/
String setSlot(int row, int column, String text, boolean update);
/**
* Flush all queued changes to the user.
*/
void update();
}

View File

@@ -0,0 +1,39 @@
package net.md_5.bungee.api.tab;
import lombok.Getter;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@NoArgsConstructor
public abstract class TabListAdapter implements TabListHandler
{
@Getter
private ProxiedPlayer player;
@Override
public void init(ProxiedPlayer player)
{
this.player = player;
}
@Override
public void onConnect()
{
}
@Override
public void onDisconnect()
{
}
@Override
public void onServerChange()
{
}
@Override
public void onPingChange(int ping)
{
}
}

View File

@@ -1,23 +1,30 @@
package net.md_5.bungee.api;
package net.md_5.bungee.api.tab;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public interface TabListHandler
{
/**
* Called so that this class may set member fields to keep track of its
* internal state. You should not do any packet sending or manipulation of
* the passed player, other than storing it.
*
* @param player the player to be associated with this list
*/
void init(ProxiedPlayer player);
/**
* Called when this player first connects to the proxy.
*/
void onConnect();
/**
* Called when a player first connects to the proxy.
*
* @param player the connecting player
*/
public void onConnect(ProxiedPlayer player);
/**
* Called when a player changes their connected server.
*
* @param player the player who changed servers
*/
public void onServerChange(ProxiedPlayer player);
void onServerChange();
/**
* Called when a players ping changes. The new ping will have not updated in
@@ -26,14 +33,14 @@ public interface TabListHandler
* @param player the player who's ping changed
* @param ping the player's new ping.
*/
public void onPingChange(ProxiedPlayer player, int ping);
void onPingChange(int ping);
/**
* Called when a player disconnects.
*
* @param player the disconnected player
*/
public void onDisconnect(ProxiedPlayer player);
void onDisconnect();
/**
* Called when a list update packet is sent from server to client.
@@ -44,5 +51,5 @@ public interface TabListHandler
* @param ping ping of the subject player
* @return whether to send the packet to the client
*/
public boolean onListUpdate(ProxiedPlayer player, String name, boolean online, int ping);
boolean onListUpdate(String name, boolean online, int ping);
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -6,13 +6,13 @@
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Config</name>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

20
event/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.6.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId>
<version>1.6.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Event</name>
<description>Generic java event dispatching API intended for use with BungeeCord</description>
</project>

View File

@@ -0,0 +1,202 @@
package net.md_5.bungee.event;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
public class EventBus
{
private final Map<Class<?>, Map<Byte, Map<Object, Method[]>>> byListenerAndPriority = new HashMap<>();
private final Map<Class<?>, EventHandlerMethod[]> byEventBaked = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Logger logger;
public EventBus()
{
this( null );
}
public EventBus(Logger logger)
{
this.logger = ( logger == null ) ? Logger.getGlobal() : logger;
}
public void post(Object event)
{
lock.readLock().lock();
try
{
EventHandlerMethod[] handlers = byEventBaked.get( event.getClass() );
if ( handlers != null )
{
for ( EventHandlerMethod method : handlers )
{
try
{
method.invoke( event );
} catch ( IllegalAccessException ex )
{
throw new Error( "Method became inaccessible: " + event, ex );
} catch ( IllegalArgumentException ex )
{
throw new Error( "Method rejected target/argument: " + event, ex );
} catch ( InvocationTargetException ex )
{
logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
}
}
}
} finally
{
lock.readLock().unlock();
}
}
private Map<Class<?>, Map<Byte, Set<Method>>> findHandlers(Object listener)
{
Map<Class<?>, Map<Byte, Set<Method>>> handler = new HashMap<>();
for ( Method m : listener.getClass().getDeclaredMethods() )
{
EventHandler annotation = m.getAnnotation( EventHandler.class );
if ( annotation != null )
{
Class<?>[] params = m.getParameterTypes();
if ( params.length != 1 )
{
logger.log( Level.INFO, "Method {0} in class {1} annotated with {2} does not have single argument", new Object[]
{
m, listener.getClass(), annotation
} );
continue;
}
Map<Byte, Set<Method>> prioritiesMap = handler.get( params[0] );
if ( prioritiesMap == null )
{
prioritiesMap = new HashMap<>();
handler.put( params[0], prioritiesMap );
}
Set<Method> priority = prioritiesMap.get( annotation.priority() );
if ( priority == null )
{
priority = new HashSet<>();
prioritiesMap.put( annotation.priority(), priority );
}
priority.add( m );
}
}
return handler;
}
public void register(Object listener)
{
Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
lock.writeLock().lock();
try
{
for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
{
Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() );
if ( prioritiesMap == null )
{
prioritiesMap = new HashMap<>();
byListenerAndPriority.put( e.getKey(), prioritiesMap );
}
for ( Map.Entry<Byte, Set<Method>> entry : e.getValue().entrySet() )
{
Map<Object, Method[]> currentPriorityMap = prioritiesMap.get( entry.getKey() );
if ( currentPriorityMap == null )
{
currentPriorityMap = new HashMap<>();
prioritiesMap.put( entry.getKey(), currentPriorityMap );
}
Method[] baked = new Method[ entry.getValue().size() ];
currentPriorityMap.put( listener, entry.getValue().toArray( baked ) );
}
bakeHandlers( e.getKey() );
}
} finally
{
lock.writeLock().unlock();
}
}
public void unregister(Object listener)
{
Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
lock.writeLock().lock();
try
{
for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
{
Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() );
if ( prioritiesMap != null )
{
for ( Byte priority : e.getValue().keySet() )
{
Map<Object, Method[]> currentPriority = prioritiesMap.get( priority );
if ( currentPriority != null )
{
currentPriority.remove( listener );
if ( currentPriority.isEmpty() )
{
prioritiesMap.remove( priority );
}
}
}
if ( prioritiesMap.isEmpty() )
{
byListenerAndPriority.remove( e.getKey() );
}
}
bakeHandlers( e.getKey() );
}
} finally
{
lock.writeLock().unlock();
}
}
/**
* Shouldn't be called without first locking the writeLock; intended for use
* only inside {@link #register(java.lang.Object) register(Object)} or
* {@link #unregister(java.lang.Object) unregister(Object)}.
*/
private void bakeHandlers(Class<?> eventClass)
{
Map<Byte, Map<Object, Method[]>> handlersByPriority = byListenerAndPriority.get( eventClass );
if ( handlersByPriority != null )
{
List<EventHandlerMethod> handlersList = new ArrayList<>( handlersByPriority.size() * 2 );
for ( byte value = Byte.MIN_VALUE; value < Byte.MAX_VALUE; value++ )
{
Map<Object, Method[]> handlersByListener = handlersByPriority.get( value );
if ( handlersByListener != null )
{
for ( Map.Entry<Object, Method[]> listenerHandlers : handlersByListener.entrySet() )
{
for ( Method method : listenerHandlers.getValue() )
{
EventHandlerMethod ehm = new EventHandlerMethod( listenerHandlers.getKey(), method );
handlersList.add( ehm );
}
}
}
}
byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) );
} else
{
byEventBaked.put( eventClass, null );
}
}
}

View File

@@ -0,0 +1,26 @@
package net.md_5.bungee.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EventHandler
{
/**
* Define the priority of the event handler.
* <p>
* Event handlers are called in order of priority:
* <ol>
* <li>LOWEST</li>
* <li>LOW</li>
* <li>NORMAL</li>
* <li>HIGH</li>
* <li>HIGHEST</li>
* </ol>
*/
byte priority() default EventPriority.NORMAL;
}

View File

@@ -0,0 +1,21 @@
package net.md_5.bungee.event;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
public class EventHandlerMethod
{
@Getter
private final Object listener;
@Getter
private final Method method;
public void invoke(Object event) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
method.invoke( listener, event );
}
}

View File

@@ -0,0 +1,19 @@
package net.md_5.bungee.event;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* Importance of the {@link EventHandler}. When executing an Event, the handlers
* are called in order of their Priority.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EventPriority
{
public static final byte LOWEST = -64;
public static final byte LOW = -32;
public static final byte NORMAL = 0;
public static final byte HIGH = 32;
public static final byte HIGHEST = 64;
}

View File

@@ -0,0 +1,42 @@
package net.md_5.bungee.event;
import java.util.concurrent.CountDownLatch;
import org.junit.Assert;
import org.junit.Test;
public class EventBusTest
{
private final EventBus bus = new EventBus();
private final CountDownLatch latch = new CountDownLatch( 2 );
@Test
public void testNestedEvents()
{
bus.register( this );
bus.post( new FirstEvent() );
Assert.assertEquals( 0, latch.getCount() );
}
@EventHandler
public void firstListener(FirstEvent event)
{
bus.post( new SecondEvent() );
Assert.assertEquals( 1, latch.getCount() );
latch.countDown();
}
@EventHandler
public void secondListener(SecondEvent event)
{
latch.countDown();
}
public static class FirstEvent
{
}
public static class SecondEvent
{
}
}

View File

@@ -0,0 +1,64 @@
package net.md_5.bungee.event;
import java.util.concurrent.CountDownLatch;
import org.junit.Assert;
import org.junit.Test;
public class EventPriorityTest
{
private final EventBus bus = new EventBus();
private final CountDownLatch latch = new CountDownLatch( 5 );
@Test
public void testPriority()
{
bus.register( this );
bus.register( new EventPriorityListenerPartner() );
bus.post( new PriorityTestEvent() );
Assert.assertEquals( 0, latch.getCount() );
}
@EventHandler(priority = EventPriority.LOWEST)
public void onLowestPriority(PriorityTestEvent event)
{
Assert.assertEquals( 5, latch.getCount() );
latch.countDown();
}
@EventHandler
public void onNormalPriority(PriorityTestEvent event)
{
Assert.assertEquals( 3, latch.getCount() );
latch.countDown();
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onHighestPriority(PriorityTestEvent event)
{
Assert.assertEquals( 1, latch.getCount() );
latch.countDown();
}
public static class PriorityTestEvent
{
}
public class EventPriorityListenerPartner
{
@EventHandler(priority = EventPriority.HIGH)
public void onHighPriority(PriorityTestEvent event)
{
Assert.assertEquals( 2, latch.getCount() );
latch.countDown();
}
@EventHandler(priority = EventPriority.LOW)
public void onLowPriority(PriorityTestEvent event)
{
Assert.assertEquals( 4, latch.getCount() );
latch.countDown();
}
}
}

View File

@@ -0,0 +1,28 @@
package net.md_5.bungee.event;
import org.junit.Assert;
import org.junit.Test;
public class UnregisteringListenerTest
{
private final EventBus bus = new EventBus();
@Test
public void testPriority()
{
bus.register( this );
bus.unregister( this );
bus.post( new TestEvent() );
}
@EventHandler
public void onEvent(TestEvent evt)
{
Assert.fail( "Event listener wasn't unregistered" );
}
public static class TestEvent
{
}
}

31
nb-configuration.xml Normal file
View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

25
pom.xml
View File

@@ -11,16 +11,16 @@
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>BungeeCord</name>
<description>Parent project for all BungeeCord modules.</description>
<url>https://github.com/ElasticPortalSuite/BungeeCord</url>
<url>https://github.com/SpigotMC/BungeeCord</url>
<inceptionYear>2012</inceptionYear>
<organization>
<name>Elastic Portal Suite</name>
<url>https://github.com/ElasticPortalSuite</url>
<url>https://github.com/SpigotMC</url>
</organization>
<licenses>
<license>
@@ -38,18 +38,19 @@
<modules>
<module>api</module>
<module>event</module>
<module>protocol</module>
<module>proxy</module>
</modules>
<scm>
<connection>scm:git:git@github.com:ElasticPortalSuite/BungeeCord.git</connection>
<developerConnection>scm:git:git@github.com:ElasticPortalSuite/BungeeCord.git</developerConnection>
<url>git@github.com:ElasticPortalSuite/BungeeCord.git</url>
<connection>scm:git:git@github.com:SpigotMC/BungeeCord.git</connection>
<developerConnection>scm:git:git@github.com:SpigotMC/BungeeCord.git</developerConnection>
<url>git@github.com:SpigotMC/BungeeCord.git</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/ElasticPortalSuite/BungeeCord/issues</url>
<url>https://github.com/SpigotMC/BungeeCord/issues</url>
</issueManagement>
<ciManagement>
<system>jenkins</system>
@@ -58,15 +59,21 @@
<properties>
<build.number>unknown</build.number>
<netty.version>4.0.0.CR1</netty.version>
<netty.version>4.0.9.Final</netty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>0.11.8</version>
<version>0.12.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -6,13 +6,13 @@
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Protocol</name>
@@ -23,7 +23,7 @@
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
<scope>provided</scope>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,33 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.forge.Forge1Login;
import net.md_5.bungee.protocol.skip.PacketReader;
public class Forge extends Vanilla
{
@Getter
private static final Forge instance = new Forge();
public Forge()
{
classes[0x01] = Forge1Login.class;
skipper = new PacketReader( this ); // TODO: :(
}
@Override
public DefinedPacket read(short packetId, ByteBuf buf)
{
int start = buf.readerIndex();
DefinedPacket packet = read( packetId, buf, this );
if ( buf.readerIndex() == start )
{
packet = super.read( packetId, buf );
}
return packet;
}
}

View File

@@ -0,0 +1,38 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class MinecraftInput
{
private final ByteBuf buf;
public byte readByte()
{
return buf.readByte();
}
public short readUnisgnedByte()
{
return buf.readUnsignedByte();
}
public int readInt()
{
return buf.readInt();
}
public String readString()
{
short len = buf.readShort();
char[] c = new char[ len ];
for ( int i = 0; i < c.length; i++ )
{
c[i] = buf.readChar();
}
return new String( c );
}
}

View File

@@ -0,0 +1,56 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.nio.charset.Charset;
import java.util.Arrays;
public class MinecraftOutput
{
private final ByteBuf buf;
public MinecraftOutput()
{
buf = Unpooled.buffer();
}
public byte[] toArray()
{
if ( buf.hasArray() )
{
return Arrays.copyOfRange( buf.array(), buf.arrayOffset(), buf.arrayOffset() + buf.writerIndex() );
} else
{
byte[] b = new byte[ buf.writerIndex() ];
buf.readBytes( b );
return b;
}
}
public MinecraftOutput writeByte(byte b)
{
buf.writeByte( b );
return this;
}
public void writeInt(int i)
{
buf.writeInt( i );
}
public void writeString(String s)
{
char[] cc = s.toCharArray();
buf.writeShort( cc.length );
for ( char c : cc )
{
buf.writeChar( c );
}
}
public void writeStringUTF8WithoutLengthHeaderBecauseDinnerboneStuffedUpTheMCBrandPacket(String s)
{
buf.writeBytes( s.getBytes( Charset.forName( "UTF-8" ) ) );
}
}

View File

@@ -0,0 +1,7 @@
package net.md_5.bungee.protocol;
public enum OpCode
{
BOOLEAN, BULK_CHUNK, BYTE, BYTE_INT, DOUBLE, FLOAT, INT, INT_3, INT_BYTE, ITEM, LONG, METADATA, OPTIONAL_MOTION, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, USHORT_BYTE, OPTIONAL_WINDOW
}

View File

@@ -0,0 +1,20 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import java.lang.reflect.Constructor;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.skip.PacketReader;
public interface Protocol
{
PacketReader getSkipper();
DefinedPacket read(short packetId, ByteBuf buf);
OpCode[][] getOpCodes();
Class<? extends DefinedPacket>[] getClasses();
Constructor<? extends DefinedPacket>[] getConstructors();
}

View File

@@ -1,38 +1,131 @@
package net.md_5.bungee.protocol;
import static net.md_5.bungee.protocol.PacketDefinitions.OpCode.*;
import io.netty.buffer.ByteBuf;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import lombok.Getter;
import static net.md_5.bungee.protocol.OpCode.*;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
import net.md_5.bungee.protocol.packet.Packet1Login;
import net.md_5.bungee.protocol.packet.Packet2CEntityProperties;
import net.md_5.bungee.protocol.packet.Packet2Handshake;
import net.md_5.bungee.protocol.packet.Packet3Chat;
import net.md_5.bungee.protocol.packet.Packet9Respawn;
import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
import net.md_5.bungee.protocol.packet.PacketCBTabComplete;
import net.md_5.bungee.protocol.packet.PacketCCSettings;
import net.md_5.bungee.protocol.packet.PacketCDClientStatus;
import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
import net.md_5.bungee.protocol.packet.PacketCFScoreboardScore;
import net.md_5.bungee.protocol.packet.PacketD0DisplayScoreboard;
import net.md_5.bungee.protocol.packet.PacketD1Team;
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
import net.md_5.bungee.protocol.packet.PacketFCEncryptionResponse;
import net.md_5.bungee.protocol.packet.PacketFDEncryptionRequest;
import net.md_5.bungee.protocol.packet.PacketFEPing;
import net.md_5.bungee.protocol.packet.PacketFFKick;
import net.md_5.bungee.protocol.skip.PacketReader;
public class PacketDefinitions
public class Vanilla implements Protocol
{
public static final OpCode[][] opCodes = new OpCode[ 512 ][];
public static final int VANILLA_PROTOCOL = 0;
public static final int FORGE_PROTOCOL = 256;
public static final byte PROTOCOL_VERSION = 74;
public static final String GAME_VERSION = "1.6.2";
@Getter
private static final Vanilla instance = new Vanilla();
/*========================================================================*/
@Getter
private final OpCode[][] opCodes = new OpCode[ 256 ][];
@SuppressWarnings("unchecked")
@Getter
protected Class<? extends DefinedPacket>[] classes = new Class[ 256 ];
@SuppressWarnings("unchecked")
@Getter
private Constructor<? extends DefinedPacket>[] constructors = new Constructor[ 256 ];
@Getter
protected PacketReader skipper;
/*========================================================================*/
public enum OpCode
public Vanilla()
{
BOOLEAN, BULK_CHUNK, BYTE, BYTE_INT, DOUBLE, FLOAT, INT, INT_3, INT_BYTE, ITEM, LONG, METADATA, OPTIONAL_MOTION, SCORE, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, TEAM, USHORT_BYTE
classes[0x00] = Packet0KeepAlive.class;
classes[0x01] = Packet1Login.class;
classes[0x02] = Packet2Handshake.class;
classes[0x03] = Packet3Chat.class;
classes[0x09] = Packet9Respawn.class;
classes[0xC9] = PacketC9PlayerListItem.class;
classes[0x2C] = Packet2CEntityProperties.class;
classes[0xCC] = PacketCCSettings.class;
classes[0xCB] = PacketCBTabComplete.class;
classes[0xCD] = PacketCDClientStatus.class;
classes[0xCE] = PacketCEScoreboardObjective.class;
classes[0xCF] = PacketCFScoreboardScore.class;
classes[0xD0] = PacketD0DisplayScoreboard.class;
classes[0xD1] = PacketD1Team.class;
classes[0xFA] = PacketFAPluginMessage.class;
classes[0xFC] = PacketFCEncryptionResponse.class;
classes[0xFD] = PacketFDEncryptionRequest.class;
classes[0xFE] = PacketFEPing.class;
classes[0xFF] = PacketFFKick.class;
skipper = new PacketReader( this );
}
static
@Override
public DefinedPacket read(short packetId, ByteBuf buf)
{
opCodes[0x00] = new OpCode[]
int start = buf.readerIndex();
DefinedPacket packet = read( packetId, buf, this );
if ( buf.readerIndex() == start )
{
INT
};
opCodes[0x01] = new OpCode[]
throw new RuntimeException( "Unknown packet id " + packetId );
}
return packet;
}
public static DefinedPacket read(short id, ByteBuf buf, Protocol protocol)
{
DefinedPacket packet = packet( id, protocol );
if ( packet != null )
{
INT, STRING, BYTE, BYTE, BYTE, BYTE, BYTE
};
opCodes[0x02] = new OpCode[]
packet.read( buf );
return packet;
}
protocol.getSkipper().tryRead( id, buf );
return null;
}
public static DefinedPacket packet(short id, Protocol protocol)
{
DefinedPacket ret = null;
Class<? extends DefinedPacket> clazz = protocol.getClasses()[id];
if ( clazz != null )
{
BYTE, STRING, STRING, INT
};
opCodes[0x03] = new OpCode[]
{
STRING
};
try
{
Constructor<? extends DefinedPacket> constructor = protocol.getConstructors()[id];
if ( constructor == null )
{
constructor = clazz.getDeclaredConstructor();
constructor.setAccessible( true );
protocol.getConstructors()[id] = constructor;
}
if ( constructor != null )
{
ret = constructor.newInstance();
}
} catch ( NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex )
{
}
}
return ret;
}
{
opCodes[0x04] = new OpCode[]
{
LONG, LONG
@@ -51,11 +144,7 @@ public class PacketDefinitions
};
opCodes[0x08] = new OpCode[]
{
SHORT, SHORT, FLOAT
};
opCodes[0x09] = new OpCode[]
{
INT, BYTE, BYTE, SHORT, STRING
FLOAT, SHORT, FLOAT
};
opCodes[0x0A] = new OpCode[]
{
@@ -95,7 +184,7 @@ public class PacketDefinitions
};
opCodes[0x13] = new OpCode[]
{
INT, BYTE
INT, BYTE, INT
};
opCodes[0x14] = new OpCode[]
{
@@ -121,6 +210,10 @@ public class PacketDefinitions
{
INT, INT, INT, INT, SHORT
};
opCodes[0x1B] = new OpCode[]
{
FLOAT, FLOAT, BOOLEAN, BOOLEAN
};
opCodes[0x1C] = new OpCode[]
{
INT, SHORT, SHORT, SHORT
@@ -159,7 +252,7 @@ public class PacketDefinitions
};
opCodes[0x27] = new OpCode[]
{
INT, INT
INT, INT, BOOLEAN
};
opCodes[0x28] = new OpCode[]
{
@@ -227,7 +320,7 @@ public class PacketDefinitions
};
opCodes[0x64] = new OpCode[]
{
BYTE, BYTE, STRING, BYTE, BOOLEAN
OPTIONAL_WINDOW
};
opCodes[0x65] = new OpCode[]
{
@@ -273,74 +366,25 @@ public class PacketDefinitions
{
INT, SHORT, INT, BYTE, SHORT_BYTE
};
opCodes[0x85] = new OpCode[]
{
BYTE, INT, INT, INT
};
opCodes[0xC3] = new OpCode[]
{
SHORT, SHORT, INT_BYTE
};
opCodes[0xC8] = new OpCode[]
{
INT, BYTE
};
opCodes[0xC9] = new OpCode[]
{
STRING, BOOLEAN, SHORT
INT, INT
};
opCodes[0xCA] = new OpCode[]
{
BYTE, BYTE, BYTE
BYTE, FLOAT, FLOAT
};
opCodes[0xCB] = new OpCode[]
{
STRING
};
opCodes[0xCC] = new OpCode[]
{
STRING, BYTE, BYTE, BYTE, BOOLEAN
};
opCodes[0xCD] = new OpCode[]
{
BYTE
};
opCodes[0xCE] = new OpCode[]
{
STRING, STRING, BYTE
};
opCodes[0xCF] = new OpCode[]
{
SCORE
};
opCodes[0xD0] = new OpCode[]
{
BYTE, STRING
};
opCodes[0xD1] = new OpCode[]
{
TEAM
};
opCodes[0xFA] = new OpCode[]
{
STRING, SHORT_BYTE
};
opCodes[0xFC] = new OpCode[]
{
SHORT_BYTE, SHORT_BYTE
};
opCodes[0xFD] = new OpCode[]
{
STRING, SHORT_BYTE, SHORT_BYTE
};
opCodes[0xFE] = new OpCode[]
{
BYTE
};
opCodes[0xFF] = new OpCode[]
{
STRING
};
/*========================== Minecraft Forge ===========================*/
opCodes[0x01 + FORGE_PROTOCOL] = new OpCode[]
{
INT, STRING, BYTE, INT, BYTE, BYTE, BYTE
};
}
}

View File

@@ -1,19 +0,0 @@
package net.md_5.bungee.protocol.netty;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
public class Score extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
{
STRING.read( in );
if ( in.readByte() == 0 )
{
STRING.read( in );
INT.read( in );
}
}
}

View File

@@ -1,26 +0,0 @@
package net.md_5.bungee.protocol.netty;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class Team extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
{
STRING.read( in );
byte mode = in.readByte();
if ( mode == 0 || mode == 2 )
{
STRING.read( in );
STRING.read( in );
STRING.read( in );
BYTE.read( in );
}
if ( mode == 0 || mode == 3 || mode == 4 )
{
STRING_ARRAY.read( in );
}
}
}

View File

@@ -1,29 +1,8 @@
package net.md_5.bungee.packet;
package net.md_5.bungee.protocol.packet;
import net.md_5.bungee.netty.ChannelWrapper;
public abstract class PacketHandler
public abstract class AbstractPacketHandler
{
@Override
public abstract String toString();
public void connected(ChannelWrapper channel) throws Exception
{
}
public void disconnected(ChannelWrapper channel) throws Exception
{
}
public void exception(Throwable t) throws Exception
{
}
public void handle(byte[] buf) throws Exception
{
}
public void handle(Packet0KeepAlive alive) throws Exception
{
}
@@ -44,6 +23,10 @@ public abstract class PacketHandler
{
}
public void handle(Packet2CEntityProperties properties) throws Exception
{
}
public void handle(PacketC9PlayerListItem playerList) throws Exception
{
}
@@ -91,4 +74,8 @@ public abstract class PacketHandler
public void handle(PacketFFKick kick) throws Exception
{
}
public void handle(PacketCBTabComplete tabComplete) throws Exception
{
}
}

View File

@@ -0,0 +1,69 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public abstract class DefinedPacket
{
private final int id;
public final int getId()
{
return id;
}
public void writeString(String s, ByteBuf buf)
{
// TODO: Check len - use Guava?
buf.writeShort( s.length() );
for ( char c : s.toCharArray() )
{
buf.writeChar( c );
}
}
public String readString(ByteBuf buf)
{
// TODO: Check len - use Guava?
short len = buf.readShort();
char[] chars = new char[ len ];
for ( int i = 0; i < len; i++ )
{
chars[i] = buf.readChar();
}
return new String( chars );
}
public void writeArray(byte[] b, ByteBuf buf)
{
// TODO: Check len - use Guava?
buf.writeShort( b.length );
buf.writeBytes( b );
}
public byte[] readArray(ByteBuf buf)
{
// TODO: Check len - use Guava?
short len = buf.readShort();
byte[] ret = new byte[ len ];
buf.readBytes( ret );
return ret;
}
public abstract void read(ByteBuf buf);
public abstract void write(ByteBuf buf);
public abstract void handle(AbstractPacketHandler handler) throws Exception;
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
@Override
public abstract String toString();
}

View File

@@ -0,0 +1,38 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet0KeepAlive extends DefinedPacket
{
private int randomId;
private Packet0KeepAlive()
{
super( 0x00 );
}
@Override
public void read(ByteBuf buf)
{
randomId = buf.readInt();
}
@Override
public void write(ByteBuf buf)
{
buf.writeInt( randomId );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,73 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet1Login extends DefinedPacket
{
protected int entityId;
protected String levelType;
protected byte gameMode;
protected int dimension;
protected byte difficulty;
protected byte unused;
protected byte maxPlayers;
protected Packet1Login()
{
super( 0x01 );
}
public Packet1Login(int entityId, String levelType, byte gameMode, byte dimension, byte difficulty, byte unused, byte maxPlayers)
{
this( entityId, levelType, gameMode, (int) dimension, difficulty, unused, maxPlayers );
}
public Packet1Login(int entityId, String levelType, byte gameMode, int dimension, byte difficulty, byte unused, byte maxPlayers)
{
this();
this.entityId = entityId;
this.levelType = levelType;
this.gameMode = gameMode;
this.dimension = dimension;
this.difficulty = difficulty;
this.unused = unused;
this.maxPlayers = maxPlayers;
}
@Override
public void read(ByteBuf buf)
{
entityId = buf.readInt();
levelType = readString( buf );
gameMode = buf.readByte();
dimension = buf.readByte();
difficulty = buf.readByte();
unused = buf.readByte();
maxPlayers = buf.readByte();
}
@Override
public void write(ByteBuf buf)
{
buf.writeInt( entityId );
writeString( levelType, buf );
buf.writeByte( gameMode );
buf.writeByte( dimension );
buf.writeByte( difficulty );
buf.writeByte( unused );
buf.writeByte( maxPlayers );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,49 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import java.util.HashMap;
import java.util.Map;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet2CEntityProperties extends DefinedPacket
{
public Packet2CEntityProperties()
{
super( 0x2C );
}
@Override
public void read(ByteBuf buf)
{
buf.readInt();
int recordCount = buf.readInt();
for ( int i = 0; i < recordCount; i++ )
{
readString( buf );
buf.readDouble();
short size = buf.readShort();
for ( short s = 0; s < size; s++ )
{
buf.skipBytes( 25 ); // long, long, double, byte
}
}
}
@Override
public void write(ByteBuf buf)
{
throw new UnsupportedOperationException();
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,47 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet2Handshake extends DefinedPacket
{
private byte procolVersion;
private String username;
private String host;
private int port;
private Packet2Handshake()
{
super( 0x02 );
}
@Override
public void read(ByteBuf buf)
{
procolVersion = buf.readByte();
username = readString( buf );
host = readString( buf );
port = buf.readInt();
}
@Override
public void write(ByteBuf buf)
{
buf.writeByte( procolVersion );
writeString( username, buf );
writeString( host, buf );
buf.writeInt( port );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,46 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet3Chat extends DefinedPacket
{
private String message;
private Packet3Chat()
{
super( 0x03 );
}
public Packet3Chat(String message)
{
this();
this.message = message;
}
@Override
public void read(ByteBuf buf)
{
message = readString( buf );
}
@Override
public void write(ByteBuf buf)
{
writeString( message, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,58 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet9Respawn extends DefinedPacket
{
private int dimension;
private byte difficulty;
private byte gameMode;
private short worldHeight;
private String levelType;
private Packet9Respawn()
{
super( 0x09 );
}
public Packet9Respawn(int dimension, byte difficulty, byte gameMode, short worldHeight, String levelType)
{
this();
this.dimension = dimension;
this.difficulty = difficulty;
this.gameMode = gameMode;
this.worldHeight = worldHeight;
this.levelType = levelType;
}
@Override
public void read(ByteBuf buf)
{
dimension = buf.readInt();
difficulty = buf.readByte();
gameMode = buf.readByte();
worldHeight = buf.readShort();
levelType = readString( buf );
}
@Override
public void write(ByteBuf buf)
{
buf.writeInt( dimension );
buf.writeByte( difficulty );
buf.writeByte( gameMode );
buf.writeShort( worldHeight );
writeString( levelType, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,52 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketC9PlayerListItem extends DefinedPacket
{
private String username;
private boolean online;
private short ping;
private PacketC9PlayerListItem()
{
super( 0xC9 );
}
public PacketC9PlayerListItem(String username, boolean online, short ping)
{
super( 0xC9 );
this.username = username;
this.online = online;
this.ping = ping;
}
@Override
public void read(ByteBuf buf)
{
username = readString( buf );
online = buf.readBoolean();
ping = buf.readShort();
}
@Override
public void write(ByteBuf buf)
{
writeString( username, buf );
buf.writeBoolean( online );
buf.writeShort( ping );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,56 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketCBTabComplete extends DefinedPacket
{
private String cursor;
private String[] commands;
private PacketCBTabComplete()
{
super( 0xCB );
}
public PacketCBTabComplete(String[] alternatives)
{
this();
commands = alternatives;
}
@Override
public void read(ByteBuf buf)
{
cursor = readString( buf );
}
@Override
public void write(ByteBuf buf)
{
String tab = "";
for ( String alternative : commands )
{
if ( tab.isEmpty() )
{
tab = alternative + " ";
} else
{
tab += "\0" + alternative + " ";
}
}
writeString( tab, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,48 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketCCSettings extends DefinedPacket
{
private String locale;
private byte viewDistance;
private byte chatFlags;
private byte difficulty;
private boolean showCape;
private PacketCCSettings()
{
super( 0xCC );
}
@Override
public void read(ByteBuf buf)
{
locale = readString( buf );
viewDistance = buf.readByte();
chatFlags = buf.readByte();
difficulty = buf.readByte();
showCape = buf.readBoolean();
}
@Override
public void write(ByteBuf buf)
{
writeString( locale, buf );
buf.writeByte( viewDistance );
buf.writeByte( chatFlags );
buf.writeByte( difficulty );
buf.writeBoolean( showCape );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,42 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketCDClientStatus extends DefinedPacket
{
private byte payload;
private PacketCDClientStatus()
{
super( 0xCD );
}
public PacketCDClientStatus(byte payload)
{
this();
this.payload = payload;
}
@Override
public void read(ByteBuf buf)
{
payload = buf.readByte();
}
@Override
public void write(ByteBuf buf)
{
buf.writeByte( payload );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,55 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketCEScoreboardObjective extends DefinedPacket
{
private String name;
private String text;
/**
* 0 to create, 1 to remove.
*/
private byte action;
private PacketCEScoreboardObjective()
{
super( 0xCE );
}
public PacketCEScoreboardObjective(String name, String text, byte action)
{
this();
this.name = name;
this.text = text;
this.action = action;
}
@Override
public void read(ByteBuf buf)
{
name = readString( buf );
text = readString( buf );
action = buf.readByte();
}
@Override
public void write(ByteBuf buf)
{
writeString( name, buf );
writeString( text, buf );
buf.writeByte( action );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,56 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketCFScoreboardScore extends DefinedPacket
{
private String itemName;
/**
* 0 = create / update, 1 = remove.
*/
private byte action;
private String scoreName;
private int value;
private PacketCFScoreboardScore()
{
super( 0xCF );
}
@Override
public void read(ByteBuf buf)
{
itemName = readString( buf );
action = buf.readByte();
if ( action != 1 )
{
scoreName = readString( buf );
value = buf.readInt();
}
}
@Override
public void write(ByteBuf buf)
{
writeString( itemName, buf );
buf.writeByte( action );
if ( action != 1 )
{
writeString( scoreName, buf );
buf.writeInt( value );
}
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,44 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketD0DisplayScoreboard extends DefinedPacket
{
/**
* 0 = list, 1 = side, 2 = below.
*/
private byte position;
private String name;
private PacketD0DisplayScoreboard()
{
super( 0xD0 );
}
@Override
public void read(ByteBuf buf)
{
position = buf.readByte();
name = readString( buf );
}
@Override
public void write(ByteBuf buf)
{
buf.writeByte( position );
writeString( name, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,92 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketD1Team extends DefinedPacket
{
private String name;
/**
* 0 - create, 1 remove, 2 info update, 3 player add, 4 player remove.
*/
private byte mode;
private String displayName;
private String prefix;
private String suffix;
private boolean friendlyFire;
private short playerCount;
private String[] players;
private PacketD1Team()
{
super( 0xD1 );
}
/**
* Packet to destroy a team.
*
* @param name
*/
public PacketD1Team(String name)
{
this();
this.name = name;
this.mode = 1;
}
@Override
public void read(ByteBuf buf)
{
name = readString( buf );
mode = buf.readByte();
if ( mode == 0 || mode == 2 )
{
displayName = readString( buf );
prefix = readString( buf );
suffix = readString( buf );
friendlyFire = buf.readBoolean();
}
if ( mode == 0 || mode == 3 || mode == 4 )
{
players = new String[ buf.readShort() ];
for ( int i = 0; i < getPlayers().length; i++ )
{
players[i] = readString( buf );
}
}
}
@Override
public void write(ByteBuf buf)
{
writeString( name, buf );
buf.writeByte( mode );
if ( mode == 0 || mode == 2 )
{
writeString( displayName, buf );
writeString( prefix, buf );
writeString( suffix, buf );
buf.writeBoolean( friendlyFire );
}
if ( mode == 0 || mode == 3 || mode == 4 )
{
buf.writeShort( players.length );
for ( int i = 0; i < players.length; i++ )
{
writeString( players[i], buf );
}
}
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,64 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import net.md_5.bungee.protocol.MinecraftInput;
import net.md_5.bungee.protocol.MinecraftOutput;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFAPluginMessage extends DefinedPacket
{
private String tag;
private byte[] data;
private PacketFAPluginMessage()
{
super( 0xFA );
}
public PacketFAPluginMessage(String tag, byte[] data)
{
this();
this.tag = tag;
this.data = data;
}
@Override
public void read(ByteBuf buf)
{
tag = readString( buf );
data = readArray( buf );
}
@Override
public void write(ByteBuf buf)
{
writeString( tag, buf );
writeArray( data, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
public DataInput getStream()
{
return new DataInputStream( new ByteArrayInputStream( data ) );
}
public MinecraftInput getMCStream()
{
return new MinecraftInput( Unpooled.wrappedBuffer( data ) );
}
}

View File

@@ -0,0 +1,48 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFCEncryptionResponse extends DefinedPacket
{
private byte[] sharedSecret;
private byte[] verifyToken;
private PacketFCEncryptionResponse()
{
super( 0xFC );
}
public PacketFCEncryptionResponse(byte[] sharedSecret, byte[] verifyToken)
{
this();
this.sharedSecret = sharedSecret;
this.verifyToken = verifyToken;
}
@Override
public void read(ByteBuf buf)
{
sharedSecret = readArray( buf );
verifyToken = readArray( buf );
}
@Override
public void write(ByteBuf buf)
{
writeArray( sharedSecret, buf );
writeArray( verifyToken, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,52 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFDEncryptionRequest extends DefinedPacket
{
private String serverId;
private byte[] publicKey;
private byte[] verifyToken;
private PacketFDEncryptionRequest()
{
super( 0xFD );
}
public PacketFDEncryptionRequest(String serverId, byte[] publicKey, byte[] verifyToken)
{
this();
this.serverId = serverId;
this.publicKey = publicKey;
this.verifyToken = verifyToken;
}
@Override
public void read(ByteBuf buf)
{
serverId = readString( buf );
publicKey = readArray( buf );
verifyToken = readArray( buf );
}
@Override
public void write(ByteBuf buf)
{
writeString( serverId, buf );
writeArray( publicKey, buf );
writeArray( verifyToken, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,36 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFEPing extends DefinedPacket
{
private byte version;
private PacketFEPing()
{
super( 0xFE );
}
@Override
public void read(ByteBuf buf)
{
version = buf.readByte();
}
@Override
public void write(ByteBuf buf)
{
buf.writeByte( version );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,44 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketFFKick extends DefinedPacket
{
private String message;
private PacketFFKick()
{
super( 0xFF );
}
public PacketFFKick(String message)
{
this();
this.message = message;
}
@Override
public void read(ByteBuf buf)
{
message = readString( buf );
}
@Override
public void write(ByteBuf buf)
{
writeString( message, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,52 @@
package net.md_5.bungee.protocol.packet.forge;
import net.md_5.bungee.protocol.packet.*;
import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Forge1Login extends Packet1Login
{
private Forge1Login()
{
super();
}
public Forge1Login(int entityId, String levelType, byte gameMode, int dimension, byte difficulty, byte unused, byte maxPlayers)
{
super( entityId, levelType, gameMode, dimension, difficulty, unused, maxPlayers );
}
@Override
public void read(ByteBuf buf)
{
entityId = buf.readInt();
levelType = readString( buf );
gameMode = buf.readByte();
dimension = buf.readInt();
difficulty = buf.readByte();
unused = buf.readByte();
maxPlayers = buf.readByte();
}
@Override
public void write(ByteBuf buf)
{
buf.writeInt( entityId );
writeString( levelType, buf );
buf.writeByte( gameMode );
buf.writeInt( dimension );
buf.writeByte( difficulty );
buf.writeByte( unused );
buf.writeByte( maxPlayers );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -1,13 +1,12 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
public class BulkChunk extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
short count = in.readShort();
int size = in.readInt();

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class ByteHeader extends Instruction
{
@@ -14,7 +13,7 @@ class ByteHeader extends Instruction
}
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
byte size = in.readByte();
for ( byte b = 0; b < size; b++ )

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
abstract class Instruction
{
@@ -19,17 +18,16 @@ abstract class Instruction
static final Instruction LONG = new Jump( 8 );
static final Instruction METADATA = new MetaData();
static final Instruction OPTIONAL_MOTION = new OptionalMotion();
static final Instruction SCORE = new Score();
static final Instruction SHORT = new Jump( 2 );
static final Instruction SHORT_BYTE = new ShortHeader( BYTE );
static final Instruction SHORT_ITEM = new ShortHeader( ITEM );
static final Instruction STRING = new ShortHeader( new Jump( 2 ) );
static final Instruction TEAM = new Team();
static final Instruction USHORT_BYTE = new UnsignedShortByte();
static final Instruction OPTIONAL_WINDOW = new OptionalWindow();
// Illegal forward references below this line
static final Instruction BYTE_INT = new ByteHeader( INT );
// Custom instructions
static final Instruction STRING_ARRAY = new ShortHeader( STRING );
abstract void read(ByteBuf in) throws IOException;
abstract void read(ByteBuf in);
}

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class IntHeader extends Instruction
{
@@ -14,7 +13,7 @@ class IntHeader extends Instruction
}
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
int size = in.readInt();
for ( int i = 0; i < size; i++ )

View File

@@ -1,13 +1,12 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class Item extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
short type = in.readShort();
if ( type >= 0 )

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class Jump extends Instruction
{
@@ -18,7 +17,7 @@ class Jump extends Instruction
}
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
in.skipBytes( len );
}

View File

@@ -1,13 +1,12 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class MetaData extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
int x = in.readUnsignedByte();
while ( x != 127 )

View File

@@ -1,13 +1,12 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class OptionalMotion extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
int data = in.readInt();
if ( data > 0 )

View File

@@ -0,0 +1,21 @@
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
public class OptionalWindow extends Instruction
{
@Override
void read(ByteBuf in)
{
BYTE.read( in );
byte type = in.readByte();
STRING.read( in );
BYTE.read( in );
BOOLEAN.read( in );
if ( type == 11 )
{
INT.read( in );
}
}
}

View File

@@ -1,24 +1,24 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.md_5.bungee.protocol.PacketDefinitions;
import net.md_5.bungee.protocol.PacketDefinitions.OpCode;
import net.md_5.bungee.protocol.OpCode;
import net.md_5.bungee.protocol.Protocol;
public class PacketReader
{
private static final Instruction[][] instructions = new Instruction[ PacketDefinitions.opCodes.length ][];
private final Instruction[][] instructions;
static
public PacketReader(Protocol protocol)
{
instructions = new Instruction[ protocol.getOpCodes().length ][];
for ( int i = 0; i < instructions.length; i++ )
{
List<Instruction> output = new ArrayList<>();
OpCode[] enums = PacketDefinitions.opCodes[i];
OpCode[] enums = protocol.getOpCodes()[i];
if ( enums != null )
{
for ( OpCode struct : enums )
@@ -59,35 +59,16 @@ public class PacketReader
}
}
private static void readPacket(int packetId, ByteBuf in, int protocol) throws IOException
public void tryRead(short packetId, ByteBuf in)
{
Instruction[] packetDef = null;
if ( packetId + protocol < instructions.length )
{
packetDef = instructions[packetId + protocol];
}
Instruction[] packetDef = instructions[packetId];
if ( packetDef == null )
if ( packetDef != null )
{
if ( protocol == PacketDefinitions.VANILLA_PROTOCOL )
for ( Instruction instruction : packetDef )
{
throw new IOException( "Unknown packet id " + packetId );
} else
{
readPacket( packetId, in, PacketDefinitions.VANILLA_PROTOCOL );
return;
instruction.read( in );
}
}
for ( Instruction instruction : packetDef )
{
instruction.read( in );
}
}
public static void readPacket(ByteBuf in, int protocol) throws IOException
{
int packetId = in.readUnsignedByte();
readPacket( packetId, in, protocol );
}
}

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class ShortHeader extends Instruction
{
@@ -14,7 +13,7 @@ class ShortHeader extends Instruction
}
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
short size = in.readShort();
for ( short s = 0; s < size; s++ )

View File

@@ -1,13 +1,12 @@
package net.md_5.bungee.protocol.netty;
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
class UnsignedShortByte extends Instruction
{
@Override
void read(ByteBuf in) throws IOException
void read(ByteBuf in)
{
int size = in.readUnsignedShort();
in.skipBytes( size );

View File

@@ -0,0 +1,45 @@
package net.md_5.bungee.protocol;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import net.md_5.bungee.protocol.packet.AbstractPacketHandler;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import org.junit.Assert;
import org.junit.Test;
public class PacketTest
{
@Test
public void testPackets() throws Exception
{
AbstractPacketHandler handler = new AbstractPacketHandler()
{
};
for ( short i = 0; i < 256; i++ )
{
Class<? extends DefinedPacket> clazz = Vanilla.getInstance().getClasses()[ i];
if ( clazz != null )
{
Assert.assertTrue( "Packet " + clazz + " is not public", Modifier.isPublic( clazz.getModifiers() ) );
DefinedPacket packet = Vanilla.packet( i, Vanilla.getInstance() );
Assert.assertTrue( "Could not create packet with id " + i + " and class " + clazz, packet != null );
Assert.assertTrue( "Packet with id " + i + " does not have correct class (expected " + clazz + " but got " + packet.getClass(), packet.getClass() == clazz );
Assert.assertTrue( "Packet " + clazz + " does not report correct id", packet.getId() == i );
Assert.assertTrue( "Packet " + clazz + " does not have custom hash code", packet.hashCode() != System.identityHashCode( packet ) );
Assert.assertTrue( "Packet " + clazz + " does not have custom toString", packet.toString().indexOf( '@' ) == -1 );
// TODO: Enable this test again in v2
// Assert.assertTrue( "Packet " + clazz + " does not have private no args constructor", Modifier.isPrivate( clazz.getDeclaredConstructor().getModifiers() ) );
for ( Field field : clazz.getDeclaredFields() )
{
// TODO: Enable this test again in v2
// Assert.assertTrue( "Packet " + clazz + " has non private field " + field, Modifier.isPrivate( field.getModifiers() ) );
}
packet.handle( handler ); // Make sure there are no exceptions
}
}
}
}

View File

@@ -0,0 +1,14 @@
package net.md_5.bungee.protocol;
import org.junit.Assert;
import org.junit.Test;
public class ProtocolTest
{
@Test
public void testProtocol()
{
Assert.assertFalse( "Protocols should have different login packet", Vanilla.getInstance().getClasses()[0x01] == Forge.getInstance().classes[0x01] );
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -6,31 +6,41 @@
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.5-SNAPSHOT</version>
<version>1.6.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Proxy</name>
<description>Proxy component of the Elastic Portal Suite</description>
<properties>
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
</properties>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<artifactId>netty-codec-http</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
@@ -52,13 +62,25 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.24</version>
<scope>compile</scope>
<version>5.1.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.18.0-GA</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>BungeeCord</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<!-- Don't deploy proxy to maven repo, only APIs -->
@@ -77,6 +99,7 @@
<manifestEntries>
<Main-Class>net.md_5.bungee.BungeeCord</Main-Class>
<Implementation-Version>${describe}</Implementation-Version>
<Specification-Version>${maven.build.timestamp}</Specification-Version>
</manifestEntries>
</archive>
</configuration>
@@ -84,7 +107,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>

View File

@@ -1,11 +1,11 @@
package net.md_5.bungee;
import com.google.common.io.ByteStreams;
import net.md_5.bungee.log.BungeeLogger;
import net.md_5.bungee.reconnect.YamlReconnectHandler;
import net.md_5.bungee.scheduler.BungeeScheduler;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.providers.netty.NettyAsyncHttpProvider;
import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig;
import com.google.gson.Gson;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
@@ -14,44 +14,55 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import net.md_5.bungee.config.Configuration;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import jline.UnsupportedTerminal;
import jline.console.ConsoleReader;
import jline.internal.Log;
import lombok.Getter;
import lombok.Setter;
import lombok.Synchronized;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ReconnectHandler;
import net.md_5.bungee.api.TabListHandler;
import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.api.tab.CustomTabList;
import net.md_5.bungee.command.*;
import net.md_5.bungee.config.YamlConfig;
import net.md_5.bungee.log.LoggingOutputStream;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.scheduler.BungeeThreadPool;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.Packet3Chat;
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
import net.md_5.bungee.protocol.Vanilla;
import net.md_5.bungee.tab.Custom;
import net.md_5.bungee.util.CaseInsensitiveMap;
import org.fusesource.jansi.AnsiConsole;
/**
* Main BungeeCord proxy class.
@@ -59,14 +70,6 @@ import net.md_5.bungee.scheduler.BungeeThreadPool;
public class BungeeCord extends ProxyServer
{
/**
* Server protocol version.
*/
public static final byte PROTOCOL_VERSION = 60;
/**
* Server game version.
*/
public static final String GAME_VERSION = "1.5";
/**
* Current operation state.
*/
@@ -76,14 +79,15 @@ public class BungeeCord extends ProxyServer
*/
public final Configuration config = new Configuration();
/**
* Thread pools.
* Localization bundle.
*/
public final ScheduledThreadPoolExecutor executors = new BungeeThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() );
public final ResourceBundle bundle = ResourceBundle.getBundle( "messages_en" );
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() );
/**
* locations.yml save thread.
*/
private final Timer saveThread = new Timer( "Reconnect Saver" );
private final Timer metricsThread = new Timer( "Metrics Thread" );
/**
* Server socket listener.
*/
@@ -91,13 +95,8 @@ public class BungeeCord extends ProxyServer
/**
* Fully qualified connections.
*/
public Map<String, UserConnection> connections = new ConcurrentHashMap<>();
/**
* Tab list handler
*/
@Getter
@Setter
public TabListHandler tabListHandler;
private final Map<String, UserConnection> connections = new CaseInsensitiveMap<>();
private final ReadWriteLock connectionLock = new ReentrantReadWriteLock();
/**
* Plugin manager.
*/
@@ -113,12 +112,14 @@ public class BungeeCord extends ProxyServer
@Getter
private final File pluginsFolder = new File( "plugins" );
@Getter
private final TaskScheduler scheduler = new BungeeScheduler();
private final BungeeScheduler scheduler = new BungeeScheduler();
@Getter
private final AsyncHttpClient httpClient = new AsyncHttpClient(
new NettyAsyncHttpProvider(
new AsyncHttpClientConfig.Builder().setAsyncHttpClientProviderConfig(
new NettyAsyncHttpProviderConfig().addProperty( NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE, executors ) ).setExecutorService( executors ).build() ) );
private ConsoleReader consoleReader;
@Getter
private final Logger logger;
public final Gson gson = new Gson();
@Getter
private ConnectionThrottle connectionThrottle;
{
@@ -132,6 +133,7 @@ public class BungeeCord extends ProxyServer
getPluginManager().registerCommand( null, new CommandBungee() );
getPluginManager().registerCommand( null, new CommandPerms() );
getPluginManager().registerCommand( null, new CommandSend() );
getPluginManager().registerCommand( null, new CommandFind() );
registerChannel( "BungeeCord" );
}
@@ -141,6 +143,23 @@ public class BungeeCord extends ProxyServer
return (BungeeCord) ProxyServer.getInstance();
}
public BungeeCord() throws IOException
{
Log.setOutput( new PrintStream( ByteStreams.nullOutputStream() ) ); // TODO: Bug JLine
AnsiConsole.systemInstall();
consoleReader = new ConsoleReader();
logger = new BungeeLogger( this );
System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) );
System.setOut( new PrintStream( new LoggingOutputStream( logger, Level.INFO ), true ) );
if ( consoleReader.getTerminal() instanceof UnsupportedTerminal )
{
logger.info( "Unable to initialize fancy terminal. To fix this on Windows, install the correct Microsoft Visual C++ 2008 Runtime" );
logger.info( "NOTE: This error is non crucial, and BungeeCord will still function correctly! Do not bug the author about it unless you are still unable to get it working" );
}
}
/**
* Starts a new instance of BungeeCord.
*
@@ -149,15 +168,18 @@ public class BungeeCord extends ProxyServer
*/
public static void main(String[] args) throws Exception
{
Calendar deadline = Calendar.getInstance();
deadline.set( 2013, 5, 26 ); // year, month, date
if ( Calendar.getInstance().after( deadline ) )
if ( BungeeCord.class.getPackage().getSpecificationVersion() != null )
{
System.err.println( "*** Warning, this build is outdated ***" );
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
System.err.println( "*** You will get NO support regarding this build ***" );
System.err.println( "*** Server will start in 15 seconds ***" );
Thread.sleep( TimeUnit.SECONDS.toMillis( 15 ) );
Calendar deadline = Calendar.getInstance();
deadline.add( Calendar.WEEK_OF_YEAR, 2 );
if ( Calendar.getInstance().after( new SimpleDateFormat( "yyyyMMdd" ).parse( BungeeCord.class.getPackage().getSpecificationVersion() ) ) )
{
System.err.println( "*** Warning, this build is outdated ***" );
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
System.err.println( "*** You will get NO support regarding this build ***" );
System.err.println( "*** Server will start in 30 seconds ***" );
Thread.sleep( TimeUnit.SECONDS.toMillis( 30 ) );
}
}
BungeeCord bungee = new BungeeCord();
@@ -165,16 +187,14 @@ public class BungeeCord extends ProxyServer
bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
bungee.start();
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
while ( bungee.isRunning )
{
String line = br.readLine();
String line = bungee.getConsoleReader().readLine( ">" );
if ( line != null )
{
boolean handled = getInstance().getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line );
if ( !handled )
if ( !bungee.getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ) )
{
System.err.println( "Command not found" );
bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" );
}
}
}
@@ -189,17 +209,24 @@ public class BungeeCord extends ProxyServer
@Override
public void start() throws Exception
{
ResourceLeakDetector.setEnabled( false ); // Eats performance
pluginsFolder.mkdir();
pluginManager.loadPlugins( pluginsFolder );
pluginManager.detectPlugins( pluginsFolder );
config.load();
if ( reconnectHandler == null )
for ( ListenerInfo info : config.getListeners() )
{
reconnectHandler = new YamlReconnectHandler();
if ( !info.isForceDefault() && reconnectHandler == null )
{
reconnectHandler = new YamlReconnectHandler();
break;
}
}
isRunning = true;
pluginManager.enablePlugins();
pluginManager.loadAndEnablePlugins();
connectionThrottle = new ConnectionThrottle( config.getThrottle() );
startListeners();
saveThread.scheduleAtFixedRate( new TimerTask()
@@ -207,24 +234,20 @@ public class BungeeCord extends ProxyServer
@Override
public void run()
{
getReconnectHandler().save();
if ( getReconnectHandler() != null )
{
getReconnectHandler().save();
}
}
}, 0, TimeUnit.MINUTES.toMillis( 5 ) );
new Metrics().start();
metricsThread.scheduleAtFixedRate( new Metrics(), 0, TimeUnit.MINUTES.toMillis( Metrics.PING_INTERVAL ) );
}
public void startListeners()
{
for ( final ListenerInfo info : config.getListeners() )
{
new ServerBootstrap()
.channel( NioServerSocketChannel.class )
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.group( eventLoops )
.localAddress( info.getHost() )
.bind().addListener( new ChannelFutureListener()
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
public void operationComplete(ChannelFuture future) throws Exception
@@ -238,7 +261,14 @@ public class BungeeCord extends ProxyServer
getLogger().log( Level.WARNING, "Could not bind to host " + info.getHost(), future.cause() );
}
}
} );
};
new ServerBootstrap()
.channel( NioServerSocketChannel.class )
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.group( eventLoops )
.localAddress( info.getHost() )
.bind().addListener( listener );
}
}
@@ -261,37 +291,67 @@ public class BungeeCord extends ProxyServer
@Override
public void stop()
{
this.isRunning = false;
httpClient.close();
executors.shutdown();
stopListeners();
getLogger().info( "Closing pending connections" );
getLogger().info( "Disconnecting " + connections.size() + " connections" );
for ( UserConnection user : connections.values() )
new Thread( "Shutdown Thread" )
{
user.disconnect( "Proxy restarting, brb." );
}
@Override
public void run()
{
BungeeCord.this.isRunning = false;
getLogger().info( "Closing IO threads" );
eventLoops.shutdown();
stopListeners();
getLogger().info( "Closing pending connections" );
getLogger().info( "Saving reconnect locations" );
reconnectHandler.save();
saveThread.cancel();
connectionLock.readLock().lock();
try
{
getLogger().info( "Disconnecting " + connections.size() + " connections" );
for ( UserConnection user : connections.values() )
{
user.disconnect( getTranslation( "restart" ) );
}
} finally
{
connectionLock.readLock().unlock();
}
// TODO: Fix this shit
getLogger().info( "Disabling plugins" );
for ( Plugin plugin : pluginManager.getPlugins() )
{
plugin.onDisable();
getScheduler().cancel( plugin );
}
getLogger().info( "Closing IO threads" );
eventLoops.shutdownGracefully();
try
{
eventLoops.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS );
} catch ( InterruptedException ex )
{
}
getLogger().info( "Thank you and goodbye" );
System.exit( 0 );
if ( reconnectHandler != null )
{
getLogger().info( "Saving reconnect locations" );
reconnectHandler.save();
reconnectHandler.close();
}
saveThread.cancel();
metricsThread.cancel();
// TODO: Fix this shit
getLogger().info( "Disabling plugins" );
for ( Plugin plugin : pluginManager.getPlugins() )
{
try
{
plugin.onDisable();
} catch ( Throwable t )
{
getLogger().severe( "Exception disabling plugin " + plugin.getDescription().getName() );
t.printStackTrace();
}
getScheduler().cancel( plugin );
}
scheduler.shutdown();
getLogger().info( "Thankyou and goodbye" );
System.exit( 0 );
}
}.start();
}
/**
@@ -301,9 +361,16 @@ public class BungeeCord extends ProxyServer
*/
public void broadcast(DefinedPacket packet)
{
for ( UserConnection con : connections.values() )
connectionLock.readLock().lock();
try
{
con.sendPacket( packet );
for ( UserConnection con : connections.values() )
{
con.unsafe().sendPacket( packet );
}
} finally
{
connectionLock.readLock().unlock();
}
}
@@ -320,29 +387,49 @@ public class BungeeCord extends ProxyServer
}
@Override
public Logger getLogger()
public String getTranslation(String name)
{
return BungeeLogger.instance;
String translation = "<translation '" + name + "' missing>";
try
{
translation = bundle.getString( name );
} catch ( MissingResourceException ex )
{
}
return translation;
}
@Override
@SuppressWarnings("unchecked") // TODO: Abstract more
@SuppressWarnings("unchecked")
public Collection<ProxiedPlayer> getPlayers()
{
return (Collection) connections.values();
connectionLock.readLock().lock();
try
{
return (Collection) new HashSet<>( connections.values() );
} finally
{
connectionLock.readLock().unlock();
}
}
@Override
public int getOnlineCount()
{
return connections.size();
}
@Override
public ProxiedPlayer getPlayer(String name)
{
return connections.get( name );
}
@Override
public Server getServer(String name)
{
Collection<ProxiedPlayer> users = getServers().get( name ).getPlayers();
return ( users != null && !users.isEmpty() ) ? users.iterator().next().getServer() : null;
connectionLock.readLock().lock();
try
{
return connections.get( name );
} finally
{
connectionLock.readLock().unlock();
}
}
@Override
@@ -380,32 +467,25 @@ public class BungeeCord extends ProxyServer
public PacketFAPluginMessage registerChannels()
{
StringBuilder sb = new StringBuilder();
for ( String s : getChannels() )
{
sb.append( s );
sb.append( '\00' );
}
byte[] payload = sb.substring( 0, sb.length() - 1 ).getBytes();
return new PacketFAPluginMessage( "REGISTER", payload );
return new PacketFAPluginMessage( "REGISTER", Util.format( pluginChannels, "\00" ).getBytes() );
}
@Override
public byte getProtocolVersion()
{
return PROTOCOL_VERSION;
return Vanilla.PROTOCOL_VERSION;
}
@Override
public String getGameVersion()
{
return GAME_VERSION;
return Vanilla.GAME_VERSION;
}
@Override
public ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted)
public ServerInfo constructServerInfo(String name, InetSocketAddress address, String motd, boolean restricted)
{
return new BungeeServerInfo( name, address, restricted );
return new BungeeServerInfo( name, address, motd, restricted );
}
@Override
@@ -413,4 +493,48 @@ public class BungeeCord extends ProxyServer
{
return ConsoleCommandSender.getInstance();
}
@Override
public void broadcast(String message)
{
getConsole().sendMessage( message );
// TODO: Here too
String encoded = BungeeCord.getInstance().gson.toJson( message );
broadcast( new Packet3Chat( "{\"text\":" + encoded + "}" ) );
}
public void addConnection(UserConnection con)
{
connectionLock.writeLock().lock();
try
{
connections.put( con.getName(), con );
} finally
{
connectionLock.writeLock().unlock();
}
}
public void removeConnection(UserConnection con)
{
connectionLock.writeLock().lock();
try
{
connections.remove( con.getName() );
} finally
{
connectionLock.writeLock().unlock();
}
}
@Override
public CustomTabList customTabList(ProxiedPlayer player)
{
return new Custom( player );
}
public Collection<String> getDisabledCommands()
{
return config.getDisabledCommands();
}
}

View File

@@ -1,99 +0,0 @@
package net.md_5.bungee;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* Logger to handle formatting and storage of the proxy's logger.
*/
public class BungeeLogger extends Logger
{
private static final Formatter formatter = new ConsoleLogFormatter();
static final BungeeLogger instance = new BungeeLogger();
public BungeeLogger()
{
super( "BungeeCord", null );
try
{
FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true );
handler.setFormatter( formatter );
addHandler( handler );
} catch ( IOException ex )
{
System.err.println( "Could not register logger!" );
ex.printStackTrace();
}
}
@Override
public void log(LogRecord record)
{
super.log( record );
String message = formatter.format( record );
if ( record.getLevel() == Level.SEVERE || record.getLevel() == Level.WARNING )
{
System.err.print( message );
} else
{
System.out.print( message );
}
}
public static class ConsoleLogFormatter extends Formatter
{
private SimpleDateFormat formatter = new SimpleDateFormat( "HH:mm:ss" );
@Override
public String format(LogRecord logrecord)
{
StringBuilder formatted = new StringBuilder();
formatted.append( formatter.format( logrecord.getMillis() ) );
Level level = logrecord.getLevel();
if ( level == Level.FINEST )
{
formatted.append( " [FINEST] " );
} else if ( level == Level.FINER )
{
formatted.append( " [FINER] " );
} else if ( level == Level.FINE )
{
formatted.append( " [FINE] " );
} else if ( level == Level.INFO )
{
formatted.append( " [INFO] " );
} else if ( level == Level.WARNING )
{
formatted.append( " [WARNING] " );
} else if ( level == Level.SEVERE )
{
formatted.append( " [SEVERE] " );
}
formatted.append( formatMessage( logrecord ) );
formatted.append( '\n' );
Throwable throwable = logrecord.getThrown();
if ( throwable != null )
{
StringWriter writer = new StringWriter();
throwable.printStackTrace( new PrintWriter( writer ) );
formatted.append( writer );
}
return formatted.toString();
}
}
}

View File

@@ -1,5 +1,6 @@
package net.md_5.bungee;
import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
@@ -9,15 +10,14 @@ import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -25,8 +25,8 @@ import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.connection.PingHandler;
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
@RequiredArgsConstructor
public class BungeeServerInfo implements ServerInfo
@@ -38,9 +38,11 @@ public class BungeeServerInfo implements ServerInfo
private final InetSocketAddress address;
private final Collection<ProxiedPlayer> players = new ArrayList<>();
@Getter
private final String motd;
@Getter
private final boolean restricted;
@Getter
private final Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
private final Queue<DefinedPacket> packetQueue = new LinkedList<>();
@Synchronized("players")
public void addPlayer(ProxiedPlayer player)
@@ -64,6 +66,7 @@ public class BungeeServerInfo implements ServerInfo
@Override
public boolean canAccess(CommandSender player)
{
Preconditions.checkNotNull( player, "player" );
return !restricted || player.hasPermission( "bungeecord.server." + name );
}
@@ -79,30 +82,32 @@ public class BungeeServerInfo implements ServerInfo
return address.hashCode();
}
// TODO: Don't like this method
@Override
public void sendData(String channel, byte[] data)
{
Server server = ProxyServer.getInstance().getServer( getName() );
Preconditions.checkNotNull( channel, "channel" );
Preconditions.checkNotNull( data, "data" );
Server server = ( players.isEmpty() ) ? null : players.iterator().next().getServer();
if ( server != null )
{
server.sendData( channel, data );
} else
{
packetQueue.add( new PacketFAPluginMessage( channel, data ) );
synchronized ( packetQueue )
{
packetQueue.add( new PacketFAPluginMessage( channel, data ) );
}
}
}
@Override
public void ping(final Callback<ServerPing> callback)
{
new Bootstrap()
.channel( NioSocketChannel.class )
.group( BungeeCord.getInstance().eventLoops )
.handler( PipelineUtils.BASE )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
.remoteAddress( getAddress() )
.connect()
.addListener( new ChannelFutureListener()
Preconditions.checkNotNull( callback, "callback" );
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
public void operationComplete(ChannelFuture future) throws Exception
@@ -115,6 +120,14 @@ public class BungeeServerInfo implements ServerInfo
callback.done( null, future.cause() );
}
}
} );
};
new Bootstrap()
.channel( NioSocketChannel.class )
.group( BungeeCord.getInstance().eventLoops )
.handler( PipelineUtils.BASE )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
.remoteAddress( getAddress() )
.connect()
.addListener( listener );
}
}

View File

@@ -0,0 +1,27 @@
package net.md_5.bungee;
import gnu.trove.map.hash.TObjectLongHashMap;
import java.net.InetAddress;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class ConnectionThrottle
{
private final TObjectLongHashMap<InetAddress> throttle = new TObjectLongHashMap<>();
private final int throttleTime;
public void unthrottle(InetAddress address)
{
throttle.remove( address );
}
public boolean throttle(InetAddress address)
{
long value = throttle.get( address );
long currentTime = System.currentTimeMillis();
throttle.put( address, currentTime );
return value != 0 && currentTime - value < throttleTime;
}
}

View File

@@ -1,23 +1,22 @@
package net.md_5.bungee;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import net.md_5.bungee.packet.PacketFCEncryptionResponse;
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
import lombok.Getter;
import net.md_5.bungee.protocol.packet.PacketFCEncryptionResponse;
import net.md_5.bungee.protocol.packet.PacketFDEncryptionRequest;
/**
* Class containing all encryption related methods for the proxy.
@@ -27,14 +26,22 @@ public class EncryptionUtil
private static final Random random = new Random();
public static KeyPair keys;
@Getter
private static SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" );
public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException
static
{
if ( keys == null )
try
{
keys = KeyPairGenerator.getInstance( "RSA" ).generateKeyPair();
} catch ( NoSuchAlgorithmException ex )
{
throw new ExceptionInInitializerError( ex );
}
}
public static PacketFDEncryptionRequest encryptRequest()
{
String hash = ( BungeeCord.getInstance().config.isOnlineMode() ) ? Long.toString( random.nextLong(), 16 ) : "-";
byte[] pubKey = keys.getPublic().getEncoded();
byte[] verify = new byte[ 4 ];
@@ -42,22 +49,19 @@ public class EncryptionUtil
return new PacketFDEncryptionRequest( hash, pubKey, verify );
}
public static SecretKey getSecret(PacketFCEncryptionResponse resp, PacketFDEncryptionRequest request) throws BadPaddingException, IllegalBlockSizeException, IllegalStateException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
public static SecretKey getSecret(PacketFCEncryptionResponse resp, PacketFDEncryptionRequest request) throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance( "RSA" );
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
byte[] decrypted = cipher.doFinal( resp.verifyToken );
byte[] decrypted = cipher.doFinal( resp.getVerifyToken() );
if ( !Arrays.equals( request.verifyToken, decrypted ) )
if ( !Arrays.equals( request.getVerifyToken(), decrypted ) )
{
throw new IllegalStateException( "Key pairs do not match!" );
}
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
byte[] shared = resp.sharedSecret;
byte[] secret = cipher.doFinal( shared );
return new SecretKeySpec( secret, "AES" );
return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" );
}
public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException
@@ -66,4 +70,16 @@ public class EncryptionUtil
cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) );
return cip;
}
public static PublicKey getPubkey(PacketFDEncryptionRequest request) throws GeneralSecurityException
{
return KeyFactory.getInstance( "RSA" ).generatePublic( new X509EncodedKeySpec( request.getPublicKey() ) );
}
public static byte[] encrypt(Key key, byte[] b) throws GeneralSecurityException
{
Cipher hasher = Cipher.getInstance( "RSA" );
hasher.init( Cipher.ENCRYPT_MODE, key );
return hasher.doFinal( b );
}
}

View File

@@ -1,5 +1,7 @@
package net.md_5.bungee;
import io.netty.buffer.ByteBuf;
/**
* Class to rewrite integers within packets.
*/
@@ -102,6 +104,10 @@ public class EntityMap
{
1
};
entityIds[0x2C] = new int[]
{
1
};
entityIds[0x37] = new int[]
{
1
@@ -113,20 +119,20 @@ public class EntityMap
};
}
public static void rewrite(byte[] packet, int oldId, int newId)
public static void rewrite(ByteBuf packet, int oldId, int newId)
{
int packetId = packet[0] & 0xFF;
int packetId = packet.getUnsignedByte( 0 );
if ( packetId == 0x1D )
{ // bulk entity
for ( int pos = 2; pos < packet.length; pos += 4 )
for ( int pos = 2; pos < packet.readableBytes(); pos += 4 )
{
int readId = readInt( packet, pos );
int readId = packet.getInt( pos );
if ( readId == oldId )
{
setInt( packet, pos, newId );
packet.setInt( pos, newId );
} else if ( readId == newId )
{
setInt( packet, pos, oldId );
packet.setInt( pos, oldId );
}
}
} else
@@ -136,40 +142,28 @@ public class EntityMap
{
for ( int pos : idArray )
{
int readId = readInt( packet, pos );
int readId = packet.getInt( pos );
if ( readId == oldId )
{
setInt( packet, pos, newId );
packet.setInt( pos, newId );
} else if ( readId == newId )
{
setInt( packet, pos, oldId );
packet.setInt( pos, oldId );
}
}
}
}
if ( packetId == 0x17 )
{
int type = packet[5] & 0xFF;
int type = packet.getUnsignedByte( 5 );
if ( type == 60 || type == 90 )
{
if ( readInt( packet, 20 ) == oldId )
int index20 = packet.getInt( 20 );
if ( packet.readableBytes() > 24 && index20 == oldId )
{
setInt( packet, 20, newId );
packet.setInt( 20, newId );
}
}
}
}
private static void setInt(byte[] buf, int pos, int i)
{
buf[pos] = (byte) ( i >> 24 );
buf[pos + 1] = (byte) ( i >> 16 );
buf[pos + 2] = (byte) ( i >> 8 );
buf[pos + 3] = (byte) i;
}
private static int readInt(byte[] buf, int pos)
{
return ( ( ( buf[pos] & 0xFF ) << 24 ) | ( ( buf[pos + 1] & 0xFF ) << 16 ) | ( ( buf[pos + 2] & 0xFF ) << 8 ) | buf[pos + 3] & 0xFF );
}
}

View File

@@ -8,9 +8,10 @@ import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.TimerTask;
import net.md_5.bungee.api.ProxyServer;
public class Metrics extends Thread
public class Metrics extends TimerTask
{
/**
@@ -28,41 +29,25 @@ public class Metrics extends Thread
/**
* Interval of time to ping (in minutes)
*/
private final static int PING_INTERVAL = 10;
public Metrics()
{
super( "Metrics Gathering Thread" );
setDaemon( true );
}
final static int PING_INTERVAL = 10;
boolean firstPost = true;
@Override
public void run()
{
boolean firstPost = true;
while ( true )
try
{
try
{
// We use the inverse of firstPost because if it is the first time we are posting,
// it is not a interval ping, so it evaluates to FALSE
// Each time thereafter it will evaluate to TRUE, i.e PING!
postPlugin( !firstPost );
// We use the inverse of firstPost because if it is the first time we are posting,
// it is not a interval ping, so it evaluates to FALSE
// Each time thereafter it will evaluate to TRUE, i.e PING!
postPlugin( !firstPost );
// After the first post we set firstPost to false
// Each post thereafter will be a ping
firstPost = false;
} catch ( IOException ex )
{
ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
}
try
{
sleep( PING_INTERVAL * 1000 * 60 );
} catch ( InterruptedException ex )
{
break;
}
// After the first post we set firstPost to false
// Each post thereafter will be a ping
firstPost = false;
} catch ( IOException ex )
{
ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
}
}
@@ -76,7 +61,7 @@ public class Metrics extends Thread
data.append( encode( "guid" ) ).append( '=' ).append( encode( BungeeCord.getInstance().config.getUuid() ) );
encodeDataPair( data, "version", ProxyServer.getInstance().getVersion() );
encodeDataPair( data, "server", "0" );
encodeDataPair( data, "players", Integer.toString( ProxyServer.getInstance().getPlayers().size() ) );
encodeDataPair( data, "players", Integer.toString( ProxyServer.getInstance().getOnlineCount() ) );
encodeDataPair( data, "revision", String.valueOf( REVISION ) );
// If we're pinging, append it

Some files were not shown because too many files have changed in this diff Show More