379 Commits

Author SHA1 Message Date
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
md_5
09d04c34cb Increase thread pool timeout to try and reduce churn 2013-04-30 11:32:29 +10:00
md_5
3132d2c7cf Increment intent counter 2013-04-29 18:53:16 +10:00
md_5
3682e8ba3a Catch throwable not exception for enabling plugins 2013-04-28 17:45:25 +10:00
md_5
82d5954f2f Revert "Load plugins in alphabetical-ish order."
This reverts commit 384228b751.
2013-04-28 17:44:23 +10:00
Steve Anton
384228b751 Load plugins in alphabetical-ish order. 2013-04-28 17:35:27 +10:00
md_5
50f8bd2eb0 Finish encapsulating all userconnection fields 2013-04-28 11:36:36 +10:00
md_5
ff32764f9a Encapsulate ping fields 2013-04-28 11:19:19 +10:00
md_5
a1f928b210 Don't expose the channel 2013-04-28 10:45:20 +10:00
md_5
5bf616dc4d Forgot that mutex 2013-04-28 10:41:03 +10:00
md_5
0d7759f50b Eeek, name should be constant! 2013-04-28 10:39:41 +10:00
md_5
824bdc5491 Refactor permissions to be lock free, and clean up duplicate references 2013-04-28 10:37:18 +10:00
md_5
ebff48ff86 Remove remnants of forge in initial handler 2013-04-28 10:27:13 +10:00
md_5
6efba44e5a Cleanup userconnection constructor 2013-04-28 10:26:26 +10:00
md_5
9f8ad518e8 Don't use trove sets 2013-04-28 10:21:15 +10:00
md_5
885a96c0c6 Update lombok/asynchttp/guava versions 2013-04-28 10:12:37 +10:00
md_5
5d0ff24f70 Update async http 2013-04-28 10:11:11 +10:00
md_5
ca5db43f70 Don't spew error about long usernames, just disconnect 2013-04-28 10:07:23 +10:00
md_5
49a22f188f Alert when we cannot bind 2013-04-28 09:42:38 +10:00
md_5
ce7c095243 Make ReusableChannelPromise less hacky 2013-04-28 08:53:14 +10:00
md_5
cbb08ec58b Duh, of course connect isn't called, because we are already connected! 2013-04-27 22:52:02 +10:00
md_5
349949d154 @normanmaurer, @trustin in relation to netty/1317 can we consider this a motion to allow channel.write(Object,null), I think it is a very good idea and cannot see it breaking any existing functionality at all. 2013-04-27 22:24:57 +10:00
md_5
92e7faa346 Stab at closing #285, /send current issues 2013-04-27 18:34:43 +10:00
md_5
6b21fdaaea Refactor packet writes into their own channel handler class. 2013-04-27 18:29:12 +10:00
md_5
fa9dd7e27f Synchronize yaml save to close #286, crash on end 2013-04-27 18:17:40 +10:00
md_5
f44cf6c8e9 Move some bootstrapping to the connected method, #267 2013-04-27 12:27:40 +10:00
md_5
6bf9df31f5 Fix async/login event 2013-04-27 12:25:03 +10:00
md_5
c08764990d Bigger logs, with rotation! 2013-04-26 20:23:16 +10:00
md_5
a82e6f3eea Use new servers 2013-04-26 17:48:32 +10:00
md_5
26cee397e6 Only alow config reloads to ADD servers. 2013-04-26 17:31:44 +10:00
md_5
dc6835c21b Add reason when default server is not defined 2013-04-26 17:23:04 +10:00
md_5
be30c8b89a Rename tab list classes 2013-04-26 17:20:03 +10:00
md_5
70e10c382e Properly interface ServerInfo class. 2013-04-26 17:13:00 +10:00
md_5
93ea108acb Complete issue #190 - unthread the login event for maximum efficiency. 2013-04-26 17:00:09 +10:00
md_5
a63739277b Remove depreceated register methods. 2013-04-26 16:49:37 +10:00
md-5
112d543c2a bump date 2013-04-26 11:50:20 +10:00
Robin Lambertz
7338e20e98 Change jarfile property in Plugin to file 2013-04-26 07:35:08 +10:00
Robin Lambertz
18a5534499 Add jarfile property to Plugin 2013-04-26 07:34:56 +10:00
md_5
e540626a28 Rewrite only arrows and rods. Closes #270 2013-04-20 09:46:23 +10:00
md_5
5e2bcc2907 Use custom AnnotatedHandlerFinder and pretend all events are thread safe 2013-04-15 09:15:20 +10:00
md_5
a38b3ce9f2 Try removing connections via string key and direct map access. Closes issue #267 2013-04-14 18:51:33 +10:00
md_5
c615b2362f Revert "Case shouldn't really matter for groups and permissions"
This reverts commit a67d4a1697.
2013-04-14 11:57:50 +10:00
md_5
5620c4679d Fix issue #262 and put all scoreboard bugs to bed 2013-04-14 09:33:36 +10:00
md_5
a9ad4889f7 Revert "Bungee join / part messages"
This reverts commit 7108bd4deb.
2013-04-13 18:28:11 +10:00
md_5
7108bd4deb Bungee join / part messages 2013-04-13 18:17:08 +10:00
md_5
ac1119bef8 Move pending connects check 2013-04-13 11:51:34 +10:00
md_5
dac259933b Add pending connects for bad plugin message based connects 2013-04-13 09:49:08 +10:00
md_5
a57ae83d62 Return from method when we can't connect 2013-04-13 09:46:21 +10:00
md_5
a00c91c03a Fix users showing as disconnected on global ping list 2013-04-13 09:37:56 +10:00
md_5
a67d4a1697 Case shouldn't really matter for groups and permissions 2013-04-12 19:41:20 +10:00
md_5
b87fff2614 Fix users being disconnected without the event q_q 2013-04-12 12:43:17 +10:00
md_5
0c144c38db Fix issue #251 - hidden servers in list command 2013-04-12 12:40:36 +10:00
md_5
5592f81e97 Proper equals on servers 2013-04-12 08:22:17 +10:00
md_5
5d1a2c59a7 ConcurrentHashMap is junk - lets stick to standard unless issues arise. 2013-04-11 20:32:49 +10:00
md_5
88c99f071e Reduce ram usage by ~65kb / player. See #229 2013-04-11 20:30:07 +10:00
md_5
19c3c23b86 Reformat all code 2013-04-11 20:26:55 +10:00
Björn Teichmann
5dfe83cf6d add fallback_server so we can still have a different default server on first join 2013-04-11 20:23:30 +10:00
Steve Anton
fecaf76acf Initialize depends set to an empty set to prevent a NPE when no depends given. 2013-04-11 11:21:35 +10:00
Steve Anton
c8c1028bd9 Implement support for plugin hard dependencies. 2013-04-11 09:10:40 +10:00
md-5
51be33dbd7 Merge pull request #261 from roblabla/patch-2
Fix LoginEvent not being called.
2013-04-10 14:47:29 -07:00
Robin Lambertz
8cac038a07 Fix LoginEvent not being called.
If the server is in offline mode, the LoginEvent should be called.
2013-04-10 14:47:57 +02:00
md-5
edceaf072c Merge pull request #259 from roblabla/patch-2
Fix Crash with Teams. For real this time.
2013-04-08 17:26:10 -07:00
Robin Lambertz
f935f93d98 Fix two small typos. 2013-04-09 01:18:00 +01:00
Robin Lambertz
e5c457df04 Add new Team instances to the list of teams. 2013-04-09 02:10:41 +02:00
md-5
d4f60e65c3 Merge pull request #258 from roblabla/patch-2
Fix the crash with teams not being cleared
2013-04-08 16:25:05 -07:00
Robin Lambertz
548b2f2c60 Fix the crash with teams not being cleared
You forgot to actually add the team to the list of teams...
2013-04-09 01:23:08 +02:00
md-5
3733ecf628 Merge pull request #250 from weaondara/master
check the player's server before connecting
2013-04-04 14:06:40 -07:00
weaondara
6e9cdb2e20 send message to player if target is current server 2013-04-04 18:44:46 +03:00
weaondara
d900a5eae7 setDisplayName bug fix
first remove
then change and add again
2013-04-02 12:18:43 +03:00
weaondara
68712ab854 check the player's server before really connecting
otherwise the player will be kicked with the message "Logged in from another location" if the target server is the current server
2013-04-02 02:13:29 +03:00
md-5
d067662967 Merge pull request #245 from weaondara/master
add permission to check in PermissionCheckEvent
2013-03-31 19:04:14 -07:00
weaondara
88a52bc4e9 set displayName in function setDisplayName 2013-03-31 16:25:53 +03:00
weaondara
d684f5de69 added permission in constructor 2013-03-31 12:36:21 +03:00
weaondara
a316d6ccdc Update PermissionCheckEvent.java 2013-03-31 12:30:46 +03:00
md_5
5de8ac89e5 Remove any scores before updating - see #238 2013-03-29 08:08:13 +11:00
md_5
8707995503 Nullcheck getServer on failed reconnect handler to account for failings on first connect 2013-03-28 22:24:56 +11:00
md_5
caea1e3fa5 Set server as obsolete when kicked. 2013-03-28 19:53:04 +11:00
md_5
4096012d8e Bump to netty release 2013-03-27 17:39:42 +11:00
md_5
acbf6d3137 Add current server check to failed listener too 2013-03-26 18:12:28 +11:00
md_5
3a3fb27d9a Fixx issue #228 and #188 - CSV classes and ALL target for player list 2013-03-26 17:53:36 +11:00
md-5
e506957d38 Fix current server check - #216 2013-03-26 11:50:36 +11:00
md_5
8003dc50c4 Fix #227 - don't colour first name on list 2013-03-25 19:25:06 +11:00
md_5
75bc2738b9 Check length before checking if command. 2013-03-24 16:32:54 +11:00
md_5
cf4846baa9 Add issue #221 - permission check event 2013-03-24 16:29:45 +11:00
md_5
120a4dc401 Remove HTTP request which snuck in. 2013-03-24 16:25:53 +11:00
md_5
6881597692 Fix issue #156 - actually register packet for reading 2013-03-24 11:53:07 +11:00
md_5
0ffb557557 Try my artistic vision on the list command. 2013-03-24 11:44:30 +11:00
md_5
5f7ecf9d3b Fix issue #213 - servers crashing clients. 2013-03-24 11:39:14 +11:00
md_5
e07f1e603a Lowercase forced server checks - #223 2013-03-24 11:02:24 +11:00
md_5
430b5ff392 Bump expirary date back to 26/4/2013 2013-03-24 09:00:32 +11:00
md_5
81fe547a7c Server admins see stack trace and die. See #225 2013-03-24 08:41:25 +11:00
md_5
04fb1df3e1 Don't null out scores after objectives. 2013-03-23 20:58:21 +11:00
md_5
73aaf58009 Try and make the client not crash when switching teams. @lazertester 2013-03-23 20:49:47 +11:00
md_5
fd062503e1 Skeleton team object. TODO: Make @mbax come and show me how to rewrite entire API. Also interface 2013-03-23 20:41:35 +11:00
md_5
c97f113497 Add team packet. 2013-03-23 20:32:37 +11:00
md_5
14fcb90395 Fire server kick for logins too 2013-03-23 19:38:00 +11:00
md_5
39009d8c96 Check players have a server before sending 2013-03-23 13:45:22 +11:00
md_5
13f394b9a7 Send the players real IP for use in Spigot servers. 2013-03-23 11:24:37 +11:00
md_5
9838a09a8c Add slightly modified version of #220 - ChatColor.getByChar 2013-03-21 21:29:26 +11:00
md_5
ffddcf939f Rewrite cancel method as it is posing some issues 2013-03-21 20:16:41 +11:00
md_5
3d8143c36e Use Bungee thread pool for additional logging 2013-03-21 16:28:30 +11:00
md_5
5e31b158e9 Work around JDK stupidity with regards to thread count 2013-03-21 16:25:05 +11:00
md_5
a59e0f0b6b Async close http client as it appears bugged 2013-03-21 13:54:40 +11:00
md_5
517655f54e How did that debug stay there? 2013-03-21 13:45:56 +11:00
md_5
55ec76beee Allow users to connect 2013-03-21 13:37:38 +11:00
md_5
a564d4c7f1 Fix issue #219 - make the scheduler work 2013-03-21 13:35:35 +11:00
md_5
86b864ce21 Fix small javadoc warning 2013-03-20 20:36:33 +11:00
md_5
6813b82b84 Fix issue #216 and #219 by moving current server check to the connect method. 2013-03-20 20:25:42 +11:00
md_5
27d454524f Use asynchttpclient instead 2013-03-20 19:10:59 +11:00
md_5
8827feacfb Implement high performance HTTP api for plugins with jetty. 2013-03-20 18:52:26 +11:00
md_5
692610cd7e Add asynchronous event API from issue #200 2013-03-19 20:09:15 +11:00
md_5
1edd27963f Add issue #215 - scheduler API 2013-03-19 19:45:34 +11:00
md_5
730c05aaad Actually print time it took 2013-03-19 17:23:07 +11:00
md_5
2bae6cafc2 Try and prevent inefficient plugins by nagging when events take longer than 0.25ms to process. Someone let me know if this spams and I need to increase the max time. 2013-03-19 17:21:54 +11:00
md_5
5a15d5387c Use as many threads as the system can handle. 2013-03-19 16:07:39 +11:00
md_5
3fed94fbf2 Stop trying to be Forge for now. 2013-03-19 12:07:59 +11:00
md_5
1dd661f619 Use a byte array in the ping handler for simplicity 2013-03-19 12:07:13 +11:00
md_5
e364cff44e Fix loading config. 2013-03-19 12:00:57 +11:00
md_5
dfa47f740d Add feature #208 - Bungee texture packs. 2013-03-19 11:54:13 +11:00
md_5
87fcef0658 Implement PR #212 - sendMessages() for CommandSenders 2013-03-19 11:43:37 +11:00
md_5
49f2f5f28b Fix issue #211 - ServerInfo.ping 2013-03-19 11:40:56 +11:00
md_5
a12debf6d0 Revert "Add info on registered channels to /bungee to enable users to help identify cause of random timeouts & resource leaks"
This reverts commit b3c34815b0.
2013-03-18 17:36:57 +11:00
md_5
5d3cb452fe Fix issue #206 by updating netty. Big shoutout to Trustin and the Netty project. 2013-03-18 17:36:41 +11:00
md_5
b3c34815b0 Add info on registered channels to /bungee to enable users to help identify cause of random timeouts & resource leaks 2013-03-17 09:03:20 +11:00
md_5
40768c1711 Update Netty to 4.0.0-Beta3 2013-03-17 09:01:12 +11:00
md_5
b16da7d048 Be sure to null serverSentScoreboard each connect. 2013-03-16 22:35:13 +11:00
md_5
e681c8906d Add server kick event 2013-03-16 21:32:11 +11:00
md_5
ce40391717 Untested attempt at sending users to default server when their server goes down. 2013-03-16 21:17:41 +11:00
md_5
2e51ec4fba *add else statement to prevent duplicate exception logging 2013-03-16 21:09:16 +11:00
md_5
54098c8989 Attempt to track, and remove scoreboards at reconnect 2013-03-16 21:06:28 +11:00
md_5
156ea30c32 Add scoreboard packets + API classes. Still unimplemented. 2013-03-16 20:48:51 +11:00
md_5
34f4bae923 Fix score add definition 2013-03-16 20:37:36 +11:00
md_5
6bb089074e Clear scoreboards on reconnect. 2013-03-16 20:13:01 +11:00
md_5
70c73211a0 Fix remapping of entities (fishing floats) 2013-03-16 13:07:51 +11:00
md_5
c059345802 Add send command, closes issue #197 2013-03-16 12:14:15 +11:00
md_5
f5b4e1242d Add #205 more methods in Plugin for getting resources / data folders. 2013-03-16 11:53:25 +11:00
md_5
5365e5fb92 Bump the other pom versions to 1.5 too. 2013-03-15 21:21:58 +11:00
md_5
dc2ef1eac7 Bump api to 1.5 2013-03-15 21:19:10 +11:00
md_5
113dada511 Depreceate old register command / listener methods. 2013-03-15 21:08:50 +11:00
md_5
8ea5205fef Reuse single packet instance to save overhead 2013-03-15 21:03:45 +11:00
md_5
05d76c3f67 Pretty up login fail message. Fixes #201 2013-03-15 21:01:35 +11:00
md_5
d6e29b3f29 Fix issue #203 - errors in Util.exception when no trace present. 2013-03-15 20:38:40 +11:00
md_5
bd479ba083 Remove outdated $() logger getter. 2013-03-14 21:33:22 +11:00
md_5
704fe11b05 Make forced hosts take higher priority than force default. Closes #184 2013-03-14 21:18:59 +11:00
md_5
4811e7be4f Implement PostLoginEvent, closes #191 2013-03-14 21:11:16 +11:00
md_5
ac426f0c3f Use global netty version variable. 2013-03-14 20:35:58 +11:00
md_5
0f30024040 Now that we don't deploy the proxy we don't need the shaded identifier 2013-03-14 20:28:24 +11:00
md_5
5bfab582df Fix #156 - Bukkit trying to get client settings. 2013-03-14 20:22:26 +11:00
md_5
ec1de0f636 Add global slot limit. Fixes feature request #40 2013-03-14 20:14:01 +11:00
md_5
9bf6a58ba2 Update snakeyaml and guava versions. 2013-03-14 19:51:36 +11:00
md_5
9483c0228b Make protocol only depend on netty-buffer 2013-03-14 19:50:44 +11:00
md_5
09bb7a93d2 Update MySQL version 2013-03-14 19:50:17 +11:00
md_5
8935e77118 Use the Netty byte array encoder now that it is fixed. 2013-03-14 19:49:31 +11:00
md_5
b71d253de2 Clean up cipher codec for maximum speed and minimal memory copy. 2013-03-14 19:39:03 +11:00
md_5
d54f2462a8 Less error for IOExceptions, more debug for exceptions in the exception handler. 2013-03-14 19:22:54 +11:00
md_5
c1ff4ffb89 Update to 1.5 & don't deploy proxy to maven, only APIs 2013-03-14 17:50:33 +11:00
md_5
58f1ab208b Reenable encryption + online mode. 2013-03-14 17:46:57 +11:00
md_5
d5f25b07e5 *remove wrapper class 2013-03-14 17:25:24 +11:00
md_5
30b381853c Change packet handling from ByteBufs to byte arrays to work around netty bug. Connection now appears to be stable - just need to add an optimized encryption algorithm back. 2013-03-14 17:24:32 +11:00
md_5
b0820208e6 @normanm @trustin This hack appears to fix all my issues with writing ByteBufs directly to channels. new @lazertester.party(). Others, please note this still isn't even a beta quality build. Due to memory laziness this build will only accept one connection. 2013-03-13 20:11:41 +11:00
md_5
bc0a076e4b Don't sync, its an optimization - right? 2013-03-13 18:08:43 +11:00
md_5
1763dd3078 Remove unneeded channel option. Still trying to fix all these disconnect errors though. 2013-03-13 18:07:07 +11:00
md_5
373ec187fb Fix /server command to only show server name 2013-03-12 18:07:25 +11:00
md_5
ac2c96c2ea This test WITHOUT encryption reveals something is majorly wrong with our packet decoding causing random and frequent disconnects. 2013-03-12 17:53:18 +11:00
md_5
5688099605 Also make sure we get the restricted value per server 2013-03-12 17:14:50 +11:00
md_5
1d2afae98e Fix canAccess check 2013-03-12 17:13:50 +11:00
md_5
4805087e38 Case insensitive sort 2013-03-12 17:11:06 +11:00
md_5
e2f134ec08 Actually alphabetize players - thanks @Grooohm for the good spot! 2013-03-12 16:56:09 +11:00
md_5
92c1450909 Fix issue #129 - make chat event fire for commands, and add isCommand method. 2013-03-12 15:15:25 +11:00
md_5
fea3642550 Add #183 - restricted servers 2013-03-12 12:13:23 +11:00
md_5
f02d17c979 Fix issue #55 - show alerts in console 2013-03-12 12:03:21 +11:00
md_5
3dac86a94c Add getConsole() to the API 2013-03-12 12:01:47 +11:00
md_5
758e196479 Rewrite list command for per server breakdown, and alphabetizing. 2013-03-12 11:58:04 +11:00
md_5
5dd3384b31 Add GetServer channel, see #145 2013-03-12 11:43:37 +11:00
md_5
59efec128d Add ProxiedPlayer.chat, see #146 2013-03-12 11:38:11 +11:00
md_5
3b90737273 Update CipherCodec to try and fix issues. 2013-03-12 11:21:39 +11:00
md_5
f9f664f9b6 Try using out.writerIndex instead of readerIndex 2013-03-11 20:27:19 +11:00
md_5
9edcda7ace Downgrade NIO to 1.4.7 2013-03-11 18:28:10 +11:00
md_5
cd1420d9cd Fix entity rewrites on falling blocks. 2013-03-11 14:31:51 +11:00
md_5
a3e1493ce1 Merge NIO into master. I would not recommend this on a production server at all. Its 1.5 anyway. 2013-03-11 13:30:29 +11:00
md_5
9fb814003b Handle kicks when trying to login to server. Now we have to be about done. 2013-03-11 10:15:14 +11:00
md_5
76319371f7 Small code / api cleanup. Need to fix kicking in general, especially in ServerConnector before release. 2013-03-11 10:06:14 +11:00
md_5
2e33ab1460 Remove outdated javadoc 2013-03-11 09:52:53 +11:00
md_5
cfd10f5714 Handle when user gets kicked midway through login to other server 2013-03-11 09:47:42 +11:00
md_5
6ae5feee60 Error handling when cannot connect to server. 2013-03-11 09:39:37 +11:00
md_5
0b7b87d9c3 Wait 100ms before closing socket to give server time to process. 2013-03-11 09:30:49 +11:00
md_5
fa5804ec86 OMG WTF BBQ IT WORKS. LETS IRON OUT BUGS AND GET OUR RELEASE TARGET, ONLY 12 HOURS LEFT. 2013-03-11 09:18:39 +11:00
md_5
6e18403cd9 Ensure all resources are closed all the time. 2013-03-11 09:11:36 +11:00
md_5
f67ad024f5 Make builds expire. 2013-03-10 20:01:30 +11:00
md_5
a0989a8932 Update for 1.5. This build only supports 1.5, not any other versions. 2013-03-10 12:41:13 +11:00
md_5
3be83cb5a4 Disconnect when exception caught 2013-03-09 17:59:44 +11:00
md_5
0ce02251d4 Start work on a little spit and polish, with cleaner exceptions. 2013-03-09 16:47:13 +11:00
md_5
b4d104d258 *actually compile (wire up the broadcast method) 2013-03-09 14:31:39 +11:00
md_5
0c69814db7 Make sendMessage work again 2013-03-09 14:29:54 +11:00
md_5
f0766ebcfb Bridge packets both ways. 2013-03-09 14:27:24 +11:00
md_5
f0a19e0f45 Small cleanup of packet classes 2013-03-09 14:23:40 +11:00
md_5
6525502836 We can login now. Kindof. *wipes sweat off brow. 2013-03-09 14:19:12 +11:00
md_5
504f703fbb This is so tiring 2013-03-09 12:35:32 +11:00
md_5
4fb85721a9 Nearly ready to get a working connection, however few hacks due to own shortcomings and netty shortcomings. 2013-03-09 12:08:26 +11:00
md_5
e12bc1d92e Closer to connecting, trying to figure out this encryption bug also present in first connect on Spigot 2013-03-09 10:47:12 +11:00
md_5
bcaafc206f Advancing further in the login process. 2013-03-09 10:10:36 +11:00
md_5
8a96555cc7 We can now get pings! 2013-03-09 09:54:19 +11:00
md_5
45c848a4fd Cleanup channel initialization - now its time to get this show working. 2013-03-09 09:45:10 +11:00
md_5
9e0ae0a70d Thread login auth properly. 2013-03-08 18:26:59 +11:00
md_5
9ad9003974 Tigthen access + javadocs on a few netty related classes. 2013-03-08 18:07:21 +11:00
md_5
e18fe49cf9 Whats this? It compiles. Doesn't mean it near works though. 2013-03-08 17:52:17 +11:00
md_5
c3d702a5b3 Switch main packet interface to bytebuf 2013-03-08 17:35:01 +11:00
md_5
cdf26f7950 Nearing a compilale state, gonna have to redo the bootstrap. 2013-03-08 17:24:09 +11:00
md_5
644deee3c6 Redo parts of login and connection sequences 2013-03-07 21:33:49 +11:00
md_5
b6e76f4054 Cleanup permissions and disconnect sequences 2013-03-07 21:04:03 +11:00
md_5
0f9cc76633 Update Server interface to remove depreceated methods 2013-03-07 20:52:19 +11:00
md_5
0afc52c130 Upstream and downstream bridges are now uber sexy 2013-03-07 20:47:39 +11:00
md_5
0077af58d0 Start work on netty overhaul 2013-03-07 20:05:41 +11:00
167 changed files with 6525 additions and 2789 deletions

7
.gitignore vendored
View File

@@ -1,12 +1,11 @@
# Eclipse stuff
.classpath/
.project/
.settings/
.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.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-API</name>
@@ -22,13 +22,31 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>13.0.1</version>
<version>14.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.7.17</version>
<scope>compile</scope>
</dependency>
<dependency>
<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>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.11</version>
<version>1.12</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -1,5 +1,7 @@
package net.md_5.bungee.api;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
@@ -105,13 +107,30 @@ public enum ChatColor
* Pattern to remove all colour codes.
*/
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
/**
* Colour instances keyed by their active character.
*/
private static final Map<Character, ChatColor> BY_CHAR = new HashMap<>();
/**
* The code appended to {@link #COLOR_CHAR} to make usable colour.
*/
private final char code;
/**
* This colour's colour char prefixed by the {@link #COLOR_CHAR}.
*/
private final String toString;
static
{
for ( ChatColor colour : values() )
{
BY_CHAR.put( colour.code, colour );
}
}
private ChatColor(char code)
{
this.code = code;
this.toString = new String( new char[]
{
COLOR_CHAR, code
@@ -153,4 +172,15 @@ public enum ChatColor
}
return new String( b );
}
/**
* Get the colour represented by the specified code.
*
* @param code the code to search for
* @return the mapped colour, or null if non exists
*/
public static ChatColor getByChar(char code)
{
return BY_CHAR.get( code );
}
}

View File

@@ -19,6 +19,14 @@ public interface CommandSender
*/
public void sendMessage(String message);
/**
* Send several messages to this sender. Each message will be sent
* separately.
*
* @param messages the messages to send
*/
public void sendMessages(String... messages);
/**
* Get all groups this user is part of. This returns an unmodifiable
* collection.

View File

@@ -2,6 +2,8 @@ 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;
import java.util.Map;
@@ -10,8 +12,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
{
@@ -46,6 +49,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}.
@@ -69,20 +79,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
@@ -124,21 +120,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.
*
@@ -208,7 +189,64 @@ public abstract class ProxyServer
*
* @param name name of the server
* @param address connectable Minecraft address + port of the server
* @param restricted whether the server info restricted property will be set
* @return the constructed instance
*/
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address);
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted);
/**
* Returns the console overlord for this proxy. Being the console, this
* command server cannot have permissions or groups, and will be able to
* execute anything.
*
* @return the console command sender of this proxy
*/
public abstract CommandSender getConsole();
/**
* Return the folder used to load plugins from.
*
* @return the folder used to load plugin
*/
public abstract File getPluginsFolder();
/**
* Get the scheduler instance for this proxy.
*
* @return the in use scheduler
*/
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.
*
* @return the server's {@link AsyncHttpClient} instance
*/
public abstract AsyncHttpClient getHttpClient();
/**
* 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 current number of connected players
*/
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);
}

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

@@ -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
@@ -32,6 +33,11 @@ public class ListenerInfo
* Name of the server which users will be taken to by default.
*/
private final String defaultServer;
/**
* Name of the server which users will be taken when current server goes
* down.
*/
private final String fallbackServer;
/**
* Whether reconnect locations will be used, or else the user is simply
* transferred to the default server on connect.
@@ -42,4 +48,13 @@ public class ListenerInfo
* transferred depending on the host they connect to.
*/
private final Map<String, String> forcedHosts;
/**
* Get the texture pack used for servers connected to this proxy. May be
* null.
*/
private final TexturePackInfo texturePack;
/**
* Class used to build tab lists for this player.
*/
private final Class<? extends TabListHandler> tabList;
}

View File

@@ -1,69 +1,49 @@
package net.md_5.bungee.api.config;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Synchronized;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Class used to represent a server to connect to.
*/
@Data
@AllArgsConstructor
public abstract class ServerInfo
public interface ServerInfo
{
/**
* Name this server displays as.
*/
private final String name;
/**
* Connectable address of this server.
*/
private final InetSocketAddress address;
/**
* Players connected to a server defined by these properties.
*/
private final Collection<ProxiedPlayer> players = new ArrayList<>();
/**
* Add a player to the internal set of this server.
* Get the name of this server.
*
* @param player the player to add
* @return the configured name for this server address
*/
@Synchronized("players")
public void addPlayer(ProxiedPlayer player)
{
players.add( player );
}
String getName();
/**
* Remove a player form the internal set of this server.
* Gets the connectable host + port pair for this server. Implementations
* expect this to be used as the unique identifier per each instance of this
* class.
*
* @param player the player to remove
* @return the IP and port pair for this server
*/
@Synchronized("players")
public void removePlayer(ProxiedPlayer player)
{
players.remove( player );
}
InetSocketAddress getAddress();
/**
* Get the set of all players on this server.
*
* @return an unmodifiable collection of all players on this server
*/
@Synchronized("players")
public Collection<ProxiedPlayer> getPlayers()
{
return Collections.unmodifiableCollection( players );
}
Collection<ProxiedPlayer> getPlayers();
/**
* Whether the player can access this server. It will only return false when
* the player has no permission and this server is restricted.
*
* @param sender the player to check access for
* @return whether access is granted to this server
*/
boolean canAccess(CommandSender sender);
/**
* Send data by any available means to this server.
@@ -71,12 +51,12 @@ public abstract class ServerInfo
* @param channel the channel to send this data via
* @param data the data to send
*/
public abstract void sendData(String channel, byte[] data);
void sendData(String channel, byte[] data);
/**
* Asynchronously gets the current player count on this server.
*
* @param callback the callback to call when the count has been retrieved.
*/
public abstract void ping(Callback<ServerPing> callback);
void ping(Callback<ServerPing> callback);
}

View File

@@ -0,0 +1,17 @@
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,5 +16,33 @@ public interface Connection
*
* @return the remote address
*/
public InetSocketAddress getAddress();
InetSocketAddress getAddress();
/**
* Disconnects this end of the connection for the specified reason. If this
* is an {@link ProxiedPlayer} the respective server connection will be
* closed too.
*
* @param reason the reason shown to the player / sent to the server on
* disconnect
*/
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

@@ -30,14 +30,6 @@ public interface PendingConnection extends Connection
*/
public InetSocketAddress getVirtualHost();
/**
* Completely kick this user from the proxy and all of its child
* connections.
*
* @param reason the disconnect reason displayed to the player
*/
public void disconnect(String reason);
/**
* Get the listener that accepted this connection.
*

View File

@@ -2,6 +2,8 @@ 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.config.TexturePackInfo;
import net.md_5.bungee.api.tab.TabListHandler;
/**
* Represents a player who's connection is being connected to somewhere else,
@@ -15,7 +17,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 +25,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,28 +34,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();
/**
* Disconnect (remove) this player from the proxy with the specified reason.
*
* @param reason the reason displayed to the player
*/
public void disconnect(String reason);
int getPing();
/**
* Send a plugin message to this player.
@@ -61,12 +56,41 @@ 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
*/
void chat(String message);
/**
* Send a request to change the players texture pack.
*
* @param pack the pack to request
*/
void setTexturePack(TexturePackInfo pack);
/**
* 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();
}

View File

@@ -1,7 +1,5 @@
package net.md_5.bungee.api.connection;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ServerInfo;
/**
@@ -24,14 +22,4 @@ public interface Server extends Connection
* @param data the data to send
*/
public abstract void sendData(String channel, byte[] data);
/**
* Asynchronously gets the current player count on this server.
*
* @param callback the callback to call when the count has been retrieved.
* @deprecated use the corresponding method in {@link ServerInfo} for
* clarity
*/
@Deprecated
public abstract void ping(Callback<ServerPing> callback);
}

View File

@@ -0,0 +1,75 @@
package net.md_5.bungee.api.event;
import com.google.common.base.Preconditions;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.plugin.Event;
import net.md_5.bungee.api.plugin.Plugin;
/**
* Represents an event which depends on the result of asynchronous operations.
*
* @param <T> Type of this event
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class AsyncEvent<T> extends Event
{
private final Callback<T> done;
private final Set<Plugin> intents = Collections.newSetFromMap( new ConcurrentHashMap<Plugin, Boolean>() );
private final AtomicBoolean fired = new AtomicBoolean();
private final AtomicInteger latch = new AtomicInteger();
@Override
@SuppressWarnings("unchecked")
public void postCall()
{
fired.set( true );
if ( latch.get() == 0 )
{
done.done( (T) this, null );
}
}
/**
* Register an intent that this plugin will continue to perform work on a
* background task, and wishes to let the event proceed once the registered
* background task has completed.
*
* @param plugin the plugin registering this intent
*/
public void registerIntent(Plugin plugin)
{
Preconditions.checkState( !fired.get(), "Event %s has already been fired", this );
Preconditions.checkState( !intents.contains( plugin ), "Plugin %s already registered intent for event %s", plugin, this );
intents.add( plugin );
latch.incrementAndGet();
}
/**
* Notifies this event that this plugin has done all its required processing
* and wishes to let the event proceed.
*
* @param plugin a plugin which has an intent registered for this event
*/
@SuppressWarnings("unchecked")
public void completeIntent(Plugin plugin)
{
Preconditions.checkState( intents.contains( plugin ), "Plugin %s has not registered intent for event %s", plugin, this );
intents.remove( plugin );
if ( latch.decrementAndGet() == 0 && fired.get() )
{
done.done( (T) this, null );
}
}
}

View File

@@ -30,4 +30,14 @@ public class ChatEvent extends TargetedEvent implements Cancellable
super( sender, receiver );
this.message = message;
}
/**
* Checks whether this message is valid as a command
*
* @return if this message is a command
*/
public boolean isCommand()
{
return message.length() > 0 && message.charAt( 0 ) == '/';
}
}

View File

@@ -3,9 +3,9 @@ package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
/**
* Event called to represent a player logging in.
@@ -13,7 +13,7 @@ import net.md_5.bungee.api.plugin.Event;
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class LoginEvent extends Event implements Cancellable
public class LoginEvent extends AsyncEvent<LoginEvent> implements Cancellable
{
/**
@@ -28,4 +28,10 @@ public class LoginEvent extends Event implements Cancellable
* Connection attempting to login.
*/
private final PendingConnection connection;
public LoginEvent(PendingConnection connection, Callback<LoginEvent> done)
{
super( done );
this.connection = connection;
}
}

View File

@@ -0,0 +1,40 @@
package net.md_5.bungee.api.event;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Event;
/**
* Called when the permission of a CommandSender is checked.
*/
@Data
@AllArgsConstructor
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class PermissionCheckEvent extends Event
{
/**
* The command sender being checked for a permission.
*/
private final CommandSender sender;
/**
* The permission to check.
*/
private final String permission;
/**
* The outcome of this permission check.
*/
@Getter(AccessLevel.NONE)
private boolean hasPermission;
public boolean hasPermission()
{
return hasPermission;
}
}

View File

@@ -0,0 +1,23 @@
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;
/**
* Event called as soon as a connection has an {@link ProxiedPlayer} and is
* ready to be connected to a server.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class PostLoginEvent extends Event
{
/**
* The player involved with this event.
*/
private final ProxiedPlayer player;
}

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

@@ -0,0 +1,43 @@
package net.md_5.bungee.api.event;
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;
/**
* Represents a player getting kicked from a server.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class ServerKickEvent extends Event implements Cancellable
{
/**
* Cancelled status.
*/
private boolean cancelled;
/**
* Player being kicked.
*/
private final ProxiedPlayer player;
/**
* Kick reason.
*/
private String kickReason;
/**
* Server to send player to if this event is cancelled.
*/
private ServerInfo cancelServer;
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer)
{
this.player = player;
this.kickReason = kickReason;
this.cancelServer = cancelServer;
}
}

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

@@ -4,5 +4,12 @@ package net.md_5.bungee.api.plugin;
* Dummy class which all callable events must extend.
*/
public abstract class Event
{
/**
* Method called after this event has been dispatched to all handlers.
*/
public void postCall()
{
}
}

View File

@@ -1,6 +1,10 @@
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;
/**
@@ -12,6 +16,12 @@ public class Plugin
@Getter
private PluginDescription description;
@Getter
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
@@ -36,13 +46,41 @@ public class Plugin
{
}
/**
* Gets the data folder where this plugin may store arbitrary data. It will
* be a child of {@link ProxyServer#getPluginsFolder()}.
*
* @return the data folder of this plugin
*/
public final File getDataFolder()
{
return new File( getProxy().getPluginsFolder(), getDescription().getName() );
}
/**
* Get a resource from within this plugins jar or container. Care must be
* taken to close the returned stream.
*
* @param name the full path name of this resource
* @return the stream for getting this resource, or null if it does not
* exist
*/
public final InputStream getResourceAsStream(String name)
{
return getClass().getClassLoader().getResourceAsStream( name );
}
/**
* Called by the loader to initialize the fields in this plugin.
*
* @param description the description that describes this plugin
* @param jarfile this plugins jar or container
*/
final void init(PluginDescription description)
final void init(ProxyServer proxy, PluginDescription description)
{
this.proxy = proxy;
this.description = description;
this.file = description.getFile();
this.logger = new PluginLogger( this );
}
}

View File

@@ -1,12 +1,17 @@
package net.md_5.bungee.api.plugin;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* POJO representing the plugin.yml file.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PluginDescription
{
@@ -27,8 +32,12 @@ public class PluginDescription
* Plugin author.
*/
private String author;
public PluginDescription()
{
}
/**
* Plugin hard dependencies.
*/
private Set<String> depends = new HashSet<>();
/**
* File we were loaded from.
*/
private File file = null;
}

View File

@@ -0,0 +1,25 @@
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() + "] ";
setParent( ProxyServer.getInstance().getLogger() );
}
@Override
public void log(LogRecord logRecord)
{
logRecord.setMessage( pluginName + logRecord.getMessage() );
super.log( logRecord );
}
}

View File

@@ -1,45 +1,62 @@
package net.md_5.bungee.api.plugin;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.EventBus;
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.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.regex.Pattern;
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.event.EventBus;
import net.md_5.bungee.event.EventHandler;
import org.yaml.snakeyaml.Yaml;
/**
* Class to manage bridging between plugin duties and implementation duties, for
* example event handling and plugin management.
*/
@RequiredArgsConstructor
public class PluginManager
{
private static final Pattern argsSplit = Pattern.compile( " " );
/*========================================================================*/
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<>();
@SuppressWarnings("unchecked")
public PluginManager(ProxyServer proxy)
{
this.proxy = proxy;
eventBus = new EventBus( proxy.getLogger(), Subscribe.class, EventHandler.class );
}
/**
* Register a command so that it may be executed.
*
* @param plugin the plugin owning this command
* @param command the command to register
*/
public void registerCommand(Command command)
public void registerCommand(Plugin plugin, Command command)
{
commandMap.put( command.getName().toLowerCase(), command );
for ( String alias : command.getAliases() )
@@ -78,7 +95,7 @@ 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;
}
@@ -115,65 +132,114 @@ public class PluginManager
return plugins.get( name );
}
/**
* Enable all plugins by calling the {@link Plugin#onEnable()} method.
*/
public void enablePlugins()
public void loadAndEnablePlugins()
{
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() )
Map<PluginDescription, Boolean> pluginStatuses = new HashMap<>();
for ( Map.Entry<String, PluginDescription> entry : toLoad.entrySet() )
{
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() )
{
Plugin plugin = entry.getValue();
try
{
plugin.onEnable();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{
entry.getKey(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
} );
} catch ( Exception ex )
} catch ( Throwable t )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + entry.getKey(), ex );
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
}
}
}
/**
* 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
private boolean enablePlugin(Map<PluginDescription, Boolean> pluginStatuses, Stack<PluginDescription> dependStack, PluginDescription plugin)
{
Preconditions.checkNotNull( file, "file" );
Preconditions.checkArgument( file.isFile(), "Must load from file" );
if ( pluginStatuses.containsKey( plugin ) )
{
return pluginStatuses.get( plugin );
}
try ( JarFile jar = new JarFile( file ) )
{
JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
// success status
boolean status = true;
try ( InputStream in = jar.getInputStream( pdf ) )
// try to load dependencies first
for ( String dependName : plugin.getDepends() )
{
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 ( PluginDescription element : dependStack )
{
dependencyGraph.append( element.getName() ).append( " -> " );
}
dependencyGraph.append( plugin.getName() ).append( " -> " ).append( dependName );
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph );
status = false;
} else
{
dependStack.push( plugin );
dependStatus = this.enablePlugin( pluginStatuses, dependStack, depend );
dependStack.pop();
}
}
if ( dependStatus == Boolean.FALSE )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
{
depend.getName(), plugin.getName()
} );
status = false;
}
if ( !status )
{
break;
}
}
// do actual loading
if ( status )
{
try
{
PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
URLClassLoader loader = new PluginClassloader( new URL[]
{
file.toURI().toURL()
plugin.getFile().toURI().toURL()
} );
Class<?> main = loader.loadClass( desc.getMain() );
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
Class<?> main = loader.loadClass( plugin.getMain() );
Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance();
plugin.init( desc );
plugins.put( desc.getName(), plugin );
plugin.onLoad();
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[]
{
desc.getName(), desc.getVersion(), desc.getAuthor()
plugin.getName(), plugin.getVersion(), plugin.getAuthor()
} );
} catch ( Throwable t )
{
proxy.getLogger().log( Level.WARNING, "Error enabling plugin " + plugin.getName(), t );
}
}
pluginStatuses.put( plugin, status );
return status;
}
/**
@@ -181,7 +247,7 @@ public class PluginManager
*
* @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" );
@@ -190,9 +256,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 );
@@ -211,7 +285,20 @@ public class PluginManager
*/
public <T extends Event> T callEvent(T event)
{
Preconditions.checkNotNull( event, "event" );
long start = System.nanoTime();
eventBus.post( event );
event.postCall();
long elapsed = start - System.nanoTime();
if ( elapsed > 250000 )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Event {0} took more {1}ns to process!", new Object[]
{
event, elapsed
} );
}
return event;
}
@@ -220,10 +307,21 @@ public class PluginManager
* Object which wish to receive events must be annotated with the
* {@link Subscribe} annotation.
*
* @param plugin the owning plugin
* @param listener the listener to register events for
*/
public void registerListener(Listener listener)
public void registerListener(Plugin plugin, Listener listener)
{
for ( Method method : listener.getClass().getDeclaredMethods() )
{
if ( method.isAnnotationPresent( Subscribe.class ) )
{
proxy.getLogger().log( Level.WARNING, "Listener " + listener + " has registered using depreceated subscribe annotation!"
+ " Please advice author to update to @EventHandler."
+ " As a server owner you may safely ignore this.", new Exception() );
}
}
eventBus.register( listener );
}
}

View File

@@ -0,0 +1,41 @@
package net.md_5.bungee.api.scheduler;
import java.util.concurrent.TimeUnit;
import net.md_5.bungee.api.plugin.Plugin;
/**
* Represents a task scheduled for execution by the {@link TaskScheduler}.
*/
public interface ScheduledTask
{
/**
* Gets the unique ID of this task.
*
* @return this tasks ID
*/
int getId();
/**
* Return the plugin which scheduled this task for execution.
*
* @return the owning plugin
*/
Plugin getOwner();
/**
* Get the actual method which will be executed by this task.
*
* @return the {@link Runnable} behind this task
*/
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
*/
long getDelay(TimeUnit unit);
}

View File

@@ -0,0 +1,74 @@
package net.md_5.bungee.api.scheduler;
import java.util.concurrent.TimeUnit;
import net.md_5.bungee.api.plugin.Plugin;
/**
* This interface represents a scheduler which may be used to queue, delay and
* execute tasks in an asynchronous fashion.
*/
public interface TaskScheduler
{
/**
* Cancel a task to prevent it from executing, or if its a repeating task,
* prevent its further execution.
*
* @param id the id of the task to cancel
*/
void cancel(int id);
/**
* Cancel a task to prevent it from executing, or if its a repeating task,
* prevent its further execution.
*
* @param task the task to cancel
*/
void cancel(ScheduledTask task);
/**
* Cancel all tasks owned by this plugin, this preventing them from being
* executed hereon in.
*
* @param plugin the plugin owning the tasks to be cancelled
* @return the number of tasks cancelled by this method
*/
int cancel(Plugin plugin);
/**
* Schedule a task to be executed asynchronously. The task will commence
* running as soon as this method returns.
*
* @param owner the plugin owning this task
* @param task the task to run
* @return the scheduled task
*/
ScheduledTask runAsync(Plugin owner, Runnable task);
/**
* Schedules a task to be executed asynchronously after the specified delay
* is up.
*
* @param owner the plugin owning this task
* @param task the task to run
* @param delay the delay before this task will be executed
* @param unit the unit in which the delay will be measured
* @return the scheduled task
*/
ScheduledTask schedule(Plugin owner, Runnable task, long delay, TimeUnit unit);
/**
* Schedules a task to be executed asynchronously after the specified delay
* is up. The scheduled task will continue running at the specified
* interval. The interval will not begin to count down until the last task
* invocation is complete.
*
* @param owner the plugin owning this task
* @param task the task to run
* @param delay the delay in milliseconds before this task will be executed
* @param period the interval before subsequent executions of this task
* @param unit the unit in which the delay and period will be measured
* @return the scheduled task
*/
ScheduledTask schedule(Plugin owner, Runnable task, long delay, long period, TimeUnit unit);
}

View File

@@ -0,0 +1,20 @@
package net.md_5.bungee.api.score;
import lombok.Data;
/**
* Represents an objective entry.
*/
@Data
public class Objective
{
/**
* Name of the objective.
*/
private final String name;
/**
* Value of the objective.
*/
private final String value; // displayName
}

View File

@@ -0,0 +1,10 @@
package net.md_5.bungee.api.score;
/**
* Represents locations for a scoreboard to be displayed.
*/
public enum Position
{
LIST, SIDEBAR, BELOW;
}

View File

@@ -0,0 +1,24 @@
package net.md_5.bungee.api.score;
import lombok.Data;
/**
* Represents a scoreboard score entry.
*/
@Data
public class Score
{
/**
* Name to be displayed in the list.
*/
private final String itemName; // Player
/**
* Unique name of the score.
*/
private final String scoreName; // Score
/**
* Value of the score.
*/
private final int value;
}

View File

@@ -0,0 +1,100 @@
package net.md_5.bungee.api.score;
import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Scoreboard
{
/**
* Unique name for this scoreboard.
*/
private String name;
/**
* Position of this scoreboard.
*/
private Position position;
/**
* Objectives for this scoreboard.
*/
private final Map<String, Objective> objectives = new HashMap<>();
/**
* Scores for this scoreboard.
*/
private final Map<String, Score> scores = new HashMap<>();
/**
* Teams on this board.
*/
private final Map<String, Team> teams = new HashMap<>();
public Collection<Objective> getObjectives()
{
return Collections.unmodifiableCollection( objectives.values() );
}
public Collection<Score> getScores()
{
return Collections.unmodifiableCollection( scores.values() );
}
public Collection<Team> getTeams()
{
return Collections.unmodifiableCollection( teams.values() );
}
public void addObjective(Objective objective)
{
Preconditions.checkNotNull( objective, "objective" );
Preconditions.checkArgument( !objectives.containsKey( objective.getName() ), "Objective %s already exists in this scoreboard", objective.getName() );
objectives.put( objective.getName(), objective );
}
public void addScore(Score score)
{
Preconditions.checkNotNull( score, "score" );
scores.put( score.getItemName(), score );
}
public void addTeam(Team team)
{
Preconditions.checkNotNull( team, "team" );
Preconditions.checkArgument( !teams.containsKey( team.getName() ), "Team %s already exists in this scoreboard", team.getName() );
teams.put( team.getName(), team );
}
public Team getTeam(String name)
{
return teams.get( name );
}
public void removeObjective(String objectiveName)
{
objectives.remove( objectiveName );
}
public void removeScore(String scoreName)
{
scores.remove( scoreName );
}
public void removeTeam(String teamName)
{
teams.remove( teamName );
}
public void clear()
{
name = null;
position = null;
objectives.clear();
scores.clear();
teams.clear();
}
}

View File

@@ -0,0 +1,36 @@
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 boolean friendlyFire;
private Set<String> players = new HashSet<>();
public Collection<String> getPlayers()
{
return Collections.unmodifiableSet( players );
}
public void addPlayer(String name)
{
players.add( name );
}
public void removePlayer(String name)
{
players.remove( name );
}
}

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

@@ -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>

29
event/pom.xml Normal file
View File

@@ -0,0 +1,29 @@
<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.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId>
<version>1.5-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Event</name>
<description>Generic java event dispatching API intended for use with BungeeCord</description>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,163 @@
package net.md_5.bungee.event;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
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<Object, Method[]>> eventToHandler = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Logger logger;
private final Class<? extends Annotation>[] annotations;
public EventBus()
{
this( null, (Class<? extends Annotation>[]) null );
}
public EventBus(Logger logger)
{
this( logger, (Class<? extends Annotation>[]) null );
}
@SuppressWarnings("unchecked")
public EventBus(Class<? extends Annotation>... annotations)
{
this( null, annotations );
}
@SuppressWarnings("unchecked")
public EventBus(Logger logger, Class<? extends Annotation>... annotations)
{
this.logger = ( logger == null ) ? Logger.getGlobal() : logger;
this.annotations = ( annotations == null || annotations.length == 0 ) ? new Class[]
{
EventHandler.class
} : annotations;
}
public void post(Object event)
{
lock.readLock().lock();
try
{
Map<Object, Method[]> handlers = eventToHandler.get( event.getClass() );
if ( handlers != null )
{
for ( Map.Entry<Object, Method[]> handler : handlers.entrySet() )
{
for ( Method method : handler.getValue() )
{
try
{
method.invoke( handler.getKey(), 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, handler.getKey() ), ex.getCause() );
}
}
}
}
} finally
{
lock.readLock().unlock();
}
}
private Map<Class<?>, Set<Method>> findHandlers(Object listener)
{
Map<Class<?>, Set<Method>> handler = new HashMap<>();
for ( Method m : listener.getClass().getDeclaredMethods() )
{
for ( Class<? extends Annotation> annotation : annotations )
{
if ( m.isAnnotationPresent( annotation ) )
{
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;
}
Set<Method> existing = handler.get( params[0] );
if ( existing == null )
{
existing = new HashSet<>();
handler.put( params[0], existing );
}
existing.add( m );
break;
}
}
}
return handler;
}
public void register(Object listener)
{
Map<Class<?>, Set<Method>> handler = findHandlers( listener );
lock.writeLock().lock();
try
{
for ( Map.Entry<Class<?>, Set<Method>> e : handler.entrySet() )
{
Map<Object, Method[]> a = eventToHandler.get( e.getKey() );
if ( a == null )
{
a = new HashMap<>();
eventToHandler.put( e.getKey(), a );
}
Method[] baked = new Method[ e.getValue().size() ];
a.put( listener, e.getValue().toArray( baked ) );
}
} finally
{
lock.writeLock().unlock();
}
}
public void unregister(Object listener)
{
Map<Class<?>, Set<Method>> handler = findHandlers( listener );
lock.writeLock().lock();
try
{
for ( Map.Entry<Class<?>, Set<Method>> e : handler.entrySet() )
{
Map<Object, Method[]> a = eventToHandler.get( e.getKey() );
if ( a != null )
{
a.remove( listener );
if ( a.isEmpty() )
{
eventToHandler.remove( e.getKey() );
}
}
}
} finally
{
lock.writeLock().unlock();
}
}
}

View File

@@ -0,0 +1,12 @@
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
{
}

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( latch.getCount(), 0 );
}
@EventHandler
public void firstListener(FirstEvent event)
{
bus.post( new SecondEvent() );
Assert.assertEquals( latch.getCount(), 1 );
latch.countDown();
}
@EventHandler
public void secondListener(SecondEvent event)
{
latch.countDown();
}
public static class FirstEvent
{
}
public static class SecondEvent
{
}
}

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>

16
pom.xml
View File

@@ -11,7 +11,7 @@
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<packaging>pom</packaging>
<name>BungeeCord</name>
@@ -38,6 +38,7 @@
<modules>
<module>api</module>
<module>event</module>
<module>protocol</module>
<module>proxy</module>
</modules>
@@ -57,15 +58,22 @@
</ciManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build.number>unknown</build.number>
<netty.version>4.0.0.CR7</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.6</version>
<version>0.11.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -92,7 +100,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>

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,15 +6,24 @@
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Protocol</name>
<description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
<scope>provided</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,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
}

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,39 +1,128 @@
package net.md_5.mendax;
package net.md_5.bungee.protocol;
import static net.md_5.mendax.PacketDefinitions.OpCode.*;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
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.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.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
{
private static final int MAX_PACKET = 256;
public static final OpCode[][] opCodes = new OpCode[ MAX_PACKET * 2 ][];
public static final int VANILLA_PROTOCOL = 0;
public static final int FORGE_PROTOCOL = MAX_PACKET;
public static final byte PROTOCOL_VERSION = 61;
public static final String GAME_VERSION = "1.5.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, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, 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[0xCC] = PacketCCSettings.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)
{
INT, STRING, BYTE, BYTE, BYTE, BYTE, BYTE
};
opCodes[0x02] = new OpCode[]
DefinedPacket packet = packet( id, protocol );
if ( packet != null )
{
BYTE, STRING, STRING, INT
};
opCodes[0x03] = 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 )
{
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;
}
{
STRING
};
opCodes[0x04] = new OpCode[]
{
LONG, LONG
@@ -54,10 +143,6 @@ public class PacketDefinitions
{
SHORT, SHORT, FLOAT
};
opCodes[0x09] = new OpCode[]
{
INT, BYTE, BYTE, SHORT, STRING
};
opCodes[0x0A] = new OpCode[]
{
BOOLEAN
@@ -214,6 +299,10 @@ public class PacketDefinitions
{
STRING, INT, INT, INT, FLOAT, BYTE
};
opCodes[0x3F] = new OpCode[]
{
STRING, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, INT
};
opCodes[0x46] = new OpCode[]
{
BYTE, BYTE
@@ -224,7 +313,7 @@ public class PacketDefinitions
};
opCodes[0x64] = new OpCode[]
{
BYTE, BYTE, STRING, BYTE
BYTE, BYTE, STRING, BYTE, BOOLEAN
};
opCodes[0x65] = new OpCode[]
{
@@ -278,10 +367,6 @@ public class PacketDefinitions
{
INT, BYTE
};
opCodes[0xC9] = new OpCode[]
{
STRING, BOOLEAN, SHORT
};
opCodes[0xCA] = new OpCode[]
{
BYTE, BYTE, BYTE
@@ -290,37 +375,5 @@ public class PacketDefinitions
{
STRING
};
opCodes[0xCC] = new OpCode[]
{
STRING, BYTE, BYTE, BYTE, BOOLEAN
};
opCodes[0xCD] = new OpCode[]
{
BYTE
};
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[]
{
}; // Should be byte, screw you too bitchy server admins!
opCodes[0xFF] = new OpCode[]
{
STRING
};
/*========================== Minecraft Forge ===========================*/
opCodes[0x01 + FORGE_PROTOCOL] = new OpCode[]
{
INT, STRING, BYTE, INT, BYTE, BYTE, BYTE
};
}
}

View File

@@ -1,70 +1,73 @@
package net.md_5.bungee.packet;
package net.md_5.bungee.protocol.packet;
public abstract class PacketHandler
public abstract class AbstractPacketHandler
{
private void nop(DefinedPacket packet)
{
throw new UnsupportedOperationException( "No handler defined for packet " + packet.getClass() );
}
public void handle(Packet0KeepAlive alive) throws Exception
{
nop( alive );
}
public void handle(Packet1Login login) throws Exception
{
nop( login );
}
public void handle(Packet2Handshake handshake) throws Exception
{
nop( handshake );
}
public void handle(Packet3Chat chat) throws Exception
{
nop( chat );
}
public void handle(Packet9Respawn respawn) throws Exception
{
nop( respawn );
}
public void handle(PacketC9PlayerListItem playerList) throws Exception
{
nop( playerList );
}
public void handle(PacketCCSettings settings) throws Exception
{
}
public void handle(PacketCDClientStatus clientStatus) throws Exception
{
nop( clientStatus );
}
public void handle(PacketCEScoreboardObjective objective) throws Exception
{
}
public void handle(PacketCFScoreboardScore score) throws Exception
{
}
public void handle(PacketD0DisplayScoreboard displayScoreboard) throws Exception
{
}
public void handle(PacketD1Team team) throws Exception
{
}
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
{
nop( pluginMessage );
}
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
{
nop( encryptResponse );
}
public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
{
nop( encryptRequest );
}
public void handle(PacketFEPing ping) throws Exception
{
nop( ping );
}
public void handle(PacketFFKick kick) throws Exception
{
nop( kick );
}
}

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,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,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 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,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,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 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 );
}
}

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

@@ -0,0 +1,16 @@
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
public class BulkChunk extends Instruction
{
@Override
void read(ByteBuf in)
{
short count = in.readShort();
int size = in.readInt();
in.readBoolean();
in.skipBytes( size + count * 12 );
}
}

View File

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

View File

@@ -1,7 +1,6 @@
package net.md_5.mendax.datainput;
package net.md_5.bungee.protocol.skip;
import java.io.DataInput;
import java.io.IOException;
import io.netty.buffer.ByteBuf;
abstract class Instruction
{
@@ -26,11 +25,8 @@ abstract class Instruction
static final Instruction USHORT_BYTE = new UnsignedShortByte();
// 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(DataInput in, byte[] buffer) throws IOException;
final void skip(DataInput in, byte[] buffer, int len) throws IOException
{
in.readFully( buffer, 0, len );
}
abstract void read(ByteBuf in);
}

View File

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

View File

@@ -0,0 +1,18 @@
package net.md_5.bungee.protocol.skip;
import io.netty.buffer.ByteBuf;
class Item extends Instruction
{
@Override
void read(ByteBuf in)
{
short type = in.readShort();
if ( type >= 0 )
{
in.skipBytes( 3 );
SHORT_BYTE.read( in );
}
}
}

View File

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

View File

@@ -1,13 +1,12 @@
package net.md_5.mendax.datainput;
package net.md_5.bungee.protocol.skip;
import java.io.DataInput;
import java.io.IOException;
import io.netty.buffer.ByteBuf;
class MetaData extends Instruction
{
@Override
void read(DataInput in, byte[] buffer) throws IOException
void read(ByteBuf in)
{
int x = in.readUnsignedByte();
while ( x != 127 )
@@ -16,25 +15,25 @@ class MetaData extends Instruction
switch ( type )
{
case 0:
BYTE.read( in, buffer );
BYTE.read( in );
break;
case 1:
SHORT.read( in, buffer );
SHORT.read( in );
break;
case 2:
INT.read( in, buffer );
INT.read( in );
break;
case 3:
FLOAT.read( in, buffer );
FLOAT.read( in );
break;
case 4:
STRING.read( in, buffer );
STRING.read( in );
break;
case 5:
ITEM.read( in, buffer );
ITEM.read( in );
break;
case 6:
skip( in, buffer, 12 ); // int, int, int
in.skipBytes( 12 ); // int, int, int
break;
default:
throw new IllegalArgumentException( "Unknown metadata type " + type );

View File

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

View File

@@ -1,24 +1,24 @@
package net.md_5.mendax.datainput;
package net.md_5.bungee.protocol.skip;
import java.io.DataInput;
import java.io.IOException;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import net.md_5.mendax.PacketDefinitions;
import net.md_5.mendax.PacketDefinitions.OpCode;
import net.md_5.bungee.protocol.OpCode;
import net.md_5.bungee.protocol.Protocol;
public class DataInputPacketReader
public class PacketReader
{
private static final Instruction[][] instructions = new Instruction[ 256 ][];
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 DataInputPacketReader
}
}
private static void readPacket(int packetId, DataInput in, byte[] buffer, 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 )
{
throw new IOException( "Unknown packet id " + packetId );
} else
{
readPacket( packetId, in, buffer, PacketDefinitions.VANILLA_PROTOCOL );
return;
}
}
for ( Instruction instruction : packetDef )
{
instruction.read( in, buffer );
instruction.read( in );
}
}
public static void readPacket(DataInput in, byte[] buffer, int protocol) throws IOException
{
int packetId = in.readUnsignedByte();
readPacket( packetId, in, buffer, protocol );
}
}

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
package net.md_5.mendax.datainput;
import java.io.DataInput;
import java.io.IOException;
public class BulkChunk extends Instruction
{
@Override
void read(DataInput in, byte[] buffer) throws IOException
{
short count = in.readShort();
int size = in.readInt();
in.readBoolean();
skip( in, buffer, size + count * 12 );
}
}

View File

@@ -1,19 +0,0 @@
package net.md_5.mendax.datainput;
import java.io.DataInput;
import java.io.IOException;
class Item extends Instruction
{
@Override
void read(DataInput in, byte[] buffer) throws IOException
{
short type = in.readShort();
if ( type >= 0 )
{
skip( in, buffer, 3 );
SHORT_BYTE.read( in, buffer );
}
}
}

View File

@@ -1,18 +0,0 @@
package net.md_5.mendax.datainput;
import java.io.DataInput;
import java.io.IOException;
public class OptionalMotion extends Instruction
{
@Override
void read(DataInput in, byte[] buffer) throws IOException
{
int data = in.readInt();
if ( data > 0 )
{
skip( in, buffer, 6 );
}
}
}

View File

@@ -1,15 +0,0 @@
package net.md_5.mendax.datainput;
import java.io.DataInput;
import java.io.IOException;
public class UnsignedShortByte extends Instruction
{
@Override
void read(DataInput in, byte[] buffer) throws IOException
{
int size = in.readUnsignedShort();
skip( in, buffer, 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,39 +6,86 @@
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.4.7-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Proxy</name>
<description>Proxy component of the Elastic Portal Suite</description>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</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>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.trove4j</groupId>
<artifactId>trove4j</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.22</version>
<version>5.1.24</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.18.0-GA</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>BungeeCord</finalName>
<plugins>
<plugin>
<!-- Don't deploy proxy to maven repo, only APIs -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@@ -75,7 +122,6 @@
</excludes>
</filter>
</filters>
<finalName>${project.build.finalName}-shaded</finalName>
</configuration>
</plugin>
</plugins>

View File

@@ -1,43 +1,72 @@
package net.md_5.bungee;
import com.google.common.io.ByteStreams;
import net.md_5.bungee.log.BungeeLogger;
import net.md_5.bungee.reconnect.SQLReconnectHandler;
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 io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import net.md_5.bungee.config.Configuration;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
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.ExecutorService;
import java.util.concurrent.Executors;
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 static net.md_5.bungee.Logger.$;
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.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.log.LoggingOutputStream;
import net.md_5.bungee.netty.PipelineUtils;
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.scheduler.BungeeThreadPool;
import net.md_5.bungee.tab.Custom;
import net.md_5.bungee.util.CaseInsensitiveMap;
import org.fusesource.jansi.AnsiConsole;
/**
* Main BungeeCord proxy class.
@@ -45,14 +74,6 @@ import net.md_5.bungee.packet.PacketFAPluginMessage;
public class BungeeCord extends ProxyServer
{
/**
* Server protocol version.
*/
public static final byte PROTOCOL_VERSION = 51;
/**
* Server game version.
*/
public static final String GAME_VERSION = "1.4.6";
/**
* Current operation state.
*/
@@ -62,32 +83,33 @@ public class BungeeCord extends ProxyServer
*/
public final Configuration config = new Configuration();
/**
* Thread pool.
* Localization bundle.
*/
public final ExecutorService threadPool = Executors.newCachedThreadPool();
public final ResourceBundle bundle = ResourceBundle.getBundle( "messages_en" );
/**
* Thread pools.
*/
public final ScheduledThreadPoolExecutor executors = new BungeeThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() );
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( Runtime.getRuntime().availableProcessors(), 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.
*/
private Collection<ListenThread> listeners = new HashSet<>();
private Collection<Channel> listeners = new HashSet<>();
/**
* 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.
*/
@Getter
public final PluginManager pluginManager = new PluginManager();
public final PluginManager pluginManager = new PluginManager( this );
@Getter
@Setter
private ReconnectHandler reconnectHandler;
@@ -95,17 +117,33 @@ public class BungeeCord extends ProxyServer
@Setter
private ConfigurationAdapter configurationAdapter = new YamlConfig();
private final Collection<String> pluginChannels = new HashSet<>();
@Getter
private final File pluginsFolder = new File( "plugins" );
@Getter
private final TaskScheduler 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() ) );
@Getter
private ConsoleReader consoleReader;
@Getter
private final Logger logger;
{
getPluginManager().registerCommand( new CommandReload() );
getPluginManager().registerCommand( new CommandEnd() );
getPluginManager().registerCommand( new CommandList() );
getPluginManager().registerCommand( new CommandServer() );
getPluginManager().registerCommand( new CommandIP() );
getPluginManager().registerCommand( new CommandAlert() );
getPluginManager().registerCommand( new CommandBungee() );
getPluginManager().registerCommand( new CommandPerms() );
// TODO: Proper fallback when we interface the manager
getPluginManager().registerCommand( null, new CommandReload() );
getPluginManager().registerCommand( null, new CommandEnd() );
getPluginManager().registerCommand( null, new CommandList() );
getPluginManager().registerCommand( null, new CommandServer() );
getPluginManager().registerCommand( null, new CommandIP() );
getPluginManager().registerCommand( null, new CommandAlert() );
getPluginManager().registerCommand( null, new CommandBungee() );
getPluginManager().registerCommand( null, new CommandPerms() );
getPluginManager().registerCommand( null, new CommandSend() );
getPluginManager().registerCommand( null, new CommandFind() );
registerChannel( "BungeeCord" );
}
@@ -115,29 +153,55 @@ 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.
*
* @param args command line arguments, currently none are used
* @throws IOException when the server cannot be started
* @throws Exception when the server cannot be started
*/
public static void main(String[] args) throws IOException
public static void main(String[] args) throws Exception
{
Calendar deadline = Calendar.getInstance();
deadline.set( 2013, 7, 1 ); // year, month, date
if ( Calendar.getInstance().after( deadline ) )
{
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 ) );
}
BungeeCord bungee = new BungeeCord();
ProxyServer.setInstance( bungee );
$().info( "Enabled BungeeCord version " + bungee.getVersion() );
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" );
}
}
}
@@ -147,21 +211,21 @@ public class BungeeCord extends ProxyServer
* Start this proxy instance by loading the configuration, plugins and
* starting the connect thread.
*
* @throws IOException
* @throws Exception
*/
public void start() throws IOException
@Override
public void start() throws Exception
{
File plugins = new File( "plugins" );
plugins.mkdir();
pluginManager.loadPlugins( plugins );
pluginsFolder.mkdir();
pluginManager.detectPlugins( pluginsFolder );
config.load();
if ( reconnectHandler == null )
{
reconnectHandler = new YamlReconnectHandler();
reconnectHandler = new SQLReconnectHandler();
}
isRunning = true;
pluginManager.enablePlugins();
pluginManager.loadAndEnablePlugins();
startListeners();
@@ -173,40 +237,49 @@ public class BungeeCord extends ProxyServer
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 ( ListenerInfo info : config.getListeners() )
for ( final ListenerInfo info : config.getListeners() )
{
try
ChannelFutureListener listener = new ChannelFutureListener()
{
ListenThread listener = new ListenThread( info );
listener.start();
listeners.add( listener );
$().info( "Listening on " + info.getHost() );
} catch ( IOException ex )
@Override
public void operationComplete(ChannelFuture future) throws Exception
{
$().log( Level.SEVERE, "Could not start listener " + info, ex );
if ( future.isSuccess() )
{
listeners.add( future.channel() );
getLogger().info( "Listening on " + info.getHost() );
} else
{
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 );
}
}
public void stopListeners()
{
for ( ListenThread listener : listeners )
for ( Channel listener : listeners )
{
$().log( Level.INFO, "Closing listen thread {0}", listener.socket );
getLogger().log( Level.INFO, "Closing listener {0}", listener );
try
{
listener.interrupt();
listener.socket.close();
listener.join();
} catch ( InterruptedException | IOException ex )
listener.close().syncUninterruptibly();
} catch ( ChannelException ex )
{
$().severe( "Could not close listen thread" );
getLogger().severe( "Could not close listen thread" );
}
}
listeners.clear();
@@ -215,44 +288,59 @@ public class BungeeCord extends ProxyServer
@Override
public void stop()
{
this.isRunning = false;
new Thread( "Shutdown Thread" )
{
@Override
public void run()
{
BungeeCord.this.isRunning = false;
httpClient.close();
executors.shutdown();
stopListeners();
$().info( "Closing pending connections" );
threadPool.shutdown();
getLogger().info( "Closing pending connections" );
$().info( "Disconnecting " + connections.size() + " connections" );
connectionLock.readLock().lock();
try
{
getLogger().info( "Disconnecting " + connections.size() + " connections" );
for ( UserConnection user : connections.values() )
{
user.disconnect( "Proxy restarting, brb." );
user.disconnect( getTranslation( "restart" ) );
}
} finally
{
connectionLock.readLock().unlock();
}
$().info( "Saving reconnect locations" );
reconnectHandler.save();
saveThread.cancel();
getLogger().info( "Closing IO threads" );
eventLoops.shutdownGracefully();
try
{
eventLoops.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS );
} catch ( InterruptedException ex )
{
}
$().info( "Disabling plugins" );
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() )
{
plugin.onDisable();
getScheduler().cancel( plugin );
}
$().info( "Thank you and goodbye" );
getLogger().info( "Thankyou and goodbye" );
System.exit( 0 );
}
/**
* Miscellaneous method to set options on a socket based on those in the
* configuration.
*
* @param socket to set the options on
* @throws IOException when the underlying set methods thrown an exception
*/
public void setSocketOptions(Socket socket) throws IOException
{
socket.setSoTimeout( config.getTimeout() );
socket.setTrafficClass( 0x18 );
socket.setTcpNoDelay( true );
}.start();
}
/**
@@ -261,10 +349,17 @@ public class BungeeCord extends ProxyServer
* @param packet the packet to send
*/
public void broadcast(DefinedPacket packet)
{
connectionLock.readLock().lock();
try
{
for ( UserConnection con : connections.values() )
{
con.packetQueue.add( packet );
con.unsafe().sendPacket( packet );
}
} finally
{
connectionLock.readLock().unlock();
}
}
@@ -281,29 +376,49 @@ public class BungeeCord extends ProxyServer
}
@Override
public Logger getLogger()
public String getTranslation(String name)
{
return $();
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)
connectionLock.readLock().lock();
try
{
Collection<ProxiedPlayer> users = getServers().get( name ).getPlayers();
return ( users != null && !users.isEmpty() ) ? users.iterator().next().getServer() : null;
return connections.get( name );
} finally
{
connectionLock.readLock().unlock();
}
}
@Override
@@ -341,31 +456,67 @@ 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)
public ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted)
{
return new BungeeServerInfo( name, address );
return new BungeeServerInfo( name, address, restricted );
}
@Override
public CommandSender getConsole()
{
return ConsoleCommandSender.getInstance();
}
@Override
public void broadcast(String message)
{
getConsole().sendMessage( message );
broadcast( new Packet3Chat( message ) );
}
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 );
}
}

View File

@@ -1,74 +1,131 @@
package net.md_5.bungee;
import java.io.DataOutputStream;
import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.net.Socket;
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.ProxyServer;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ServerPing;
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.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketFFKick;
import net.md_5.bungee.packet.PacketStream;
import net.md_5.mendax.PacketDefinitions;
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.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
public class BungeeServerInfo extends ServerInfo
@RequiredArgsConstructor
public class BungeeServerInfo implements ServerInfo
{
@Getter
private final Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
private final String name;
@Getter
private final InetSocketAddress address;
private final Collection<ProxiedPlayer> players = new ArrayList<>();
@Getter
private final boolean restricted;
@Getter
private final Queue<DefinedPacket> packetQueue = new LinkedList<>();
public BungeeServerInfo(String name, InetSocketAddress address)
@Synchronized("players")
public void addPlayer(ProxiedPlayer player)
{
super( name, address );
players.add( player );
}
@Synchronized("players")
public void removePlayer(ProxiedPlayer player)
{
players.remove( player );
}
@Synchronized("players")
@Override
public Collection<ProxiedPlayer> getPlayers()
{
return Collections.unmodifiableCollection( players );
}
@Override
public boolean canAccess(CommandSender player)
{
Preconditions.checkNotNull( player, "player" );
return !restricted || player.hasPermission( "bungeecord.server." + name );
}
@Override
public boolean equals(Object obj)
{
return ( obj instanceof ServerInfo ) && Objects.equals( getAddress(), ( (ServerInfo) obj ).getAddress() );
}
@Override
public int hashCode()
{
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
{
synchronized ( packetQueue )
{
packetQueue.add( new PacketFAPluginMessage( channel, data ) );
}
}
}
@Override
public void ping(final Callback<ServerPing> callback)
{
new Thread()
Preconditions.checkNotNull( callback, "callback" );
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
public void run()
public void operationComplete(ChannelFuture future) throws Exception
{
try ( Socket socket = new Socket(); )
if ( future.isSuccess() )
{
socket.connect( getAddress() );
DataOutputStream out = new DataOutputStream( socket.getOutputStream() );
out.write( 0xFE );
out.write( 0x01 );
PacketStream in = new PacketStream( socket.getInputStream(), PacketDefinitions.VANILLA_PROTOCOL );
PacketFFKick response = new PacketFFKick( in.readPacket() );
String[] split = response.message.split( "\00" );
ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) );
callback.done( ping, null );
} catch ( Throwable t )
future.channel().pipeline().get( HandlerBoss.class ).setHandler( new PingHandler( BungeeServerInfo.this, callback ) );
} else
{
callback.done( null, t );
callback.done( null, future.cause() );
}
}
}.start();
};
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

@@ -1,29 +1,22 @@
package net.md_5.bungee;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
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.
@@ -32,15 +25,23 @@ public class EncryptionUtil
{
private static final Random random = new Random();
private static KeyPair keys;
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 ];
@@ -48,52 +49,37 @@ 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 boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException
{
String encName = URLEncoder.encode( username, "UTF-8" );
MessageDigest sha = MessageDigest.getInstance( "SHA-1" );
for ( byte[] bit : new byte[][]
{
connectionHash.getBytes( "ISO_8859_1" ), shared.getEncoded(), keys.getPublic().getEncoded()
} )
{
sha.update( bit );
}
String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" );
String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash;
String reply;
try ( BufferedReader in = new BufferedReader( new InputStreamReader( new URL( authURL ).openStream() ) ) )
{
reply = in.readLine();
}
return "YES".equals( reply );
}
public static Cipher getCipher(int opMode, Key shared) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException
{
Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" );
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

@@ -40,7 +40,7 @@ public class EntityMap
};
entityIds[0x17] = new int[]
{
1, 20
1 //, 20
};
entityIds[0x18] = new int[]
{
@@ -115,7 +115,7 @@ public class EntityMap
public static void rewrite(byte[] packet, int oldId, int newId)
{
int packetId = Util.getId( packet );
int packetId = packet[0] & 0xFF;
if ( packetId == 0x1D )
{ // bulk entity
for ( int pos = 2; pos < packet.length; pos += 4 )
@@ -147,6 +147,18 @@ public class EntityMap
}
}
}
if ( packetId == 0x17 )
{
int type = packet[5] & 0xFF;
if ( type == 60 || type == 90 )
{
int index20 = readInt( packet, 20 );
if ( packet.length > 24 && index20 == oldId )
{
setInt( packet, 20, newId );
}
}
}
}
private static void setInt(byte[] buf, int pos, int i)

View File

@@ -1,61 +0,0 @@
package net.md_5.bungee;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import static net.md_5.bungee.Logger.$;
import net.md_5.bungee.packet.PacketFFKick;
import net.md_5.bungee.packet.PacketStream;
/**
* Class to represent a Minecraft connection.
*/
@EqualsAndHashCode
@RequiredArgsConstructor
public class GenericConnection
{
protected final Socket socket;
protected final PacketStream stream;
@Getter
public String name;
@Getter
public String displayName;
/**
* Close the socket with the specified reason.
*
* @param reason to disconnect
*/
public void disconnect(String reason)
{
if ( socket.isClosed() )
{
return;
}
log( "disconnected with " + reason );
try
{
stream.write( new PacketFFKick( "[Proxy] " + reason ) );
} catch ( IOException ex )
{
} finally
{
try
{
socket.shutdownOutput();
socket.close();
} catch ( IOException ioe )
{
}
}
}
public void log(String message)
{
$().info( socket.getInetAddress() + ( ( name == null ) ? " " : " [" + name + "] " ) + message );
}
}

View File

@@ -1,232 +0,0 @@
package net.md_5.bungee;
import com.google.common.base.Preconditions;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import lombok.Getter;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.packet.Packet1Login;
import net.md_5.bungee.packet.Packet2Handshake;
import net.md_5.bungee.packet.PacketCDClientStatus;
import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketFCEncryptionResponse;
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
import net.md_5.bungee.packet.PacketFEPing;
import net.md_5.bungee.packet.PacketFFKick;
import net.md_5.bungee.packet.PacketHandler;
import net.md_5.bungee.packet.PacketStream;
import net.md_5.mendax.PacketDefinitions;
public class InitialHandler extends PacketHandler implements Runnable, PendingConnection
{
private final Socket socket;
@Getter
private final ListenerInfo listener;
private PacketStream stream;
private Packet1Login forgeLogin;
private Packet2Handshake handshake;
private PacketFDEncryptionRequest request;
private List<PacketFAPluginMessage> loginMessages = new ArrayList<>();
private State thisState = State.HANDSHAKE;
private static final PacketFAPluginMessage forgeMods = new PacketFAPluginMessage( "FML", new byte[]
{
0, 0, 0, 0, 0, 2
} );
public InitialHandler(Socket socket, ListenerInfo info) throws IOException
{
this.socket = socket;
this.listener = info;
stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), PacketDefinitions.VANILLA_PROTOCOL );
}
private enum State
{
HANDSHAKE, ENCRYPT, LOGIN, FINISHED;
}
@Override
public void handle(Packet1Login login) throws Exception
{
Preconditions.checkState( thisState == State.LOGIN, "Not expecting FORGE LOGIN" );
Preconditions.checkState( forgeLogin == null, "Already received FORGE LOGIN" );
forgeLogin = login;
stream.setProtocol( PacketDefinitions.FORGE_PROTOCOL );
}
@Override
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
{
loginMessages.add( pluginMessage );
}
@Override
public void handle(PacketFEPing ping) throws Exception
{
socket.setSoTimeout( 100 );
boolean newPing = false;
try
{
socket.getInputStream().read();
newPing = true;
} catch ( IOException ex )
{
}
ServerPing pingevent = new ServerPing( BungeeCord.PROTOCOL_VERSION, BungeeCord.GAME_VERSION,
listener.getMotd(), ProxyServer.getInstance().getPlayers().size(), listener.getMaxPlayers() );
pingevent = ProxyServer.getInstance().getPluginManager().callEvent( new ProxyPingEvent( this, pingevent ) ).getResponse();
String response = ( newPing ) ? ChatColor.COLOR_CHAR + "1"
+ "\00" + pingevent.getProtocolVersion()
+ "\00" + pingevent.getGameVersion()
+ "\00" + pingevent.getMotd()
+ "\00" + pingevent.getCurrentPlayers()
+ "\00" + pingevent.getMaxPlayers()
: pingevent.getMotd() + ChatColor.COLOR_CHAR + pingevent.getCurrentPlayers() + ChatColor.COLOR_CHAR + pingevent.getMaxPlayers();
disconnect( response );
}
@Override
public void handle(Packet2Handshake handshake) throws Exception
{
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
this.handshake = handshake;
stream.write( forgeMods );
stream.write( request = EncryptionUtil.encryptRequest() );
thisState = State.ENCRYPT;
}
@Override
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
{
Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" );
SecretKey shared = EncryptionUtil.getSecret( encryptResponse, request );
if ( BungeeCord.getInstance().config.isOnlineMode() && !EncryptionUtil.isAuthenticated( handshake.username, request.serverId, shared ) )
{
throw new KickException( "Not authenticated with minecraft.net" );
}
// Check for multiple connections
ProxiedPlayer old = ProxyServer.getInstance().getPlayer( handshake.username );
if ( old != null )
{
old.disconnect( "You are already connected to the server" );
}
// fire login event
LoginEvent event = new LoginEvent( this );
ProxyServer.getInstance().getPluginManager().callEvent( event );
if ( event.isCancelled() )
{
throw new KickException( event.getCancelReason() );
}
stream.write( new PacketFCEncryptionResponse() );
stream = new PacketStream( new CipherInputStream( socket.getInputStream(), EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, shared ) ),
new CipherOutputStream( socket.getOutputStream(), EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, shared ) ), stream.getProtocol() );
thisState = State.LOGIN;
}
@Override
public void handle(PacketCDClientStatus clientStatus) throws Exception
{
Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" );
UserConnection userCon = new UserConnection( socket, this, stream, handshake, forgeLogin, loginMessages );
ServerInfo server = ProxyServer.getInstance().getReconnectHandler().getServer( userCon );
userCon.connect( server, true );
thisState = State.FINISHED;
}
@Override
public void run()
{
try
{
while ( thisState != State.FINISHED )
{
byte[] buf = stream.readPacket();
DefinedPacket packet = DefinedPacket.packet( buf );
packet.handle( this );
}
} catch ( KickException ex )
{
disconnect( "[Proxy - Kicked] " + ex.getMessage() );
} catch ( EOFException ex )
{
} catch ( Exception ex )
{
disconnect( "[Proxy Error] " + Util.exception( ex ) );
ex.printStackTrace();
}
}
@Override
public void disconnect(String reason)
{
thisState = State.FINISHED;
try
{
stream.write( new PacketFFKick( reason ) );
} catch ( IOException ioe )
{
} finally
{
try
{
socket.shutdownOutput();
socket.close();
} catch ( IOException ioe2 )
{
}
}
}
@Override
public String getName()
{
return ( handshake == null ) ? null : handshake.username;
}
@Override
public byte getVersion()
{
return ( handshake == null ) ? -1 : handshake.procolVersion;
}
@Override
public InetSocketAddress getVirtualHost()
{
return ( handshake == null ) ? null : new InetSocketAddress( handshake.host, handshake.port );
}
@Override
public InetSocketAddress getAddress()
{
return (InetSocketAddress) socket.getRemoteSocketAddress();
}
}

View File

@@ -1,14 +0,0 @@
package net.md_5.bungee;
/**
* Exception, which when thrown will disconnect the player from the proxy with
* the specified message.
*/
public class KickException extends RuntimeException
{
public KickException(String message)
{
super( message );
}
}

View File

@@ -1,48 +0,0 @@
package net.md_5.bungee;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import static net.md_5.bungee.Logger.$;
import net.md_5.bungee.api.config.ListenerInfo;
/**
* Thread to listen and dispatch incoming connections to the proxy.
*/
public class ListenThread extends Thread
{
public final ServerSocket socket;
private final ListenerInfo info;
public ListenThread(ListenerInfo info) throws IOException
{
super( "Listen Thread - " + info );
this.info = info;
socket = new ServerSocket();
socket.bind( info.getHost() );
}
@Override
public void run()
{
while ( !isInterrupted() )
{
try
{
Socket client = socket.accept();
BungeeCord.getInstance().setSocketOptions( client );
$().info( client.getInetAddress() + " has connected" );
InitialHandler handler = new InitialHandler( client, info );
BungeeCord.getInstance().threadPool.submit( handler );
} catch ( SocketException ex )
{
ex.printStackTrace(); // Now people can see why their operating system is failing them and stop bitching at me!
} catch ( IOException ex )
{
ex.printStackTrace(); // TODO
}
}
}
}

View File

@@ -1,109 +0,0 @@
package net.md_5.bungee;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
/**
* Logger to handle formatting and storage of the proxy's logger.
*/
public class Logger extends java.util.logging.Logger
{
private static final Formatter formatter = new ConsoleLogFormatter();
private static final Logger instance = new Logger();
public Logger()
{
super( "RubberBand", null );
try
{
FileHandler handler = new FileHandler( "proxy.log", 1 << 14, 1, 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 );
}
}
/**
* Gets the current logger instance.
*
* @return the current logger instance
*/
public static Logger $()
{
return instance;
}
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

@@ -8,10 +8,10 @@ import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import static net.md_5.bungee.Logger.$;
import java.util.TimerTask;
import net.md_5.bungee.api.ProxyServer;
public class Metrics extends Thread
public class Metrics extends TimerTask
{
/**
@@ -29,19 +29,11 @@ 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
{
@@ -55,15 +47,7 @@ public class Metrics extends Thread
firstPost = false;
} catch ( IOException ex )
{
$().info( "[Metrics] " + ex.getMessage() );
}
try
{
sleep( PING_INTERVAL * 1000 * 60 );
} catch ( InterruptedException ex )
{
break;
}
ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
}
}
@@ -77,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