167 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
148 changed files with 3926 additions and 2409 deletions

7
.gitignore vendored
View File

@@ -1,12 +1,11 @@
# Eclipse stuff # Eclipse stuff
.classpath/ .classpath
.project/ .project
.settings/ .settings
# netbeans # netbeans
nbproject/ nbproject/
nbactions.xml nbactions.xml
nb-configuration.xml
# we use maven! # we use maven!
build.xml 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

@@ -28,7 +28,19 @@
<dependency> <dependency>
<groupId>com.ning</groupId> <groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId> <artifactId>async-http-client</artifactId>
<version>1.7.14</version> <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> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

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

View File

@@ -12,9 +12,9 @@ import lombok.Getter;
import net.md_5.bungee.api.config.ConfigurationAdapter; import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; 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.Plugin;
import net.md_5.bungee.api.scheduler.TaskScheduler; import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.api.tab.CustomTabList;
public abstract class ProxyServer public abstract class ProxyServer
{ {
@@ -49,6 +49,13 @@ public abstract class ProxyServer
*/ */
public abstract String getVersion(); 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 * Gets the main logger which can be used as a suitable replacement for
* {@link System#out} and {@link System#err}. * {@link System#out} and {@link System#err}.
@@ -72,20 +79,6 @@ public abstract class ProxyServer
*/ */
public abstract ProxiedPlayer getPlayer(String name); 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 * Return all servers registered to this proxy, keyed by name. Unlike the
* methods in {@link ConfigurationAdapter#getServers()}, this will not * methods in {@link ConfigurationAdapter#getServers()}, this will not
@@ -127,21 +120,6 @@ public abstract class ProxyServer
*/ */
public abstract void setConfigurationAdapter(ConfigurationAdapter adapter); 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. * Get the currently in use reconnect handler.
* *
@@ -247,4 +225,28 @@ public abstract class ProxyServer
* @return the server's {@link AsyncHttpClient} instance * @return the server's {@link AsyncHttpClient} instance
*/ */
public abstract AsyncHttpClient getHttpClient(); 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 * @param player the connecting player
* @return the server to connect to * @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 * 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 * @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 * Save all pending reconnect locations. Whilst not used for database
* connections, this method will be called at a predefined interval to allow * connections, this method will be called at a predefined interval to allow
* the saving of reconnect files. * 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.net.InetSocketAddress;
import java.util.Map; import java.util.Map;
import lombok.Data; import lombok.Data;
import net.md_5.bungee.api.tab.TabListHandler;
/** /**
* Class representing the configuration of a server listener. Used for allowing * Class representing the configuration of a server listener. Used for allowing
@@ -52,4 +53,8 @@ public class ListenerInfo
* null. * null.
*/ */
private final TexturePackInfo texturePack; private final TexturePackInfo texturePack;
/**
* Class used to build tab lists for this player.
*/
private final Class<? extends TabListHandler> tabList;
} }

View File

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

View File

@@ -2,6 +2,8 @@ package net.md_5.bungee.api.connection;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.config.ServerInfo; 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, * 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 * @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 * 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 * @param name the name to set
*/ */
public void setDisplayName(String name); void setDisplayName(String name);
/** /**
* Connects / transfers this user to the specified connection, gracefully * Connects / transfers this user to the specified connection, gracefully
@@ -32,21 +34,21 @@ public interface ProxiedPlayer extends Connection, CommandSender
* *
* @param target the new server to connect to * @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. * Gets the server this player is connected to.
* *
* @return 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. * Gets the ping time between the proxy and this connection.
* *
* @return the current ping time * @return the current ping time
*/ */
public int getPing(); int getPing();
/** /**
* Send a plugin message to this player. * Send a plugin message to this player.
@@ -54,19 +56,41 @@ public interface ProxiedPlayer extends Connection, CommandSender
* @param channel the channel to send this data via * @param channel the channel to send this data via
* @param data the data to send * @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. * Get the pending connection that belongs to this player.
* *
* @return the pending connection that this player used * @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. * Make this player chat (say something), to the server he is currently on.
* *
* @param message the message to say * @param message the message to say
*/ */
public void chat(String message); 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

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

View File

@@ -1,18 +1,17 @@
package net.md_5.bungee.api.event; package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
@Data @Data
@AllArgsConstructor
@ToString(callSuper = false) @ToString(callSuper = false)
@EqualsAndHashCode(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. * Server the player will be connected to.
*/ */
private ServerInfo target; 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,22 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
/**
* Called when a player has changed servers.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class ServerSwitchEvent extends Event
{
/**
* Player whom the server is for.
*/
private final ProxiedPlayer player;
}

View File

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

View File

@@ -1,5 +1,6 @@
package net.md_5.bungee.api.plugin; package net.md_5.bungee.api.plugin;
import java.io.File;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -35,4 +36,8 @@ public class PluginDescription
* Plugin hard dependencies. * Plugin hard dependencies.
*/ */
private Set<String> depends = new HashSet<>(); 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,15 +1,16 @@
package net.md_5.bungee.api.plugin; package net.md_5.bungee.api.plugin;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
@@ -20,7 +21,8 @@ import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.event.EventBus;
import net.md_5.bungee.event.EventHandler;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
/** /**
@@ -36,9 +38,17 @@ public class PluginManager
private final ProxyServer proxy; private final ProxyServer proxy;
/*========================================================================*/ /*========================================================================*/
private final Yaml yaml = new Yaml(); private final Yaml yaml = new Yaml();
private final EventBus eventBus = new EventBus(); private final EventBus eventBus;
private final Map<String, Plugin> plugins = new HashMap<>(); private final Map<String, Plugin> plugins = new LinkedHashMap<>();
private final Map<String, Command> commandMap = new HashMap<>(); 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. * Register a command so that it may be executed.
@@ -85,7 +95,7 @@ public class PluginManager
String permission = command.getPermission(); String permission = command.getPermission();
if ( permission != null && !permission.isEmpty() && !sender.hasPermission( permission ) ) 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; return true;
} }
@@ -122,23 +132,37 @@ public class PluginManager
return plugins.get( name ); return plugins.get( name );
} }
/** public void loadAndEnablePlugins()
* Enable all plugins by calling the {@link Plugin#onEnable()} method.
*/
public void enablePlugins()
{ {
Map<Plugin, Boolean> pluginStatuses = new HashMap<>(); Map<PluginDescription, Boolean> pluginStatuses = new HashMap<>();
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() ) for ( Map.Entry<String, PluginDescription> entry : toLoad.entrySet() )
{ {
Plugin plugin = entry.getValue(); PluginDescription plugin = entry.getValue();
if ( !this.enablePlugin( pluginStatuses, new Stack<Plugin>(), plugin ) ) if ( !enablePlugin( pluginStatuses, new Stack<PluginDescription>(), plugin ) )
{ {
ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() ); ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() );
} }
} }
toLoad.clear();
toLoad = null;
for ( Plugin plugin : plugins.values() )
{
try
{
plugin.onEnable();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
} );
} catch ( Throwable t )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
}
}
} }
private boolean enablePlugin(Map<Plugin, Boolean> pluginStatuses, Stack<Plugin> dependStack, Plugin plugin) private boolean enablePlugin(Map<PluginDescription, Boolean> pluginStatuses, Stack<PluginDescription> dependStack, PluginDescription plugin)
{ {
if ( pluginStatuses.containsKey( plugin ) ) if ( pluginStatuses.containsKey( plugin ) )
{ {
@@ -149,9 +173,9 @@ public class PluginManager
boolean status = true; boolean status = true;
// try to load dependencies first // try to load dependencies first
for ( String dependName : plugin.getDescription().getDepends() ) for ( String dependName : plugin.getDepends() )
{ {
Plugin depend = this.plugins.get( dependName ); PluginDescription depend = toLoad.get( dependName );
Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE; Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE;
if ( dependStatus == null ) if ( dependStatus == null )
@@ -159,11 +183,11 @@ public class PluginManager
if ( dependStack.contains( depend ) ) if ( dependStack.contains( depend ) )
{ {
StringBuilder dependencyGraph = new StringBuilder(); StringBuilder dependencyGraph = new StringBuilder();
for ( Plugin element : dependStack ) for ( PluginDescription element : dependStack )
{ {
dependencyGraph.append( element.getDescription().getName() ).append( " -> " ); dependencyGraph.append( element.getName() ).append( " -> " );
} }
dependencyGraph.append( plugin.getDescription().getName() ).append( " -> " ).append( dependName ); dependencyGraph.append( plugin.getName() ).append( " -> " ).append( dependName );
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph ); ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph );
status = false; status = false;
} else } else
@@ -178,7 +202,7 @@ public class PluginManager
{ {
ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[] ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
{ {
depend.getDescription().getName(), plugin.getDescription().getName() depend.getName(), plugin.getName()
} ); } );
status = false; status = false;
} }
@@ -194,15 +218,23 @@ public class PluginManager
{ {
try try
{ {
plugin.onEnable(); URLClassLoader loader = new PluginClassloader( new URL[]
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{ {
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor() plugin.getFile().toURI().toURL()
} );
Class<?> main = loader.loadClass( plugin.getMain() );
Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance();
clazz.init( proxy, plugin );
plugins.put( plugin.getName(), clazz );
clazz.onLoad();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
plugin.getName(), plugin.getVersion(), plugin.getAuthor()
} ); } );
} catch ( Throwable t ) } catch ( Throwable t )
{ {
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t ); proxy.getLogger().log( Level.WARNING, "Error enabling plugin " + plugin.getName(), t );
status = false;
} }
} }
@@ -210,51 +242,12 @@ public class PluginManager
return status; return status;
} }
/**
* Load a plugin from the specified file. This file must be in jar format.
* This will not enable plugins, {@link #enablePlugins()} must be called.
*
* @param file the file to load from
* @throws Exception Any exceptions encountered when loading a plugin from
* this file.
*/
public void loadPlugin(File file) throws Exception
{
Preconditions.checkNotNull( file, "file" );
Preconditions.checkArgument( file.isFile(), "Must load from file" );
try ( JarFile jar = new JarFile( file ) )
{
JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
try ( InputStream in = jar.getInputStream( pdf ) )
{
PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
URLClassLoader loader = new PluginClassloader( new URL[]
{
file.toURI().toURL()
} );
Class<?> main = loader.loadClass( desc.getMain() );
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
plugin.init( proxy, desc, file );
plugins.put( desc.getName(), plugin );
plugin.onLoad();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
desc.getName(), desc.getVersion(), desc.getAuthor()
} );
}
}
}
/** /**
* Load all plugins from the specified folder. * Load all plugins from the specified folder.
* *
* @param folder the folder to search for plugins in * @param folder the folder to search for plugins in
*/ */
public void loadPlugins(File folder) public void detectPlugins(File folder)
{ {
Preconditions.checkNotNull( folder, "folder" ); Preconditions.checkNotNull( folder, "folder" );
Preconditions.checkArgument( folder.isDirectory(), "Must load from a directory" ); Preconditions.checkArgument( folder.isDirectory(), "Must load from a directory" );
@@ -263,9 +256,17 @@ public class PluginManager
{ {
if ( file.isFile() && file.getName().endsWith( ".jar" ) ) 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 ) } catch ( Exception ex )
{ {
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load plugin from file " + file, ex ); ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load plugin from file " + file, ex );
@@ -311,6 +312,16 @@ public class PluginManager
*/ */
public void registerListener(Plugin plugin, 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 ); eventBus.register( listener );
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,23 +1,30 @@
package net.md_5.bungee.api; package net.md_5.bungee.api.tab;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
public interface TabListHandler 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. * Called when a player first connects to the proxy.
* *
* @param player the connecting player * @param player the connecting player
*/ */
public void onConnect(ProxiedPlayer player); void onServerChange();
/**
* Called when a player changes their connected server.
*
* @param player the player who changed servers
*/
public void onServerChange(ProxiedPlayer player);
/** /**
* Called when a players ping changes. The new ping will have not updated in * 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 player the player who's ping changed
* @param ping the player's new ping. * @param ping the player's new ping.
*/ */
public void onPingChange(ProxiedPlayer player, int ping); void onPingChange(int ping);
/** /**
* Called when a player disconnects. * Called when a player disconnects.
* *
* @param player the disconnected player * @param player the disconnected player
*/ */
public void onDisconnect(ProxiedPlayer player); void onDisconnect();
/** /**
* Called when a list update packet is sent from server to client. * 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 * @param ping ping of the subject player
* @return whether to send the packet to the client * @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>

View File

@@ -38,6 +38,7 @@
<modules> <modules>
<module>api</module> <module>api</module>
<module>event</module>
<module>protocol</module> <module>protocol</module>
<module>proxy</module> <module>proxy</module>
</modules> </modules>
@@ -58,11 +59,17 @@
<properties> <properties>
<build.number>unknown</build.number> <build.number>unknown</build.number>
<netty.version>4.0.0.CR1</netty.version> <netty.version>4.0.0.CR7</netty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>

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,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,38 +1,128 @@
package net.md_5.bungee.protocol; package net.md_5.bungee.protocol;
import static net.md_5.bungee.protocol.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
{ {
public static final OpCode[][] opCodes = new OpCode[ 512 ][]; public static final byte PROTOCOL_VERSION = 61;
public static final int VANILLA_PROTOCOL = 0; public static final String GAME_VERSION = "1.5.2";
public static final int FORGE_PROTOCOL = 256; @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()
{ {
classes[0x00] = Packet0KeepAlive.class;
BOOLEAN, BULK_CHUNK, BYTE, BYTE_INT, DOUBLE, FLOAT, INT, INT_3, INT_BYTE, ITEM, LONG, METADATA, OPTIONAL_MOTION, SCORE, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, TEAM, USHORT_BYTE classes[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 throw new RuntimeException( "Unknown packet id " + packetId );
}; }
opCodes[0x01] = new OpCode[] return packet;
}
public static DefinedPacket read(short id, ByteBuf buf, Protocol protocol)
{ {
INT, STRING, BYTE, BYTE, BYTE, BYTE, BYTE DefinedPacket packet = packet( id, protocol );
}; if ( packet != null )
opCodes[0x02] = new OpCode[]
{ {
BYTE, STRING, STRING, INT packet.read( buf );
}; return packet;
opCodes[0x03] = new OpCode[] }
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[] opCodes[0x04] = new OpCode[]
{ {
LONG, LONG LONG, LONG
@@ -53,10 +143,6 @@ public class PacketDefinitions
{ {
SHORT, SHORT, FLOAT SHORT, SHORT, FLOAT
}; };
opCodes[0x09] = new OpCode[]
{
INT, BYTE, BYTE, SHORT, STRING
};
opCodes[0x0A] = new OpCode[] opCodes[0x0A] = new OpCode[]
{ {
BOOLEAN BOOLEAN
@@ -281,10 +367,6 @@ public class PacketDefinitions
{ {
INT, BYTE INT, BYTE
}; };
opCodes[0xC9] = new OpCode[]
{
STRING, BOOLEAN, SHORT
};
opCodes[0xCA] = new OpCode[] opCodes[0xCA] = new OpCode[]
{ {
BYTE, BYTE, BYTE BYTE, BYTE, BYTE
@@ -293,54 +375,5 @@ public class PacketDefinitions
{ {
STRING STRING
}; };
opCodes[0xCC] = new OpCode[]
{
STRING, BYTE, BYTE, BYTE, BOOLEAN
};
opCodes[0xCD] = new OpCode[]
{
BYTE
};
opCodes[0xCE] = new OpCode[]
{
STRING, STRING, BYTE
};
opCodes[0xCF] = new OpCode[]
{
SCORE
};
opCodes[0xD0] = new OpCode[]
{
BYTE, STRING
};
opCodes[0xD1] = new OpCode[]
{
TEAM
};
opCodes[0xFA] = new OpCode[]
{
STRING, SHORT_BYTE
};
opCodes[0xFC] = new OpCode[]
{
SHORT_BYTE, SHORT_BYTE
};
opCodes[0xFD] = new OpCode[]
{
STRING, SHORT_BYTE, SHORT_BYTE
};
opCodes[0xFE] = new OpCode[]
{
BYTE
};
opCodes[0xFF] = new OpCode[]
{
STRING
};
/*========================== Minecraft Forge ===========================*/
opCodes[0x01 + FORGE_PROTOCOL] = new OpCode[]
{
INT, STRING, BYTE, INT, BYTE, BYTE, BYTE
};
} }
} }

View File

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

View File

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

View File

@@ -1,29 +1,8 @@
package net.md_5.bungee.packet; package net.md_5.bungee.protocol.packet;
import net.md_5.bungee.netty.ChannelWrapper; public abstract class AbstractPacketHandler
public abstract class PacketHandler
{ {
@Override
public abstract String toString();
public void connected(ChannelWrapper channel) throws Exception
{
}
public void disconnected(ChannelWrapper channel) throws Exception
{
}
public void exception(Throwable t) throws Exception
{
}
public void handle(byte[] buf) throws Exception
{
}
public void handle(Packet0KeepAlive alive) throws Exception public void handle(Packet0KeepAlive alive) throws Exception
{ {
} }

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,6 +31,12 @@
<version>${netty.version}</version> <version>${netty.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.11</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId> <artifactId>bungeecord-protocol</artifactId>
@@ -53,7 +59,19 @@
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>5.1.24</version> <version>5.1.24</version>
<scope>compile</scope> <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> </dependency>
</dependencies> </dependencies>

View File

@@ -1,5 +1,8 @@
package net.md_5.bungee; 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 net.md_5.bungee.scheduler.BungeeScheduler;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClient;
@@ -15,43 +18,55 @@ import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.Configuration;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.InputStreamReader; import java.io.IOException;
import java.io.PrintStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; 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.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import jline.UnsupportedTerminal;
import jline.console.ConsoleReader;
import jline.internal.Log;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.Synchronized; import lombok.Synchronized;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ReconnectHandler; 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.ConfigurationAdapter;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; 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.Plugin;
import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.TaskScheduler; 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.command.*;
import net.md_5.bungee.config.YamlConfig; import net.md_5.bungee.config.YamlConfig;
import net.md_5.bungee.log.LoggingOutputStream;
import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFAPluginMessage; 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.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. * Main BungeeCord proxy class.
@@ -59,14 +74,6 @@ import net.md_5.bungee.scheduler.BungeeThreadPool;
public class BungeeCord extends ProxyServer public class BungeeCord extends ProxyServer
{ {
/**
* Server protocol version.
*/
public static final byte PROTOCOL_VERSION = 60;
/**
* Server game version.
*/
public static final String GAME_VERSION = "1.5";
/** /**
* Current operation state. * Current operation state.
*/ */
@@ -75,15 +82,20 @@ public class BungeeCord extends ProxyServer
* Configuration. * Configuration.
*/ */
public final Configuration config = new Configuration(); public final Configuration config = new Configuration();
/**
* Localization bundle.
*/
public final ResourceBundle bundle = ResourceBundle.getBundle( "messages_en" );
/** /**
* Thread pools. * Thread pools.
*/ */
public final ScheduledThreadPoolExecutor executors = new BungeeThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() ); public final ScheduledThreadPoolExecutor executors = new BungeeThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() );
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO 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. * locations.yml save thread.
*/ */
private final Timer saveThread = new Timer( "Reconnect Saver" ); private final Timer saveThread = new Timer( "Reconnect Saver" );
private final Timer metricsThread = new Timer( "Metrics Thread" );
/** /**
* Server socket listener. * Server socket listener.
*/ */
@@ -91,13 +103,8 @@ public class BungeeCord extends ProxyServer
/** /**
* Fully qualified connections. * Fully qualified connections.
*/ */
public Map<String, UserConnection> connections = new ConcurrentHashMap<>(); private final Map<String, UserConnection> connections = new CaseInsensitiveMap<>();
/** private final ReadWriteLock connectionLock = new ReentrantReadWriteLock();
* Tab list handler
*/
@Getter
@Setter
public TabListHandler tabListHandler;
/** /**
* Plugin manager. * Plugin manager.
*/ */
@@ -119,6 +126,10 @@ public class BungeeCord extends ProxyServer
new NettyAsyncHttpProvider( new NettyAsyncHttpProvider(
new AsyncHttpClientConfig.Builder().setAsyncHttpClientProviderConfig( new AsyncHttpClientConfig.Builder().setAsyncHttpClientProviderConfig(
new NettyAsyncHttpProviderConfig().addProperty( NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE, executors ) ).setExecutorService( executors ).build() ) ); new NettyAsyncHttpProviderConfig().addProperty( NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE, executors ) ).setExecutorService( executors ).build() ) );
@Getter
private ConsoleReader consoleReader;
@Getter
private final Logger logger;
{ {
@@ -132,6 +143,7 @@ public class BungeeCord extends ProxyServer
getPluginManager().registerCommand( null, new CommandBungee() ); getPluginManager().registerCommand( null, new CommandBungee() );
getPluginManager().registerCommand( null, new CommandPerms() ); getPluginManager().registerCommand( null, new CommandPerms() );
getPluginManager().registerCommand( null, new CommandSend() ); getPluginManager().registerCommand( null, new CommandSend() );
getPluginManager().registerCommand( null, new CommandFind() );
registerChannel( "BungeeCord" ); registerChannel( "BungeeCord" );
} }
@@ -141,6 +153,23 @@ public class BungeeCord extends ProxyServer
return (BungeeCord) ProxyServer.getInstance(); 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. * Starts a new instance of BungeeCord.
* *
@@ -150,7 +179,7 @@ public class BungeeCord extends ProxyServer
public static void main(String[] args) throws Exception public static void main(String[] args) throws Exception
{ {
Calendar deadline = Calendar.getInstance(); Calendar deadline = Calendar.getInstance();
deadline.set( 2013, 5, 26 ); // year, month, date deadline.set( 2013, 7, 1 ); // year, month, date
if ( Calendar.getInstance().after( deadline ) ) if ( Calendar.getInstance().after( deadline ) )
{ {
System.err.println( "*** Warning, this build is outdated ***" ); System.err.println( "*** Warning, this build is outdated ***" );
@@ -165,16 +194,14 @@ public class BungeeCord extends ProxyServer
bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() ); bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
bungee.start(); bungee.start();
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
while ( bungee.isRunning ) while ( bungee.isRunning )
{ {
String line = br.readLine(); String line = bungee.getConsoleReader().readLine( ">" );
if ( line != null ) if ( line != null )
{ {
boolean handled = getInstance().getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ); if ( !bungee.getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ) )
if ( !handled )
{ {
System.err.println( "Command not found" ); bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" );
} }
} }
} }
@@ -190,15 +217,15 @@ public class BungeeCord extends ProxyServer
public void start() throws Exception public void start() throws Exception
{ {
pluginsFolder.mkdir(); pluginsFolder.mkdir();
pluginManager.loadPlugins( pluginsFolder ); pluginManager.detectPlugins( pluginsFolder );
config.load(); config.load();
if ( reconnectHandler == null ) if ( reconnectHandler == null )
{ {
reconnectHandler = new YamlReconnectHandler(); reconnectHandler = new SQLReconnectHandler();
} }
isRunning = true; isRunning = true;
pluginManager.enablePlugins(); pluginManager.loadAndEnablePlugins();
startListeners(); startListeners();
@@ -210,21 +237,14 @@ public class BungeeCord extends ProxyServer
getReconnectHandler().save(); getReconnectHandler().save();
} }
}, 0, TimeUnit.MINUTES.toMillis( 5 ) ); }, 0, TimeUnit.MINUTES.toMillis( 5 ) );
metricsThread.scheduleAtFixedRate( new Metrics(), 0, TimeUnit.MINUTES.toMillis( Metrics.PING_INTERVAL ) );
new Metrics().start();
} }
public void startListeners() public void startListeners()
{ {
for ( final ListenerInfo info : config.getListeners() ) for ( final ListenerInfo info : config.getListeners() )
{ {
new ServerBootstrap() ChannelFutureListener listener = new ChannelFutureListener()
.channel( NioServerSocketChannel.class )
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.group( eventLoops )
.localAddress( info.getHost() )
.bind().addListener( new ChannelFutureListener()
{ {
@Override @Override
public void operationComplete(ChannelFuture future) throws Exception public void operationComplete(ChannelFuture future) throws Exception
@@ -238,7 +258,14 @@ public class BungeeCord extends ProxyServer
getLogger().log( Level.WARNING, "Could not bind to host " + info.getHost(), future.cause() ); getLogger().log( Level.WARNING, "Could not bind to host " + info.getHost(), future.cause() );
} }
} }
} ); };
new ServerBootstrap()
.channel( NioServerSocketChannel.class )
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.group( eventLoops )
.localAddress( info.getHost() )
.bind().addListener( listener );
} }
} }
@@ -261,7 +288,12 @@ public class BungeeCord extends ProxyServer
@Override @Override
public void stop() public void stop()
{ {
this.isRunning = false; new Thread( "Shutdown Thread" )
{
@Override
public void run()
{
BungeeCord.this.isRunning = false;
httpClient.close(); httpClient.close();
executors.shutdown(); executors.shutdown();
@@ -269,18 +301,33 @@ public class BungeeCord extends ProxyServer
stopListeners(); stopListeners();
getLogger().info( "Closing pending connections" ); getLogger().info( "Closing pending connections" );
connectionLock.readLock().lock();
try
{
getLogger().info( "Disconnecting " + connections.size() + " connections" ); getLogger().info( "Disconnecting " + connections.size() + " connections" );
for ( UserConnection user : connections.values() ) for ( UserConnection user : connections.values() )
{ {
user.disconnect( "Proxy restarting, brb." ); user.disconnect( getTranslation( "restart" ) );
}
} finally
{
connectionLock.readLock().unlock();
} }
getLogger().info( "Closing IO threads" ); getLogger().info( "Closing IO threads" );
eventLoops.shutdown(); eventLoops.shutdownGracefully();
try
{
eventLoops.awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS );
} catch ( InterruptedException ex )
{
}
getLogger().info( "Saving reconnect locations" ); getLogger().info( "Saving reconnect locations" );
reconnectHandler.save(); reconnectHandler.save();
reconnectHandler.close();
saveThread.cancel(); saveThread.cancel();
metricsThread.cancel();
// TODO: Fix this shit // TODO: Fix this shit
getLogger().info( "Disabling plugins" ); getLogger().info( "Disabling plugins" );
@@ -290,9 +337,11 @@ public class BungeeCord extends ProxyServer
getScheduler().cancel( plugin ); getScheduler().cancel( plugin );
} }
getLogger().info( "Thank you and goodbye" ); getLogger().info( "Thankyou and goodbye" );
System.exit( 0 ); System.exit( 0 );
} }
}.start();
}
/** /**
* Broadcasts a packet to all clients that is connected to this instance. * Broadcasts a packet to all clients that is connected to this instance.
@@ -300,10 +349,17 @@ public class BungeeCord extends ProxyServer
* @param packet the packet to send * @param packet the packet to send
*/ */
public void broadcast(DefinedPacket packet) public void broadcast(DefinedPacket packet)
{
connectionLock.readLock().lock();
try
{ {
for ( UserConnection con : connections.values() ) for ( UserConnection con : connections.values() )
{ {
con.sendPacket( packet ); con.unsafe().sendPacket( packet );
}
} finally
{
connectionLock.readLock().unlock();
} }
} }
@@ -320,29 +376,49 @@ public class BungeeCord extends ProxyServer
} }
@Override @Override
public Logger getLogger() public String getTranslation(String name)
{ {
return BungeeLogger.instance; String translation = "<translation '" + name + "' missing>";
try
{
translation = bundle.getString( name );
} catch ( MissingResourceException ex )
{
}
return translation;
} }
@Override @Override
@SuppressWarnings("unchecked") // TODO: Abstract more @SuppressWarnings("unchecked")
public Collection<ProxiedPlayer> getPlayers() 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 @Override
public ProxiedPlayer getPlayer(String name) public ProxiedPlayer getPlayer(String name)
{ {
return connections.get( name ); connectionLock.readLock().lock();
} try
@Override
public Server getServer(String name)
{ {
Collection<ProxiedPlayer> users = getServers().get( name ).getPlayers(); return connections.get( name );
return ( users != null && !users.isEmpty() ) ? users.iterator().next().getServer() : null; } finally
{
connectionLock.readLock().unlock();
}
} }
@Override @Override
@@ -380,26 +456,19 @@ public class BungeeCord extends ProxyServer
public PacketFAPluginMessage registerChannels() public PacketFAPluginMessage registerChannels()
{ {
StringBuilder sb = new StringBuilder(); return new PacketFAPluginMessage( "REGISTER", Util.format( pluginChannels, "\00" ).getBytes() );
for ( String s : getChannels() )
{
sb.append( s );
sb.append( '\00' );
}
byte[] payload = sb.substring( 0, sb.length() - 1 ).getBytes();
return new PacketFAPluginMessage( "REGISTER", payload );
} }
@Override @Override
public byte getProtocolVersion() public byte getProtocolVersion()
{ {
return PROTOCOL_VERSION; return Vanilla.PROTOCOL_VERSION;
} }
@Override @Override
public String getGameVersion() public String getGameVersion()
{ {
return GAME_VERSION; return Vanilla.GAME_VERSION;
} }
@Override @Override
@@ -413,4 +482,41 @@ public class BungeeCord extends ProxyServer
{ {
return ConsoleCommandSender.getInstance(); 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,99 +0,0 @@
package net.md_5.bungee;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* Logger to handle formatting and storage of the proxy's logger.
*/
public class BungeeLogger extends Logger
{
private static final Formatter formatter = new ConsoleLogFormatter();
static final BungeeLogger instance = new BungeeLogger();
public BungeeLogger()
{
super( "BungeeCord", null );
try
{
FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true );
handler.setFormatter( formatter );
addHandler( handler );
} catch ( IOException ex )
{
System.err.println( "Could not register logger!" );
ex.printStackTrace();
}
}
@Override
public void log(LogRecord record)
{
super.log( record );
String message = formatter.format( record );
if ( record.getLevel() == Level.SEVERE || record.getLevel() == Level.WARNING )
{
System.err.print( message );
} else
{
System.out.print( message );
}
}
public static class ConsoleLogFormatter extends Formatter
{
private SimpleDateFormat formatter = new SimpleDateFormat( "HH:mm:ss" );
@Override
public String format(LogRecord logrecord)
{
StringBuilder formatted = new StringBuilder();
formatted.append( formatter.format( logrecord.getMillis() ) );
Level level = logrecord.getLevel();
if ( level == Level.FINEST )
{
formatted.append( " [FINEST] " );
} else if ( level == Level.FINER )
{
formatted.append( " [FINER] " );
} else if ( level == Level.FINE )
{
formatted.append( " [FINE] " );
} else if ( level == Level.INFO )
{
formatted.append( " [INFO] " );
} else if ( level == Level.WARNING )
{
formatted.append( " [WARNING] " );
} else if ( level == Level.SEVERE )
{
formatted.append( " [SEVERE] " );
}
formatted.append( formatMessage( logrecord ) );
formatted.append( '\n' );
Throwable throwable = logrecord.getThrown();
if ( throwable != null )
{
StringWriter writer = new StringWriter();
throwable.printStackTrace( new PrintWriter( writer ) );
formatted.append( writer );
}
return formatted.toString();
}
}
}

View File

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

View File

@@ -1,23 +1,22 @@
package net.md_5.bungee; package net.md_5.bungee;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key; import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import net.md_5.bungee.packet.PacketFCEncryptionResponse; import lombok.Getter;
import net.md_5.bungee.packet.PacketFDEncryptionRequest; 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. * Class containing all encryption related methods for the proxy.
@@ -27,14 +26,22 @@ public class EncryptionUtil
private static final Random random = new Random(); private static final Random random = new Random();
public 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(); 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 ) : "-"; String hash = ( BungeeCord.getInstance().config.isOnlineMode() ) ? Long.toString( random.nextLong(), 16 ) : "-";
byte[] pubKey = keys.getPublic().getEncoded(); byte[] pubKey = keys.getPublic().getEncoded();
byte[] verify = new byte[ 4 ]; byte[] verify = new byte[ 4 ];
@@ -42,22 +49,19 @@ public class EncryptionUtil
return new PacketFDEncryptionRequest( hash, pubKey, verify ); 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 cipher = Cipher.getInstance( "RSA" );
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() ); 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!" ); throw new IllegalStateException( "Key pairs do not match!" );
} }
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() ); cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
byte[] shared = resp.sharedSecret; return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" );
byte[] secret = cipher.doFinal( shared );
return new SecretKeySpec( secret, "AES" );
} }
public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException
@@ -66,4 +70,16 @@ public class EncryptionUtil
cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) ); cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) );
return cip; 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

@@ -152,7 +152,8 @@ public class EntityMap
int type = packet[5] & 0xFF; int type = packet[5] & 0xFF;
if ( type == 60 || type == 90 ) if ( type == 60 || type == 90 )
{ {
if ( readInt( packet, 20 ) == oldId ) int index20 = readInt( packet, 20 );
if ( packet.length > 24 && index20 == oldId )
{ {
setInt( packet, 20, newId ); setInt( packet, 20, newId );
} }

View File

@@ -8,9 +8,10 @@ import java.io.UnsupportedEncodingException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.TimerTask;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
public class Metrics extends Thread public class Metrics extends TimerTask
{ {
/** /**
@@ -28,19 +29,11 @@ public class Metrics extends Thread
/** /**
* Interval of time to ping (in minutes) * Interval of time to ping (in minutes)
*/ */
private final static int PING_INTERVAL = 10; final static int PING_INTERVAL = 10;
boolean firstPost = true;
public Metrics()
{
super( "Metrics Gathering Thread" );
setDaemon( true );
}
@Override @Override
public void run() public void run()
{
boolean firstPost = true;
while ( true )
{ {
try try
{ {
@@ -56,14 +49,6 @@ public class Metrics extends Thread
{ {
ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() ); ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
} }
try
{
sleep( PING_INTERVAL * 1000 * 60 );
} catch ( InterruptedException ex )
{
break;
}
}
} }
/** /**
@@ -76,7 +61,7 @@ public class Metrics extends Thread
data.append( encode( "guid" ) ).append( '=' ).append( encode( BungeeCord.getInstance().config.getUuid() ) ); data.append( encode( "guid" ) ).append( '=' ).append( encode( BungeeCord.getInstance().config.getUuid() ) );
encodeDataPair( data, "version", ProxyServer.getInstance().getVersion() ); encodeDataPair( data, "version", ProxyServer.getInstance().getVersion() );
encodeDataPair( data, "server", "0" ); 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 ) ); encodeDataPair( data, "revision", String.valueOf( REVISION ) );
// If we're pinging, append it // If we're pinging, append it

View File

@@ -0,0 +1,18 @@
package net.md_5.bungee;
import net.md_5.bungee.protocol.packet.Packet9Respawn;
import net.md_5.bungee.protocol.packet.PacketCDClientStatus;
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
public class PacketConstants
{
public static final Packet9Respawn DIM1_SWITCH = new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
public static final Packet9Respawn DIM2_SWITCH = new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
public static final PacketCDClientStatus CLIENT_LOGIN = new PacketCDClientStatus( (byte) 0 );
public static final PacketFAPluginMessage FORGE_MOD_REQUEST = new PacketFAPluginMessage( "FML", new byte[]
{
0, 0, 0, 0, 0, 2
} );
public static final PacketFAPluginMessage I_AM_BUNGEE = new PacketFAPluginMessage( "BungeeCord", new byte[ 0 ] );
}

View File

@@ -1,6 +1,5 @@
package net.md_5.bungee; package net.md_5.bungee;
import io.netty.channel.Channel;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.Getter; import lombok.Getter;
@@ -8,9 +7,9 @@ import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.packet.Packet1Login; import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.protocol.packet.PacketFFKick;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ServerConnection implements Server public class ServerConnection implements Server
@@ -21,23 +20,29 @@ public class ServerConnection implements Server
@Getter @Getter
private final BungeeServerInfo info; private final BungeeServerInfo info;
@Getter @Getter
private final Packet1Login loginPacket;
@Getter
@Setter @Setter
private boolean isObsolete; private boolean isObsolete;
private final Unsafe unsafe = new Unsafe()
{
@Override
public void sendPacket(DefinedPacket packet)
{
ch.write( packet );
}
};
@Override @Override
public void sendData(String channel, byte[] data) public void sendData(String channel, byte[] data)
{ {
ch.write( new PacketFAPluginMessage( channel, data ) ); unsafe().sendPacket( new PacketFAPluginMessage( channel, data ) );
} }
@Override @Override
public synchronized void disconnect(String reason) public synchronized void disconnect(String reason)
{ {
if ( ch.getHandle().isActive() ) if ( !ch.isClosed() )
{ {
ch.write( new PacketFFKick( reason ) ); unsafe().sendPacket( new PacketFFKick( reason ) );
ch.getHandle().eventLoop().schedule( new Runnable() ch.getHandle().eventLoop().schedule( new Runnable()
{ {
@Override @Override
@@ -54,4 +59,10 @@ public class ServerConnection implements Server
{ {
return getInfo().getAddress(); return getInfo().getAddress();
} }
@Override
public Unsafe unsafe()
{
return unsafe;
}
} }

View File

@@ -1,33 +1,44 @@
package net.md_5.bungee; package net.md_5.bungee;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import java.security.PublicKey;
import java.util.Objects; import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.event.ServerConnectedEvent; import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.event.ServerKickEvent; import net.md_5.bungee.api.event.ServerKickEvent;
import net.md_5.bungee.api.scoreboard.Objective; import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.scoreboard.Scoreboard; import net.md_5.bungee.api.score.Objective;
import net.md_5.bungee.api.scoreboard.Team; import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.api.score.Team;
import net.md_5.bungee.connection.CancelSendSignal; import net.md_5.bungee.connection.CancelSendSignal;
import net.md_5.bungee.connection.DownstreamBridge; import net.md_5.bungee.connection.DownstreamBridge;
import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.netty.CipherDecoder;
import net.md_5.bungee.packet.Packet1Login; import net.md_5.bungee.netty.CipherEncoder;
import net.md_5.bungee.packet.Packet9Respawn; import net.md_5.bungee.netty.PacketDecoder;
import net.md_5.bungee.packet.PacketCDClientStatus; import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.packet.PacketCEScoreboardObjective; import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.packet.PacketD1Team; import net.md_5.bungee.protocol.Forge;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.packet.PacketFDEncryptionRequest; import net.md_5.bungee.protocol.packet.Packet1Login;
import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.protocol.packet.Packet9Respawn;
import net.md_5.bungee.packet.PacketHandler; import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
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.PacketFFKick;
import net.md_5.bungee.protocol.packet.forge.Forge1Login;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ServerConnector extends PacketHandler public class ServerConnector extends PacketHandler
@@ -38,11 +49,26 @@ public class ServerConnector extends PacketHandler
private final UserConnection user; private final UserConnection user;
private final BungeeServerInfo target; private final BungeeServerInfo target;
private State thisState = State.ENCRYPT_REQUEST; private State thisState = State.ENCRYPT_REQUEST;
private SecretKey secretkey;
private boolean sentMessages;
private enum State private enum State
{ {
ENCRYPT_REQUEST, LOGIN, FINISHED; ENCRYPT_REQUEST, ENCRYPT_RESPONSE, LOGIN, FINISHED;
}
@Override
public void exception(Throwable t) throws Exception
{
String message = "Exception Connecting:" + Util.exception( t );
if ( user.getServer() == null )
{
user.disconnect( message );
} else
{
user.sendMessage( ChatColor.RED + message );
}
} }
@Override @Override
@@ -52,12 +78,17 @@ public class ServerConnector extends PacketHandler
ByteArrayDataOutput out = ByteStreams.newDataOutput(); ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF( "Login" ); out.writeUTF( "Login" );
out.writeUTF( user.getAddress().getAddress().getHostAddress() ); out.writeUTF( user.getAddress().getHostString() );
out.writeInt( user.getAddress().getPort() ); out.writeInt( user.getAddress().getPort() );
channel.write( new PacketFAPluginMessage( "BungeeCord", out.toByteArray() ) ); channel.write( new PacketFAPluginMessage( "BungeeCord", out.toByteArray() ) );
channel.write( user.getPendingConnection().getHandshake() ); channel.write( user.getPendingConnection().getHandshake() );
channel.write( PacketCDClientStatus.CLIENT_LOGIN );
// Skip encryption if we are not using Forge
if ( user.getPendingConnection().getForgeLogin() == null )
{
channel.write( PacketConstants.CLIENT_LOGIN );
}
} }
@Override @Override
@@ -71,18 +102,32 @@ public class ServerConnector extends PacketHandler
{ {
Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" ); Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
ServerConnection server = new ServerConnection( ch, target, login ); ServerConnection server = new ServerConnection( ch, target );
ServerConnectedEvent event = new ServerConnectedEvent( user, server ); ServerConnectedEvent event = new ServerConnectedEvent( user, server );
bungee.getPluginManager().callEvent( event ); bungee.getPluginManager().callEvent( event );
ch.write( BungeeCord.getInstance().registerChannels() ); ch.write( BungeeCord.getInstance().registerChannels() );
Queue<DefinedPacket> packetQueue = target.getPacketQueue();
// TODO: Race conditions with many connects synchronized ( packetQueue )
Queue<DefinedPacket> packetQueue = ( (BungeeServerInfo) target ).getPacketQueue(); {
while ( !packetQueue.isEmpty() ) while ( !packetQueue.isEmpty() )
{ {
ch.write( packetQueue.poll() ); ch.write( packetQueue.poll() );
} }
}
for ( PacketFAPluginMessage message : user.getPendingConnection().getRegisterMessages() )
{
ch.write( message );
}
if ( !sentMessages )
{
for ( PacketFAPluginMessage message : user.getPendingConnection().getLoginMessages() )
{
ch.write( message );
}
}
if ( user.getSettings() != null ) if ( user.getSettings() != null )
{ {
ch.write( user.getSettings() ); ch.write( user.getSettings() );
@@ -93,38 +138,40 @@ public class ServerConnector extends PacketHandler
if ( user.getServer() == null ) if ( user.getServer() == null )
{ {
// Once again, first connection // Once again, first connection
user.setClientEntityId( login.entityId ); user.setClientEntityId( login.getEntityId() );
user.setServerEntityId( login.entityId ); user.setServerEntityId( login.getEntityId() );
// Set tab list size
Packet1Login modLogin = new Packet1Login( // Set tab list size, this sucks balls, TODO: what shall we do about packet mutability
login.entityId, Packet1Login modLogin;
login.levelType, if ( ch.getHandle().pipeline().get( PacketDecoder.class ).getProtocol() == Forge.getInstance() )
login.gameMode, {
(byte) login.dimension, modLogin = new Forge1Login( login.getEntityId(), login.getLevelType(), login.getGameMode(), login.getDimension(), login.getDifficulty(), login.getUnused(),
login.difficulty,
login.unused,
(byte) user.getPendingConnection().getListener().getTabListSize() ); (byte) user.getPendingConnection().getListener().getTabListSize() );
user.sendPacket( modLogin );
} else } else
{ {
bungee.getTabListHandler().onServerChange( user ); modLogin = new Packet1Login( login.getEntityId(), login.getLevelType(), login.getGameMode(), (byte) login.getDimension(), login.getDifficulty(), login.getUnused(),
(byte) user.getPendingConnection().getListener().getTabListSize() );
}
user.unsafe().sendPacket( modLogin );
} else
{
user.getTabList().onServerChange();
Scoreboard serverScoreboard = user.getServerSentScoreboard(); Scoreboard serverScoreboard = user.getServerSentScoreboard();
for ( Objective objective : serverScoreboard.getObjectives() ) for ( Objective objective : serverScoreboard.getObjectives() )
{ {
user.sendPacket( new PacketCEScoreboardObjective( objective.getName(), objective.getValue(), (byte) 1 ) ); user.unsafe().sendPacket( new PacketCEScoreboardObjective( objective.getName(), objective.getValue(), (byte) 1 ) );
} }
for ( Team team : serverScoreboard.getTeams() ) for ( Team team : serverScoreboard.getTeams() )
{ {
user.sendPacket( PacketD1Team.destroy( team.getName() ) ); user.unsafe().sendPacket( new PacketD1Team( team.getName() ) );
} }
serverScoreboard.clear(); serverScoreboard.clear();
user.sendPacket( Packet9Respawn.DIM1_SWITCH ); user.sendDimensionSwitch();
user.sendPacket( Packet9Respawn.DIM2_SWITCH );
user.setServerEntityId( login.entityId ); user.setServerEntityId( login.getEntityId() );
user.sendPacket( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) ); user.unsafe().sendPacket( new Packet9Respawn( login.getDimension(), login.getDifficulty(), login.getGameMode(), (short) 256, login.getLevelType() ) );
// Remove from old servers // Remove from old servers
user.getServer().setObsolete( true ); user.getServer().setObsolete( true );
@@ -149,6 +196,8 @@ public class ServerConnector extends PacketHandler
ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new DownstreamBridge( bungee, user, server ) ); ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new DownstreamBridge( bungee, user, server ) );
} }
bungee.getPluginManager().callEvent( new ServerSwitchEvent( user ) );
thisState = State.FINISHED; thisState = State.FINISHED;
throw new CancelSendSignal(); throw new CancelSendSignal();
@@ -158,6 +207,39 @@ public class ServerConnector extends PacketHandler
public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
{ {
Preconditions.checkState( thisState == State.ENCRYPT_REQUEST, "Not expecting ENCRYPT_REQUEST" ); Preconditions.checkState( thisState == State.ENCRYPT_REQUEST, "Not expecting ENCRYPT_REQUEST" );
// Only need to handle this if we want to use encryption
if ( user.getPendingConnection().getForgeLogin() != null )
{
PublicKey publickey = EncryptionUtil.getPubkey( encryptRequest );
this.secretkey = EncryptionUtil.getSecret();
byte[] shared = EncryptionUtil.encrypt( publickey, secretkey.getEncoded() );
byte[] token = EncryptionUtil.encrypt( publickey, encryptRequest.getVerifyToken() );
ch.write( new PacketFCEncryptionResponse( shared, token ) );
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, secretkey );
ch.getHandle().pipeline().addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
thisState = State.ENCRYPT_RESPONSE;
} else
{
thisState = State.LOGIN;
}
}
@Override
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
{
Preconditions.checkState( thisState == State.ENCRYPT_RESPONSE, "Not expecting ENCRYPT_RESPONSE" );
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, secretkey );
ch.getHandle().pipeline().addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
ch.write( user.getPendingConnection().getForgeLogin() );
ch.write( PacketConstants.CLIENT_LOGIN );
thisState = State.LOGIN; thisState = State.LOGIN;
} }
@@ -169,14 +251,14 @@ public class ServerConnector extends PacketHandler
{ {
def = null; def = null;
} }
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.message, def ) ); ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.getMessage(), def ) );
if ( event.isCancelled() && event.getCancelServer() != null ) if ( event.isCancelled() && event.getCancelServer() != null )
{ {
user.connect( event.getCancelServer() ); user.connect( event.getCancelServer() );
return; return;
} }
String message = ChatColor.RED + "Kicked whilst connecting to " + target.getName() + ": " + kick.message; String message = bungee.getTranslation( "connect_kick" ) + target.getName() + ": " + kick.getMessage();
if ( user.getServer() == null ) if ( user.getServer() == null )
{ {
user.disconnect( message ); user.disconnect( message );
@@ -186,6 +268,41 @@ public class ServerConnector extends PacketHandler
} }
} }
@Override
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
{
if ( pluginMessage.equals( PacketConstants.I_AM_BUNGEE ) )
{
throw new IllegalStateException( "May not connect to another BungeCord!" );
}
if ( pluginMessage.getTag().equals( "FML" ) && ( pluginMessage.getData()[0] & 0xFF ) == 0 )
{
ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.getData() );
in.readUnsignedByte();
int count = in.readInt();
for ( int i = 0; i < count; i++ )
{
in.readUTF();
}
if ( in.readByte() != 0 )
{
// TODO: Using forge flag
ch.getHandle().pipeline().get( PacketDecoder.class ).setProtocol( Forge.getInstance() );
}
}
user.unsafe().sendPacket( pluginMessage ); // We have to forward these to the user, especially with Forge as stuff might break
if ( !sentMessages && user.getPendingConnection().getForgeLogin() != null )
{
for ( PacketFAPluginMessage message : user.getPendingConnection().getLoginMessages() )
{
ch.write( message );
}
sentMessages = true;
}
}
@Override @Override
public String toString() public String toString()
{ {

View File

@@ -8,6 +8,7 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -21,20 +22,22 @@ import lombok.Setter;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.config.TexturePackInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PermissionCheckEvent; import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.scoreboard.Scoreboard; import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.api.tab.TabListHandler;
import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.packet.Packet3Chat; import net.md_5.bungee.protocol.packet.Packet3Chat;
import net.md_5.bungee.packet.Packet9Respawn; import net.md_5.bungee.protocol.packet.PacketCCSettings;
import net.md_5.bungee.packet.PacketCCSettings; import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.protocol.packet.PacketFFKick;
import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.util.CaseInsensitiveSet;
@RequiredArgsConstructor @RequiredArgsConstructor
public final class UserConnection implements ProxiedPlayer public final class UserConnection implements ProxiedPlayer
@@ -49,7 +52,6 @@ public final class UserConnection implements ProxiedPlayer
@NonNull @NonNull
private final String name; private final String name;
@Getter @Getter
@NonNull
private final InitialHandler pendingConnection; private final InitialHandler pendingConnection;
/*========================================================================*/ /*========================================================================*/
@Getter @Getter
@@ -61,6 +63,8 @@ public final class UserConnection implements ProxiedPlayer
private final Collection<ServerInfo> pendingConnects = new HashSet<>(); private final Collection<ServerInfo> pendingConnects = new HashSet<>();
/*========================================================================*/ /*========================================================================*/
@Getter @Getter
private TabListHandler tabList;
@Getter
@Setter @Setter
private int sentPingId; private int sentPingId;
@Getter @Getter
@@ -70,10 +74,11 @@ public final class UserConnection implements ProxiedPlayer
@Setter @Setter
private int ping = 100; private int ping = 100;
/*========================================================================*/ /*========================================================================*/
private final Collection<String> groups = new HashSet<>(); private final Collection<String> groups = new CaseInsensitiveSet();
private final Collection<String> permissions = new HashSet<>(); private final Collection<String> permissions = new CaseInsensitiveSet();
/*========================================================================*/ /*========================================================================*/
@Getter @Getter
@Setter
private int clientEntityId; private int clientEntityId;
@Getter @Getter
@Setter @Setter
@@ -87,10 +92,26 @@ public final class UserConnection implements ProxiedPlayer
@Getter @Getter
private String displayName; private String displayName;
/*========================================================================*/ /*========================================================================*/
private final Unsafe unsafe = new Unsafe()
{
@Override
public void sendPacket(DefinedPacket packet)
{
ch.write( packet );
}
};
public void init() public void init()
{ {
this.displayName = name; this.displayName = name;
try
{
this.tabList = getPendingConnection().getListener().getTabList().getDeclaredConstructor().newInstance();
} catch ( ReflectiveOperationException ex )
{
throw new RuntimeException( ex );
}
this.tabList.init( this );
Collection<String> g = bungee.getConfigurationAdapter().getGroups( name ); Collection<String> g = bungee.getConfigurationAdapter().getGroups( name );
for ( String s : g ) for ( String s : g )
@@ -99,9 +120,11 @@ public final class UserConnection implements ProxiedPlayer
} }
} }
public void sendPacket(DefinedPacket p) @Override
public void setTabList(TabListHandler tabList)
{ {
ch.write( p ); tabList.init( this );
this.tabList = tabList;
} }
public void sendPacket(byte[] b) public void sendPacket(byte[] b)
@@ -112,7 +135,7 @@ public final class UserConnection implements ProxiedPlayer
@Deprecated @Deprecated
public boolean isActive() public boolean isActive()
{ {
return ch.getHandle().isActive(); return !ch.isClosed();
} }
@Override @Override
@@ -120,9 +143,9 @@ public final class UserConnection implements ProxiedPlayer
{ {
Preconditions.checkNotNull( name, "displayName" ); Preconditions.checkNotNull( name, "displayName" );
Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" ); Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" );
bungee.getTabListHandler().onDisconnect( this ); getTabList().onDisconnect();
displayName = name; displayName = name;
bungee.getTabListHandler().onConnect( this ); getTabList().onConnect();
} }
@Override @Override
@@ -131,19 +154,26 @@ public final class UserConnection implements ProxiedPlayer
connect( target, false ); connect( target, false );
} }
void sendDimensionSwitch()
{
unsafe().sendPacket( PacketConstants.DIM1_SWITCH );
unsafe().sendPacket( PacketConstants.DIM2_SWITCH );
}
public void connectNow(ServerInfo target) public void connectNow(ServerInfo target)
{ {
sendPacket( Packet9Respawn.DIM1_SWITCH ); sendDimensionSwitch();
sendPacket( Packet9Respawn.DIM2_SWITCH );
connect( target ); connect( target );
} }
public void connect(ServerInfo info, final boolean retry) public void connect(ServerInfo info, final boolean retry)
{ {
ServerConnectEvent event = new ServerConnectEvent( this, info ); ServerConnectEvent event = new ServerConnectEvent( this, info );
ProxyServer.getInstance().getPluginManager().callEvent( event ); if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
{
return;
}
Preconditions.checkArgument( event.getTarget() instanceof BungeeServerInfo, "BungeeCord can only connect to BungeeServerInfo instances" );
final BungeeServerInfo target = (BungeeServerInfo) event.getTarget(); // Update in case the event changed target final BungeeServerInfo target = (BungeeServerInfo) event.getTarget(); // Update in case the event changed target
if ( getServer() != null && Objects.equals( getServer().getInfo(), target ) ) if ( getServer() != null && Objects.equals( getServer().getInfo(), target ) )
@@ -159,10 +189,7 @@ public final class UserConnection implements ProxiedPlayer
pendingConnects.add( target ); pendingConnects.add( target );
new Bootstrap() ChannelInitializer initializer = new ChannelInitializer()
.channel( NioSocketChannel.class )
.group( BungeeCord.getInstance().eventLoops )
.handler( new ChannelInitializer()
{ {
@Override @Override
protected void initChannel(Channel ch) throws Exception protected void initChannel(Channel ch) throws Exception
@@ -170,10 +197,8 @@ public final class UserConnection implements ProxiedPlayer
PipelineUtils.BASE.initChannel( ch ); PipelineUtils.BASE.initChannel( ch );
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) ); ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
} }
} ) };
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable ChannelFutureListener listener = new ChannelFutureListener()
.remoteAddress( target.getAddress() )
.connect().addListener( new ChannelFutureListener()
{ {
@Override @Override
public void operationComplete(ChannelFuture future) throws Exception public void operationComplete(ChannelFuture future) throws Exception
@@ -186,21 +211,33 @@ public final class UserConnection implements ProxiedPlayer
ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getFallbackServer() ); ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getFallbackServer() );
if ( retry & target != def && ( getServer() == null || def != getServer().getInfo() ) ) if ( retry & target != def && ( getServer() == null || def != getServer().getInfo() ) )
{ {
sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the lobby server" ); sendMessage( bungee.getTranslation( "fallback_lobby" ) );
connect( def, false ); connect( def, false );
} else } else
{ {
if ( server == null ) if ( server == null )
{ {
disconnect( "Could not connect to default server, please try again later: " + future.cause().getClass().getName() ); disconnect( bungee.getTranslation( "fallback_kick" ) + future.cause().getClass().getName() );
} else } else
{ {
sendMessage( ChatColor.RED + "Could not connect to selected server, please try again later: " + future.cause().getClass().getName() ); sendMessage( bungee.getTranslation( "fallback_kick" ) + future.cause().getClass().getName() );
} }
} }
} }
} }
} ); };
Bootstrap b = new Bootstrap()
.channel( NioSocketChannel.class )
.group( BungeeCord.getInstance().eventLoops )
.handler( initializer )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
.remoteAddress( target.getAddress() );
// Windows is bugged, multi homed users will just have to live with random connecting IPs
if ( !PlatformDependent.isWindows() )
{
b.localAddress( getPendingConnection().getListener().getHost().getHostString(), 0 );
}
b.connect().addListener( listener );
} }
@Override @Override
@@ -209,8 +246,8 @@ public final class UserConnection implements ProxiedPlayer
if ( ch.getHandle().isActive() ) if ( ch.getHandle().isActive() )
{ {
bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason ); bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason );
sendPacket( new PacketFFKick( reason ) ); unsafe().sendPacket( new PacketFFKick( reason ) );
ch.getHandle().close(); ch.close();
if ( server != null ) if ( server != null )
{ {
server.disconnect( "Quitting" ); server.disconnect( "Quitting" );
@@ -228,7 +265,7 @@ public final class UserConnection implements ProxiedPlayer
@Override @Override
public void sendMessage(String message) public void sendMessage(String message)
{ {
sendPacket( new Packet3Chat( message ) ); unsafe().sendPacket( new Packet3Chat( message ) );
} }
@Override @Override
@@ -243,7 +280,7 @@ public final class UserConnection implements ProxiedPlayer
@Override @Override
public void sendData(String channel, byte[] data) public void sendData(String channel, byte[] data)
{ {
sendPacket( new PacketFAPluginMessage( channel, data ) ); unsafe().sendPacket( new PacketFAPluginMessage( channel, data ) );
} }
@Override @Override
@@ -308,9 +345,15 @@ public final class UserConnection implements ProxiedPlayer
return name; return name;
} }
public void setClientEntityId(int clientEntityId) @Override
public void setTexturePack(TexturePackInfo pack)
{ {
Preconditions.checkState( this.clientEntityId == 0, "Client entityId already set!" ); unsafe().sendPacket( new PacketFAPluginMessage( "MC|TPack", ( pack.getUrl() + "\00" + pack.getSize() ).getBytes() ) );
this.clientEntityId = clientEntityId; }
@Override
public Unsafe unsafe()
{
return unsafe;
} }
} }

View File

@@ -2,7 +2,6 @@ package net.md_5.bungee;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** /**
* Series of utility classes to perform various operations. * Series of utility classes to perform various operations.

View File

@@ -1,93 +0,0 @@
package net.md_5.bungee;
import com.google.common.base.Preconditions;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ReconnectHandler;
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 org.yaml.snakeyaml.Yaml;
public class YamlReconnectHandler implements ReconnectHandler
{
private final Yaml yaml = new Yaml();
private final File file = new File( "locations.yml" );
/*========================================================================*/
private Map<String, String> data;
@SuppressWarnings("unchecked")
public YamlReconnectHandler()
{
try
{
file.createNewFile();
try ( FileReader rd = new FileReader( file ) )
{
data = yaml.loadAs( rd, Map.class );
}
} catch ( IOException ex )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load reconnect locations", ex );
}
if ( data == null )
{
data = new HashMap<>();
}
}
@Override
public ServerInfo getServer(ProxiedPlayer player)
{
ListenerInfo listener = player.getPendingConnection().getListener();
String name;
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName().toLowerCase() );
if ( forced == null && listener.isForceDefault() )
{
forced = listener.getDefaultServer();
}
String server = ( forced == null ) ? data.get( key( player ) ) : forced;
name = ( server != null ) ? server : listener.getDefaultServer();
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
if ( info == null )
{
info = ProxyServer.getInstance().getServerInfo( listener.getDefaultServer() );
}
Preconditions.checkState( info != null, "Default server not defined" );
return info;
}
@Override
public void setServer(ProxiedPlayer player)
{
data.put( key( player ), player.getServer().getInfo().getName() );
}
private String key(ProxiedPlayer player)
{
InetSocketAddress host = player.getPendingConnection().getVirtualHost();
return player.getName() + ";" + host.getHostString() + ":" + host.getPort();
}
@Override
public synchronized void save()
{
try ( FileWriter wr = new FileWriter( file ) )
{
yaml.dump( data, wr );
} catch ( IOException ex )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save reconnect locations", ex );
}
}
}

View File

@@ -29,8 +29,7 @@ public class CommandAlert extends Command
args[0] = args[0].substring( 2, args[0].length() ); args[0] = args[0].substring( 2, args[0].length() );
} else } else
{ {
builder.append( ChatColor.DARK_PURPLE ); builder.append( ProxyServer.getInstance().getTranslation( "alert" ) );
builder.append( "[Alert] " );
} }
for ( String s : args ) for ( String s : args )
@@ -40,10 +39,8 @@ public class CommandAlert extends Command
} }
String message = builder.substring( 0, builder.length() - 1 ); String message = builder.substring( 0, builder.length() - 1 );
for ( ProxiedPlayer player : ProxyServer.getInstance().getPlayers() )
{ ProxyServer.getInstance().broadcast( message );
player.sendMessage( message );
}
ProxyServer.getInstance().getConsole().sendMessage( message ); ProxyServer.getInstance().getConsole().sendMessage( message );
} }
} }

View File

@@ -0,0 +1,35 @@
package net.md_5.bungee.command;
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.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
public class CommandFind extends Command
{
public CommandFind()
{
super( "find", "bungeecord.command.find" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length != 1 )
{
sender.sendMessage( ChatColor.RED + "Please follow this command by a user name" );
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null || player.getServer() == null )
{
sender.sendMessage( ChatColor.RED + "That user is not online" );
} else
{
sender.sendMessage( ChatColor.BLUE + args[0] + " is online at " + player.getServer().getInfo().getName() );
}
}
}
}

View File

@@ -58,6 +58,6 @@ public class CommandList extends Command
sender.sendMessage( message.toString() ); sender.sendMessage( message.toString() );
} }
sender.sendMessage( "Total players online: " + ProxyServer.getInstance().getPlayers().size() ); sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players" ) + ProxyServer.getInstance().getOnlineCount() );
} }
} }

View File

@@ -19,6 +19,7 @@ public class CommandReload extends Command
BungeeCord.getInstance().config.load(); BungeeCord.getInstance().config.load();
BungeeCord.getInstance().stopListeners(); BungeeCord.getInstance().stopListeners();
BungeeCord.getInstance().startListeners(); BungeeCord.getInstance().startListeners();
sender.sendMessage( ChatColor.GREEN + "Reloaded config, please restart if you have any issues" ); sender.sendMessage( ChatColor.BOLD.toString() + ChatColor.RED.toString() + "BungeeCord has been reloaded."
+ " This is NOT advisable and you will not be supported with any issues that arise! Please restart BungeeCord ASAP." );
} }
} }

View File

@@ -26,7 +26,8 @@ public class CommandSend extends Command
ServerInfo target = ProxyServer.getInstance().getServerInfo( args[1] ); ServerInfo target = ProxyServer.getInstance().getServerInfo( args[1] );
if ( target == null ) if ( target == null )
{ {
sender.sendMessage( ChatColor.RED + "Target server does not exist" ); sender.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
return;
} }
if ( args[0].equalsIgnoreCase( "all" ) ) if ( args[0].equalsIgnoreCase( "all" ) )
@@ -40,6 +41,7 @@ public class CommandSend extends Command
if ( !( sender instanceof ProxiedPlayer ) ) if ( !( sender instanceof ProxiedPlayer ) )
{ {
sender.sendMessage( ChatColor.RED + "Only in game players can use this command" ); sender.sendMessage( ChatColor.RED + "Only in game players can use this command" );
return;
} }
ProxiedPlayer player = (ProxiedPlayer) sender; ProxiedPlayer player = (ProxiedPlayer) sender;
for ( ProxiedPlayer p : player.getServer().getInfo().getPlayers() ) for ( ProxiedPlayer p : player.getServer().getInfo().getPlayers() )
@@ -52,6 +54,7 @@ public class CommandSend extends Command
if ( player == null ) if ( player == null )
{ {
sender.sendMessage( ChatColor.RED + "That player is not online" ); sender.sendMessage( ChatColor.RED + "That player is not online" );
return;
} }
summon( player, target, sender ); summon( player, target, sender );
} }

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.command; package net.md_5.bungee.command;
import java.util.Map; import java.util.Map;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
@@ -30,6 +29,8 @@ public class CommandServer extends Command
Map<String, ServerInfo> servers = ProxyServer.getInstance().getServers(); Map<String, ServerInfo> servers = ProxyServer.getInstance().getServers();
if ( args.length == 0 ) if ( args.length == 0 )
{ {
player.sendMessage( ProxyServer.getInstance().getTranslation( "current_server" ) + player.getServer().getInfo().getName() );
StringBuilder serverList = new StringBuilder(); StringBuilder serverList = new StringBuilder();
for ( ServerInfo server : servers.values() ) for ( ServerInfo server : servers.values() )
{ {
@@ -43,16 +44,16 @@ public class CommandServer extends Command
{ {
serverList.setLength( serverList.length() - 2 ); serverList.setLength( serverList.length() - 2 );
} }
player.sendMessage( ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString() ); player.sendMessage( ProxyServer.getInstance().getTranslation( "server_list" ) + serverList.toString() );
} else } else
{ {
ServerInfo server = servers.get( args[0] ); ServerInfo server = servers.get( args[0] );
if ( server == null ) if ( server == null )
{ {
player.sendMessage( ChatColor.RED + "The specified server does not exist" ); player.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
} else if ( !server.canAccess( player ) ) } else if ( !server.canAccess( player ) )
{ {
player.sendMessage( ChatColor.RED + "You don't have permission to access this server" ); player.sendMessage( ProxyServer.getInstance().getTranslation( "no_server_permission" ) );
} else } else
{ {
player.connect( server ); player.connect( server );

View File

@@ -3,8 +3,8 @@ package net.md_5.bungee.command;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import lombok.Getter; import lombok.Getter;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
/** /**
* Command sender representing the proxy console. * Command sender representing the proxy console.
@@ -22,7 +22,7 @@ public class ConsoleCommandSender implements CommandSender
@Override @Override
public void sendMessage(String message) public void sendMessage(String message)
{ {
System.out.println( ChatColor.stripColor( message ) ); ProxyServer.getInstance().getLogger().info( message );
} }
@Override @Override

View File

@@ -1,6 +1,7 @@
package net.md_5.bungee.config; package net.md_5.bungee.config;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import gnu.trove.map.TMap;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -9,9 +10,10 @@ import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ConfigurationAdapter; import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.tablist.GlobalPing; import net.md_5.bungee.tab.GlobalPing;
import net.md_5.bungee.tablist.Global; import net.md_5.bungee.tab.Global;
import net.md_5.bungee.tablist.ServerUnique; import net.md_5.bungee.tab.ServerUnique;
import net.md_5.bungee.util.CaseInsensitiveMap;
/** /**
* Core configuration for the proxy. * Core configuration for the proxy.
@@ -20,14 +22,6 @@ import net.md_5.bungee.tablist.ServerUnique;
public class Configuration public class Configuration
{ {
/**
* The default tab list options available for picking.
*/
private enum DefaultTabList
{
GLOBAL, GLOBAL_PING, SERVER;
}
/** /**
* Time before users are disconnected due to no network activity. * Time before users are disconnected due to no network activity.
*/ */
@@ -43,7 +37,7 @@ public class Configuration
/** /**
* Set of all servers. * Set of all servers.
*/ */
private Map<String, ServerInfo> servers; private TMap<String, ServerInfo> servers;
/** /**
* Should we check minecraft.net auth. * Should we check minecraft.net auth.
*/ */
@@ -55,30 +49,12 @@ public class Configuration
ConfigurationAdapter adapter = ProxyServer.getInstance().getConfigurationAdapter(); ConfigurationAdapter adapter = ProxyServer.getInstance().getConfigurationAdapter();
adapter.load(); adapter.load();
listeners = adapter.getListeners();
timeout = adapter.getInt( "timeout", timeout ); timeout = adapter.getInt( "timeout", timeout );
uuid = adapter.getString( "stats", uuid ); uuid = adapter.getString( "stats", uuid );
onlineMode = adapter.getBoolean( "online_mode", onlineMode ); onlineMode = adapter.getBoolean( "online_mode", onlineMode );
playerLimit = adapter.getInt( "player_limit", playerLimit ); playerLimit = adapter.getInt( "player_limit", playerLimit );
DefaultTabList tab = DefaultTabList.valueOf( adapter.getString( "tab_list", "GLOBAL_PING" ) );
if ( tab == null )
{
tab = DefaultTabList.GLOBAL_PING;
}
switch ( tab )
{
case GLOBAL:
ProxyServer.getInstance().setTabListHandler( new Global() );
break;
case GLOBAL_PING:
ProxyServer.getInstance().setTabListHandler( new GlobalPing() );
break;
case SERVER:
ProxyServer.getInstance().setTabListHandler( new ServerUnique() );
break;
}
listeners = adapter.getListeners();
Preconditions.checkArgument( listeners != null && !listeners.isEmpty(), "No listeners defined." ); Preconditions.checkArgument( listeners != null && !listeners.isEmpty(), "No listeners defined." );
Map<String, ServerInfo> newServers = adapter.getServers(); Map<String, ServerInfo> newServers = adapter.getServers();
@@ -86,7 +62,7 @@ public class Configuration
if ( servers == null ) if ( servers == null )
{ {
servers = newServers; servers = new CaseInsensitiveMap<>( newServers );
} else } else
{ {
for ( ServerInfo oldServer : servers.values() ) for ( ServerInfo oldServer : servers.values() )

View File

@@ -14,6 +14,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.Util; import net.md_5.bungee.Util;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
@@ -21,12 +22,27 @@ import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.config.TexturePackInfo; import net.md_5.bungee.api.config.TexturePackInfo;
import net.md_5.bungee.api.tab.TabListHandler;
import net.md_5.bungee.tab.Global;
import net.md_5.bungee.tab.GlobalPing;
import net.md_5.bungee.tab.ServerUnique;
import net.md_5.bungee.util.CaseInsensitiveMap;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
public class YamlConfig implements ConfigurationAdapter public class YamlConfig implements ConfigurationAdapter
{ {
/**
* The default tab list options available for picking.
*/
@RequiredArgsConstructor
private enum DefaultTabList
{
GLOBAL( Global.class ), GLOBAL_PING( GlobalPing.class ), SERVER( ServerUnique.class );
private final Class<? extends TabListHandler> clazz;
}
private Yaml yaml; private Yaml yaml;
private Map config; private Map config;
private final File file = new File( "config.yml" ); private final File file = new File( "config.yml" );
@@ -48,7 +64,10 @@ public class YamlConfig implements ConfigurationAdapter
if ( config == null ) if ( config == null )
{ {
config = new HashMap(); config = new CaseInsensitiveMap();
} else
{
config = new CaseInsensitiveMap( config );
} }
} catch ( IOException ex ) } catch ( IOException ex )
{ {
@@ -186,11 +205,18 @@ public class YamlConfig implements ConfigurationAdapter
String host = get( "host", "0.0.0.0:25577", val ); String host = get( "host", "0.0.0.0:25577", val );
int tabListSize = get( "tab_size", 60, val ); int tabListSize = get( "tab_size", 60, val );
InetSocketAddress address = Util.getAddr( host ); InetSocketAddress address = Util.getAddr( host );
Map<String, String> forced = get( "forced_hosts", forcedDef, val ); Map<String, String> forced = new CaseInsensitiveMap<>( get( "forced_hosts", forcedDef, val ) );
String textureURL = get( "texture_url", null, val ); String textureURL = get( "texture_url", null, val );
int textureSize = get( "texture_size", 16, val ); int textureSize = get( "texture_size", 16, val );
TexturePackInfo texture = ( textureURL == null ) ? null : new TexturePackInfo( textureURL, textureSize ); TexturePackInfo texture = ( textureURL == null ) ? null : new TexturePackInfo( textureURL, textureSize );
ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, texture ); String tabListName = get( "tab_list", "GLOBAL_PING", val );
DefaultTabList value = DefaultTabList.valueOf( tabListName.toUpperCase() );
if ( value == null )
{
value = DefaultTabList.GLOBAL_PING;
}
ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, texture, value.clazz );
ret.add( info ); ret.add( info );
} }

View File

@@ -3,7 +3,6 @@ package net.md_5.bungee.connection;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import io.netty.channel.Channel;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.EntityMap; import net.md_5.bungee.EntityMap;
@@ -14,25 +13,25 @@ import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ChatEvent;
import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerKickEvent; import net.md_5.bungee.api.event.ServerKickEvent;
import net.md_5.bungee.api.scoreboard.Objective; import net.md_5.bungee.api.score.Objective;
import net.md_5.bungee.api.scoreboard.Position; import net.md_5.bungee.api.score.Position;
import net.md_5.bungee.api.scoreboard.Score; import net.md_5.bungee.api.score.Score;
import net.md_5.bungee.api.scoreboard.Scoreboard; import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.api.scoreboard.Team; import net.md_5.bungee.api.score.Team;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.packet.Packet0KeepAlive; import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.packet.Packet3Chat; import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
import net.md_5.bungee.packet.PacketC9PlayerListItem; import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
import net.md_5.bungee.packet.PacketCEScoreboardObjective; import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
import net.md_5.bungee.packet.PacketCFScoreboardScore; import net.md_5.bungee.protocol.packet.PacketCFScoreboardScore;
import net.md_5.bungee.packet.PacketD0DisplayScoreboard; import net.md_5.bungee.protocol.packet.PacketD0DisplayScoreboard;
import net.md_5.bungee.packet.PacketD1Team; import net.md_5.bungee.protocol.packet.PacketD1Team;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.protocol.packet.PacketFFKick;
import net.md_5.bungee.packet.PacketHandler;
;
@RequiredArgsConstructor @RequiredArgsConstructor
public class DownstreamBridge extends PacketHandler public class DownstreamBridge extends PacketHandler
@@ -65,41 +64,32 @@ public class DownstreamBridge extends PacketHandler
if ( !server.isObsolete() ) if ( !server.isObsolete() )
{ {
con.disconnect( "[Proxy] Lost connection to server D:" ); con.disconnect( bungee.getTranslation( "lost_connection" ) );
} }
} }
@Override @Override
public void handle(byte[] buf) throws Exception public void handle(byte[] buf) throws Exception
{
if ( !server.isObsolete() )
{ {
EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() ); EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() );
con.sendPacket( buf ); con.sendPacket( buf );
} }
}
@Override @Override
public void handle(Packet0KeepAlive alive) throws Exception public void handle(Packet0KeepAlive alive) throws Exception
{ {
con.setSentPingId( alive.id ); con.setSentPingId( alive.getRandomId() );
con.setSentPingTime( System.currentTimeMillis() ); con.setSentPingTime( System.currentTimeMillis() );
} }
@Override
public void handle(Packet3Chat chat) throws Exception
{
ChatEvent chatEvent = new ChatEvent( con.getServer(), con, chat.message );
bungee.getPluginManager().callEvent( chatEvent );
if ( chatEvent.isCancelled() )
{
throw new CancelSendSignal();
}
}
@Override @Override
public void handle(PacketC9PlayerListItem playerList) throws Exception public void handle(PacketC9PlayerListItem playerList) throws Exception
{ {
if ( !bungee.getTabListHandler().onListUpdate( con, playerList.username, playerList.online, playerList.ping ) ) if ( !con.getTabList().onListUpdate( playerList.getUsername(), playerList.isOnline(), playerList.getPing() ) )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
@@ -109,13 +99,13 @@ public class DownstreamBridge extends PacketHandler
public void handle(PacketCEScoreboardObjective objective) throws Exception public void handle(PacketCEScoreboardObjective objective) throws Exception
{ {
Scoreboard serverScoreboard = con.getServerSentScoreboard(); Scoreboard serverScoreboard = con.getServerSentScoreboard();
switch ( objective.action ) switch ( objective.getAction() )
{ {
case 0: case 0:
serverScoreboard.addObjective( new Objective( objective.name, objective.text ) ); serverScoreboard.addObjective( new Objective( objective.getName(), objective.getText() ) );
break; break;
case 1: case 1:
serverScoreboard.removeObjective( objective.name ); serverScoreboard.removeObjective( objective.getName() );
break; break;
} }
} }
@@ -124,15 +114,15 @@ public class DownstreamBridge extends PacketHandler
public void handle(PacketCFScoreboardScore score) throws Exception public void handle(PacketCFScoreboardScore score) throws Exception
{ {
Scoreboard serverScoreboard = con.getServerSentScoreboard(); Scoreboard serverScoreboard = con.getServerSentScoreboard();
switch ( score.action ) switch ( score.getAction() )
{ {
case 0: case 0:
Score s = new Score( score.itemName, score.scoreName, score.value ); Score s = new Score( score.getItemName(), score.getScoreName(), score.getValue() );
serverScoreboard.removeScore( score.itemName ); serverScoreboard.removeScore( score.getItemName() );
serverScoreboard.addScore( s ); serverScoreboard.addScore( s );
break; break;
case 1: case 1:
serverScoreboard.removeScore( score.itemName ); serverScoreboard.removeScore( score.getItemName() );
break; break;
} }
} }
@@ -141,8 +131,8 @@ public class DownstreamBridge extends PacketHandler
public void handle(PacketD0DisplayScoreboard displayScoreboard) throws Exception public void handle(PacketD0DisplayScoreboard displayScoreboard) throws Exception
{ {
Scoreboard serverScoreboard = con.getServerSentScoreboard(); Scoreboard serverScoreboard = con.getServerSentScoreboard();
serverScoreboard.setName( displayScoreboard.name ); serverScoreboard.setName( displayScoreboard.getName() );
serverScoreboard.setPosition( Position.values()[displayScoreboard.position] ); serverScoreboard.setPosition( Position.values()[displayScoreboard.getPosition()] );
} }
@Override @Override
@@ -150,37 +140,37 @@ public class DownstreamBridge extends PacketHandler
{ {
Scoreboard serverScoreboard = con.getServerSentScoreboard(); Scoreboard serverScoreboard = con.getServerSentScoreboard();
// Remove team and move on // Remove team and move on
if ( team.mode == 1 ) if ( team.getMode() == 1 )
{ {
serverScoreboard.removeTeam( team.name ); serverScoreboard.removeTeam( team.getName() );
return; return;
} }
// Create or get old team // Create or get old team
Team t; Team t;
if ( team.mode == 0 ) if ( team.getMode() == 0 )
{ {
t = new Team( team.name ); t = new Team( team.getName() );
serverScoreboard.addTeam( t ); serverScoreboard.addTeam( t );
} else } else
{ {
t = serverScoreboard.getTeam( team.name ); t = serverScoreboard.getTeam( team.getName() );
} }
if ( t != null ) if ( t != null )
{ {
if ( team.mode == 0 || team.mode == 2 ) if ( team.getMode() == 0 || team.getMode() == 2 )
{ {
t.setDisplayName( team.displayName ); t.setDisplayName( team.getDisplayName() );
t.setPrefix( team.prefix ); t.setPrefix( team.getPrefix() );
t.setSuffix( team.suffix ); t.setSuffix( team.getSuffix() );
t.setFriendlyMode( team.friendlyFire ); t.setFriendlyFire( team.isFriendlyFire() );
} }
if ( team.players != null ) if ( team.getPlayers() != null )
{ {
for ( String s : team.players ) for ( String s : team.getPlayers() )
{ {
if ( team.mode == 0 || team.mode == 3 ) if ( team.getMode() == 0 || team.getMode() == 3 )
{ {
t.addPlayer( s ); t.addPlayer( s );
} else } else
@@ -195,20 +185,20 @@ public class DownstreamBridge extends PacketHandler
@Override @Override
public void handle(PacketFAPluginMessage pluginMessage) throws Exception public void handle(PacketFAPluginMessage pluginMessage) throws Exception
{ {
ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.data ); ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.getData() );
PluginMessageEvent event = new PluginMessageEvent( con.getServer(), con, pluginMessage.tag, pluginMessage.data.clone() ); PluginMessageEvent event = new PluginMessageEvent( con.getServer(), con, pluginMessage.getTag(), pluginMessage.getData().clone() );
if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
if ( pluginMessage.tag.equals( "MC|TPack" ) && con.getPendingConnection().getListener().getTexturePack() != null ) if ( pluginMessage.getTag().equals( "MC|TPack" ) && con.getPendingConnection().getListener().getTexturePack() != null )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
if ( pluginMessage.tag.equals( "BungeeCord" ) ) if ( pluginMessage.getTag().equals( "BungeeCord" ) )
{ {
ByteArrayDataOutput out = ByteStreams.newDataOutput(); ByteArrayDataOutput out = ByteStreams.newDataOutput();
String subChannel = in.readUTF(); String subChannel = in.readUTF();
@@ -265,20 +255,29 @@ public class DownstreamBridge extends PacketHandler
} }
if ( subChannel.equals( "PlayerCount" ) ) if ( subChannel.equals( "PlayerCount" ) )
{ {
ServerInfo server = bungee.getServerInfo( in.readUTF() ); String target = in.readUTF();
out.writeUTF( "PlayerCount" );
if ( target.equals( "ALL" ) )
{
out.writeUTF( "ALL" );
out.writeInt( bungee.getOnlineCount() );
} else
{
ServerInfo server = bungee.getServerInfo( target );
if ( server != null ) if ( server != null )
{ {
out.writeUTF( "PlayerCount" );
out.writeUTF( server.getName() ); out.writeUTF( server.getName() );
out.writeInt( server.getPlayers().size() ); out.writeInt( server.getPlayers().size() );
} }
} }
}
if ( subChannel.equals( "PlayerList" ) ) if ( subChannel.equals( "PlayerList" ) )
{ {
String target = in.readUTF(); String target = in.readUTF();
out.writeUTF( "PlayerList" ); out.writeUTF( "PlayerList" );
if ( target.equals( "ALL" ) ) if ( target.equals( "ALL" ) )
{ {
out.writeUTF( "ALL" );
out.writeUTF( Util.csv( bungee.getPlayers() ) ); out.writeUTF( Util.csv( bungee.getPlayers() ) );
} else } else
{ {
@@ -329,13 +328,13 @@ public class DownstreamBridge extends PacketHandler
{ {
def = null; def = null;
} }
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.message, def ) ); ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.getMessage(), def ) );
if ( event.isCancelled() && event.getCancelServer() != null ) if ( event.isCancelled() && event.getCancelServer() != null )
{ {
con.connectNow( event.getCancelServer() ); con.connectNow( event.getCancelServer() );
} else } else
{ {
con.disconnect( "[Kicked] " + event.getKickReason() ); con.disconnect( bungee.getTranslation( "server_kick" ) + event.getKickReason() );
} }
server.setObsolete( true ); server.setObsolete( true );
throw new CancelSendSignal(); throw new CancelSendSignal();

View File

@@ -10,12 +10,14 @@ import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.BungeeCord; import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.EncryptionUtil; import net.md_5.bungee.EncryptionUtil;
import net.md_5.bungee.PacketConstants;
import net.md_5.bungee.UserConnection; import net.md_5.bungee.UserConnection;
import net.md_5.bungee.Util; import net.md_5.bungee.Util;
import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.Callback;
@@ -29,17 +31,24 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ProxyPingEvent; import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.netty.CipherCodec;
import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.packet.Packet2Handshake; import net.md_5.bungee.netty.CipherDecoder;
import net.md_5.bungee.packet.PacketCDClientStatus; import net.md_5.bungee.netty.CipherEncoder;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.netty.PacketDecoder;
import net.md_5.bungee.packet.PacketFCEncryptionResponse; import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.packet.PacketFDEncryptionRequest; import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.packet.PacketFEPing; import net.md_5.bungee.protocol.Forge;
import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.protocol.Vanilla;
import net.md_5.bungee.packet.PacketHandler; import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.Packet1Login;
import net.md_5.bungee.protocol.packet.Packet2Handshake;
import net.md_5.bungee.protocol.packet.PacketCDClientStatus;
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;
@RequiredArgsConstructor @RequiredArgsConstructor
public class InitialHandler extends PacketHandler implements PendingConnection public class InitialHandler extends PacketHandler implements PendingConnection
@@ -50,12 +59,24 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Getter @Getter
private final ListenerInfo listener; private final ListenerInfo listener;
@Getter @Getter
private Packet1Login forgeLogin;
@Getter
private Packet2Handshake handshake; private Packet2Handshake handshake;
private PacketFDEncryptionRequest request; private PacketFDEncryptionRequest request;
@Getter
private List<PacketFAPluginMessage> loginMessages = new ArrayList<>(); private List<PacketFAPluginMessage> loginMessages = new ArrayList<>();
@Getter
private List<PacketFAPluginMessage> registerMessages = new ArrayList<>();
private State thisState = State.HANDSHAKE; private State thisState = State.HANDSHAKE;
private SecretKey sharedKey; private SecretKey sharedKey;
private boolean disconnected; private final Unsafe unsafe = new Unsafe()
{
@Override
public void sendPacket(DefinedPacket packet)
{
ch.write( packet );
}
};
private enum State private enum State
{ {
@@ -77,15 +98,22 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override @Override
public void handle(PacketFAPluginMessage pluginMessage) throws Exception public void handle(PacketFAPluginMessage pluginMessage) throws Exception
{
// TODO: Unregister?
if ( pluginMessage.getTag().equals( "REGISTER" ) )
{
registerMessages.add( pluginMessage );
} else
{ {
loginMessages.add( pluginMessage ); loginMessages.add( pluginMessage );
} }
}
@Override @Override
public void handle(PacketFEPing ping) throws Exception public void handle(PacketFEPing ping) throws Exception
{ {
ServerPing response = new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), ServerPing response = new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(),
listener.getMotd(), bungee.getPlayers().size(), listener.getMaxPlayers() ); listener.getMotd(), bungee.getOnlineCount(), listener.getMaxPlayers() );
response = bungee.getPluginManager().callEvent( new ProxyPingEvent( this, response ) ).getResponse(); response = bungee.getPluginManager().callEvent( new ProxyPingEvent( this, response ) ).getResponse();
@@ -98,21 +126,54 @@ public class InitialHandler extends PacketHandler implements PendingConnection
disconnect( kickMessage ); disconnect( kickMessage );
} }
@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;
ch.getHandle().pipeline().get( PacketDecoder.class ).setProtocol( Forge.getInstance() );
}
@Override @Override
public void handle(Packet2Handshake handshake) throws Exception public void handle(Packet2Handshake handshake) throws Exception
{ {
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
if ( handshake.username.length() > 16 ) this.handshake = handshake;
bungee.getLogger().log( Level.INFO, "{0} has connected", this );
if ( handshake.getProcolVersion() > Vanilla.PROTOCOL_VERSION )
{
disconnect( "Outdated server!" );
} else if ( handshake.getProcolVersion() < Vanilla.PROTOCOL_VERSION )
{
disconnect( "Outdated client!" );
}
if ( handshake.getUsername().length() > 16 )
{ {
disconnect( "Cannot have username longer than 16 characters" ); disconnect( "Cannot have username longer than 16 characters" );
return; return;
} }
int limit = BungeeCord.getInstance().config.getPlayerLimit(); int limit = BungeeCord.getInstance().config.getPlayerLimit();
Preconditions.checkState( limit <= 0 || bungee.getPlayers().size() < limit, "Server is full!" ); if ( limit > 0 && bungee.getOnlineCount() > limit )
{
disconnect( bungee.getTranslation( "proxy_full" ) );
return;
}
this.handshake = handshake; // If offline mode and they are already on, don't allow connect
ch.write( request = EncryptionUtil.encryptRequest() ); if ( !BungeeCord.getInstance().config.isOnlineMode() && bungee.getPlayer( handshake.getUsername() ) != null )
{
disconnect( bungee.getTranslation( "already_connected" ) );
return;
}
unsafe().sendPacket( PacketConstants.I_AM_BUNGEE );
unsafe().sendPacket( PacketConstants.FORGE_MOD_REQUEST );
unsafe().sendPacket( request = EncryptionUtil.encryptRequest() );
thisState = State.ENCRYPT; thisState = State.ENCRYPT;
} }
@@ -122,6 +183,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection
Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" ); Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" );
sharedKey = EncryptionUtil.getSecret( encryptResponse, request ); sharedKey = EncryptionUtil.getSecret( encryptResponse, request );
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, sharedKey );
ch.getHandle().pipeline().addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
if ( BungeeCord.getInstance().config.isOnlineMode() ) if ( BungeeCord.getInstance().config.isOnlineMode() )
{ {
String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" ); String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" );
@@ -129,7 +193,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
MessageDigest sha = MessageDigest.getInstance( "SHA-1" ); MessageDigest sha = MessageDigest.getInstance( "SHA-1" );
for ( byte[] bit : new byte[][] for ( byte[] bit : new byte[][]
{ {
request.serverId.getBytes( "ISO_8859_1" ), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded() request.getServerId().getBytes( "ISO_8859_1" ), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()
} ) } )
{ {
sha.update( bit ); sha.update( bit );
@@ -155,7 +219,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override @Override
public void onThrowable(Throwable t) public void onThrowable(Throwable t)
{ {
disconnect( "Error occured while contacting login servers, are they down?" + Util.exception( t ) ); disconnect( bungee.getTranslation( "mojang_fail" ) );
bungee.getLogger().log( Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", t );
} }
} ); } );
} else } else
@@ -167,10 +232,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection
private void finish() throws GeneralSecurityException private void finish() throws GeneralSecurityException
{ {
// Check for multiple connections // Check for multiple connections
ProxiedPlayer old = bungee.getPlayer( handshake.username ); ProxiedPlayer old = bungee.getPlayer( handshake.getUsername() );
if ( old != null ) if ( old != null )
{ {
old.disconnect( "You are already connected to the server" ); old.disconnect( bungee.getTranslation( "already_connected" ) );
} }
Callback<LoginEvent> complete = new Callback<LoginEvent>() Callback<LoginEvent> complete = new Callback<LoginEvent>()
@@ -182,23 +247,29 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{ {
disconnect( result.getCancelReason() ); disconnect( result.getCancelReason() );
} }
if ( disconnected ) if ( ch.isClosed() )
{ {
return; return;
} }
thisState = InitialHandler.State.LOGIN;
ch.getHandle().eventLoop().execute( new Runnable()
{
@Override
public void run()
{
unsafe().sendPacket( new PacketFCEncryptionResponse( new byte[ 0 ], new byte[ 0 ] ) );
try try
{ {
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey ); Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey );
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, sharedKey ); ch.getHandle().pipeline().addBefore( PipelineUtils.DECRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
ch.write( new PacketFCEncryptionResponse() );
ch.getHandle().pipeline().addBefore( "decoder", "cipher", new CipherCodec( encrypt, decrypt ) );
thisState = InitialHandler.State.LOGIN;
} catch ( GeneralSecurityException ex ) } catch ( GeneralSecurityException ex )
{ {
disconnect( "Cipher error: " + Util.exception( ex ) ); disconnect( "Cipher error: " + Util.exception( ex ) );
} }
} }
} );
}
}; };
// fire login event // fire login event
@@ -210,7 +281,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{ {
Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" ); Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" );
UserConnection userCon = new UserConnection( (BungeeCord) bungee, ch,getName(), this ); UserConnection userCon = new UserConnection( (BungeeCord) bungee, ch, getName(), this );
userCon.init(); userCon.init();
bungee.getPluginManager().callEvent( new PostLoginEvent( userCon ) ); bungee.getPluginManager().callEvent( new PostLoginEvent( userCon ) );
@@ -227,30 +298,29 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override @Override
public synchronized void disconnect(String reason) public synchronized void disconnect(String reason)
{ {
if ( ch.getHandle().isActive() ) if ( !ch.isClosed() )
{ {
ch.write( new PacketFFKick( reason ) ); unsafe().sendPacket( new PacketFFKick( reason ) );
ch.getHandle().close(); ch.close();
disconnected = true;
} }
} }
@Override @Override
public String getName() public String getName()
{ {
return ( handshake == null ) ? null : handshake.username; return ( handshake == null ) ? null : handshake.getUsername();
} }
@Override @Override
public byte getVersion() public byte getVersion()
{ {
return ( handshake == null ) ? -1 : handshake.procolVersion; return ( handshake == null ) ? -1 : handshake.getProcolVersion();
} }
@Override @Override
public InetSocketAddress getVirtualHost() public InetSocketAddress getVirtualHost()
{ {
return ( handshake == null ) ? null : new InetSocketAddress( handshake.host, handshake.port ); return ( handshake == null ) ? null : new InetSocketAddress( handshake.getHost(), handshake.getPort() );
} }
@Override @Override
@@ -259,6 +329,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection
return (InetSocketAddress) ch.getHandle().remoteAddress(); return (InetSocketAddress) ch.getHandle().remoteAddress();
} }
@Override
public Unsafe unsafe()
{
return unsafe;
}
@Override @Override
public String toString() public String toString()
{ {

View File

@@ -5,8 +5,8 @@ import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.packet.PacketHandler; import net.md_5.bungee.protocol.packet.PacketFFKick;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PingHandler extends PacketHandler public class PingHandler extends PacketHandler
@@ -34,7 +34,7 @@ public class PingHandler extends PacketHandler
@Override @Override
public void handle(PacketFFKick kick) throws Exception public void handle(PacketFFKick kick) throws Exception
{ {
String[] split = kick.message.split( "\00" ); String[] split = kick.getMessage().split( "\00" );
ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) ); ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) );
callback.done( ping, null ); callback.done( ping, null );
} }

View File

@@ -1,7 +1,5 @@
package net.md_5.bungee.connection; package net.md_5.bungee.connection;
import io.netty.channel.Channel;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.BungeeCord; import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.EntityMap; import net.md_5.bungee.EntityMap;
import net.md_5.bungee.UserConnection; import net.md_5.bungee.UserConnection;
@@ -12,11 +10,11 @@ import net.md_5.bungee.api.event.ChatEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.packet.Packet0KeepAlive; import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.packet.Packet3Chat; import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
import net.md_5.bungee.packet.PacketCCSettings; import net.md_5.bungee.protocol.packet.Packet3Chat;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.protocol.packet.PacketCCSettings;
import net.md_5.bungee.packet.PacketHandler; import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
public class UpstreamBridge extends PacketHandler public class UpstreamBridge extends PacketHandler
{ {
@@ -29,14 +27,14 @@ public class UpstreamBridge extends PacketHandler
this.bungee = bungee; this.bungee = bungee;
this.con = con; this.con = con;
BungeeCord.getInstance().connections.put( con.getName(), con ); BungeeCord.getInstance().addConnection( con );
bungee.getTabListHandler().onConnect( con ); con.getTabList().onConnect();
con.sendPacket( BungeeCord.getInstance().registerChannels() ); con.unsafe().sendPacket( BungeeCord.getInstance().registerChannels() );
TexturePackInfo texture = con.getPendingConnection().getListener().getTexturePack(); TexturePackInfo texture = con.getPendingConnection().getListener().getTexturePack();
if ( texture != null ) if ( texture != null )
{ {
con.sendPacket( new PacketFAPluginMessage( "MC|TPack", ( texture.getUrl() + "\00" + texture.getSize() ).getBytes() ) ); con.setTexturePack( texture );
} }
} }
@@ -52,8 +50,8 @@ public class UpstreamBridge extends PacketHandler
// We lost connection to the client // We lost connection to the client
PlayerDisconnectEvent event = new PlayerDisconnectEvent( con ); PlayerDisconnectEvent event = new PlayerDisconnectEvent( con );
bungee.getPluginManager().callEvent( event ); bungee.getPluginManager().callEvent( event );
bungee.getTabListHandler().onDisconnect( con ); con.getTabList().onDisconnect();
BungeeCord.getInstance().connections.remove( con.getName() ); //TODO: Better way, why do we need to raw access? BungeeCord.getInstance().removeConnection( con );
if ( con.getServer() != null ) if ( con.getServer() != null )
{ {
@@ -74,10 +72,10 @@ public class UpstreamBridge extends PacketHandler
@Override @Override
public void handle(Packet0KeepAlive alive) throws Exception public void handle(Packet0KeepAlive alive) throws Exception
{ {
if ( alive.id == con.getSentPingId() ) if ( alive.getRandomId() == con.getSentPingId() )
{ {
int newPing = (int) ( System.currentTimeMillis() - con.getSentPingTime() ); int newPing = (int) ( System.currentTimeMillis() - con.getSentPingTime() );
bungee.getTabListHandler().onPingChange( con, newPing ); con.getTabList().onPingChange( newPing );
con.setPing( newPing ); con.setPing( newPing );
} }
} }
@@ -85,14 +83,14 @@ public class UpstreamBridge extends PacketHandler
@Override @Override
public void handle(Packet3Chat chat) throws Exception public void handle(Packet3Chat chat) throws Exception
{ {
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.message ); ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.getMessage() );
if ( bungee.getPluginManager().callEvent( chatEvent ).isCancelled() ) if ( bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
if ( chatEvent.isCommand() ) if ( chatEvent.isCommand() )
{ {
if ( bungee.getPluginManager().dispatchCommand( con, chat.message.substring( 1 ) ) ) if ( bungee.getPluginManager().dispatchCommand( con, chat.getMessage().substring( 1 ) ) )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
@@ -108,16 +106,27 @@ public class UpstreamBridge extends PacketHandler
@Override @Override
public void handle(PacketFAPluginMessage pluginMessage) throws Exception public void handle(PacketFAPluginMessage pluginMessage) throws Exception
{ {
if ( pluginMessage.tag.equals( "BungeeCord" ) ) if ( pluginMessage.getTag().equals( "BungeeCord" ) )
{
throw new CancelSendSignal();
}
// Hack around Forge race conditions
if ( pluginMessage.getTag().equals( "FML" ) && ( pluginMessage.getData()[0] & 0xFF ) == 1 )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
PluginMessageEvent event = new PluginMessageEvent( con, con.getServer(), pluginMessage.tag, pluginMessage.data.clone() ); PluginMessageEvent event = new PluginMessageEvent( con, con.getServer(), pluginMessage.getTag(), pluginMessage.getData().clone() );
if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
{ {
throw new CancelSendSignal(); throw new CancelSendSignal();
} }
// TODO: Unregister as well?
if ( pluginMessage.getTag().equals( "REGISTER" ) )
{
con.getPendingConnection().getRegisterMessages().add( pluginMessage );
}
} }
@Override @Override

View File

@@ -0,0 +1,48 @@
package net.md_5.bungee.log;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import net.md_5.bungee.BungeeCord;
public class BungeeLogger extends Logger
{
private final BungeeCord bungee;
private final ColouredWriter writer;
private final Formatter formatter = new ConciseFormatter();
private final LogDispatcher dispatcher = new LogDispatcher( this );
public BungeeLogger(BungeeCord bungee)
{
super( "BungeeCord", null );
this.bungee = bungee;
this.writer = new ColouredWriter( bungee.getConsoleReader() );
try
{
FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true );
handler.setFormatter( formatter );
addHandler( handler );
} catch ( IOException ex )
{
System.err.println( "Could not register logger!" );
ex.printStackTrace();
}
dispatcher.start();
}
@Override
public void log(LogRecord record)
{
dispatcher.queue( record );
}
void doLog(LogRecord record)
{
super.log( record );
writer.print( formatter.format( record ) );
}
}

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