547 Commits

Author SHA1 Message Date
BlackHole
7d2c2ab074 Add ComponentBuilder.insertion() 2016-02-22 08:44:57 +11:00
md_5
0646a3090a Fix scoreboards / tab lists getting out of sync due to race conditions / overlap. 2016-02-19 09:07:12 +11:00
md_5
afc02082e6 Make it clear when users connected Bungee to itself. 2016-02-17 10:34:04 +11:00
Tux
848cad2a59 Fix cancelling TabCompleteEvent.
The packet would get sent regardless if the event was cancelled, only if there were no suggestions available.
2016-02-07 17:44:07 +11:00
md_5
9c4380a201 System.err already has priority prefix. 2016-02-05 12:53:07 +11:00
md_5
8490d611bf [#1717] Perform a copy if Netty isn't using a direct address for any reason. 2016-02-05 12:50:42 +11:00
Thinkofdeath
a0f2c42d38 Clear the address cache entry when a connection fails
Should help solve the issues caused by mojang swapping the ip address of
the session server.
2016-02-05 10:19:31 +11:00
Jedediah Smith
40c0618a3a Fix NPE from duplicating TranslatableComponent 2016-02-04 23:12:19 +00:00
BlackHole
fa3678bcdd Add new 1.8 chat features 2016-02-04 23:11:35 +00:00
md_5
e556fd7150 Add Connection.isConnected()Z API.
Idea from kamcio96 in #1693
2016-02-05 10:09:47 +11:00
md_5
841c81cdc4 [#1714] Make a copy of the main Minecraft buffer as EntityMap.rewriteVarInt may require more bytes than available. 2016-01-24 19:45:02 +11:00
md_5
052131c1fa Reduce amount of memcpy within proxy pipeline. 2016-01-24 11:22:39 +11:00
md_5
79dbdea107 Use more realistic cipher test sizes and counts. 2016-01-16 14:03:47 +11:00
md_5
7fb1f4b81f Replace gitdescribe with Scriptus. 2016-01-16 13:52:53 +11:00
md_5
255d7fde9a Ensure native zlib actually loads. 2016-01-16 13:33:09 +11:00
md_5
7907610eeb Compatability and benchmark fixes for native code. 2016-01-16 13:29:50 +11:00
kamcio96
83e27f07e6 Use static instances of EntityMap 2016-01-16 12:44:32 +11:00
md_5
5cff0b2171 Remove redundant synchronization on ServerConnection.disconnect 2016-01-16 12:33:42 +11:00
md_5
2c86592ecd [#1649] Don't parse disconnect reason for servers.
Reported by @kamcio96
2016-01-16 12:29:13 +11:00
md_5
f5552963b8 Update Netty and enable EPoll transport. 2016-01-16 11:37:17 +11:00
md_5
1182affa09 Rewrite host parsing to account for IPv6. 2016-01-14 09:52:16 +11:00
Tux
a1895c556f Make BoundedArrayList follow the old behavior.
Unit tests have been included.
2016-01-11 14:09:07 +11:00
md_5
aa214c0b54 [#1695] Use BoundedArrayList to cap list size for REGISTER channels. 2016-01-10 19:00:00 +11:00
Luuk Jacobs
81bd3b5f71 Fix typo in ClickEvent.java 2016-01-05 09:14:34 +11:00
kamcio96
b6e26e0c09 Load NativeCode manually, not in constructor.
Bungee will call load method from main thread
https://github.com/SpigotMC/BungeeCord/blob/master/proxy/src/main/java/net/md_5/bungee/BungeeCord.java#L202
2015-11-19 08:42:01 +11:00
Joseph Hirschfeld
19f2e7b13e DNS IPs should only be cached for 1 minute. 2015-11-12 12:58:18 +11:00
xxyy
ba448b5670 Add configurable proxy command logging.
This commit adds a config switch that allows users to turn off
 the logging of proxy commands. It is set to off by default to
 prevent unwanted log spam and keep current behaviour.
Log proxy commands

This commit changes the PluginManager to print a message to
console and the log when a proxy command is executed.
This may assist with debugging and miscellaneous
investigations.
2015-10-26 20:59:53 +11:00
md_5
013320fd9e #1852: Fix packet compression when BungeeCord and the server are set to different compression levels. 2015-09-16 19:44:55 +10:00
md_5
8a1030e21c #1583: Two additions to console behaviour:
1) Stop trying to read anything if the console is hooked up to /dev/null
2) Don't even bother in the first place if --noconsole is used as an argument.
2015-09-16 15:09:40 +10:00
Thinkofdeath
c626254825 Only enable compression for 1.8 clients 2015-09-13 08:59:42 +01:00
md_5
4e94c278da [#1567] Let BungeeCord dictate the network compression threshold. 2015-09-13 09:29:39 +10:00
jfr
06ad0f9310 Reorder PlayerJoinEvent / UpstreamBridge initialization.
Placing the PlayerJoinEvent in front of the Upstream Bridge handling prevents us from actually accessing the target server in PostJoinEvent handlers.
2015-09-07 18:26:41 +10:00
md_5
c4a3a052d7 Update to Netty 4.0.31.Final 2015-09-06 12:11:41 +10:00
md_5
7ec1a1aa4e Fix / clarify behaviour of matchPlayer. 2015-09-05 13:42:33 +10:00
Tux
59208aad86 Improve EventBus by removing read locking.
This is primarily done by using ConcurrentHashMap, which has lock-free reads and thread-contention-based writes. Only one thread at a time can register threads, however, as baking events isn't thread safe (and there's no reason to make it thread-safe anyway).

My own benchmarks indicate 1.4-2.2 million operations/ms throughput and approximately ~1ns/event post for four threads posting events.
2015-09-05 13:33:18 +10:00
zml
bd07be8772 chat: Correct placeholder handling in translatable
This change makes TranslatableComponent still scan a
translation string for placeholders, even if the translation string
itself is being used (rather than a value found from the
ResourceBundle). This matches vanilla behavior and allows plugins that
use this system with serverside translations and other custom
translation systems to send output to the server console correctly.
2015-08-30 14:50:37 -07:00
pvmac2194
b3d15d53d6 Add <server> in the usage of /send
Just a small cosmetic fix that shows server owners that this argument exists.
2015-08-14 18:31:28 +10:00
Thinkofdeath
6343416c0c Update the PluginMessage packet to respect the protocol limit changes in 1.8 2015-07-16 23:07:30 +01:00
kamcio96
81d1c46a0d Support uuid in config.yml 2015-07-13 19:46:15 +10:00
md_5
6e5132f914 #1537: Allow /send <server> <server>.
If the first argument is a server, send all players from that server. Servers take priority in the case a player and server share a name.
2015-07-13 19:32:12 +10:00
Daniel Naylor
f3c14cf064 Fix telling newer 1.7 Forge clients that they are outdated. Fixes #1476
* Only check the version of FML if we are running Minecraft 1.7.10 - 1.8 clients are always valid.
* If the version of FML that is reported is 7.10.99.99 - report the Forge version instead.
2015-07-10 12:33:22 +10:00
Marc Baloup
a3a31fd2dd Correct handling of command's parameters for tab completion
Hi :)

There is some problem with TabExecutor and I think I found the problem.

Exemple :
/send <playerName|all|current> <targetServer>
When I write "/send " (with trailing space) and press Tab, I don't have any suggestion. But when I wrote the first character of an online player, it suggest all players which start with this same character (expected behavior)

The problem is the same when I wrote "/send marcbal " (with trailing space). When I press Tab key, I have "marcbal" suggested instead of servers suggestion. (the result is "/send marcbal marcbal"). But when i wrote the first character of a server name, it suggest all servers which start with this same character (expected behavior)

other exemple : there is 2 players online in the same server : me (marcbal) and marcbaleeee (for exemple)
when I wrote "/send marcbal " (with trailing space) and I press Tab key, I have the suggestion of the 2 player names.

I deduced that the onTabComplete() don't care about the trailing space of the command string (exemple : "/send marcbal ") and probably a problem when splitting (removing last trailing empty args), so, the onTabComplete method try to complete the first argument "marcbal" instead of the second empty argument.

To split the command line, you use the method Pattern.split(CharSequence) that remove trailing empty strings http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#split(java.lang.CharSequence)
So, we should use the other split() method, with a second argument which has to be a negative integer value. (see Javadoc)

PS : Sorry for my bad english x)
2015-07-10 12:28:26 +10:00
md_5
cc3a8c067e Check channel active before adding handlers. #1489 2015-06-06 18:41:54 +10:00
Joe
918d7229c2 Fix Deadlock in EventBus post #1493 2015-06-05 13:03:43 +10:00
sha-2
6c4e684de9 Move the compiler configuration to properties 2015-05-31 10:51:49 +10:00
minecraftshamrock
9cf57ca929 Fix AsyncEvent callback triggering.
Before this, two concurrent postCall() and completeIntent() calls might
cause the callback to be called more than once.
2015-05-18 20:22:32 +10:00
Adam
70564d9f44 Fix BaseComponent.duplicate() ignoring extra 2015-05-18 20:18:57 +10:00
Nathan Poirier
8622cf3af4 Fix BadPacketException handling It is catched by DecoderException and was not processed correctly by HandlerBoss So the console was spammed when that exception is thrown 2015-05-18 20:18:19 +10:00
Nick
5236dd301a Use an actual ArrayList when creating a TextComponent with extras.
Arrays#asList returns a java.util.Arrays.ArrayList which does not support adding more elements.  This previously broke BaseComponent#addExtra.
2015-05-18 20:16:45 +10:00
md_5
ddfd76ebda Make MinecraftDecoder a MessageToMessageDecoder 2015-05-18 20:16:12 +10:00
Thinkofdeath
93959cab4b Only force remove the player from 1.8 client's tab list 2015-05-10 22:51:14 +01:00
Thinkofdeath
585ab4f453 Readd the delay removed in f9773a69c3 2015-05-08 20:41:21 +01:00
Thinkofdeath
76052b92d3 Don't send a Kick during the STATUS protocol as it doesn't support it 2015-05-08 20:32:12 +01:00
Thinkofdeath
f9773a69c3 Don't send a kick to the server on disconnect
This was dropped from the protocol in 1.6 and was left in bungee by
mistake
2015-05-08 19:39:22 +01:00
Tux
88e71ead05 Add reset() and retain() methods to ComponentBuilder.
This method is simple: it resets the current part to default settings, keeping only the current text. It therefore acts like the old ChatColor.RESET
code. The retain method allows for more control over what is reset.

Add a test to verify proper functioning of reset()/retain().
2015-05-06 09:57:48 +01:00
md_5
d1e1ce4cdb Add option to disable native code. 2015-05-03 10:35:48 +10:00
md_5
fa828eba31 Allow using properties to change leak detection level. 2015-04-13 09:51:31 +10:00
Thinkofdeath
d76c8d4f33 Ensure the copy is freed if an exception occurs during parsing packets 2015-04-12 01:47:10 +01:00
Thinkofdeath
a48c458306 Don't send bungeecord plugin messages to the client 2015-04-08 18:22:16 +01:00
portalBlock
3973c511f5 Show the no permission message even if the command is a TabExecutor but the command was not dispatched by the tab completer. 2015-03-22 13:54:05 +11:00
Jonas Konrad
78ea41015f Fix component deserialization on obfuscated and strikethrough tags 2015-03-16 20:24:37 +11:00
Thinkofdeath
bd2eaf6879 Add missing scoreboard positions (Fixes #1397) 2015-03-12 16:42:01 +00:00
Thinkofdeath
f2d17cb216 Follow vanilla and kick the joining user on connect if the name is already taken (Fixes #1383) 2015-02-28 13:07:13 +00:00
md_5
f2673c5876 Kick only in online mode: #1382 2015-02-28 13:00:04 +11:00
Thinkofdeath
e1951c5d66 Fix spectator mode with ip-forwarding enabled 2015-02-25 22:31:05 +00:00
Thinkofdeath
988490ba87 1.8 isn't a snapshot anymore 2015-02-25 22:27:50 +00:00
md_5
62981e4c70 Downgrade netty to work around #1372 2015-02-15 10:27:30 +11:00
md_5
5699e86270 Looks like Jenkins may be having lombok issues with the new version. 2015-02-14 19:09:19 +11:00
md_5
61cee2d27c Fix formatting in a bunch of files. 2015-02-14 18:36:58 +11:00
md_5
2055c98ebe Add reason for stopping and extra arguments. 2015-02-14 18:33:49 +11:00
Joshua Rodriguez
415d5860e4 Allow setting a connections uuid when in offline mode 2015-02-14 18:33:28 +11:00
md_5
3776feb559 Don't allow duplicate UUIDs on the proxy. 2015-02-14 18:26:27 +11:00
Jonas Konrad
668cdabdf7 Fix BaseComponent.toString stack overflow 2015-02-14 18:21:43 +11:00
md_5
3e26eecd4e Fix formatting issue pointed out in #1370 2015-02-14 18:21:24 +11:00
md_5
7c1f232e85 Update depend versions, limit use of most Java 1.7 APIs. 2015-02-08 13:07:41 +11:00
md_5
2b49358bea Fix EventBus unit test. 2015-02-08 12:48:26 +11:00
md_5
b4997f6379 Add better exception handling for native code. 2015-02-08 09:11:52 +11:00
md_5
eeaa44e1e7 #1362: Tighter catch on errors in native lib compilation. 2015-02-08 08:08:20 +11:00
md_5
f4ae511af0 2 megabytes is sufficient for testing purposes. 2015-02-07 14:19:33 +11:00
md_5
32693aeaff Don't compile with the stdlib, I don't think it is needed. 2015-02-07 14:14:33 +11:00
md_5
0d569ac0d1 Refactor native code and implement our own JNI wrapper around zlib.
The previous native cipher code has been refactored so that it may be loaded and used slightly more generically, allowing more native components to be easily added as time goes on.
I have also written a new native code compression module, which wraps around zlib in the same manner that Inflater / Deflater does, however it operates directly on the memory addresses of it's input / output buffers which means that we can save one, or maybe even two copies. To support this, the VarInt decoder has been adjusted to always use a native buffer.
2015-02-07 14:06:41 +11:00
Thinkofdeath
e6da9cbba8 Manually reset the networkaddress.cache.ttl value to the defaults (Fixes #1268)
When searching for the networkaddress.cache.* values java will default to these
values when it can't find a manually set value, however if a security manager is
in place then as a special case java will set the cache lifetime to infinite.
Manually setting the values solves this issue.
2015-02-04 08:50:52 +00:00
md_5
61d2765715 Only check name for server reload modification. 2015-01-24 13:34:38 +11:00
portalBlock
e68ed48fc3 Use the cached constructor when instantiating a packet. 2015-01-22 18:25:45 +11:00
md_5
cf722de1d2 #1130: Add scheduler unit tests and make more robust. 2015-01-18 12:09:38 +11:00
Daniel Naylor
28496e0471 Add API for getting whether the user is a Forge user.
For Minecraft+Forge 1.8 we can detect whether the user is a Forge user before we get the mod list, due to the changes to the initial (not FML|HS) handshake that are now made (which is for vanilla client support). Bungee can exploit this to detect FML clients from the off, but it still does not tell us what the mod list is. Thus, creating this API method for users who simply need to know whether the user is connected via FML is no longer a duplication of the getModList api method.
2015-01-12 19:23:07 +11:00
Daniel Naylor
4809f1f80a Update IP forwarding to ignore FML and other null character delimited tokens (for now)
For Forge 1.8, a new \0FML\0 token is included in the handshake packet host field. Whilst from a Forge <-> Bungee standpoint, this is good in the long run (we can detect Forge/FML clients right from the off, allowing us to expose a reliable API for detecting modded 1.8+ clients), it plays havoc with IP forwarding on Spigot servers, as they expect a very specific format.

Until we can look at improving this situation (probably by creating an updated IP forwarding protocol on the server side), this removes the FML marker from the handshake whenever IP forwarding is on. If you have a FML 1.8 network, IP forwarding MUST be off.

With thanks to @geNAZt for finding the issue.
2015-01-12 19:23:07 +11:00
Tux
ae12554316 Faster Travis CI builds
See http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
2015-01-12 11:55:09 +11:00
xxyy
5091515f0b Add TabCompleteResponseEvent 2015-01-12 11:53:28 +11:00
md_5
52b75cd18b Don't nuke command on tab complete if no args specified. 2015-01-12 11:07:43 +11:00
md_5
a5f2b423d4 #1325 - Fix /server with extra args. 2015-01-12 11:07:32 +11:00
mrapple
54c9ade1a6 Move mojang translations to chat module, fixes SpigotMC/BungeeCord#1301 2014-12-23 19:23:24 +11:00
md_5
bc6d7719a7 Finish single argument for command server tab completion. 2014-12-22 20:14:20 +11:00
md_5
2ab0715226 Fix server command to properly tab complete, be case insensitive. 2014-12-22 20:02:19 +11:00
md_5
1711223b02 Use string lists in preference to string arrays. 2014-12-22 19:59:14 +11:00
md_5
cd15b82361 Compile chat API with 1.6 2014-12-16 16:09:25 +11:00
zreed
22084b2c75 Try loading native cipher from java.library.path first 2014-12-16 13:21:25 +11:00
Tux
972b4c1fe5 Further fix task clean up issues by moving the BungeeScheduler's cancel(ScheduledTask) method to just call the cancel() method on the task. The cancel call was moved to BungeeTask's cancel method.
This patch makes the patch transparent to existing callers using cancel(ScheduledTask) from the scheduler. It also simplifies some logic in BungeeTask itself.
2014-12-14 17:18:49 +11:00
Tux
36c4af35de Fix issues cleaning up after repeating tasks.
A mass of NullPointerExceptions would be outputted when tasks were stopped. This is resolved by checking if we are still running (indicating a possible scheduler cancel) before telling the scheduler to pull the plug.

Ideally, the entire BungeeCord scheduler should be rewritten. It has quite a few issues which could be avoided with a new system.
2014-12-14 17:18:49 +11:00
md_5
02d3660f32 Fix fancy terminal on Windows.
This is a workaround for quite possibly the weirdest bug I have ever encountered in my life! When jansi attempts to extract its natives, by default it tries to extract a specific version, using the loading class's implementation version. Normally this works completely fine, however when on Windows certain characters such as - and : can trigger special behaviour. Furthermore this behaviour only occurs in specific combinations due to the parsing done by jansi. For example test-test works fine, but test-test-test does not! In order to avoid this all together but still keep our versions the same as they were, we set the override property to the essentially garbage version BungeeCord. This version is only used when extracting the libraries to their temp folder.
2014-12-14 13:07:13 +11:00
md_5
37e37e9a55 Fix timestamp format.... 2014-12-13 11:53:03 +11:00
md_5
a03c21cc09 Tweak outdated timers a little. 2014-12-13 11:14:16 +11:00
md_5
45bf7a9ab9 Move ansi console system install right into the bootstrap. 2014-12-13 11:13:40 +11:00
Thinkofdeath
ef364d9053 Actually put the tasks in the tasksByPlugin Multimap (Fixes #1278) 2014-11-26 16:42:45 +00:00
Minecrell
f19cc7fe4f Add chat position API. 2014-11-22 09:17:37 +11:00
Minecrell
772c8d7f2b Improve legacy client ping support. 2014-11-15 10:18:40 +00:00
Thinkofdeath
830f18a357 Manually remove the player from everyone's tab list on disconnect
This is needed because when the player disconnects from bungee they are removed from the connection map, this causes the tab-list rewrite to fail due the player no longer being on bungee and therefor it ends up not removing the player (online vs offline uuid). This would only happen on servers without ip-forwarding enabled
2014-11-11 10:33:22 +00:00
elmo92
c21275b877 Don't send no permission message on tab completion.
Instead returns the empty completions.
2014-11-08 10:08:42 +11:00
md_5
532a94382b Block really wrong offline mode names, see #1270 2014-11-08 10:04:59 +11:00
md_5
56c372a3ce Fix HTTP client now that Mojang has their stuff together. 2014-11-08 10:02:48 +11:00
Minecrell
faf903469e Add methods to load configurations with defaults.
Add Configuration constructor for empty configurations.

Use defaults for the Configuration object getter.
2014-10-17 20:37:59 +11:00
Daniel Naylor
4d389df7c8 Be more selective when sending reset packets.
Always send the packet when going from modded -> anything, send the packet later when going from vanilla -> modded, never send it on a vanilla -> vanilla switch.
2014-10-17 20:37:10 +11:00
Daniel Naylor
cfad2c65d4 Implement Support for MinecraftForge / FML 1.7.10
Additional implementation help provided by @jk-5 and @bloodmc.
2014-09-27 19:38:28 +10:00
md_5
8715c5fd82 Actually update translations to 1.8 2014-09-27 08:25:36 +10:00
md_5
cbb190cfd3 Fix a few import ordering issues. 2014-09-26 10:11:16 +10:00
md_5
acf5f2f443 Fix bootstrap Java 7 warner so that it actually works! 2014-09-25 11:13:23 +10:00
md_5
57a07dc2e2 Update Depend Versions:
Guava 17.0 -> 18.0
Lombok 1.12.6 -> 1.14.8
Gson 2.2.4 -> 2.3
SnakeYaml 1.13 -> 1.14
2014-09-25 11:05:21 +10:00
Minecrell
6fcfb5aecb Add more message arguments. See #1214. 2014-09-25 10:53:41 +10:00
md_5
4e3b5670a0 [#1224] Add connect / read timeout for module download incase Jenkins is down. 2014-09-25 10:52:16 +10:00
md_5
ee3b209c2d Declare game version support as 1.8 2014-09-25 10:49:20 +10:00
md_5
25ee8a1496 Revert "Revert "Update Mojang translations to 1.8""
This reverts commit dd1a28ea1e.
2014-09-25 10:47:27 +10:00
md_5
988905331f Force javadoc plugin 2.7 2014-09-25 10:47:17 +10:00
md_5
dd1a28ea1e Revert "Update Mojang translations to 1.8"
This reverts commit 5ec36efb52.
2014-09-25 10:41:40 +10:00
md_5
0a0146b68a [#1219] Fix Title#reset javadoc link 2014-09-25 10:30:52 +10:00
md_5
ca2227bad4 [#1212] Clarify Javadoc of ServerConnectEvent 2014-09-25 10:23:52 +10:00
md_5
3cd4f169bd [#1191] Stop getPermissions from creating blank groups. 2014-09-25 10:21:15 +10:00
md_5
5ec36efb52 Update Mojang translations to 1.8 2014-09-25 10:18:29 +10:00
md_5
dcc9be9dfe [#1218] Check which player instance we remove from the connection maps.
Legitimate but slightly sneaky fix for the issue of racing for addition / removal. An alternate fix would be a multimap.
2014-09-12 18:45:50 +10:00
md_5
efdedbd4e8 Trove should not be a core depend. 2014-09-12 18:24:14 +10:00
Thinkofdeath
1623fb6952 Only update the dns cache on new lookups (Fixes #1221)
This causes the cached entry to be looked up every 5 minutes instead of the previous system where it was kept as long as it was used. This fixes an issue where after Mojang's session servers go down the ip address tends to change. This caused bungee to repeatedly hit the old (inactive) one which with the previous system would be re-cached every time someone tried to connect
2014-09-11 10:18:34 +01:00
Minecrell
4e353e9277 Add Title API. 2014-09-07 09:30:57 +01:00
Minecrell
d6b7157c1c Add player list header / footer API. 2014-09-07 09:26:52 +01:00
Steve Anton
bc48ab3fb8 Make ProxyPingEvent async 2014-09-07 12:03:55 +10:00
Thinkofdeath
65ae8b4c6a Correctly send the MC|Brand packet 2014-09-02 13:54:52 +01:00
Thinkofdeath
73d7e0cf99 Bump protocol to 1.8 2014-09-02 09:44:07 +01:00
md_5
2cec5f344a Update versions to 1.8... oops 2014-09-01 16:56:03 +10:00
md_5
4573285a70 Fix build expiration. 2014-09-01 16:47:16 +10:00
md_5
5282a8f45a Just use server specific tab lists for now. 2014-08-31 19:18:19 +10:00
md_5
6eedc77954 Remove javadoc / delombok for now, interferes with build process. 2014-08-31 19:05:51 +10:00
md_5
f15eed338d Fix tab list 2014-08-31 19:01:24 +10:00
md_5
faa284c8fc Move chat API into own submodule. 2014-08-31 18:56:03 +10:00
Thinkofdeath
4bb3850b40 Fix another missed offline mode case 2014-08-31 09:34:31 +01:00
Thinkofdeath
1f132876e6 Handle offline mode in the tab list 2014-08-31 09:25:31 +01:00
md_5
c822c48fef Add BungeeCord to the outdated ping message. 2014-08-31 18:23:45 +10:00
Thinkofdeath
26521cf2ff Add support for Minecraft 1.8.x
This commit allows BungeeCord to support Minecraft clients both of versions 1.7.x and of 1.8.x. There should be no breakages to any other support, however following their deprecation and uselessness within 1.8, the Tab list APIs have been removed.

Please report any issues to GitHub and be sure to mention client, server and BungeeCord versions.

When used with an appropriate server jar (such as multi protocol Spigot), this will allow clients of many versions to concurrently be connected to the same set of servers.
2014-08-31 09:03:12 +01:00
md_5
e99bbff22e Fix Javadoc for Jenkins... maybe 2014-08-31 13:53:53 +10:00
md_5
19b48672af Fix longstanding forward typo 2014-08-31 10:13:50 +10:00
md_5
f81b8a3550 Use delombok for JavaDoc generation, #1188 2014-08-31 10:12:11 +10:00
md_5
87797ef719 Fix slight JavaDoc formatting issue. 2014-08-31 09:27:59 +10:00
Thinkofdeath
86c5e321f2 Disable epoll by default due to timeouts its causing
Anyone who wants to continue testing epoll for us may do so with `-Dbungee.epoll=true`
2014-08-21 08:11:11 +01:00
mrapple
41ccf3f9d3 Fix LoginEvent firing after PreLoginEvent is cancelled 2014-08-19 22:43:05 +01:00
Thinkofdeath
e573f7b89c Correct toLegacyText the same way toPlainText was previously 2014-08-17 18:50:28 +01:00
Minecrell
664c66fb97 Ignore unknown entries in plugin description files.
Fixes #955.
2014-08-17 19:58:15 +10:00
md_5
caa562c4a1 Final nail in the coffin for xxx cannot be cast to yyy bug. 2014-08-17 09:54:37 +10:00
md_5
483805067d Update Netty and enable Epoll as bugs have been fixed. 2014-08-17 09:52:59 +10:00
Tux
5e0aa2e60d Finalize the ServerDisconnectEvent target field.
There is no good reason for it to be mutable in the first place, considering that the event instance is not used after the method is called.
2014-08-17 09:34:06 +10:00
IfarPL
f9f9c3213d Added 'ForwardToPlayer' subchannel 2014-08-16 20:20:54 +10:00
Thinkofdeath
be35e283ec Revert "Mojang allows multiple status queries in a connection, we should too."
This reverts commit 77f6930280.
2014-08-11 23:17:31 +01:00
md_5
dd9bd2a2e3 Shuffle Jansi/Jline versions 2014-08-04 18:19:35 +10:00
md_5
3188d946b3 Fix javadoc 2014-08-04 16:15:46 +10:00
Ad237
072e360d0f Add KickPlayer subchannel
Can be used to kick a player who is on a different server
2014-08-03 18:13:39 +10:00
md_5
ee3efd75d7 Change a few aspects from last PR 2014-08-03 18:12:44 +10:00
Jonas Konrad
d85400bc69 Add forward parameter to only forward to online servers [Adds #1120] 2014-08-03 18:10:38 +10:00
Jonas Konrad
b544bb34cb Add ServerInfo method to send plugin message only if server is online 2014-08-03 18:10:38 +10:00
Gabscap
8676dd47f6 Disable plugins in reverse order 2014-08-03 18:09:18 +10:00
Melair
089a8dd311 Allow customisation of kicking during initial server connection. In particular allow removal of server name by modification of messages.properties. 2014-07-30 19:16:19 +10:00
Thinkofdeath
d1d4cc7bbf Tidy up the handling of Spawn Object and fix a off by one error with it 2014-07-30 09:26:17 +01:00
Thinkofdeath
74f5ffd08b Duplicate any extra components when duplicated 2014-07-25 13:22:58 +01:00
Joshua Küpper
9fc862cb74 Make ComponentBuilder clonable 2014-07-25 13:22:54 +01:00
md_5
0d174b51c5 Clarify PreLoginEvent 2014-07-22 20:27:46 +10:00
md_5
1b18e64fb2 Handle objective value changes. Although the client *shouldn't* be using this for any sort of keying, it may indeed be. Closes #1116 awaiting testing. 2014-07-22 20:24:38 +10:00
Isaias
c42d3a375f Fix uniqueId returning null in LoginEvent 2014-07-21 13:49:45 +01:00
Thinkofdeath
fc0a21f548 Correct a typo in Spawn Object rewriting 2014-07-21 09:48:21 +01:00
md_5
65eba06980 Don't let Bungee run in dirs with ! in name.
Java uses ! to indicate a resource inside of a jar/zip/other container. Running Bungee from within a directory that has a ! will cause this to muck up.
2014-07-19 21:22:20 +10:00
md_5
87a64c3f3e Fix locale object getter, closes #1113 2014-07-16 17:13:56 +10:00
md_5
be13a00386 Add missing return - closes #1114 2014-07-16 17:12:52 +10:00
md_5
949f150ea0 Fix some out of style formatting. 2014-07-12 19:50:56 +10:00
md_5
c965e60f5e Cipher thread locals should be static 2014-07-12 19:49:12 +10:00
Thinkofdeath
02e219262a Fix TranslatableComponent's handling of missing translations 2014-07-12 10:34:24 +01:00
Thinkofdeath
a0e8b172ef Restrict access to some internal methods. 2014-07-12 19:33:04 +10:00
md_5
215b70dcd7 Fix some more things picked up by static analysis. 2014-07-12 19:30:00 +10:00
md_5
a6095c680f [#1111] Ignore scorebaord objective action 2 like we did before 2014-07-12 17:33:57 +10:00
md_5
9d5c886045 Fix some more static analysis warnings 2014-07-12 14:01:06 +10:00
md_5
19bb8f72c7 Fix some static analysis warnings. 2014-07-12 13:33:13 +10:00
md_5
4dfd510583 Override context specific permission check 2014-07-10 13:56:59 +10:00
md_5
705b554b3b Add basis of grouped thread factory and make the scheduler use it. 2014-07-10 11:18:42 +10:00
md_5
6615500f08 Block all of java, not just java.lang 2014-07-10 10:50:00 +10:00
md_5
d63d5a2791 <yawkat> is that debug code i spot md_5? 2014-07-10 10:42:55 +10:00
md_5
2444dd15ab Fix #1106 - plugins using ssl throwing exceptions 2014-07-10 10:39:58 +10:00
md_5
2dd3d2101d Close #1105 - don't let security manager get replaced 2014-07-09 09:41:19 +10:00
md_5
8ce26e0370 Pass plugin into executor getter for future proofing 2014-07-08 15:53:25 +10:00
md_5
5d1b660e32 Implement Security Manager
This commit adds the basis for the intergration of a security manager into BungeeCord. The goal of the security manager is to prevent plugins from doing potentially dangerous or otherwise undesirable behaviour that may damage the stability of Bungee itself or pose a risk to the user's server.

One common theme in some Bungee plugins, especially those which were written in the very early days, is using Threads and ExecutorServices for scheduling purposes. Not only is this inefficient as there is no use of the thread caching features provided by the scheduler, it is also difficult to track who created which thread. Additionally creating threads not managed by the BungeeCord scheduler poses issues for when|if a plugin reload system is implemented, as these threads cannot be appropriately cleaned up and may continue to leak class references or perhaps even continue executing.

At this stage the SecurityManager is set to warn of prohibited actions, but not block them. For some plugins using external APIs, where usage of an ExecutorService is unavoidable, we have included an Unsafe interface to the scheduler which allows direct access to the underlying ExecutorService, or potentially a compatability wrapper.
2014-07-08 15:22:26 +10:00
md_5
7347daf203 Delay kicks in initial handler.
See source for reasoning
2014-07-08 15:16:22 +10:00
md_5
02cb1fc65b Allocate cipher with EVP_CIPHER_CTX_new. Thanks @Adam- for the tip. 2014-07-02 12:57:17 +10:00
md_5
7318750ed0 Update native cipher to make use of the OpenSSL EVP API so that it can actually utilise hardware acceleration and other goodies.
Whereas before OpenSSL would often lose benchmarks now it always wins.
2014-07-01 20:13:30 +10:00
md_5
21be93a1b1 [#1094] Remove config values set to null, add unit test for such behaviour. 2014-07-01 13:40:17 +10:00
md_5
bb69af5cd0 Disable epoll whilst we try to replicate some issues 2014-06-29 09:22:34 +10:00
Minecrell
a668da76d0 Write log messages with lower levels to the log file. 2014-06-29 09:13:16 +10:00
thinkofdeath
4cc009a9c0 Merge pull request #1089 from yawkat/typo-0
Fix typo
2014-06-28 14:04:10 +01:00
Jonas Konrad
4ef58d53b1 Fix typo 2014-06-28 13:45:26 +02:00
Jonas Konrad
afa37505c5 Fix javadocs for java 8 doclint 2014-06-28 09:05:50 +10:00
md_5
a53b63720b Add a comment 2014-06-26 19:24:05 +10:00
md_5
348457f613 Quickly test SO_REUSEADDR 2014-06-26 19:19:11 +10:00
md_5
fbb2f695b0 Show correct error when cannot bind query 2014-06-26 19:08:05 +10:00
md-5
350cbd7bb6 Merge pull request #1080 from RuriRyan/master
Fixed RemoteQuery, which broke with the Epoll update
2014-06-26 11:38:42 +10:00
RuriRyan
489242b1ef Fixed RemoteQuery, which broke with the Epoll update 2014-06-25 23:29:08 +02:00
md_5
4ac117fb4c Actually include native code, thanks again @normanmaurer 2014-06-25 18:21:59 +10:00
md_5
500b0af782 [Performance] Attempt to use Netty's Epoll implementation on Linux.
This will attempt to make use of Netty's EpollEventLoopGroup and Epoll(Server)SocketChannel when on Linux and the native libraries load correctly. It should bring a large increase in performance and hopefully reliability. Big thanks to the @netty team for implementing this and @normanmaurer for some tips on the support detection.

Feedback is appreciated.
2014-06-25 18:14:50 +10:00
md_5
2b304ecebc Close plugin log handlers on disable 2014-06-24 18:22:35 +10:00
md_5
81d83bdd8a Calling parent logger seems to work nowadays - closes #1071 2014-06-24 18:21:13 +10:00
md_5
b9f2f3cfae Add a getKeys method to the Configuration API 2014-06-24 16:59:15 +10:00
md_5
923aa05d4a Downgrade forced hosts errors to a warning log level 2014-06-24 16:56:24 +10:00
md_5
e54388a5e0 Add matchPlayer API 2014-06-23 17:26:30 +10:00
md_5
59ba644623 Make IP command tabbable - closes #999 2014-06-23 17:16:14 +10:00
md_5
fbc69543fd Reset yaml locations on any error, closes #1007 2014-06-23 17:13:41 +10:00
maciekmm
4d1f0cbb26 Added getKickedFrom() to ServerKickEvent. 2014-06-22 17:11:27 +10:00
md_5
e849afbb23 Add simple address cache for those with slow or otherwise unresponsive DNS servers.
I wonder when @netty was supposed to add async dns lookups.
2014-06-20 20:04:22 +10:00
Moehritz
1a7efeabc4 Fix unregisterCommand(s) - second try 2014-06-20 19:43:36 +10:00
Gabscap
f6e41c856c Closing all handlers on stop() 2014-06-20 19:28:01 +10:00
md_5
151344aaff Fix typo in unused readUnsignedByte method of MinecraftProtocol 2014-06-20 19:26:34 +10:00
md_5
b167a45690 Revert "Reenable throttle since we only support 1.7+"
This reverts commit f23691df23.
2014-06-13 16:25:53 +10:00
vemacs
31bd836203 Write server name in ServerIP subchannel response.
Behaves similarly to the UUIDOther implementation.
2014-06-11 16:13:32 +10:00
Jonas Konrad
4dce37cd13 Prefix "BungeeCord > " to forwarded MC|Brand messages [Fixes #1038] 2014-06-11 16:12:30 +10:00
xxyy
107d6b011d Fix ServerPing NPE w/ String favicons
Currently, passing a null favicon String to the ServerPing(Protocol, Players, String, String) constructor causes a NPE. However, passing a null `Favicon` object to the corresponding constructor does not cause one. Setting the favicon String using the setFavicon(String) method doesn't cause a NPE either.

Therefore, the NPE thrown by the constructor is inconsistent and should be avoided. Please find a sample NPE here: http://newpaste.md-5.net/pmtqjc8vl (Note the `null` favicon)

This PR changes the documented (unintended?) behaviour by adding a null check before passing the favicon String to the alternative `Favicon` object constructor. This makes the constructor consistent with the other one and the `setFavicon(String)` method. This also adds compatibility for old (made before Favicon API) plugins passing `null` favicon Strings (and expecting no favicon to be displayed instead of a NPE).

Thanks!
2014-06-11 16:09:20 +10:00
md_5
f23691df23 Reenable throttle since we only support 1.7+ 2014-06-11 16:08:33 +10:00
md_5
77f6930280 Mojang allows multiple status queries in a connection, we should too. 2014-06-11 16:07:14 +10:00
md_5
9604a9a31e Show 1.7.9 as supported version 2014-06-11 16:05:07 +10:00
md_5
91989564e5 Add method to get player's locale 2014-06-11 16:03:10 +10:00
Thinkofdeath
3c938c03c7 Fix BaseComponent calling the wrong method when inheriting from another BaseComponent (Fixes #1049) 2014-06-08 13:07:42 +01:00
md_5
9226df86f0 Lazy init vhost address. 2014-05-31 18:46:35 +10:00
md_5
00db351dd6 Deprecate tab list API - it will be removed in Minecraft 1.8 as the updates by Mojang render it useless. 2014-05-25 09:19:34 +10:00
md_5
ad2ff54b76 Bump a few versions, should probably think about exposing the new EPollEventLoop. 2014-05-15 17:07:31 +10:00
md_5
ece641da23 Change lobby to fallback in fallback_lobby translation. 2014-05-03 17:31:19 +10:00
Jonas Konrad
2af8dac70c Pull up getFaviconObject to the API 2014-05-03 17:29:48 +10:00
Thinkofdeath
33a098f4ba Merge branch 'vemacs-master' 2014-05-01 11:00:12 +01:00
vemacs
e4e01ccb55 Null check 2014-05-01 10:59:14 +01:00
thinkofdeath
6c9e6abc9f Merge pull request #1004 from GunfighterJ/master
Correct the shutdown message
2014-04-26 19:48:03 +01:00
GunfighterJ
84c7e073e0 Grammar Nazi 2014 2014-04-26 13:44:59 -05:00
Thinkofdeath
9e46739343 Improve the CommandServer tooltip
Includes the grammar fix from #953
2014-04-25 20:53:10 +01:00
thinkofdeath
8fe72383a1 Merge pull request #1002 from yawkat/server-info-tostring
Add toString for BungeeServerInfo
2014-04-25 20:33:37 +01:00
Jonas Konrad
a56bbe38b2 toString for BungeeServerInfo 2014-04-22 15:24:18 +02:00
md_5
312a74c5f1 Close #963 - log remote ping errors to console, friendly message to clients. 2014-04-19 19:48:43 +10:00
vemacs
71c86f9f90 Add ServerIP subchannel 2014-04-18 10:33:26 -06:00
Thinkofdeath
6475385f87 Use ProtocolConstants instead of raw protocol numbers 2014-04-16 14:57:09 +01:00
Thinkofdeath
6775b9230c Update Team packet's field names. unknown -> nameTagVisibility, unknown2 -> color 2014-04-16 14:46:48 +01:00
Thinkofdeath
c8e6b6fd7a Add 'Combat Event' to 14w11a's EntityMap 2014-04-16 14:34:30 +01:00
Thinkofdeath
bca3663a1f Add 'Camera' to 14w11a's EntityMap 2014-04-16 14:26:11 +01:00
Thinkofdeath
f71272a1c0 Add 14w11a EntityMap support 2014-04-16 11:56:04 +01:00
Thinkofdeath
ca7c755ecd Split up EntityMap into different protocol versions 2014-04-16 11:14:29 +01:00
md_5
5a638f2290 Enable 14w11 support, changing servers is a bit iffy though due to EntityMap not being complete. 2014-04-16 11:01:41 +10:00
md_5
3715756be7 Update packets for MINECRAFT_14_11_a 2014-04-16 10:48:40 +10:00
md_5
1a1a51b38d Replace direction strings with concrete enums 2014-04-16 10:28:07 +10:00
md_5
bc2b4db419 Replace literal numbers with usages of the ProtocolConstants class. 2014-04-16 10:18:16 +10:00
Jonas Konrad
994a996981 Fix NPE while encoding when favicon was missing 2014-04-15 20:17:26 +10:00
Jonas Konrad
e2eba52162 Revert "Revert "Implement Favicon API""
This reverts commit 13decac4b9.
2014-04-15 20:17:26 +10:00
md_5
13decac4b9 Revert "Implement Favicon API"
This reverts commit 18316eb5f8.
2014-04-15 15:08:12 +10:00
md_5
7ebe5184a4 Clarify favicon.create javadoc 2014-04-15 11:11:11 +10:00
md_5
b08f1995f6 Return most recent protocol version to 'unsupported' server lists 2014-04-15 11:08:28 +10:00
Keir Nellyer
a642346a2c Add method to get a connected player via their UUID 2014-04-15 11:07:17 +10:00
Jonas Konrad
18316eb5f8 Implement Favicon API 2014-04-15 11:05:20 +10:00
md_5
3ced0b675d Implement Skin forwarding - YOU MUST UPDATE SPIGOT FIRST 2014-04-15 10:38:15 +10:00
md_5
66a70fef5b Add #983 - disabled commands for players only. 2014-04-14 16:24:16 +10:00
md_5
dc2da29c16 Code format. 2014-04-13 14:15:41 +10:00
Thinkofdeath
3b71a2b570 Fix ping decoding (Fixes #969) 2014-04-13 00:07:11 +01:00
Thinkofdeath
1aa5379030 Handle new clients pinging old versions (Fixes #972 and Fixes #969) 2014-04-12 10:16:58 +01:00
Thinkofdeath
153bca00be Fix 1.7.7 support 2014-04-10 13:34:08 +01:00
Thinkofdeath
747628f40c Remove @RequiredArgsConstructor from PlayerInfoSerializer as the javadoc fails to build with it 2014-04-09 21:01:33 +01:00
Thinkofdeath
bf9521472b Support setting uuid's on ServerPing.PlayerInfo + fix plugins which don't provide a valid uuid. 2014-04-09 20:26:07 +01:00
Thinkofdeath
86ef046544 Pass the protocol version through when using ping pass through 2014-04-09 16:42:08 +01:00
Thinkofdeath
cd518690fd Rewrite the spawn player packet's uuid to support skins on non ip-fowarded servers 2014-04-04 12:14:04 +01:00
Thinkofdeath
1d3adc5317 1.7.6-pre1 Support 2014-04-04 21:42:10 +11:00
Keir Nellyer
13848def72 Return a Users UUID as a UUID object whilst keeping support for returning as a String 2014-04-04 21:35:07 +11:00
Keir Nellyer
a4dd0dba88 Allow 'softdepends' in plugin description 2014-04-01 19:55:59 +11:00
md_5
e025ad8ed7 Bump netty from 4.0.14 -> 4.0.17 2014-04-01 14:36:19 +11:00
Alex Ivanov
720f5df2f4 Use correct padding for UDP queries 2014-04-01 14:33:46 +11:00
md_5
52bf6184c7 Don't disable find by default 2014-03-30 15:53:57 +11:00
md_5
f7a5748464 Delay event loop initialization until after system properties are set. Closes #943 2014-03-29 15:21:04 +11:00
md-5
61e3d27ae9 Add more Java versions to Travis 2014-03-20 20:15:33 +11:00
md_5
1bbbfdb0d5 Close #938 - re add our register channel manipulation 2014-03-20 20:12:06 +11:00
thinkofdeath
a8584f81ed Merge pull request #935 from yawkat/fix-legacy-convert-npe
Fix NPE when converting invalid color character from legacy text (Fixes #934)
2014-03-15 19:24:00 +00:00
Jonas Konrad
5ae2e24c84 Fix uppercase color codes 2014-03-15 20:19:48 +01:00
Jonas Konrad
c29676e4fc Fix NPE when converting invalid color character from legacy text 2014-03-15 19:56:19 +01:00
Jonas Konrad
92ebce2ec6 Add TabCompleteEvent 2014-03-14 20:51:47 +11:00
kamcio96
9cd7c1ac03 Add ProxyReloadEvent 2014-03-14 20:49:45 +11:00
md_5
924dcaab38 Move default server fallback 2014-03-14 19:29:13 +11:00
md_5
8502ab54c0 Revert "Fix #872 - use default server if forced host not found, OR force default is set."
This reverts commit 1fb7a3bf1d.
2014-03-14 19:27:51 +11:00
md_5
3301c95066 Revert "Rejig forced hosts some more - closes #927"
This reverts commit 72cadac76e.
2014-03-14 19:27:46 +11:00
Thinkofdeath
49f4dcb5b7 Ensure we have the capacity to resize the buffer 2014-03-13 19:51:46 +00:00
Thinkofdeath
13d679e7d7 Correctly resize the spawn object packet 2014-03-13 19:30:27 +00:00
md_5
72cadac76e Rejig forced hosts some more - closes #927 2014-03-13 13:32:48 +11:00
md_5
227301ec73 Don't throw full exception on bad magic 2014-03-13 07:40:22 +11:00
Maciej Mionskowski
f2b0e3e3c3 Fixed config saving and value setting. Probably fixes #798 2014-03-11 15:21:31 +11:00
md_5
575a6b6ea0 Fix some errors related to disabling modules - #890 2014-03-11 15:16:52 +11:00
Thinkofdeath
5beafed279 Null check the locations.yml Map before changing into a CaseInsensitiveMap (Fixes #920) 2014-03-10 19:34:11 +00:00
Thinkofdeath
2a7ad3c2b2 Append/trim extra data for spawn object packets with a 0 id 2014-03-10 19:25:15 +00:00
md_5
00352f585a Revert code which wasn't meant to be commited. 2014-03-10 13:36:17 +11:00
md_5
290e31b4c5 Update some depend versions, leaving netty for a later date when there aren't as many recent changes. 2014-03-10 11:34:35 +11:00
md_5
1fb7a3bf1d Fix #872 - use default server if forced host not found, OR force default is set. 2014-03-10 11:23:26 +11:00
Jonas Konrad
52fbceec54 Fix client disconnects when a server goes down - #886 2014-03-10 11:17:52 +11:00
md_5
e609145a0d Add module authors - closes #902 2014-03-10 11:13:49 +11:00
md_5
4a7f8015e5 Close #918 - use case insensitive lookup for Yaml locations 2014-03-10 11:04:46 +11:00
md-5
003a1973d4 Update CommandEnd javadoc 2014-03-10 09:23:44 +11:00
md_5
db7f3c770d Fix EventBus when used with Byte.MAX_PRIORITY - closes #910. Also includes additional unit test cases to cover any future regressions. 2014-03-04 20:49:53 +11:00
md_5
eec3c09c32 Tweak ciper messages - closes #905 2014-02-27 16:00:44 +11:00
md_5
565af4d53e Remove switchMutex since its no longer required 2014-02-27 07:32:23 +11:00
md_5
37ed331515 Ensure user's client and server component are both in the same event loop. Probably closes #893 2014-02-26 21:28:04 +11:00
Thinkofdeath
38f12840ca Correct component loop detector 2014-02-21 20:56:10 +00:00
Thinkofdeath
941450b4e4 Detect component loops 2014-02-21 10:13:40 +00:00
md_5
e87d25c321 Remove playerCount from team - closes #889 2014-02-21 20:18:39 +11:00
md_5
886f7499fb Update compile-native script. 2014-02-16 11:40:36 +11:00
md_5
8064a3d4fb Revert "Compile native cipher on ancient CentOS for maximum compatability - closes #880"
This reverts commit 7bfa024c23.
2014-02-16 11:39:47 +11:00
md_5
5039922fa7 Revert "Compile on CentOS 6.4 with OpenSSL 1.0.0e and glibc 2.12"
This reverts commit eb753c8109.
2014-02-16 11:39:42 +11:00
md_5
eb753c8109 Compile on CentOS 6.4 with OpenSSL 1.0.0e and glibc 2.12 2014-02-15 19:37:53 -05:00
md_5
7bfa024c23 Compile native cipher on ancient CentOS for maximum compatability - closes #880 2014-02-16 00:13:13 -05:00
md_5
d98ade5a9b Mojang uses "if > 100", therefore the inverse of that is "if <=100" regarding chat length. 2014-02-13 07:25:56 +11:00
md_5
a272afd693 Some 32bit var ints need 5 bytes 2014-02-13 07:13:48 +11:00
md_5
0f24eaeea3 Mojang caps chat to 100, we should too 2014-02-12 19:59:02 +11:00
md_5
56e9e6a245 Delete extracted native code on exit 2014-02-12 19:53:33 +11:00
md_5
87f3706736 Cap VarInt down to 4 bytes. Early Mojang implementations used 32 BYTES, then they changed it to 5 BYTES. I think they mean 4 BYTES = 32 BITS. 2014-02-12 19:51:43 +11:00
md_5
90104b03b7 Only allow key sizes of 16 in native cipher 2014-02-12 19:45:28 +11:00
md_5
a9b2660aa8 Unknown git commit or version = unknown version sig 2014-02-12 19:15:00 +11:00
md_5
0b7789035f Revert as its unstable anyway. 2014-02-12 18:02:02 +11:00
md_5
3f7850dc5a Clean up JNI code, add stack trace to check up on Jenkins test. 2014-02-12 17:43:49 +11:00
md_5
2e80bf30dd Various improvements to native cipher so that it now actually destroys Java in terms of speed. Closes #871 - thanks @ninja- 2014-02-12 17:37:08 +11:00
md-5
b3627652f2 Merge pull request #869 from Cube-Space/permission-api
Added getPermissions() to the CommandSender API
2014-02-09 14:36:15 +11:00
md_5
b5216148d6 Remove a bit of synchronization 2014-02-09 11:02:26 +11:00
Fabian Fassbender
4faf507ad9 Added getPermissions() to the CommandSender API to get a unmodifiable Collection of all Permissions. The ConsoleSender returns an empty Set where as the UserConnection gives its real Permissions. 2014-02-08 14:17:01 +01:00
md_5
9a4f0a6f59 Revert disconnect change. Forgot we use void futures. 2014-02-08 12:42:27 +11:00
md_5
98a5db9abf Skip deploying of modules - speeds up Jenkins build. 2014-02-08 12:24:31 +11:00
md_5
015dc0c65a Dem string typos 2014-02-08 12:21:13 +11:00
md_5
c1b9e9032f Fix typo 2014-02-08 12:20:08 +11:00
md_5
5e5038c839 Fix Java 7 detection - closes #861 2014-02-08 09:40:08 +11:00
md_5
36ea27454d Disable selector rebuild - closes #851 2014-02-08 09:39:45 +11:00
md_5
2eb2953442 Optimize throwing of CancelSendSignal's by storing a single instance. 2014-02-08 09:36:41 +11:00
md_5
4abffa9f24 Optimize / clean up server disconnect method 2014-02-08 09:33:05 +11:00
md_5
f08df9555c Remove dead IP forward code, Mojang aint bringing that one back 2014-02-08 09:19:28 +11:00
Thinkofdeath
4c5689d10e Fix typo in EntityMap 2014-02-04 11:03:12 +00:00
md_5
224db6596e Make console log handler obey Filters. Closes #866 2014-02-04 16:44:53 +11:00
MinePlayer64
df82720ade Remove unnecessary import 2014-02-02 21:58:13 +11:00
MinePlayer64
33f87498be Use the API to build message 2014-02-02 21:58:13 +11:00
MinePlayer64
d0af22a0f2 Make /server more fancy
Let users click on a server to join it
2014-02-02 21:58:13 +11:00
md_5
4fa137a465 Add java property for log date format, closes #764 2014-02-02 12:45:53 +11:00
md_5
0d7ee821d2 Adjust wording - closes #823 2014-02-02 12:43:18 +11:00
md_5
1f1cdb47e4 Search for bungee.yml and then plugin.yml for Bungee plugins. 2014-02-02 12:22:25 +11:00
md_5
ddab9a84c4 Close #859 - more favicon validation 2014-02-02 12:20:37 +11:00
md_5
5adc0000d8 Add small benchmark to the native cipher suite - see #755 2014-02-02 10:18:01 +11:00
md_5
a0cc5d84be Add explicit casts to make clang support easier - see #755 2014-02-02 10:15:19 +11:00
Thinkofdeath
edce7f7c3d Add tab completion to /send (Fixes #853) 2014-02-01 11:21:59 +00:00
md_5
19b4c09a16 Remove unused fields 2014-02-01 10:38:43 +11:00
md_5
35a5230b52 Remove artifacts from legacy forge support. 2014-02-01 10:38:43 +11:00
md_5
90fcfecb7c Use lombok for vhost 2014-02-01 10:38:43 +11:00
md-5
542d2c2a5b Close #850 - send 127 for legacy protocol version 2014-02-01 09:27:22 +11:00
md_5
649f83dee2 Fix team packet for 1.7 servers 2014-01-28 13:28:06 +11:00
md_5
842392d59c Dual version entity ID rewriting 2014-01-27 11:39:10 +11:00
md_5
b2f517fa63 Implement dual protocol version support. 2014-01-27 11:26:27 +11:00
Thinkofdeath
5c12f900b3 Correct the extra check 2014-01-26 01:12:55 +00:00
Thinkofdeath
6641d199b3 Move the extra check out of hasFormatting 2014-01-26 00:41:13 +00:00
Thinkofdeath
83b0229277 Fix formatting leaking through components when arrays are used + cases where component's extra wouldn't serialize 2014-01-26 00:37:33 +00:00
md_5
5f7963b0c4 Add more validation to server icons. Closes #828 2014-01-22 12:16:48 +11:00
md_5
dae96ac18b Close #819 - remove trailing . from hostname if it exists 2014-01-22 12:09:29 +11:00
md_5
aa91354666 Make sure plugin onLoad is really called onLoad. This fixes regression #822 from when we implemented plugin depends. 2014-01-22 12:07:16 +11:00
md_5
05f4e69afd Bring reconnect yaml handler setting in line with what we had before (only create if needed) 2014-01-17 09:35:29 +11:00
md_5
71e64bf532 Register reconnect_yaml module 2014-01-17 09:31:46 +11:00
md_5
219d485835 Make the yaml reconnect handler a module 2014-01-17 09:25:22 +11:00
md_5
b698fa9806 Actually allow people to run these builds. 2014-01-15 07:39:25 +11:00
md_5
6602c22147 Ensure modules are enabled even when we can't detect version 2014-01-14 13:18:14 +11:00
md_5
f1b329bf21 Finish implementing modules. If anyone wants to test... be my guest. 2014-01-14 13:13:27 +11:00
Thinkofdeath
d4e4796739 Remove pointless Getter annotations 2014-01-13 19:48:49 +00:00
md_5
ab1aacbdc9 Implement a first tempt and module retrieval system 2014-01-13 15:16:17 +11:00
md_5
a426a5ec22 Implement module loader, blacklist this build from loading. 2014-01-13 14:28:07 +11:00
md_5
93cf50b4e1 Rum gitdescribe earlier in the build process. 2014-01-10 10:03:02 +11:00
md_5
7483b4d276 Shuffle some internal stuff to API so that modules will compile 2014-01-10 09:58:31 +11:00
md_5
4be58a7c00 Copy across all the commands and add their bootstrap plugins. 2014-01-10 09:53:33 +11:00
md_5
90d68bd38e Should be of jar packaging 2014-01-10 09:38:53 +11:00
md_5
7dba8c8a87 Implement skeleton modules for our 5 initial commands. 2014-01-10 09:35:27 +11:00
Joey Sacchini
cb4f70ecc7 Fix null pointer in depend resolution. This closes issue #515. 2014-01-07 14:34:07 +11:00
Thinkofdeath
2100da2a9f Don't remove the legacy decoder if a legacy packet is found
Removing the decoder causes the bytebuf to be passed on to the next handler to be parsed normal packet which causes an error with legacy handshakes.
2014-01-05 17:06:07 +00:00
md_5
986f52b1e1 Try harder to find translations when not using English. Closes #787 2014-01-05 11:34:34 +11:00
md_5
7faefde51b Add name_too_long translation 2014-01-05 11:27:19 +11:00
TheUnnamedDude
8c367d86e7 Properly kick older clients 2014-01-05 11:25:42 +11:00
md_5
80caa2b669 Code format :( 2014-01-03 19:52:35 +11:00
md_5
9f8c04ef86 Update some dependency versions: Guava, MySQL, JavaAssist, Netty. 2014-01-03 19:51:44 +11:00
Thinkofdeath
b0a8371570 Fix client crash when an empty message is sent using TextComponent.fromLegacyText 2014-01-02 15:39:38 +00:00
thinkofdeath
0d7cd78901 Merge pull request #773 from YoshiGenius/patch-1
Fix javadoc - spelling and details
2014-01-01 15:16:46 -08:00
Thinkofdeath
e7f1a88e6e Formatting fixes 2013-12-30 16:15:39 +00:00
thinkofdeath
28c8bf47ff Merge pull request #789 from kosgan10/patch-1
Fix errors for console
2013-12-30 08:14:21 -08:00
kosgan10
5e57356e6a Change Exception > Throwable 2013-12-30 18:10:39 +02:00
kosgan10
f3766bc10b Fix hover messages for players 2013-12-30 18:07:30 +02:00
kosgan10
6c795a25ff Change message to use ComponentBuilder 2013-12-30 15:43:58 +02:00
kosgan10
d3159fe6ca Fix imports 2013-12-30 15:38:13 +02:00
kosgan10
93ba9b3a3e Fix errors for console 2013-12-30 15:36:35 +02:00
Thinkofdeath
c184667a26 Add packet names as comments to EntityMap to make updating between versions easier 2013-12-28 22:55:09 +00:00
md_5
5ea4763ae9 Implement UUID forwarding, you MUST update Spigot for this to work 2013-12-24 10:10:11 +11:00
md_5
d0d0f4ec9f Make getPlayers a little safer. Should probably use a CopyOnWrite collection though. 2013-12-23 17:41:27 +11:00
YoshiGenius
0ff1f4724a Add new lines 2013-12-23 08:45:48 +11:00
YoshiGenius
1baba3cd7d Fix javadoc - spelling and details 2013-12-22 13:05:45 +11:00
Keir Nellyer
608eaace1c Fix NPE when no Callback is defined FIXES #772 2013-12-21 12:14:42 +00:00
md_5
075518b643 Implement a connect callback - see #760 2013-12-21 13:11:27 +11:00
md_5
f7d3dfd61d Show severe error for missing forced error, but don't crash 2013-12-20 18:57:13 +11:00
md_5
b713ccff10 Validate forced hosts config on reload 2013-12-19 19:30:24 +11:00
md_5
f0e1625078 Implement delombok to close #770 2013-12-19 10:04:48 +11:00
md_5
60d6f31876 Generate offline mode UUIDs the same way Mojang does. 2013-12-16 22:41:19 +11:00
Thinkofdeath
359e2b2a16 Fix typo in /alertraw error message 2013-12-15 13:17:29 +00:00
thinkofdeath
0dcba749dc Merge pull request #748 from thinkofdeath/master
Chat Component API
2013-12-14 02:31:42 -08:00
Thinkofdeath
00ac965d42 Remove class Getter annotation 2013-12-14 10:16:58 +00:00
md-5
32c6ab710a Delay is not in milliseconds, its in the TimeUnit 2013-12-13 12:46:52 +11:00
Thinkofdeath
5d68b422e5 Add ComponentBuilder + make events final
An example of ComponentBuilder usage can be found at CommandAlertRaw
2013-12-10 11:50:34 +00:00
Thinkofdeath
6093cde93f Fix last commit 2013-12-08 18:04:20 +00:00
Thinkofdeath
4537055caa Fix shooterID/ownerID not getting remapped 2013-12-08 17:36:29 +00:00
Thinkofdeath
17fc120e07 Move 0x13 to clientbound only 2013-12-08 16:47:17 +00:00
Thinkofdeath
230cca0f9e Fix server-bound entity ids as well 2013-12-08 16:42:49 +00:00
Thinkofdeath
cfda905d98 Fix entity id remapping
The old system only worked in ints and 1.7 changed some to varints. Since the length of the varint is variable more work is needed to remap ids
2013-12-08 15:09:12 +00:00
Thinkofdeath
bc746a546f Added another unit test + formatted code 2013-12-08 10:17:02 +00:00
Thinkofdeath
70bb3ddcce Added some unit tests 2013-12-08 00:48:10 +00:00
Thinkofdeath
4ef15ae764 Fixed TranslatableComponents missing the end of the translated text 2013-12-08 00:47:45 +00:00
Thinkofdeath
3f9ca85831 Add missing documentation 2013-12-07 23:35:43 +00:00
Thinkofdeath
c17fa03ccd Remove string methods from HoverEvent. TextComponents will become strings if formatting isn't used 2013-12-07 15:10:08 +00:00
Thinkofdeath
0040955204 Fix events not working when no formatting is used 2013-12-07 13:59:00 +00:00
Thinkofdeath
60e2e6bfa4 Add /alertraw command + fix events 2013-12-07 13:35:52 +00:00
Thinkofdeath
2cb3b6f934 Fix TranslatableComponent's toPlainText and toLegacyText not handling %1$s and %d 2013-12-07 13:16:10 +00:00
Thinkofdeath
c7e590e286 Move en_US.properties to mojang-translations/en_US.properties 2013-12-06 23:21:52 +00:00
Thinkofdeath
696679809d Support printing TranslatableComponents 2013-12-06 23:18:10 +00:00
Thinkofdeath
e3e551d825 Use varargs instead of arrays 2013-12-06 23:02:05 +00:00
Thinkofdeath
854b6faf0e Fixed broadcasts getting double logged 2013-12-06 22:40:41 +00:00
Thinkofdeath
890fac27c5 More formatting fixes 2013-12-06 22:40:40 +00:00
Thinkofdeath
35c1b26a20 Fix formatting 2013-12-06 22:40:40 +00:00
Thinkofdeath
2c8b15cb1e Use components for ServerKickEvent (fixes #744) + minor refactoring 2013-12-06 22:40:40 +00:00
Thinkofdeath
c20d8f9cd6 Chat Component API 2013-12-06 22:40:40 +00:00
md_5
85c27f30ee Properly abort kicks on ServerConnector 2013-12-07 09:33:37 +11:00
md-5
0446351f9d @GunfighterJ for grammar nazi 2013 2013-12-05 09:24:00 +11:00
Thinkofdeath
54d307da57 Reduce the size of json output
Nulls are not outputted (https://sites.google.com/site/gson/gson-user-guide#TOC-Null-Object-Support)
2013-12-03 08:17:38 +11:00
Thinkofdeath
81e43aab98 Update ChatConverter 2013-12-03 08:17:38 +11:00
Thinkofdeath
18db187347 Use the ChatConverter from spigot to fix 1.7 formatting bugs 2013-12-03 08:17:38 +11:00
md_5
63d49ac296 Add some failsafe length preconditions 2013-12-02 18:49:39 +11:00
hcherndon
aad83d787f Add methods to expose parts of the proxy internal config. This is depreceated as it is subject to breaking changes without warning. 2013-12-02 15:17:19 +11:00
Keir Nellyer
af751dae5a Add new plugin message sub-channels to get a players real UUID 2013-12-02 08:37:17 +11:00
md_5
f7851b0436 Implement ServerDisconnectEvent. Wow so many server switch events. 2013-11-25 11:12:49 +11:00
md_5
d7e78d0945 Ability to disable outdated message 2013-11-24 19:42:05 +11:00
md_5
94ee61cd35 Native cipher, with more smoke tests! 2013-11-19 07:16:06 +11:00
md_5
897a59254c Still segfaulting. 2013-11-18 20:00:15 +11:00
md_5
c1ba555553 Hopefully fix native cipher segfault 2013-11-18 07:18:23 +11:00
ninja-
c70006a36c Implement new, high-performance cipher in native code. Currently available only for Linux-x64, other platforms will fallback to Java cipher. 2013-11-18 07:17:14 +11:00
md_5
12ef019d69 Revert native cipher, causing SIGSEGV 2013-11-17 16:51:11 +11:00
md_5
c18537f294 Ultra blazing fast native code for 64 bit linux! Testers welcome! Thanks @ninja- for his hard work. 2013-11-17 15:59:51 +11:00
ninja-
fc189e81d5 Implement new, high-performance cipher in native code. Currently available only for Linux-x64, other platforms will fallback to Java cipher. 2013-11-17 15:59:18 +11:00
md-5
aaa8b4a53d Forgot to rewrite 0x1A, thanks @Mati0703 2013-11-17 14:45:58 +11:00
md_5
e39428ea0b Mojang can't seem to decide what chat format to use for kicks, so lets just send a literal without our own prefix through. This (uncleanly) closes #714. 2013-11-16 11:22:38 +11:00
md_5
e4602f027e Don't pointlessly create new gson instances 2013-11-16 11:11:43 +11:00
md_5
2f2406206e Fix chat event javadoc - closes #713 2013-11-16 11:11:04 +11:00
md_5
3ae8308a4b Small cleanup 2013-11-15 16:53:10 +11:00
md_5
8a38921f21 Bump netty release 2013-11-15 16:28:35 +11:00
md_5
cc0d3a8e49 Config -> Conf to prevent messup with shades. How did this skip by? 2013-11-06 20:12:39 +11:00
md_5
f81bf8e7c5 Code format + fix pom 2013-11-06 20:11:17 +11:00
zh32
e755573fb3 Removed unused import and made code nicer. 2013-11-06 20:07:16 +11:00
zh32
a201b5897a Added async PreLoginEvent to change online mode per connection. 2013-11-06 20:07:16 +11:00
md_5
b4cd88c13d Make UDP query actually work. 2013-11-06 20:02:37 +11:00
md_5
8e390b5714 Proper replacement for #701, closes #691 2013-11-05 18:16:33 +11:00
md_5
14371a1a8c Don't rewrite the actual host packet, just a copy. 2013-11-05 18:07:55 +11:00
md_5
61326db3ee Not having a good night, thought I removed that code. 2013-11-03 18:00:30 +11:00
md_5
90625bc196 Remove test code 2013-11-03 17:15:44 +11:00
md_5
155e274e72 Fix disconnects during LoginEvent when using online mode clients 2013-11-03 17:04:59 +11:00
md_5
04b52aa4f4 Close #699 - case sensitivity of command tab complete 2013-11-03 10:02:37 +11:00
md_5
4040d9f20a [Breaking] Fix player online sample 2013-11-01 22:00:46 +11:00
md_5
02619c6132 Close #689 - fix serverinfo.ping 2013-11-01 17:52:14 +11:00
md_5
26863032a1 Fix #671 - user timing out when connectNow is used. 2013-11-01 17:46:41 +11:00
md_5
a0d3bf97d1 Close #690, wire up the old proxy ping event for old clients 2013-11-01 17:43:03 +11:00
md_5
3becbe4d38 Fix #686 - rewriting of bows and fishing hooks 2013-11-01 17:38:53 +11:00
md_5
7205e69ce6 Correct some issues with being banned. See #658 which may be relevant 2013-11-01 17:24:35 +11:00
md_5
c84d6f0035 Add really efficient text -> json translation. Doesn't support format codes yet. 2013-11-01 17:14:18 +11:00
md_5
20b1b37e54 Did someone say great performance increases? 2013-10-28 20:43:28 +11:00
md_5
2117a6b7de Undo kick changes 2013-10-27 21:41:10 +11:00
md_5
e6c1015027 Reload favicon on greload 2013-10-27 20:07:36 +11:00
md_5
8665784bb5 Oh right, stage 2013-10-27 18:23:07 +11:00
md_5
efd5bd58e4 Fix colours wrapping, somewhat 2013-10-27 18:18:44 +11:00
md_5
e006673550 Fix kicks + don't allow connections to online mode servers 2013-10-27 18:12:18 +11:00
md_5
2129cb3614 Fix kicks? 2013-10-27 18:09:27 +11:00
md_5
6ce43fb876 Sample is an array 2013-10-27 13:51:06 +11:00
md_5
b9158b7322 Implement 1.7 style pings 2013-10-27 12:59:35 +11:00
md_5
5dfd14fbe5 Enhance ping API 2013-10-27 12:36:30 +11:00
md_5
e1f7b7b126 Add new ip forward method, must be manually enabled 2013-10-27 12:06:17 +11:00
md_5
4dff25f880 In favour of #678 - use supported class version 2013-10-27 11:38:28 +11:00
md_5
5dc91e3a01 1.7.2 update 2013-10-26 08:30:42 +11:00
md_5
e2e32100cd Common method to wrap text. Thanks @lazertester 2013-10-25 20:39:06 +11:00
md_5
a7e4854661 Fix kick 2013-10-25 20:09:18 +11:00
md_5
6e69d476ef Fix 1.7 potion etc support 2013-10-25 17:25:13 +11:00
md_5
1e2eda94db Don't log metrics 2013-10-24 07:14:06 +11:00
md_5
37dc600fe0 BungeeCord can into server icon 2013-10-23 20:44:48 +11:00
md_5
07d9a56567 ***WARNING - FOR MINECRAFT 1.7*** Update master to 1.7.
Merge branch 'origin/snapshot'
2013-10-23 17:45:21 +11:00
md_5
0952e53d11 Update to 1.7 poms. 2013-10-23 17:33:21 +11:00
md_5
2101964330 1.7 pre release support 2013-10-23 06:30:16 +11:00
md_5
cdf47d84d8 Fix offline mode. Latest snapshot appears to be pretty much functional, although the client itself seems to have some reliability issues. 2013-10-19 20:01:22 +11:00
md_5
b7babd2888 Fix ping player counts being swapped 2013-10-19 17:56:14 +11:00
md_5
da5fa4bb7c Fix outdatedness 2013-10-19 17:50:54 +11:00
md_5
00854988fb Latest snapshot. Doesnt seem to be worky though 2013-10-19 17:42:45 +11:00
md_5
4f8085678c 1.6.4 ping support 2013-10-19 17:27:13 +11:00
md_5
6341ad4c5a Merge branch 'origin/master' 2013-10-19 16:44:27 +11:00
md_5
9b84e75eaa Update lombok to support latest netbeans 2013-10-19 10:01:42 +11:00
md_5
194b09b2dd Dont expand events - closes #666 2013-10-19 07:10:31 +11:00
md_5
3b9af0ab85 Fix 41b support 2013-10-16 17:29:49 +11:00
md_5
18db20fe42 41b update 2013-10-15 16:29:36 +11:00
md_5
062dd38b2b Fix pinging. I love you @Sircmpwn 2013-10-12 15:50:08 +11:00
md_5
69b209bcc6 Fix /server command. Now working! 2013-10-12 15:36:22 +11:00
md_5
d96e561a6f Cleanup debug 2013-10-12 15:30:32 +11:00
md_5
26be0566f4 I love @Cobi 2013-10-12 15:29:39 +11:00
md_5
1551bf6f3a Ping stuffs, doesnt seem to work for some reason though 2013-10-12 13:51:33 +11:00
md_5
e0ebf1af21 All my work on 1.7 so far. Pinging doesnt work, but everything else is near functional. Gotta figure out wtf is happening. 2013-10-12 12:08:26 +11:00
md_5
b8c9330bd6 Sigh 2013-10-12 12:01:17 +11:00
md_5
1b41682e37 Checkpoint 2013-10-12 11:36:53 +11:00
md_5
b358fd25f5 Done with the proxy to client part. Now we just need the proxy to server part. 2013-10-11 21:40:23 +11:00
md_5
dbdae87ec6 Basically done with login 2013-10-11 21:36:28 +11:00
md_5
7121c20338 Compiles yet again 2013-10-11 20:34:21 +11:00
md_5
d900417d95 It compiles 2013-10-11 20:00:54 +11:00
md_5
7be929bb08 Update protocol - major overhaul 2013-10-11 19:26:40 +11:00
md_5
4257b81d8c WIP 2013-10-11 18:40:21 +11:00
md_5
96acdb97fd Update to latest snapshot. 2013-10-11 16:16:02 +11:00
md_5
edc5b4dc91 Close #656 - fix find filter. 2013-10-11 10:24:55 +11:00
md_5
220a95aece We cannot throttle like this if we want 1.7 compat, lets disable it until we work on a more compatible one. 2013-10-10 07:25:20 +11:00
md_5
4685099808 Close #564 adding a blank arg is a silly idea 2013-10-09 22:37:27 +11:00
md-5
6c14f40108 Reduce the time we wait for legacy pings. 2013-10-08 09:06:28 +11:00
md_5
b041d84063 Null check plugin input for servers 2013-10-07 16:55:29 +11:00
md_5
a9d3d9461f Mojang nerfed tab lists :( 2013-10-06 08:42:52 +11:00
md_5
3fc7064997 Add missing setters for handshake. 2013-10-05 09:50:20 +10:00
md_5
80001aa1f0 UDP + Java + IPV6 != Friends 2013-09-30 17:58:02 +10:00
marvin
a0d94282f6 Add PlayerHandshakeEvent which allows changing of versions and online mode status amongst other things. 2013-09-30 09:22:49 +10:00
md_5
33e11f4c44 Allow for iterables to be CSV-ified 2013-09-29 18:49:20 +10:00
md_5
b541e7aa76 Custom glist formatting 2013-09-29 18:45:11 +10:00
zaiyers
dd06937a3b changes to tab completion
* PacketCBTabComplete: options for completion should be seperated by
NUL
 * PluginManager: append an empty argument to arguments if command ends
with a whitespace (this will match all suggestions)
 * PlayerCommand: suggest only matching players instead of all players
2013-09-29 09:37:39 +10:00
md_5
891dc87b16 Allow unrecognised command line args 2013-09-28 21:00:38 +10:00
md_5
8e77cb35ff Use JDK map for throttle 2013-09-28 18:32:39 +10:00
md_5
59b32a8621 Remove access from PendingConnection interface 2013-09-28 17:37:30 +10:00
md_5
02324206e3 Clean up packets a tad 2013-09-28 17:03:33 +10:00
md_5
73ce828e6e [#637] - Fix resource bundle resolution 2013-09-26 09:24:30 +10:00
md_5
79d04bec2e [#582] Widen synchronized block for packet queue. 2013-09-26 09:20:52 +10:00
md_5
cbcd874d47 Close #626 - command line argument for version. Also refactors into a bootstrap which warns users when not using Java 7! 2013-09-25 17:21:03 +10:00
md_5
09f123ce9a guery -> query 2013-09-24 17:50:44 +10:00
md_5
0a5f8556fe Add config saving 2013-09-24 12:14:47 +10:00
md_5
103a509f26 Fix access of yaml config 2013-09-24 12:11:04 +10:00
md_5
32a5271dc3 Implement basic udp query to close #185 2013-09-24 10:09:55 +10:00
md_5
14389eb370 Use correct main class 2013-09-23 10:30:41 +10:00
md_5
a8b6a6b4aa Finish basic Yaml configuration API, complete with unit tests. Needs a lot of work with regards to how sections are handled, open to massive improvements from anyone that has more know-how. 2013-09-23 10:28:30 +10:00
md_5
8133304cce Use = for properties 2013-09-22 17:37:06 +10:00
md_5
3e8c21a485 Add specific exception for bad packets. 2013-09-21 16:57:17 +10:00
Matty Southall
f12dcc72d9 Fix compile error when compiling on OS X 2013-09-21 10:55:39 +10:00
md_5
a7a32509c7 find * -type f -print0 | xargs -0 sed -i 's/1.6.2/1.6.4/g' 2013-09-20 19:51:57 +10:00
md_5
703a393888 Procol -> Protocol. Fix typo, thanks @libraryaddict 2013-09-20 15:41:30 +10:00
md_5
3c961cd5d9 1.6.4 - MOJANG 2013-09-20 08:23:06 +10:00
md_5
12ee68a315 Update to 1.6.3 2013-09-19 17:41:01 +10:00
287 changed files with 15035 additions and 3781 deletions

4
.gitignore vendored
View File

@@ -34,4 +34,6 @@ manifest.mf
# other files # other files
*.log* *.log*
*.yml
# delombok
*/src/main/lombok

View File

@@ -1,5 +1,8 @@
sudo: false
language: java language: java
jdk: jdk:
- openjdk7 - openjdk7
- oraclejdk7
- oraclejdk8
notifications: notifications:
email: false email: false

View File

@@ -6,13 +6,13 @@
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.6.2-SNAPSHOT</version> <version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId> <artifactId>bungeecord-api</artifactId>
<version>1.6.2-SNAPSHOT</version> <version>1.8-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-API</name> <name>BungeeCord-API</name>
@@ -20,9 +20,15 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>net.md-5</groupId>
<artifactId>guava</artifactId> <artifactId>bungeecord-chat</artifactId>
<version>14.0.1</version> <version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
<version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -37,11 +43,5 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.12</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,7 +1,10 @@
package net.md_5.bungee; package net.md_5.bungee;
import com.google.common.base.Joiner;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.net.URI;
import java.net.URISyntaxException;
import java.util.UUID;
/** /**
* Series of utility classes to perform various operations. * Series of utility classes to perform various operations.
@@ -9,7 +12,7 @@ import java.util.Collection;
public class Util public class Util
{ {
private static final int DEFAULT_PORT = 25565; public static final int DEFAULT_PORT = 25565;
/** /**
* Method to transform human readable addresses into usable address objects. * Method to transform human readable addresses into usable address objects.
@@ -19,13 +22,16 @@ public class Util
*/ */
public static InetSocketAddress getAddr(String hostline) public static InetSocketAddress getAddr(String hostline)
{ {
String[] split = hostline.split( ":" ); URI uri;
int port = DEFAULT_PORT; try
if ( split.length > 1 )
{ {
port = Integer.parseInt( split[1] ); uri = new URI( "tcp://" + hostline );
} catch ( URISyntaxException ex )
{
throw new IllegalArgumentException( "Bad hostline", ex );
} }
return new InetSocketAddress( split[0], port );
return new InetSocketAddress( uri.getHost(), ( uri.getPort() ) == -1 ? DEFAULT_PORT : uri.getPort() );
} }
/** /**
@@ -54,20 +60,24 @@ public class Util
+ ( ( trace.length > 0 ) ? " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber() : "" ); + ( ( trace.length > 0 ) ? " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber() : "" );
} }
public static String csv(Collection<?> objects) public static String csv(Iterable<?> objects)
{ {
return format( objects, ", " ); return format( objects, ", " );
} }
public static String format(Collection<?> objects, String separators) public static String format(Iterable<?> objects, String separators)
{ {
StringBuilder ret = new StringBuilder(); return Joiner.on( separators ).join( objects );
for ( Object o : objects ) }
{
ret.append( o );
ret.append( separators );
}
return ( ret.length() == 0 ) ? "" : ret.substring( 0, ret.length() - separators.length() ); /**
* Converts a String to a UUID
*
* @param uuid The string to be converted
* @return The result
*/
public static UUID getUUID(String uuid)
{
return UUID.fromString( uuid.substring( 0, 8 ) + "-" + uuid.substring( 8, 12 ) + "-" + uuid.substring( 12, 16 ) + "-" + uuid.substring( 16, 20 ) + "-" + uuid.substring( 20, 32 ) );
} }
} }

View File

@@ -1,5 +1,7 @@
package net.md_5.bungee.api; package net.md_5.bungee.api;
import net.md_5.bungee.api.chat.BaseComponent;
import java.util.Collection; import java.util.Collection;
public interface CommandSender public interface CommandSender
@@ -17,6 +19,7 @@ public interface CommandSender
* *
* @param message the message to send * @param message the message to send
*/ */
@Deprecated
public void sendMessage(String message); public void sendMessage(String message);
/** /**
@@ -25,8 +28,23 @@ public interface CommandSender
* *
* @param messages the messages to send * @param messages the messages to send
*/ */
@Deprecated
public void sendMessages(String... messages); public void sendMessages(String... messages);
/**
* Send a message to this sender.
*
* @param message the message to send
*/
public void sendMessage(BaseComponent... message);
/**
* Send a message to this sender.
*
* @param message the message to send
*/
public void sendMessage(BaseComponent message);
/** /**
* Get all groups this user is part of. This returns an unmodifiable * Get all groups this user is part of. This returns an unmodifiable
* collection. * collection.
@@ -64,4 +82,12 @@ public interface CommandSender
* @param value the value of the node * @param value the value of the node
*/ */
public void setPermission(String permission, boolean value); public void setPermission(String permission, boolean value);
/**
* Get all Permissions which this CommandSender has
*
* @return a unmodifiable Collection of Strings which represent their
* permissions
*/
public Collection<String> getPermissions();
} }

View File

@@ -0,0 +1,106 @@
package net.md_5.bungee.api;
import com.google.common.io.BaseEncoding;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Favicon shown in the server list.
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Favicon
{
private static final TypeAdapter<Favicon> FAVICON_TYPE_ADAPTER = new TypeAdapter<Favicon>()
{
@Override
public void write(JsonWriter out, Favicon value) throws IOException
{
TypeAdapters.STRING.write( out, value == null ? null : value.getEncoded() );
}
@Override
public Favicon read(JsonReader in) throws IOException
{
String enc = TypeAdapters.STRING.read( in );
return enc == null ? null : create( enc );
}
};
public static TypeAdapter<Favicon> getFaviconTypeAdapter()
{
return FAVICON_TYPE_ADAPTER;
}
/**
* The base64 encoded favicon, including MIME header.
*/
@NonNull
@Getter
private final String encoded;
/**
* Creates a favicon from an image.
*
* @param image the image to create on
* @return the created favicon instance
* @throws IllegalArgumentException if the favicon is larger than
* {@link Short#MAX_VALUE} or not of dimensions 64x64 pixels.
*/
public static Favicon create(BufferedImage image)
{
// check size
if ( image.getWidth() != 64 || image.getHeight() != 64 )
{
throw new IllegalArgumentException( "Server icon must be exactly 64x64 pixels" );
}
// dump image PNG
byte[] imageBytes;
try
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write( image, "PNG", stream );
imageBytes = stream.toByteArray();
} catch ( IOException e )
{
// ByteArrayOutputStream should never throw this
throw new AssertionError( e );
}
// encode with header
String encoded = "data:image/png;base64," + BaseEncoding.base64().encode( imageBytes );
// check encoded image size
if ( encoded.length() > Short.MAX_VALUE )
{
throw new IllegalArgumentException( "Favicon file too large for server to process" );
}
// create
return new Favicon( encoded );
}
/**
* Creates a Favicon from an encoded PNG.
*
* @param encodedString a base64 mime encoded PNG string
* @return the created favicon
* @deprecated Use #create(java.awt.image.BufferedImage) instead
*/
@Deprecated
public static Favicon create(String encodedString)
{
return new Favicon( encodedString );
}
}

View File

@@ -0,0 +1,82 @@
package net.md_5.bungee.api;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo;
import java.util.Collection;
import java.util.Map;
/**
* Core configuration adaptor for the proxy api.
*
* @deprecated This class is subject to rapid change between releases
*/
@Deprecated
public interface ProxyConfig
{
/**
* Time before users are disconnected due to no network activity.
*/
int getTimeout();
/**
* UUID used for metrics.
*/
String getUuid();
/**
* Set of all listeners.
*/
Collection<ListenerInfo> getListeners();
/**
* Set of all servers.
*/
Map<String, ServerInfo> getServers();
/**
* Does the server authenticate with mojang
*/
boolean isOnlineMode();
/**
* Whether proxy commands are logged to the proxy log
*/
boolean isLogCommands();
/**
* Returns the player max.
*/
int getPlayerLimit();
/**
* A collection of disabled commands.
*/
Collection<String> getDisabledCommands();
/**
* The connection throttle delay.
*/
@Deprecated
int getThrottle();
/**
* Whether the proxy will parse IPs with spigot or not
*/
@Deprecated
boolean isIpForward();
/**
* The encoded favicon.
*
* @deprecated Use #getFaviconObject instead.
*/
@Deprecated
String getFavicon();
/**
* The favicon used for the server ping list.
*/
Favicon getFaviconObject();
}

View File

@@ -1,11 +1,13 @@
package net.md_5.bungee.api; package net.md_5.bungee.api;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.plugin.PluginManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.io.File; import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger; import java.util.logging.Logger;
import lombok.Getter; import lombok.Getter;
import net.md_5.bungee.api.config.ConfigurationAdapter; import net.md_5.bungee.api.config.ConfigurationAdapter;
@@ -13,7 +15,6 @@ 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.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
{ {
@@ -53,7 +54,7 @@ public abstract class ProxyServer
* *
* @return the localized string * @return the localized string
*/ */
public abstract String getTranslation(String name); public abstract String getTranslation(String name, Object... args);
/** /**
* 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
@@ -78,6 +79,14 @@ public abstract class ProxyServer
*/ */
public abstract ProxiedPlayer getPlayer(String name); public abstract ProxiedPlayer getPlayer(String name);
/**
* Gets a connected player via their UUID
*
* @param uuid of the player
* @return their player instance
*/
public abstract ProxiedPlayer getPlayer(UUID uuid);
/** /**
* 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
@@ -138,6 +147,13 @@ public abstract class ProxyServer
*/ */
public abstract void stop(); public abstract void stop();
/**
* Gracefully mark this instance for shutdown.
*
* @param reason the reason for stopping. This will be shown to players.
*/
public abstract void stop(String reason);
/** /**
* Start this instance so that it may accept connections. * Start this instance so that it may accept connections.
* *
@@ -173,6 +189,7 @@ public abstract class ProxyServer
* *
* @return the supported Minecraft version * @return the supported Minecraft version
*/ */
@Deprecated
public abstract String getGameVersion(); public abstract String getGameVersion();
/** /**
@@ -180,7 +197,8 @@ public abstract class ProxyServer
* *
* @return the Minecraft protocol version * @return the Minecraft protocol version
*/ */
public abstract byte getProtocolVersion(); @Deprecated
public abstract int getProtocolVersion();
/** /**
* Factory method to construct an implementation specific server info * Factory method to construct an implementation specific server info
@@ -231,15 +249,22 @@ public abstract class ProxyServer
* *
* @param message the message to broadcast * @param message the message to broadcast
*/ */
@Deprecated
public abstract void broadcast(String message); public abstract void broadcast(String message);
/** /**
* Gets a new instance of this proxies custom tab list. * Send the specified message to the console and all connected players.
* *
* @param player the player to generate this list in the context of * @param message the message to broadcast
* @return a new {@link CustomTabList} instance
*/ */
public abstract CustomTabList customTabList(ProxiedPlayer player); public abstract void broadcast(BaseComponent... message);
/**
* Send the specified message to the console and all connected players.
*
* @param message the message to broadcast
*/
public abstract void broadcast(BaseComponent message);
/** /**
* Gets the commands which are disabled and will not be run on this proxy. * Gets the commands which are disabled and will not be run on this proxy.
@@ -247,4 +272,36 @@ public abstract class ProxyServer
* @return the set of disabled commands * @return the set of disabled commands
*/ */
public abstract Collection<String> getDisabledCommands(); public abstract Collection<String> getDisabledCommands();
/**
* Gets BungeeCord's core config.
*
* @return the config.
*/
public abstract ProxyConfig getConfig();
/**
* Attempts to match any players with the given name, and returns a list of
* all possible matches.
*
* The exact algorithm to use to match players is implementation specific,
* but in general you can expect this method to return player's whose names
* begin with the specified prefix.
*
* @param match the (partial) name to match
* @return list of all possible players, singleton if there is an exact
* match
*/
public abstract Collection<ProxiedPlayer> matchPlayer(String match);
/**
* Creates a new empty title configuration. In most cases you will want to
* {@link Title#reset()} the current title first so your title won't be
* affected by a previous one.
*
* @return A new empty title configuration.
* @see Title
*/
public abstract Title createTitle();
} }

View File

@@ -1,33 +1,127 @@
package net.md_5.bungee.api; package net.md_5.bungee.api;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import net.md_5.bungee.Util;
/** /**
* Represents the standard list data returned by opening a server in the * Represents the standard list data returned by opening a server in the
* Minecraft client server list, or hitting it with a packet 0xFE. * Minecraft client server list, or hitting it with a packet 0xFE.
*/ */
@Data @Data
@NoArgsConstructor
@AllArgsConstructor
public class ServerPing public class ServerPing
{ {
/** private Protocol version;
* Numeric protocol version supported by the server.
*/ @Data
private final byte protocolVersion; @AllArgsConstructor
/** public static class Protocol
* Human readable game version. {
*/
private final String gameVersion; private String name;
/** private int protocol;
* Server MOTD. }
*/ private Players players;
private final String motd;
/** @Data
* Current amount of players on the server. @AllArgsConstructor
*/ public static class Players
private final int currentPlayers; {
/**
* Max amount of players the server will allow. private int max;
*/ private int online;
private final int maxPlayers; private PlayerInfo[] sample;
}
@Data
@AllArgsConstructor
public static class PlayerInfo
{
private String name;
private UUID uniqueId;
private static final UUID md5UUID = Util.getUUID( "af74a02d19cb445bb07f6866a861f783" );
public PlayerInfo(String name, String id)
{
setName( name );
setId( id );
}
public void setId(String id)
{
try
{
uniqueId = Util.getUUID( id );
} catch ( Exception e )
{
// Fallback on a valid uuid otherwise Minecraft complains
uniqueId = md5UUID;
}
}
public String getId()
{
return uniqueId.toString().replaceAll( "-", "" );
}
}
private String description;
private Favicon favicon;
@Data
public static class ModInfo
{
private String type = "FML";
private List<ModItem> modList = new ArrayList<>();
}
@Data
@AllArgsConstructor
public static class ModItem
{
private String modid;
private String version;
}
// Right now, we don't get the mods from the user, so we just use a stock ModInfo object to
// create the server ping. Vanilla clients will ignore this.
private final ModInfo modinfo = new ModInfo();
@Deprecated
public ServerPing(Protocol version, Players players, String description, String favicon)
{
this( version, players, description, favicon == null ? null : Favicon.create( favicon ) );
}
@Deprecated
public String getFavicon()
{
return getFaviconObject() == null ? null : getFaviconObject().getEncoded();
}
public Favicon getFaviconObject()
{
return this.favicon;
}
@Deprecated
public void setFavicon(String favicon)
{
setFavicon( favicon == null ? null : Favicon.create( favicon ) );
}
public void setFavicon(Favicon favicon)
{
this.favicon = favicon;
}
} }

View File

@@ -0,0 +1,106 @@
package net.md_5.bungee.api;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Represents a configuration of a title. A title in Minecraft consists of a
* main title and a sub title. It will {@link #fadeIn(int)}, {@link #stay(int)},
* and {@link #fadeOut(int)} for a specified amount of time. In most cases you
* will want to {@link #reset()} the current title first so your title won't be
* affected by a previous one.
* <p>
* You can create a new configuration by calling
* {@link ProxyServer#createTitle()}.
*/
public interface Title
{
/**
* Set the title to send to the player.
*
* @param text The text to use as the title.
* @return This title configuration.
*/
public Title title(BaseComponent text);
/**
* Set the title to send to the player.
*
* @param text The text to use as the title.
* @return This title configuration.
*/
public Title title(BaseComponent... text);
/**
* Set the subtitle to send to the player.
*
* @param text The text to use as the subtitle.
* @return This title configuration.
*/
public Title subTitle(BaseComponent text);
/**
* Set the subtitle to send to the player.
*
* @param text The text to use as the subtitle.
* @return This title configuration.
*/
public Title subTitle(BaseComponent... text);
/**
* Set the duration in ticks of the fade in effect of the title. Once this
* period of time is over the title will stay for the amount of time
* specified in {@link #stay(int)}. The default value for the official
* Minecraft version is 20 (1 second).
*
* @param ticks The amount of ticks (1/20 second) for the fade in effect.
* @return This title configuration.
*/
public Title fadeIn(int ticks);
/**
* Set the duration in ticks how long the title should stay on the screen.
* Once this period of time is over the title will fade out using the
* duration specified in {@link #fadeOut(int)}. The default value for the
* official Minecraft version is 60 (3 seconds).
*
* @param ticks The amount of ticks (1/20 second) for the fade in effect.
* @return This title configuration.
*/
public Title stay(int ticks);
/**
* Set the duration in ticks of the fade out effect of the title. The
* default value for the official Minecraft version is 20 (1 second).
*
* @param ticks The amount of ticks (1/20 second) for the fade out effect.
* @return This title configuration.
*/
public Title fadeOut(int ticks);
/**
* Remove the currently displayed title from the player's screen. This will
* keep the currently used display times and will only remove the title.
*
* @return This title configuration.
*/
public Title clear();
/**
* Remove the currently displayed title from the player's screen and set the
* configuration back to the default values.
*
* @return This title configuration.
*/
public Title reset();
/**
* Send this title configuration to the specified player. This is the same
* as calling {@link ProxiedPlayer#sendTitle(Title)}.
*
* @param player The player to send the title to.
* @return This title configuration.
*/
public Title send(ProxiedPlayer player);
}

View File

@@ -3,7 +3,6 @@ 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
@@ -49,9 +48,9 @@ public class ListenerInfo
*/ */
private final Map<String, String> forcedHosts; private final Map<String, String> forcedHosts;
/** /**
* Class used to build tab lists for this player. * The type of tab list to use
*/ */
private final Class<? extends TabListHandler> tabList; private final String tabListType;
/** /**
* Whether to set the local address when connecting to servers. * Whether to set the local address when connecting to servers.
*/ */
@@ -61,4 +60,12 @@ public class ListenerInfo
* server (force default server). * server (force default server).
*/ */
private final boolean pingPassthrough; private final boolean pingPassthrough;
/**
* What port to run udp query on.
*/
private final int queryPort;
/**
* Whether to enable udp query.
*/
private final boolean queryEnabled;
} }

View File

@@ -53,13 +53,27 @@ public interface ServerInfo
boolean canAccess(CommandSender sender); boolean canAccess(CommandSender sender);
/** /**
* Send data by any available means to this server. * Send data by any available means to this server. This data may be queued
* and there is no guarantee of its timely arrival.
* *
* @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
*/ */
void sendData(String channel, byte[] data); void sendData(String channel, byte[] data);
/**
* Send data by any available means to this server.
*
* @param channel the channel to send this data via
* @param data the data to send
* @param queue hold the message for later sending if it cannot be sent
* immediately.
* @return <code>true</code> if the message was sent immediately,
* <code>false</code> otherwise if queue is true, it has been queued, if it
* is false it has been discarded.
*/
boolean sendData(String channel, byte[] data, boolean queue);
/** /**
* Asynchronously gets the current player count on this server. * Asynchronously gets the current player count on this server.
* *

View File

@@ -1,7 +1,9 @@
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;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.protocol.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.
@@ -26,8 +28,37 @@ 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
*/ */
@Deprecated
void disconnect(String reason); void disconnect(String reason);
/**
* Disconnects this end of the connection for the specified reason. If this
* is an {@link ProxiedPlayer} the respective server connection will be
* closed too.
*
* @param reason the reason shown to the player / sent to the server on
* disconnect
*/
void disconnect(BaseComponent... reason);
/**
* Disconnects this end of the connection for the specified reason. If this
* is an {@link ProxiedPlayer} the respective server connection will be
* closed too.
*
* @param reason the reason shown to the player / sent to the server on
* disconnect
*/
void disconnect(BaseComponent reason);
/**
* Gets whether this connection is currently open, ie: not disconnected, and
* able to send / receive data.
*
* @return current connection status
*/
boolean isConnected();
/** /**
* Get the unsafe methods of this class. * Get the unsafe methods of this class.
* *

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 java.util.UUID;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
/** /**
@@ -14,26 +15,67 @@ public interface PendingConnection extends Connection
* *
* @return the requested username, or null if not set * @return the requested username, or null if not set
*/ */
public String getName(); String getName();
/** /**
* Get the numerical client version of the player attempting to log in. * Get the numerical client version of the player attempting to log in.
* *
* @return the protocol version of the remote client * @return the protocol version of the remote client
*/ */
public byte getVersion(); int getVersion();
/** /**
* Get the requested virtual host that the client tried to connect to. * Get the requested virtual host that the client tried to connect to.
* *
* @return request virtual host or null if invalid / not specified. * @return request virtual host or null if invalid / not specified.
*/ */
public InetSocketAddress getVirtualHost(); InetSocketAddress getVirtualHost();
/** /**
* Get the listener that accepted this connection. * Get the listener that accepted this connection.
* *
* @return the accepting listener * @return the accepting listener
*/ */
public ListenerInfo getListener(); ListenerInfo getListener();
/**
* Get this connection's UUID, if set.
*
* @return the UUID
* @deprecated In favour of {@link #getUniqueId()}
*/
@Deprecated
String getUUID();
/**
* Get this connection's UUID, if set.
*
* @return the UUID
*/
UUID getUniqueId();
/**
* Set the connection's uuid
*/
void setUniqueId(UUID uuid);
/**
* Get this connection's online mode.
*
* @return the online mode
*/
boolean isOnlineMode();
/**
* Set this connection's online mode.
*/
void setOnlineMode(boolean onlineMode);
/**
* Check if the client is using the older unsupported Minecraft protocol
* used by Minecraft clients older than 1.7.
*
* @return Whether the client is using a legacy client.
*/
boolean isLegacy();
} }

View File

@@ -1,8 +1,14 @@
package net.md_5.bungee.api.connection; package net.md_5.bungee.api.connection;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.Title;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.tab.TabListHandler;
/** /**
* Represents a player who's connection is being connected to somewhere else, * Represents a player who's connection is being connected to somewhere else,
@@ -26,6 +32,22 @@ public interface ProxiedPlayer extends Connection, CommandSender
*/ */
void setDisplayName(String name); void setDisplayName(String name);
/**
* Send a message to the specified screen position of this player.
*
* @param position the screen position
* @param message the message to send
*/
public void sendMessage(ChatMessageType position, BaseComponent... message);
/**
* Send a message to the specified screen position of this player.
*
* @param position the screen position
* @param message the message to send
*/
public void sendMessage(ChatMessageType position, BaseComponent message);
/** /**
* Connects / transfers this user to the specified connection, gracefully * Connects / transfers this user to the specified connection, gracefully
* closing the current one. Depending on the implementation, this method * closing the current one. Depending on the implementation, this method
@@ -35,6 +57,18 @@ public interface ProxiedPlayer extends Connection, CommandSender
*/ */
void connect(ServerInfo target); void connect(ServerInfo target);
/**
* Connects / transfers this user to the specified connection, gracefully
* closing the current one. Depending on the implementation, this method
* might return before the user has been connected.
*
* @param target the new server to connect to
* @param callback the method called when the connection is complete, or
* when an exception is encountered. The boolean parameter denotes success
* or failure.
*/
void connect(ServerInfo target, Callback<Boolean> callback);
/** /**
* Gets the server this player is connected to. * Gets the server this player is connected to.
* *
@@ -71,21 +105,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
*/ */
void chat(String message); void chat(String message);
/**
* Sets the new tab list for the user. At this stage it is not advisable to
* change after the user has logged in!
*
* @param list the new list
*/
void setTabList(TabListHandler list);
/**
* Get the current tab list.
*
* @return the tab list in use by this user
*/
TabListHandler getTabList();
/** /**
* Get the server which this player will be sent to next time the log in. * Get the server which this player will be sent to next time the log in.
* *
@@ -99,4 +118,97 @@ public interface ProxiedPlayer extends Connection, CommandSender
* @param server the server to set * @param server the server to set
*/ */
void setReconnectServer(ServerInfo server); void setReconnectServer(ServerInfo server);
/**
* Get this connection's UUID, if set.
*
* @return the UUID
* @deprecated In favour of {@link #getUniqueId()}
*/
@Deprecated
String getUUID();
/**
* Get this connection's UUID, if set.
*
* @return the UUID
*/
UUID getUniqueId();
/**
* Gets this player's locale.
*
* @return the locale
*/
Locale getLocale();
/**
* Set the header and footer displayed in the tab player list.
*
* @param header The header for the tab player list, null to clear it.
* @param footer The footer for the tab player list, null to clear it.
*/
void setTabHeader(BaseComponent header, BaseComponent footer);
/**
* Set the header and footer displayed in the tab player list.
*
* @param header The header for the tab player list, null to clear it.
* @param footer The footer for the tab player list, null to clear it.
*/
void setTabHeader(BaseComponent[] header, BaseComponent[] footer);
/**
* Clears the header and footer displayed in the tab player list.
*/
void resetTabHeader();
/**
* Sends a {@link Title} to this player. This is the same as calling
* {@link Title#send(ProxiedPlayer)}.
*
* @param title The title to send to the player.
* @see Title
*/
void sendTitle(Title title);
/**
* Gets whether this player is using a FML client.
* <p>
* This method is only reliable if BungeeCord links Minecraft 1.8 servers
* together, as Bungee can pick up whether a user is a Forge user with the
* initial handshake. If this is used for a 1.7 network, this might return
* <code>false</code> even if the user is a FML user, as Bungee can only
* determine this information if a handshake successfully completes.
* </p>
*
* @return <code>true</code> if it is known that the user is using a FML
* client, <code>false</code> otherwise.
*/
boolean isForgeUser();
/**
* Gets this player's Forge Mod List, if the player has sent this
* information during the lifetime of their connection to Bungee. There is
* no guarantee that information is available at any time, as it is only
* sent during a FML handshake. Therefore, this will only contain
* information for a user that has attempted joined a Forge server.
* <p>
* Consumers of this API should be aware that an empty mod list does
* <em>not</em> indicate that a user is not a Forge user, and so should not
* use this API to check for this. See the {@link #isForgeUser()
* isForgeUser} method instead.
* </p>
* <p>
* Calling this when handling a
* {@link net.md_5.bungee.api.event.ServerConnectedEvent} may be the best
* place to do so as this event occurs after a FML handshake has completed,
* if any has occurred.
* </p>
*
* @return A {@link Map} of mods, where the key is the name of the mod, and
* the value is the version. Returns an empty list if the FML handshake has
* not occurred for this {@link ProxiedPlayer} yet.
*/
Map<String, String> getModList();
} }

View File

@@ -33,11 +33,11 @@ public class AsyncEvent<T> extends Event
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void postCall() public void postCall()
{ {
fired.set( true );
if ( latch.get() == 0 ) if ( latch.get() == 0 )
{ {
done.done( (T) this, null ); done.done( (T) this, null );
} }
fired.set( true );
} }
/** /**
@@ -67,9 +67,14 @@ 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 && fired.get() ) if ( fired.get() )
{ {
done.done( (T) this, null ); if ( latch.decrementAndGet() == 0 )
{
done.done( (T) this, null );
}
} else {
latch.decrementAndGet();
} }
} }
} }

View File

@@ -7,8 +7,7 @@ import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Cancellable; import net.md_5.bungee.api.plugin.Cancellable;
/** /**
* Event called when a player sends a message to a server, or a server sends a * Event called when a player sends a message to a server.
* message to a player.
*/ */
@Data @Data
@ToString(callSuper = true) @ToString(callSuper = true)

View File

@@ -0,0 +1,34 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.plugin.Event;
import net.md_5.bungee.protocol.packet.Handshake;
/**
* Event called to represent a player first making their presence and username
* known.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class PlayerHandshakeEvent extends Event
{
/**
* Connection attempting to login.
*/
private final PendingConnection connection;
/**
* The handshake.
*/
private final Handshake handshake;
public PlayerHandshakeEvent(PendingConnection connection, Handshake handshake)
{
this.connection = connection;
this.handshake = handshake;
}
}

View File

@@ -7,8 +7,8 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
/** /**
* Event called as soon as a connection has an {@link ProxiedPlayer} and is * Event called as soon as a connection has a {@link ProxiedPlayer} and is ready
* ready to be connected to a server. * to be connected to a server.
*/ */
@Data @Data
@ToString(callSuper = false) @ToString(callSuper = false)

View File

@@ -0,0 +1,42 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.plugin.Cancellable;
/**
* Event called to represent a player first making their presence and username
* known.
*
* This will NOT contain many attributes relating to the player which are filled
* in after authentication with Mojang's servers. Examples of attributes which
* are not available include their UUID.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class PreLoginEvent extends AsyncEvent<PreLoginEvent> implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* Message to use when kicking if this event is canceled.
*/
private String cancelReason;
/**
* Connection attempting to login.
*/
private final PendingConnection connection;
public PreLoginEvent(PendingConnection connection, Callback<PreLoginEvent> done)
{
super( done );
this.connection = connection;
}
}

View File

@@ -1,9 +1,9 @@
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.Callback;
import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
@@ -12,10 +12,9 @@ import net.md_5.bungee.api.plugin.Event;
* Called when the proxy is pinged with packet 0xFE from the server list. * Called when the proxy is pinged with packet 0xFE from the server list.
*/ */
@Data @Data
@AllArgsConstructor
@ToString(callSuper = false) @ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class ProxyPingEvent extends Event public class ProxyPingEvent extends AsyncEvent<ProxyPingEvent>
{ {
/** /**
@@ -26,4 +25,11 @@ public class ProxyPingEvent extends Event
* The data to respond with. * The data to respond with.
*/ */
private ServerPing response; private ServerPing response;
public ProxyPingEvent(PendingConnection connection, ServerPing response, Callback<ProxyPingEvent> done)
{
super( done );
this.connection = connection;
this.response = response;
}
} }

View File

@@ -0,0 +1,22 @@
package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.EqualsAndHashCode;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Event;
/**
* Called when somebody reloads BungeeCord
*/
@Getter
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ProxyReloadEvent extends Event
{
/**
* Creator of the action.
*/
private final CommandSender sender;
}

View File

@@ -2,12 +2,19 @@ package net.md_5.bungee.api.event;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NonNull;
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.Cancellable;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
/**
* Called when deciding to connect to a server. At the time when this event is
* called, no connection has actually been made. Cancelling the event will
* ensure that the connection does not proceed and can be useful to prevent
* certain players from accessing certain servers.
*/
@Data @Data
@ToString(callSuper = false) @ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@@ -21,6 +28,7 @@ public class ServerConnectEvent extends Event implements Cancellable
/** /**
* Server the player will be connected to. * Server the player will be connected to.
*/ */
@NonNull
private ServerInfo target; private ServerInfo target;
/** /**
* Cancelled state. * Cancelled state.

View File

@@ -0,0 +1,29 @@
package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
@Data
@AllArgsConstructor
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
public class ServerDisconnectEvent extends Event
{
/**
* Player disconnecting from a server.
*/
@NonNull
private final ProxiedPlayer player;
/**
* Server the player is disconnecting from.
*/
@NonNull
private final ServerInfo target;
}

View File

@@ -3,6 +3,8 @@ package net.md_5.bungee.api.event;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
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.Cancellable;
@@ -25,10 +27,15 @@ public class ServerKickEvent extends Event implements Cancellable
* Player being kicked. * Player being kicked.
*/ */
private final ProxiedPlayer player; private final ProxiedPlayer player;
/**
* The server the player was kicked from, should be used in preference to
* {@link ProxiedPlayer#getServer()}.
*/
private final ServerInfo kickedFrom;
/** /**
* Kick reason. * Kick reason.
*/ */
private String kickReason; private BaseComponent[] kickReasonComponent;
/** /**
* Server to send player to if this event is cancelled. * Server to send player to if this event is cancelled.
*/ */
@@ -44,16 +51,36 @@ public class ServerKickEvent extends Event implements Cancellable
CONNECTING, CONNECTED, UNKNOWN; CONNECTING, CONNECTED, UNKNOWN;
} }
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer) @Deprecated
public ServerKickEvent(ProxiedPlayer player, BaseComponent[] kickReasonComponent, ServerInfo cancelServer)
{ {
this( player, kickReason, cancelServer, State.UNKNOWN ); this( player, kickReasonComponent, cancelServer, State.UNKNOWN );
} }
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer, State state) @Deprecated
public ServerKickEvent(ProxiedPlayer player, BaseComponent[] kickReasonComponent, ServerInfo cancelServer, State state)
{
this( player, player.getServer().getInfo(), kickReasonComponent, cancelServer, state );
}
public ServerKickEvent(ProxiedPlayer player, ServerInfo kickedFrom, BaseComponent[] kickReasonComponent, ServerInfo cancelServer, State state)
{ {
this.player = player; this.player = player;
this.kickReason = kickReason; this.kickedFrom = kickedFrom;
this.kickReasonComponent = kickReasonComponent;
this.cancelServer = cancelServer; this.cancelServer = cancelServer;
this.state = state; this.state = state;
} }
@Deprecated
public String getKickReason()
{
return BaseComponent.toLegacyText( kickReasonComponent );
}
@Deprecated
public void setKickReason(String reason)
{
kickReasonComponent = TextComponent.fromLegacyText( reason );
}
} }

View File

@@ -0,0 +1,39 @@
package net.md_5.bungee.api.event;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Cancellable;
/**
* Event called when a player uses tab completion.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class TabCompleteEvent extends TargetedEvent implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* The message the player has already entered.
*/
private final String cursor;
/**
* The suggestions that will be sent to the client. This list is mutable. If
* this list is empty, the request will be forwarded to the server.
*/
private final List<String> suggestions;
public TabCompleteEvent(Connection sender, Connection receiver, String cursor, List<String> suggestions)
{
super( sender, receiver );
this.cursor = cursor;
this.suggestions = suggestions;
}
}

View File

@@ -0,0 +1,38 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Cancellable;
import java.util.List;
/**
* Event called when a backend server sends a response to a player asking to
* tab-complete a chat message or command. Note that this is not called when
* BungeeCord or a plugin responds to a tab-complete request. Use
* {@link TabCompleteEvent} for that.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class TabCompleteResponseEvent extends TargetedEvent implements Cancellable
{
/**
* Whether the event is cancelled.
*/
private boolean cancelled;
/**
* Mutable list of suggestions sent back to the player. If this list is
* empty, an empty list is sent back to the client.
*/
private final List<String> suggestions;
public TabCompleteResponseEvent(Connection sender, Connection receiver, List<String> suggestions)
{
super( sender, receiver );
this.suggestions = suggestions;
}
}

View File

@@ -1,11 +1,15 @@
package net.md_5.bungee.api.plugin; package net.md_5.bungee.api.plugin;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger; 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;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
/** /**
* Represents any Plugin that may be loaded at runtime to enhance existing * Represents any Plugin that may be loaded at runtime to enhance existing
@@ -83,4 +87,20 @@ public class Plugin
this.file = description.getFile(); this.file = description.getFile();
this.logger = new PluginLogger( this ); this.logger = new PluginLogger( this );
} }
//
private ExecutorService service;
@Deprecated
public ExecutorService getExecutorService()
{
if ( service == null )
{
String name = ( getDescription() == null ) ? "unknown" : getDescription().getName();
service = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNameFormat( name + " Pool Thread #%1$d" )
.setThreadFactory( new GroupedThreadFactory( this, name ) ).build() );
}
return service;
}
//
} }

View File

@@ -36,8 +36,16 @@ public class PluginDescription
* Plugin hard dependencies. * Plugin hard dependencies.
*/ */
private Set<String> depends = new HashSet<>(); private Set<String> depends = new HashSet<>();
/**
* Plugin soft dependencies.
*/
private Set<String> softDepends = new HashSet<>();
/** /**
* File we were loaded from. * File we were loaded from.
*/ */
private File file = null; private File file = null;
/**
* Optional description.
*/
private String description = null;
} }

View File

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

View File

@@ -12,10 +12,12 @@ 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.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@@ -25,9 +27,12 @@ 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.connection.ProxiedPlayer;
import net.md_5.bungee.event.EventBus; import net.md_5.bungee.event.EventBus;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.introspector.PropertyUtils;
/** /**
* Class to manage bridging between plugin duties and implementation duties, for * Class to manage bridging between plugin duties and implementation duties, for
@@ -41,18 +46,26 @@ public class PluginManager
/*========================================================================*/ /*========================================================================*/
private final ProxyServer proxy; private final ProxyServer proxy;
/*========================================================================*/ /*========================================================================*/
private final Yaml yaml = new Yaml(); private final Yaml yaml;
private final EventBus eventBus; private final EventBus eventBus;
private final Map<String, Plugin> plugins = new LinkedHashMap<>(); 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<>(); private Map<String, PluginDescription> toLoad = new HashMap<>();
private Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create(); private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create();
private Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create(); private final Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public PluginManager(ProxyServer proxy) public PluginManager(ProxyServer proxy)
{ {
this.proxy = proxy; this.proxy = proxy;
// Ignore unknown entries in the plugin descriptions
Constructor yamlConstructor = new Constructor();
PropertyUtils propertyUtils = yamlConstructor.getPropertyUtils();
propertyUtils.setSkipMissingProperties( true );
yamlConstructor.setPropertyUtils( propertyUtils );
yaml = new Yaml( yamlConstructor );
eventBus = new EventBus( proxy.getLogger() ); eventBus = new EventBus( proxy.getLogger() );
} }
@@ -79,7 +92,7 @@ public class PluginManager
*/ */
public void unregisterCommand(Command command) public void unregisterCommand(Command command)
{ {
commandMap.values().remove( command ); while ( commandMap.values().remove( command ) );
commandsByPlugin.values().remove( command ); commandsByPlugin.values().remove( command );
} }
@@ -92,7 +105,8 @@ public class PluginManager
{ {
for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); ) for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); )
{ {
commandMap.values().remove( it.next() ); Command command = it.next();
while ( commandMap.values().remove( command ) );
it.remove(); it.remove();
} }
} }
@@ -112,7 +126,7 @@ public class PluginManager
*/ */
public boolean dispatchCommand(CommandSender sender, String commandLine, List<String> tabResults) public boolean dispatchCommand(CommandSender sender, String commandLine, List<String> tabResults)
{ {
String[] split = argsSplit.split( commandLine ); String[] split = argsSplit.split( commandLine, -1 );
// Check for chat that only contains " " // Check for chat that only contains " "
if ( split.length == 0 ) if ( split.length == 0 )
{ {
@@ -120,7 +134,7 @@ public class PluginManager
} }
String commandName = split[0].toLowerCase(); String commandName = split[0].toLowerCase();
if ( proxy.getDisabledCommands().contains( commandName ) ) if ( sender instanceof ProxiedPlayer && proxy.getDisabledCommands().contains( commandName ) )
{ {
return false; return false;
} }
@@ -133,7 +147,10 @@ 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( proxy.getTranslation( "no_permission" ) ); if ( !( command instanceof TabExecutor ) || tabResults == null )
{
sender.sendMessage( proxy.getTranslation( "no_permission" ) );
}
return true; return true;
} }
@@ -142,8 +159,15 @@ public class PluginManager
{ {
if ( tabResults == null ) if ( tabResults == null )
{ {
if ( proxy.getConfig().isLogCommands() )
{
proxy.getLogger().log( Level.INFO, "{0} executed command: /{1}", new Object[]
{
sender.getName(), commandLine
} );
}
command.execute( sender, args ); command.execute( sender, args );
} else if ( command instanceof TabExecutor ) } else if ( commandLine.contains( " " ) && command instanceof TabExecutor )
{ {
for ( String s : ( (TabExecutor) command ).onTabComplete( sender, args ) ) for ( String s : ( (TabExecutor) command ).onTabComplete( sender, args ) )
{ {
@@ -179,7 +203,7 @@ public class PluginManager
return plugins.get( name ); return plugins.get( name );
} }
public void loadAndEnablePlugins() public void loadPlugins()
{ {
Map<PluginDescription, Boolean> pluginStatuses = new HashMap<>(); Map<PluginDescription, Boolean> pluginStatuses = new HashMap<>();
for ( Map.Entry<String, PluginDescription> entry : toLoad.entrySet() ) for ( Map.Entry<String, PluginDescription> entry : toLoad.entrySet() )
@@ -187,12 +211,15 @@ public class PluginManager
PluginDescription plugin = entry.getValue(); PluginDescription plugin = entry.getValue();
if ( !enablePlugin( pluginStatuses, new Stack<PluginDescription>(), plugin ) ) if ( !enablePlugin( pluginStatuses, new Stack<PluginDescription>(), plugin ) )
{ {
ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() ); ProxyServer.getInstance().getLogger().log( Level.WARNING, "Failed to enable {0}", entry.getKey() );
} }
} }
toLoad.clear(); toLoad.clear();
toLoad = null; toLoad = null;
}
public void enablePlugins()
{
for ( Plugin plugin : plugins.values() ) for ( Plugin plugin : plugins.values() )
{ {
try try
@@ -216,11 +243,16 @@ public class PluginManager
return pluginStatuses.get( plugin ); return pluginStatuses.get( plugin );
} }
// combine all dependencies for 'for loop'
Set<String> dependencies = new HashSet<>();
dependencies.addAll( plugin.getDepends() );
dependencies.addAll( plugin.getSoftDepends() );
// success status // success status
boolean status = true; boolean status = true;
// try to load dependencies first // try to load dependencies first
for ( String dependName : plugin.getDepends() ) for ( String dependName : dependencies )
{ {
PluginDescription depend = toLoad.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;
@@ -235,7 +267,7 @@ public class PluginManager
dependencyGraph.append( element.getName() ).append( " -> " ); dependencyGraph.append( element.getName() ).append( " -> " );
} }
dependencyGraph.append( plugin.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: {0}", dependencyGraph );
status = false; status = false;
} else } else
{ {
@@ -245,11 +277,11 @@ public class PluginManager
} }
} }
if ( dependStatus == Boolean.FALSE ) if ( dependStatus == Boolean.FALSE && plugin.getDepends().contains( dependName ) ) // only fail if this wasn't a soft dependency
{ {
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[]
{ {
String.valueOf( depend.getName() ), plugin.getName() String.valueOf( dependName ), plugin.getName()
} ); } );
status = false; status = false;
} }
@@ -305,8 +337,12 @@ public class PluginManager
{ {
try ( JarFile jar = new JarFile( file ) ) try ( JarFile jar = new JarFile( file ) )
{ {
JarEntry pdf = jar.getJarEntry( "plugin.yml" ); JarEntry pdf = jar.getJarEntry( "bungee.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" ); if ( pdf == null )
{
pdf = jar.getJarEntry( "plugin.yml" );
}
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml or bungee.yml" );
try ( InputStream in = jar.getInputStream( pdf ) ) try ( InputStream in = jar.getInputStream( pdf ) )
{ {
@@ -381,8 +417,6 @@ public class PluginManager
/** /**
* Unregister all of a Plugin's listener. * Unregister all of a Plugin's listener.
*
* @param plugin
*/ */
public void unregisterListeners(Plugin plugin) public void unregisterListeners(Plugin plugin)
{ {

View File

@@ -2,7 +2,6 @@ package net.md_5.bungee.api.plugin;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
public interface TabExecutor public interface TabExecutor
{ {

View File

@@ -0,0 +1,34 @@
package net.md_5.bungee.api.scheduler;
import java.util.concurrent.ThreadFactory;
import lombok.Data;
import net.md_5.bungee.api.plugin.Plugin;
@Data
@Deprecated
public class GroupedThreadFactory implements ThreadFactory
{
private final ThreadGroup group;
public static class BungeeGroup extends ThreadGroup
{
private BungeeGroup(String name)
{
super( name );
}
}
public GroupedThreadFactory(Plugin plugin, String name)
{
this.group = new BungeeGroup( name );
}
@Override
public Thread newThread(Runnable r)
{
return new Thread( group, r );
}
}

View File

@@ -1,5 +1,6 @@
package net.md_5.bungee.api.scheduler; package net.md_5.bungee.api.scheduler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
@@ -65,10 +66,28 @@ public interface TaskScheduler
* *
* @param owner the plugin owning this task * @param owner the plugin owning this task
* @param task the task to run * @param task the task to run
* @param delay the delay in milliseconds before this task will be executed * @param delay the delay before this task will be executed
* @param period the interval before subsequent executions of this task * @param period the interval before subsequent executions of this task
* @param unit the unit in which the delay and period will be measured * @param unit the unit in which the delay and period will be measured
* @return the scheduled task * @return the scheduled task
*/ */
ScheduledTask schedule(Plugin owner, Runnable task, long delay, long period, TimeUnit unit); ScheduledTask schedule(Plugin owner, Runnable task, long delay, long period, TimeUnit unit);
/**
* Get the unsafe methods of this class.
*
* @return the unsafe method interface
*/
Unsafe unsafe();
interface Unsafe
{
/**
* An executor service which underlies this scheduler.
*
* @return the underlying executor service or compatible wrapper
*/
ExecutorService getExecutorService(Plugin plugin);
}
} }

View File

@@ -1,11 +1,13 @@
package net.md_5.bungee.api.score; package net.md_5.bungee.api.score;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
/** /**
* Represents an objective entry. * Represents an objective entry.
*/ */
@Data @Data
@AllArgsConstructor
public class Objective public class Objective
{ {
@@ -16,5 +18,9 @@ public class Objective
/** /**
* Value of the objective. * Value of the objective.
*/ */
private final String value; // displayName private String value;
/**
* Type; integer or hearts
*/
private final String type;
} }

View File

@@ -6,5 +6,23 @@ package net.md_5.bungee.api.score;
public enum Position public enum Position
{ {
LIST, SIDEBAR, BELOW; LIST,
SIDEBAR,
BELOW,
SIDEBAR_BLACK,
SIDEBAR_DARK_BLUE,
SIDEBAR_DARK_GREEN,
SIDEBAR_DARK_AQUA,
SIDEBAR_DARK_RED,
SIDEBAR_DARK_PURPLE,
SIDEBAR_GOLD,
SIDEBAR_GRAY,
SIDEBAR_DARK_GRAY,
SIDEBAR_BLUE,
SIDEBAR_GREEN,
SIDEBAR_AQUA,
SIDEBAR_RED,
SIDEBAR_LIGHT_PURPLE,
SIDEBAR_YELLOW,
SIDEBAR_WHITE;
} }

View File

@@ -74,6 +74,11 @@ public class Scoreboard
return teams.get( name ); return teams.get( name );
} }
public Objective getObjective(String name)
{
return objectives.get( name );
}
public void removeObjective(String objectiveName) public void removeObjective(String objectiveName)
{ {
objectives.remove( objectiveName ); objectives.remove( objectiveName );

View File

@@ -16,7 +16,9 @@ public class Team
private String displayName; private String displayName;
private String prefix; private String prefix;
private String suffix; private String suffix;
private boolean friendlyFire; private byte friendlyFire;
private String nameTagVisibility;
private byte color;
private Set<String> players = new HashSet<>(); private Set<String> players = new HashSet<>();
public Collection<String> getPlayers() public Collection<String> getPlayers()

View File

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

@@ -1,39 +0,0 @@
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,55 +0,0 @@
package net.md_5.bungee.api.tab;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public interface TabListHandler
{
/**
* Called so that this class may set member fields to keep track of its
* internal state. You should not do any packet sending or manipulation of
* the passed player, other than storing it.
*
* @param player the player to be associated with this list
*/
void init(ProxiedPlayer player);
/**
* Called when this player first connects to the proxy.
*/
void onConnect();
/**
* Called when a player first connects to the proxy.
*
* @param player the connecting player
*/
void onServerChange();
/**
* Called when a players ping changes. The new ping will have not updated in
* the player instance until this method returns.
*
* @param player the player who's ping changed
* @param ping the player's new ping.
*/
void onPingChange(int ping);
/**
* Called when a player disconnects.
*
* @param player the disconnected player
*/
void onDisconnect();
/**
* Called when a list update packet is sent from server to client.
*
* @param player receiving this packet
* @param name the player which this packet is relevant to
* @param online whether the subject player is online
* @param ping ping of the subject player
* @return whether to send the packet to the client
*/
boolean onListUpdate(String name, boolean online, int ping);
}

View File

@@ -1,6 +1,7 @@
package net.md_5.bungee.command; package net.md_5.bungee.command;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
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;
@@ -8,6 +9,10 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor; import net.md_5.bungee.api.plugin.TabExecutor;
/**
* @deprecated internal use only
*/
@Deprecated
public abstract class PlayerCommand extends Command implements TabExecutor public abstract class PlayerCommand extends Command implements TabExecutor
{ {
@@ -24,12 +29,20 @@ public abstract class PlayerCommand extends Command implements TabExecutor
@Override @Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args) public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{ {
return Iterables.transform( ProxyServer.getInstance().getPlayers(), new Function<ProxiedPlayer, String>() final String lastArg = ( args.length > 0 ) ? args[args.length - 1].toLowerCase() : "";
return Iterables.transform( Iterables.filter( ProxyServer.getInstance().getPlayers(), new Predicate<ProxiedPlayer>()
{ {
@Override @Override
public String apply(ProxiedPlayer input) public boolean apply(ProxiedPlayer player)
{ {
return input.getDisplayName(); return player.getName().toLowerCase().startsWith( lastArg );
}
} ), new Function<ProxiedPlayer, String>()
{
@Override
public String apply(ProxiedPlayer player)
{
return player.getName();
} }
} ); } );
} }

View File

@@ -0,0 +1,61 @@
package net.md_5.bungee.util;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.Util;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RequiredArgsConstructor
@RunWith(Parameterized.class)
public class AddressParseTest
{
@Parameters
public static Collection<Object[]> data()
{
return Arrays.asList( new Object[][]
{
{
"127.0.0.1", "127.0.0.1", Util.DEFAULT_PORT
},
{
"127.0.0.1:1337", "127.0.0.1", 1337
},
{
"[::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT
},
{
"[0:0:0:0::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT
},
{
"[0:0:0:0:0:0:0:1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT
},
{
"[::1]:1337", "0:0:0:0:0:0:0:1", 1337
},
{
"[0:0:0:0::1]:1337", "0:0:0:0:0:0:0:1", 1337
},
{
"[0:0:0:0:0:0:0:1]:1337", "0:0:0:0:0:0:0:1", 1337
}
} );
}
private final String line;
private final String host;
private final int port;
@Test
public void test()
{
InetSocketAddress parsed = Util.getAddr( line );
Assert.assertEquals( host, parsed.getHostString() );
Assert.assertEquals( port, parsed.getPort() );
}
}

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>

94
bootstrap/pom.xml Normal file
View File

@@ -0,0 +1,94 @@
<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.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-bootstrap</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Bootstrap</name>
<description>Java 1.6 loader for BungeeCord</description>
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
</properties>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
<version>4.8</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>BungeeCord</finalName>
<plugins>
<plugin>
<!-- Don't deploy proxy to maven repo, only APIs -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>net.md_5.bungee.Bootstrap</Main-Class>
<Implementation-Version>${describe}</Implementation-Version>
<Specification-Version>${maven.build.timestamp}</Specification-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/*.java</exclude>
<exclude>**/*.SF</exclude>
<exclude>**/*.DSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,17 @@
package net.md_5.bungee;
public class Bootstrap
{
public static void main(String[] args) throws Exception
{
if ( Float.parseFloat( System.getProperty( "java.class.version" ) ) < 51.0 )
{
System.err.println( "*** ERROR *** BungeeCord requires Java 7 or above to function! Please download and install it!" );
System.out.println( "You can check your Java version with the command: java -version" );
return;
}
BungeeCordLauncher.main( args );
}
}

View File

@@ -0,0 +1,69 @@
package net.md_5.bungee;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.command.ConsoleCommandSender;
public class BungeeCordLauncher
{
public static void main(String[] args) throws Exception
{
Security.setProperty( "networkaddress.cache.ttl", "30" );
Security.setProperty( "networkaddress.cache.negative.ttl", "10" );
OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
parser.acceptsAll( Arrays.asList( "v", "version" ) );
parser.acceptsAll( Arrays.asList( "noconsole" ) );
OptionSet options = parser.parse( args );
if ( options.has( "version" ) )
{
System.out.println( Bootstrap.class.getPackage().getImplementationVersion() );
return;
}
if ( BungeeCord.class.getPackage().getSpecificationVersion() != null && System.getProperty( "IReallyKnowWhatIAmDoingISwear" ) == null )
{
Date buildDate = new SimpleDateFormat( "yyyyMMdd" ).parse( BungeeCord.class.getPackage().getSpecificationVersion() );
Calendar deadline = Calendar.getInstance();
deadline.add( Calendar.WEEK_OF_YEAR, -4 );
if ( buildDate.before( deadline.getTime() ) )
{
System.err.println( "*** Warning, this build is outdated ***" );
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
System.err.println( "*** You will get NO support regarding this build ***" );
System.err.println( "*** Server will start in 10 seconds ***" );
Thread.sleep( TimeUnit.SECONDS.toMillis( 10 ) );
}
}
BungeeCord bungee = new BungeeCord();
ProxyServer.setInstance( bungee );
bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
bungee.start();
if ( !options.has( "noconsole" ) )
{
String line;
while ( bungee.isRunning && ( line = bungee.getConsoleReader().readLine( ">" ) ) != null )
{
if ( !bungee.getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ) )
{
bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" );
}
}
}
}
}

31
chat/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>

34
chat/pom.xml Normal file
View File

@@ -0,0 +1,34 @@
<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.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Chat</name>
<description>Minecraft JSON chat API intended for use with BungeeCord</description>
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -3,6 +3,7 @@ package net.md_5.bungee.api;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Getter;
/** /**
* Simplistic enumeration of all supported color values for chat. * Simplistic enumeration of all supported color values for chat.
@@ -13,104 +14,105 @@ public enum ChatColor
/** /**
* Represents black. * Represents black.
*/ */
BLACK( '0' ), BLACK( '0', "black" ),
/** /**
* Represents dark blue. * Represents dark blue.
*/ */
DARK_BLUE( '1' ), DARK_BLUE( '1', "dark_blue" ),
/** /**
* Represents dark green. * Represents dark green.
*/ */
DARK_GREEN( '2' ), DARK_GREEN( '2', "dark_green" ),
/** /**
* Represents dark blue (aqua). * Represents dark blue (aqua).
*/ */
DARK_AQUA( '3' ), DARK_AQUA( '3', "dark_aqua" ),
/** /**
* Represents dark red. * Represents dark red.
*/ */
DARK_RED( '4' ), DARK_RED( '4', "dark_red" ),
/** /**
* Represents dark purple. * Represents dark purple.
*/ */
DARK_PURPLE( '5' ), DARK_PURPLE( '5', "dark_purple" ),
/** /**
* Represents gold. * Represents gold.
*/ */
GOLD( '6' ), GOLD( '6', "gold" ),
/** /**
* Represents gray. * Represents gray.
*/ */
GRAY( '7' ), GRAY( '7', "gray" ),
/** /**
* Represents dark gray. * Represents dark gray.
*/ */
DARK_GRAY( '8' ), DARK_GRAY( '8', "dark_gray" ),
/** /**
* Represents blue. * Represents blue.
*/ */
BLUE( '9' ), BLUE( '9', "blue" ),
/** /**
* Represents green. * Represents green.
*/ */
GREEN( 'a' ), GREEN( 'a', "green" ),
/** /**
* Represents aqua. * Represents aqua.
*/ */
AQUA( 'b' ), AQUA( 'b', "aqua" ),
/** /**
* Represents red. * Represents red.
*/ */
RED( 'c' ), RED( 'c', "red" ),
/** /**
* Represents light purple. * Represents light purple.
*/ */
LIGHT_PURPLE( 'd' ), LIGHT_PURPLE( 'd', "light_purple" ),
/** /**
* Represents yellow. * Represents yellow.
*/ */
YELLOW( 'e' ), YELLOW( 'e', "yellow" ),
/** /**
* Represents white. * Represents white.
*/ */
WHITE( 'f' ), WHITE( 'f', "white" ),
/** /**
* Represents magical characters that change around randomly. * Represents magical characters that change around randomly.
*/ */
MAGIC( 'k' ), MAGIC( 'k', "obfuscated" ),
/** /**
* Makes the text bold. * Makes the text bold.
*/ */
BOLD( 'l' ), BOLD( 'l', "bold" ),
/** /**
* Makes a line appear through the text. * Makes a line appear through the text.
*/ */
STRIKETHROUGH( 'm' ), STRIKETHROUGH( 'm', "strikethrough" ),
/** /**
* Makes the text appear underlined. * Makes the text appear underlined.
*/ */
UNDERLINE( 'n' ), UNDERLINE( 'n', "underline" ),
/** /**
* Makes the text italic. * Makes the text italic.
*/ */
ITALIC( 'o' ), ITALIC( 'o', "italic" ),
/** /**
* Resets all previous chat colors or formats. * Resets all previous chat colors or formats.
*/ */
RESET( 'r' ); RESET( 'r', "reset" );
/** /**
* The special character which prefixes all chat colour codes. Use this if * The special character which prefixes all chat colour codes. Use this if
* you need to dynamically convert colour codes from your custom format. * you need to dynamically convert colour codes from your custom format.
*/ */
public static final char COLOR_CHAR = '\u00A7'; public static final char COLOR_CHAR = '\u00A7';
public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRr";
/** /**
* Pattern to remove all colour codes. * Pattern to remove all colour codes.
*/ */
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" ); public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
/** /**
* Colour instances keyed by their active character. * Colour instances keyed by their active character.
*/ */
private static final Map<Character, ChatColor> BY_CHAR = new HashMap<>(); private static final Map<Character, ChatColor> BY_CHAR = new HashMap<Character, ChatColor>();
/** /**
* The code appended to {@link #COLOR_CHAR} to make usable colour. * The code appended to {@link #COLOR_CHAR} to make usable colour.
*/ */
@@ -119,6 +121,8 @@ public enum ChatColor
* This colour's colour char prefixed by the {@link #COLOR_CHAR}. * This colour's colour char prefixed by the {@link #COLOR_CHAR}.
*/ */
private final String toString; private final String toString;
@Getter
private final String name;
static static
{ {
@@ -128,9 +132,10 @@ public enum ChatColor
} }
} }
private ChatColor(char code) private ChatColor(char code, String name)
{ {
this.code = code; this.code = code;
this.name = name;
this.toString = new String( new char[] this.toString = new String( new char[]
{ {
COLOR_CHAR, code COLOR_CHAR, code
@@ -164,7 +169,7 @@ public enum ChatColor
char[] b = textToTranslate.toCharArray(); char[] b = textToTranslate.toCharArray();
for ( int i = 0; i < b.length - 1; i++ ) for ( int i = 0; i < b.length - 1; i++ )
{ {
if ( b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf( b[i + 1] ) > -1 ) if ( b[i] == altColorChar && ALL_CODES.indexOf( b[i + 1] ) > -1 )
{ {
b[i] = ChatColor.COLOR_CHAR; b[i] = ChatColor.COLOR_CHAR;
b[i + 1] = Character.toLowerCase( b[i + 1] ); b[i + 1] = Character.toLowerCase( b[i + 1] );

View File

@@ -0,0 +1,12 @@
package net.md_5.bungee.api;
/**
* Represents the position on the screen where a message will appear.
*/
public enum ChatMessageType
{
CHAT,
SYSTEM,
ACTION_BAR
}

View File

@@ -0,0 +1,398 @@
package net.md_5.bungee.api.chat;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.md_5.bungee.api.ChatColor;
import java.util.ArrayList;
import java.util.List;
import lombok.ToString;
@Setter
@ToString(exclude = "parent")
@NoArgsConstructor
public abstract class BaseComponent
{
@Setter(AccessLevel.NONE)
BaseComponent parent;
/**
* The color of this component and any child components (unless overridden)
*/
private ChatColor color;
/**
* Whether this component and any child components (unless overridden) is
* bold
*/
private Boolean bold;
/**
* Whether this component and any child components (unless overridden) is
* italic
*/
private Boolean italic;
/**
* Whether this component and any child components (unless overridden) is
* underlined
*/
private Boolean underlined;
/**
* Whether this component and any child components (unless overridden) is
* strikethrough
*/
private Boolean strikethrough;
/**
* Whether this component and any child components (unless overridden) is
* obfuscated
*/
private Boolean obfuscated;
/**
* The text to insert into the chat when this component (and child
* components) are clicked while pressing the shift key
*/
@Getter
private String insertion;
/**
* Appended components that inherit this component's formatting and events
*/
@Getter
private List<BaseComponent> extra;
/**
* The action to preform when this component (and child components) are
* clicked
*/
@Getter
private ClickEvent clickEvent;
/**
* The action to preform when this component (and child components) are
* hovered over
*/
@Getter
private HoverEvent hoverEvent;
BaseComponent(BaseComponent old)
{
setColor( old.getColorRaw() );
setBold( old.isBoldRaw() );
setItalic( old.isItalicRaw() );
setUnderlined( old.isUnderlinedRaw() );
setStrikethrough( old.isStrikethroughRaw() );
setObfuscated( old.isObfuscatedRaw() );
setInsertion( old.getInsertion() );
setClickEvent( old.getClickEvent() );
setHoverEvent( old.getHoverEvent() );
if ( old.getExtra() != null )
{
for ( BaseComponent component : old.getExtra() )
{
addExtra( component.duplicate() );
}
}
}
/**
* Clones the BaseComponent and returns the clone.
*
* @return The duplicate of this BaseComponent
*/
public abstract BaseComponent duplicate();
/**
* Converts the components to a string that uses the old formatting codes
* ({@link net.md_5.bungee.api.ChatColor#COLOR_CHAR}
*
* @param components the components to convert
* @return the string in the old format
*/
public static String toLegacyText(BaseComponent... components)
{
StringBuilder builder = new StringBuilder();
for ( BaseComponent msg : components )
{
builder.append( msg.toLegacyText() );
}
return builder.toString();
}
/**
* Converts the components into a string without any formatting
*
* @param components the components to convert
* @return the string as plain text
*/
public static String toPlainText(BaseComponent... components)
{
StringBuilder builder = new StringBuilder();
for ( BaseComponent msg : components )
{
builder.append( msg.toPlainText() );
}
return builder.toString();
}
/**
* Returns the color of this component. This uses the parent's color if this
* component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE}
* is returned if no color is found.
*
* @return the color of this component
*/
public ChatColor getColor()
{
if ( color == null )
{
if ( parent == null )
{
return ChatColor.WHITE;
}
return parent.getColor();
}
return color;
}
/**
* Returns the color of this component without checking the parents color.
* May return null
*
* @return the color of this component
*/
public ChatColor getColorRaw()
{
return color;
}
/**
* Returns whether this component is bold. This uses the parent's setting if
* this component hasn't been set. false is returned if none of the parent
* chain has been set.
*
* @return whether the component is bold
*/
public boolean isBold()
{
if ( bold == null )
{
return parent != null && parent.isBold();
}
return bold;
}
/**
* Returns whether this component is bold without checking the parents
* setting. May return null
*
* @return whether the component is bold
*/
public Boolean isBoldRaw()
{
return bold;
}
/**
* Returns whether this component is italic. This uses the parent's setting
* if this component hasn't been set. false is returned if none of the
* parent chain has been set.
*
* @return whether the component is italic
*/
public boolean isItalic()
{
if ( italic == null )
{
return parent != null && parent.isItalic();
}
return italic;
}
/**
* Returns whether this component is italic without checking the parents
* setting. May return null
*
* @return whether the component is italic
*/
public Boolean isItalicRaw()
{
return italic;
}
/**
* Returns whether this component is underlined. This uses the parent's
* setting if this component hasn't been set. false is returned if none of
* the parent chain has been set.
*
* @return whether the component is underlined
*/
public boolean isUnderlined()
{
if ( underlined == null )
{
return parent != null && parent.isUnderlined();
}
return underlined;
}
/**
* Returns whether this component is underlined without checking the parents
* setting. May return null
*
* @return whether the component is underlined
*/
public Boolean isUnderlinedRaw()
{
return underlined;
}
/**
* Returns whether this component is strikethrough. This uses the parent's
* setting if this component hasn't been set. false is returned if none of
* the parent chain has been set.
*
* @return whether the component is strikethrough
*/
public boolean isStrikethrough()
{
if ( strikethrough == null )
{
return parent != null && parent.isStrikethrough();
}
return strikethrough;
}
/**
* Returns whether this component is strikethrough without checking the
* parents setting. May return null
*
* @return whether the component is strikethrough
*/
public Boolean isStrikethroughRaw()
{
return strikethrough;
}
/**
* Returns whether this component is obfuscated. This uses the parent's
* setting if this component hasn't been set. false is returned if none of
* the parent chain has been set.
*
* @return whether the component is obfuscated
*/
public boolean isObfuscated()
{
if ( obfuscated == null )
{
return parent != null && parent.isObfuscated();
}
return obfuscated;
}
/**
* Returns whether this component is obfuscated without checking the parents
* setting. May return null
*
* @return whether the component is obfuscated
*/
public Boolean isObfuscatedRaw()
{
return obfuscated;
}
public void setExtra(List<BaseComponent> components)
{
for ( BaseComponent component : components )
{
component.parent = this;
}
extra = components;
}
/**
* Appends a text element to the component. The text will inherit this
* component's formatting
*
* @param text the text to append
*/
public void addExtra(String text)
{
addExtra( new TextComponent( text ) );
}
/**
* Appends a component to the component. The text will inherit this
* component's formatting
*
* @param component the component to append
*/
public void addExtra(BaseComponent component)
{
if ( extra == null )
{
extra = new ArrayList<BaseComponent>();
}
component.parent = this;
extra.add( component );
}
/**
* Returns whether the component has any formatting or events applied to it
*
* @return Whether any formatting or events are applied
*/
public boolean hasFormatting()
{
return color != null || bold != null
|| italic != null || underlined != null
|| strikethrough != null || obfuscated != null
|| hoverEvent != null || clickEvent != null;
}
/**
* Converts the component into a string without any formatting
*
* @return the string as plain text
*/
public String toPlainText()
{
StringBuilder builder = new StringBuilder();
toPlainText( builder );
return builder.toString();
}
void toPlainText(StringBuilder builder)
{
if ( extra != null )
{
for ( BaseComponent e : extra )
{
e.toPlainText( builder );
}
}
}
/**
* Converts the component to a string that uses the old formatting codes
* ({@link net.md_5.bungee.api.ChatColor#COLOR_CHAR}
*
* @return the string in the old format
*/
public String toLegacyText()
{
StringBuilder builder = new StringBuilder();
toLegacyText( builder );
return builder.toString();
}
void toLegacyText(StringBuilder builder)
{
if ( extra != null )
{
for ( BaseComponent e : extra )
{
e.toLegacyText( builder );
}
}
}
}

View File

@@ -0,0 +1,55 @@
package net.md_5.bungee.api.chat;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import net.md_5.bungee.api.chat.ClickEvent.Action;
@Getter
@ToString
@RequiredArgsConstructor
public final class ClickEvent
{
/**
* The type of action to perform on click
*/
private final Action action;
/**
* Depends on action
*
* @see Action
*/
private final String value;
public enum Action
{
/**
* Open a url at the path given by
* {@link net.md_5.bungee.api.chat.ClickEvent#value}
*/
OPEN_URL,
/**
* Open a file at the path given by
* {@link net.md_5.bungee.api.chat.ClickEvent#value}
*/
OPEN_FILE,
/**
* Run the command given by
* {@link net.md_5.bungee.api.chat.ClickEvent#value}
*/
RUN_COMMAND,
/**
* Inserts the string given by
* {@link net.md_5.bungee.api.chat.ClickEvent#value} into the players
* text box
*/
SUGGEST_COMMAND,
/**
* Change to the page number given by
* {@link net.md_5.bungee.api.chat.ClickEvent#value} in a book
*/
CHANGE_PAGE
}
}

View File

@@ -0,0 +1,269 @@
package net.md_5.bungee.api.chat;
import net.md_5.bungee.api.ChatColor;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* ComponentBuilder simplifies creating basic messages by allowing the use of a
* chainable builder.
* </p>
* <pre>
* new ComponentBuilder("Hello ").color(ChatColor.RED).
* append("World").color(ChatColor.BLUE). append("!").bold(true).create();
* </pre>
* <p>
* All methods (excluding {@link #append(String)} and {@link #create()} work on
* the last part appended to the builder, so in the example above "Hello " would
* be {@link net.md_5.bungee.api.ChatColor#RED} and "World" would be
* {@link net.md_5.bungee.api.ChatColor#BLUE} but "!" would be bold and
* {@link net.md_5.bungee.api.ChatColor#BLUE} because append copies the previous
* part's formatting
* </p>
*/
public class ComponentBuilder
{
private TextComponent current;
private final List<BaseComponent> parts = new ArrayList<BaseComponent>();
/**
* Creates a ComponentBuilder from the other given ComponentBuilder to clone
* it.
*
* @param original the original for the new ComponentBuilder.
*/
public ComponentBuilder(ComponentBuilder original)
{
current = new TextComponent( original.current );
for ( BaseComponent baseComponent : original.parts )
{
parts.add( baseComponent.duplicate() );
}
}
/**
* Creates a ComponentBuilder with the given text as the first part.
*
* @param text the first text element
*/
public ComponentBuilder(String text)
{
current = new TextComponent( text );
}
/**
* Appends the text to the builder and makes it the current target for
* formatting. The text will have all the formatting from the previous part.
*
* @param text the text to append
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder append(String text)
{
return append( text, FormatRetention.ALL );
}
/**
* Appends the text to the builder and makes it the current target for
* formatting. You can specify the amount of formatting retained.
*
* @param text the text to append
* @param retention the formatting to retain
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder append(String text, FormatRetention retention)
{
parts.add( current );
current = new TextComponent( current );
current.setText( text );
retain( retention );
return this;
}
/**
* Sets the color of the current part.
*
* @param color the new color
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder color(ChatColor color)
{
current.setColor( color );
return this;
}
/**
* Sets whether the current part is bold.
*
* @param bold whether this part is bold
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder bold(boolean bold)
{
current.setBold( bold );
return this;
}
/**
* Sets whether the current part is italic.
*
* @param italic whether this part is italic
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder italic(boolean italic)
{
current.setItalic( italic );
return this;
}
/**
* Sets whether the current part is underlined.
*
* @param underlined whether this part is underlined
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder underlined(boolean underlined)
{
current.setUnderlined( underlined );
return this;
}
/**
* Sets whether the current part is strikethrough.
*
* @param strikethrough whether this part is strikethrough
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder strikethrough(boolean strikethrough)
{
current.setStrikethrough( strikethrough );
return this;
}
/**
* Sets whether the current part is obfuscated.
*
* @param obfuscated whether this part is obfuscated
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder obfuscated(boolean obfuscated)
{
current.setObfuscated( obfuscated );
return this;
}
/**
* Sets the insertion text for the current part.
*
* @param insertion the insertion text
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder insertion(String insertion)
{
current.setInsertion( insertion );
return this;
}
/**
* Sets the click event for the current part.
*
* @param clickEvent the click event
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder event(ClickEvent clickEvent)
{
current.setClickEvent( clickEvent );
return this;
}
/**
* Sets the hover event for the current part.
*
* @param hoverEvent the hover event
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder event(HoverEvent hoverEvent)
{
current.setHoverEvent( hoverEvent );
return this;
}
/**
* Sets the current part back to normal settings. Only text is kept.
*
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder reset()
{
return retain( FormatRetention.NONE );
}
/**
* Retains only the specified formatting. Text is not modified.
*
* @param retention the formatting to retain
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder retain(FormatRetention retention)
{
BaseComponent previous = current;
switch ( retention )
{
case NONE:
current = new TextComponent( current.getText() );
break;
case ALL:
// No changes are required
break;
case EVENTS:
current = new TextComponent( current.getText() );
current.setInsertion( previous.getInsertion() );
current.setClickEvent( previous.getClickEvent() );
current.setHoverEvent( previous.getHoverEvent() );
break;
case FORMATTING:
current.setClickEvent( null );
current.setHoverEvent( null );
break;
}
return this;
}
/**
* Returns the components needed to display the message created by this
* builder.
*
* @return the created components
*/
public BaseComponent[] create()
{
parts.add( current );
return parts.toArray( new BaseComponent[ parts.size() ] );
}
public static enum FormatRetention
{
/**
* Specify that we do not want to retain anything from the previous component.
*/
NONE,
/**
* Specify that we want the formatting retained from the previous component.
*/
FORMATTING,
/**
* Specify that we want the events retained from the previous component.
*/
EVENTS,
/**
* Specify that we want to retain everything from the previous component.
*/
ALL
}
}

View File

@@ -0,0 +1,24 @@
package net.md_5.bungee.api.chat;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@RequiredArgsConstructor
final public class HoverEvent
{
private final Action action;
private final BaseComponent[] value;
public enum Action
{
SHOW_TEXT,
SHOW_ACHIEVEMENT,
SHOW_ITEM,
SHOW_ENTITY
}
}

View File

@@ -0,0 +1,213 @@
package net.md_5.bungee.api.chat;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.md_5.bungee.api.ChatColor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TextComponent extends BaseComponent
{
private static final Pattern url = Pattern.compile( "^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$" );
/**
* Converts the old formatting system that used
* {@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} into the new json based
* system.
*
* @param message the text to convert
* @return the components needed to print the message to the client
*/
public static BaseComponent[] fromLegacyText(String message)
{
ArrayList<BaseComponent> components = new ArrayList<BaseComponent>();
StringBuilder builder = new StringBuilder();
TextComponent component = new TextComponent();
Matcher matcher = url.matcher( message );
for ( int i = 0; i < message.length(); i++ )
{
char c = message.charAt( i );
if ( c == ChatColor.COLOR_CHAR )
{
i++;
c = message.charAt( i );
if ( c >= 'A' && c <= 'Z' )
{
c += 32;
}
ChatColor format = ChatColor.getByChar( c );
if ( format == null )
{
continue;
}
if ( builder.length() > 0 )
{
TextComponent old = component;
component = new TextComponent( old );
old.setText( builder.toString() );
builder = new StringBuilder();
components.add( old );
}
switch ( format )
{
case BOLD:
component.setBold( true );
break;
case ITALIC:
component.setItalic( true );
break;
case UNDERLINE:
component.setUnderlined( true );
break;
case STRIKETHROUGH:
component.setStrikethrough( true );
break;
case MAGIC:
component.setObfuscated( true );
break;
case RESET:
format = ChatColor.WHITE;
default:
component = new TextComponent();
component.setColor( format );
break;
}
continue;
}
int pos = message.indexOf( ' ', i );
if ( pos == -1 )
{
pos = message.length();
}
if ( matcher.region( i, pos ).find() )
{ //Web link handling
if ( builder.length() > 0 )
{
TextComponent old = component;
component = new TextComponent( old );
old.setText( builder.toString() );
builder = new StringBuilder();
components.add( old );
}
TextComponent old = component;
component = new TextComponent( old );
String urlString = message.substring( i, pos );
component.setText( urlString );
component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL,
urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) );
components.add( component );
i += pos - i - 1;
component = old;
continue;
}
builder.append( c );
}
if ( builder.length() > 0 )
{
component.setText( builder.toString() );
components.add( component );
}
// The client will crash if the array is empty
if ( components.isEmpty() )
{
components.add( new TextComponent( "" ) );
}
return components.toArray( new BaseComponent[ components.size() ] );
}
/**
* The text of the component that will be displayed to the client
*/
private String text;
/**
* Creates a TextComponent with formatting and text from the passed
* component
*
* @param textComponent the component to copy from
*/
public TextComponent(TextComponent textComponent)
{
super( textComponent );
setText( textComponent.getText() );
}
/**
* Creates a TextComponent with blank text and the extras set to the passed
* array
*
* @param extras the extras to set
*/
public TextComponent(BaseComponent... extras)
{
setText( "" );
setExtra( new ArrayList<BaseComponent>( Arrays.asList( extras ) ) );
}
/**
* Creates a duplicate of this TextComponent.
*
* @return the duplicate of this TextComponent.
*/
@Override
public BaseComponent duplicate()
{
return new TextComponent( this );
}
@Override
protected void toPlainText(StringBuilder builder)
{
builder.append( text );
super.toPlainText( builder );
}
@Override
protected void toLegacyText(StringBuilder builder)
{
builder.append( getColor() );
if ( isBold() )
{
builder.append( ChatColor.BOLD );
}
if ( isItalic() )
{
builder.append( ChatColor.ITALIC );
}
if ( isUnderlined() )
{
builder.append( ChatColor.UNDERLINE );
}
if ( isStrikethrough() )
{
builder.append( ChatColor.STRIKETHROUGH );
}
if ( isObfuscated() )
{
builder.append( ChatColor.MAGIC );
}
builder.append( text );
super.toLegacyText( builder );
}
@Override
public String toString()
{
return String.format( "TextComponent{text=%s, %s}", text, super.toString() );
}
}

View File

@@ -0,0 +1,250 @@
package net.md_5.bungee.api.chat;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.md_5.bungee.api.ChatColor;
import java.util.ArrayList;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class TranslatableComponent extends BaseComponent
{
private final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" );
private final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" );
/**
* The key into the Minecraft locale files to use for the translation. The
* text depends on the client's locale setting. The console is always en_US
*/
private String translate;
/**
* The components to substitute into the translation
*/
private List<BaseComponent> with;
/**
* Creates a translatable component from the original to clone it.
*
* @param original the original for the new translatable component.
*/
public TranslatableComponent(TranslatableComponent original)
{
super( original );
setTranslate( original.getTranslate() );
if ( original.getWith() != null )
{
List<BaseComponent> temp = new ArrayList<BaseComponent>();
for ( BaseComponent baseComponent : original.getWith() )
{
temp.add( baseComponent.duplicate() );
}
setWith( temp );
}
}
/**
* Creates a translatable component with the passed substitutions
*
* @see #translate
* @see #setWith(java.util.List)
* @param translate the translation key
* @param with the {@link java.lang.String}s and
* {@link net.md_5.bungee.api.chat.BaseComponent}s to use into the
* translation
*/
public TranslatableComponent(String translate, Object... with)
{
setTranslate( translate );
List<BaseComponent> temp = new ArrayList<BaseComponent>();
for ( Object w : with )
{
if ( w instanceof String )
{
temp.add( new TextComponent( (String) w ) );
} else
{
temp.add( (BaseComponent) w );
}
}
setWith( temp );
}
/**
* Creates a duplicate of this TranslatableComponent.
*
* @return the duplicate of this TranslatableComponent.
*/
@Override
public BaseComponent duplicate()
{
return new TranslatableComponent( this );
}
/**
* Sets the translation substitutions to be used in this component. Removes
* any previously set substitutions
*
* @param components the components to substitute
*/
public void setWith(List<BaseComponent> components)
{
for ( BaseComponent component : components )
{
component.parent = this;
}
with = components;
}
/**
* Adds a text substitution to the component. The text will inherit this
* component's formatting
*
* @param text the text to substitute
*/
public void addWith(String text)
{
addWith( new TextComponent( text ) );
}
/**
* Adds a component substitution to the component. The text will inherit
* this component's formatting
*
* @param component the component to substitute
*/
public void addWith(BaseComponent component)
{
if ( with == null )
{
with = new ArrayList<BaseComponent>();
}
component.parent = this;
with.add( component );
}
@Override
protected void toPlainText(StringBuilder builder)
{
String trans;
try
{
trans = locales.getString( translate );
} catch ( MissingResourceException e ) {
trans = translate;
}
Matcher matcher = format.matcher( trans );
int position = 0;
int i = 0;
while ( matcher.find( position ) )
{
int pos = matcher.start();
if ( pos != position )
{
builder.append( trans.substring( position, pos ) );
}
position = matcher.end();
String formatCode = matcher.group( 2 );
switch ( formatCode.charAt( 0 ) )
{
case 's':
case 'd':
String withIndex = matcher.group( 1 );
with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toPlainText( builder );
break;
case '%':
builder.append( '%' );
break;
}
}
if ( trans.length() != position )
{
builder.append( trans.substring( position, trans.length() ) );
}
super.toPlainText( builder );
}
@Override
protected void toLegacyText(StringBuilder builder)
{
String trans;
try
{
trans = locales.getString( translate );
} catch ( MissingResourceException e ) {
trans = translate;
}
Matcher matcher = format.matcher( trans );
int position = 0;
int i = 0;
while ( matcher.find( position ) )
{
int pos = matcher.start();
if ( pos != position )
{
addFormat( builder );
builder.append( trans.substring( position, pos ) );
}
position = matcher.end();
String formatCode = matcher.group( 2 );
switch ( formatCode.charAt( 0 ) )
{
case 's':
case 'd':
String withIndex = matcher.group( 1 );
with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toLegacyText( builder );
break;
case '%':
addFormat( builder );
builder.append( '%' );
break;
}
}
if ( trans.length() != position )
{
addFormat( builder );
builder.append( trans.substring( position, trans.length() ) );
}
super.toLegacyText( builder );
}
private void addFormat(StringBuilder builder)
{
builder.append( getColor() );
if ( isBold() )
{
builder.append( ChatColor.BOLD );
}
if ( isItalic() )
{
builder.append( ChatColor.ITALIC );
}
if ( isUnderlined() )
{
builder.append( ChatColor.UNDERLINE );
}
if ( isStrikethrough() )
{
builder.append( ChatColor.STRIKETHROUGH );
}
if ( isObfuscated() )
{
builder.append( ChatColor.MAGIC );
}
}
}

View File

@@ -0,0 +1,149 @@
package net.md_5.bungee.chat;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import java.util.Arrays;
import java.util.HashSet;
public class BaseComponentSerializer
{
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
{
if ( object.has( "color" ) )
{
component.setColor( ChatColor.valueOf( object.get( "color" ).getAsString().toUpperCase() ) );
}
if ( object.has( "bold" ) )
{
component.setBold( object.get( "bold" ).getAsBoolean() );
}
if ( object.has( "italic" ) )
{
component.setItalic( object.get( "italic" ).getAsBoolean() );
}
if ( object.has( "underlined" ) )
{
component.setUnderlined( object.get( "underlined" ).getAsBoolean() );
}
if ( object.has( "strikethrough" ) )
{
component.setStrikethrough( object.get( "strikethrough" ).getAsBoolean() );
}
if ( object.has( "obfuscated" ) )
{
component.setObfuscated( object.get( "obfuscated" ).getAsBoolean() );
}
if ( object.has( "insertion" ) )
{
component.setInsertion( object.get( "insertion" ).getAsString() );
}
if ( object.has( "extra" ) )
{
component.setExtra( Arrays.<BaseComponent>asList( context.<BaseComponent[]>deserialize( object.get( "extra" ), BaseComponent[].class ) ) );
}
//Events
if ( object.has( "clickEvent" ) )
{
JsonObject event = object.getAsJsonObject( "clickEvent" );
component.setClickEvent( new ClickEvent(
ClickEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ),
event.get( "value" ).getAsString() ) );
}
if ( object.has( "hoverEvent" ) )
{
JsonObject event = object.getAsJsonObject( "hoverEvent" );
BaseComponent[] res;
if ( event.get( "value" ).isJsonArray() )
{
res = context.deserialize( event.get( "value" ), BaseComponent[].class );
} else
{
res = new BaseComponent[]
{
context.<BaseComponent>deserialize( event.get( "value" ), BaseComponent.class )
};
}
component.setHoverEvent( new HoverEvent( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ), res ) );
}
}
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context)
{
boolean first = false;
if ( ComponentSerializer.serializedComponents.get() == null )
{
first = true;
ComponentSerializer.serializedComponents.set( new HashSet<BaseComponent>() );
}
try
{
Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
ComponentSerializer.serializedComponents.get().add( component );
if ( component.getColorRaw() != null )
{
object.addProperty( "color", component.getColorRaw().getName() );
}
if ( component.isBoldRaw() != null )
{
object.addProperty( "bold", component.isBoldRaw() );
}
if ( component.isItalicRaw() != null )
{
object.addProperty( "italic", component.isItalicRaw() );
}
if ( component.isUnderlinedRaw() != null )
{
object.addProperty( "underlined", component.isUnderlinedRaw() );
}
if ( component.isStrikethroughRaw() != null )
{
object.addProperty( "strikethrough", component.isStrikethroughRaw() );
}
if ( component.isObfuscatedRaw() != null )
{
object.addProperty( "obfuscated", component.isObfuscatedRaw() );
}
if ( component.getInsertion() != null )
{
object.addProperty( "insertion", component.getInsertion() );
}
if ( component.getExtra() != null )
{
object.add( "extra", context.serialize( component.getExtra() ) );
}
//Events
if ( component.getClickEvent() != null )
{
JsonObject clickEvent = new JsonObject();
clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase() );
clickEvent.addProperty( "value", component.getClickEvent().getValue() );
object.add( "clickEvent", clickEvent );
}
if ( component.getHoverEvent() != null )
{
JsonObject hoverEvent = new JsonObject();
hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() );
hoverEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) );
object.add( "hoverEvent", hoverEvent );
}
} finally
{
ComponentSerializer.serializedComponents.get().remove( component );
if ( first )
{
ComponentSerializer.serializedComponents.set( null );
}
}
}
}

View File

@@ -0,0 +1,64 @@
package net.md_5.bungee.chat;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import java.lang.reflect.Type;
import java.util.HashSet;
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
{
private final static Gson gson = new GsonBuilder().
registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ).
create();
public final static ThreadLocal<HashSet<BaseComponent>> serializedComponents = new ThreadLocal<HashSet<BaseComponent>>();
public static BaseComponent[] parse(String json)
{
if ( json.startsWith( "[" ) )
{ //Array
return gson.fromJson( json, BaseComponent[].class );
}
return new BaseComponent[]
{
gson.fromJson( json, BaseComponent.class )
};
}
public static String toString(BaseComponent component)
{
return gson.toJson( component );
}
public static String toString(BaseComponent... components)
{
return gson.toJson( new TextComponent( components ) );
}
@Override
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
if ( json.isJsonPrimitive() )
{
return new TextComponent( json.getAsString() );
}
JsonObject object = json.getAsJsonObject();
if ( object.has( "translate" ) )
{
return context.deserialize( json, TranslatableComponent.class );
}
return context.deserialize( json, TextComponent.class );
}
}

View File

@@ -0,0 +1,43 @@
package net.md_5.bungee.chat;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import java.lang.reflect.Type;
import java.util.List;
public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent>
{
@Override
public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
TextComponent component = new TextComponent();
JsonObject object = json.getAsJsonObject();
deserialize( object, component, context );
component.setText( object.get( "text" ).getAsString() );
return component;
}
@Override
public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context)
{
List<BaseComponent> extra = src.getExtra();
if ( !src.hasFormatting() && ( extra == null || extra.isEmpty() ) )
{
return new JsonPrimitive( src.getText() );
}
JsonObject object = new JsonObject();
serialize( object, src, context );
object.addProperty( "text", src.getText() );
return object;
}
}

View File

@@ -0,0 +1,45 @@
package net.md_5.bungee.chat;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import java.lang.reflect.Type;
import java.util.Arrays;
public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TranslatableComponent>, JsonDeserializer<TranslatableComponent>
{
@Override
public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
TranslatableComponent component = new TranslatableComponent();
JsonObject object = json.getAsJsonObject();
deserialize( object, component, context );
component.setTranslate( object.get( "translate" ).getAsString() );
if ( object.has( "with" ) )
{
component.setWith( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "with" ), BaseComponent[].class ) ) );
}
return component;
}
@Override
public JsonElement serialize(TranslatableComponent src, Type typeOfSrc, JsonSerializationContext context)
{
JsonObject object = new JsonObject();
serialize( object, src, context );
object.addProperty( "translate", src.getTranslate() );
if ( src.getWith() != null )
{
object.add( "with", context.serialize( src.getWith() ) );
}
return object;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
package net.md_5.bungee.api.chat;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TranslatableComponentTest {
@Test
public void testMissingPlaceholdersAdded()
{
TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholders: %s", "2", "aoeu" );
assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() );
assertEquals( "§fTest string with §f2§f placeholders: §faoeu", testComponent.toLegacyText() );
}
}

View File

@@ -6,29 +6,24 @@
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.6.2-SNAPSHOT</version> <version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId> <artifactId>bungeecord-config</artifactId>
<version>1.6.2-SNAPSHOT</version> <version>1.8-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-Config</name> <name>BungeeCord-Config</name>
<description>Generic java configuration API intended for use with BungeeCord</description> <description>Generic java configuration API intended for use with BungeeCord</description>
<dependencies> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.yaml</groupId> <groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId> <artifactId>snakeyaml</artifactId>
<version>1.11</version> <version>1.14</version>
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,8 +1,10 @@
package net.md_5.bungee.config; package net.md_5.bungee.config;
import com.google.common.collect.Sets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.AccessLevel; import lombok.AccessLevel;
@@ -13,58 +15,108 @@ public final class Configuration
{ {
private static final char SEPARATOR = '.'; private static final char SEPARATOR = '.';
private final Map<String, Object> self; final Map<String, Object> self;
private Map<String, Object> comments = new HashMap<>();
private final Configuration defaults; private final Configuration defaults;
private Map<String, Object> getHolder(String path, Map<String, Object> parent, boolean create) public Configuration()
{ {
return null; this( null );
} }
private Object get(String path, Map<String, Object> holder) public Configuration(Configuration defaults)
{
this( new LinkedHashMap<String, Object>(), defaults );
}
private Configuration getSectionFor(String path)
{ {
int index = path.indexOf( SEPARATOR ); int index = path.indexOf( SEPARATOR );
String first, second;
if ( index == -1 ) if ( index == -1 )
{ {
second = path; return this;
} else
{
first = path.substring( 0, index );
second = path.substring( index + 1, path.length() );
} }
return null;
String root = path.substring( 0, index );
Object section = self.get( root );
if ( section == null )
{
section = new LinkedHashMap<>();
self.put( root, section );
}
if ( section instanceof Configuration )
{
return (Configuration) section;
}
return new Configuration( (Map) section, ( defaults == null ) ? null : defaults.getSectionFor( path ) );
}
private String getChild(String path)
{
int index = path.indexOf( SEPARATOR );
return ( index == -1 ) ? path : path.substring( index + 1 );
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T get(String path, T def) public <T> T get(String path, T def)
{ {
Object val = get( path, self ); Configuration section = getSectionFor( path );
return ( val != null && val.getClass().isInstance( def ) ) ? (T) val : (T) defaults.get( path ); Object val;
if ( section == this )
{
val = self.get( path );
} else
{
val = section.get( getChild( path ), def );
}
return ( val != null ) ? (T) val : def;
} }
public Object get(String path) public Object get(String path)
{ {
return get( path, null ); return get( path, getDefault( path ) );
} }
public Object getDefault(String path) public Object getDefault(String path)
{ {
return defaults.get( path ); return ( defaults == null ) ? null : defaults.get( path );
}
public void set(String path, Object value, String comment)
{
String child = path.substring( path.indexOf( SEPARATOR ) + 1 );
getHolder( path, self, true ).put( child, value );
getHolder( path, comments, true ).put( child, value );
} }
public void set(String path, Object value) public void set(String path, Object value)
{ {
set( path, value, null ); Configuration section = getSectionFor( path );
if ( section == this )
{
if ( value == null )
{
self.remove( path );
} else
{
self.put( path, value );
}
} else
{
section.set( getChild( path ), value );
}
}
/*------------------------------------------------------------------------*/
public Configuration getSection(String path)
{
Object def = getDefault( path );
return new Configuration( (Map) ( get( path, ( def instanceof Map ) ? def : Collections.EMPTY_MAP ) ), ( defaults == null ) ? null : defaults.getSection( path ) );
}
/**
* Gets keys, not deep by default.
*
* @return top level keys for this section
*/
public Collection<String> getKeys()
{
return Sets.newLinkedHashSet( self.keySet() );
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/

View File

@@ -1,7 +1,10 @@
package net.md_5.bungee.config; package net.md_5.bungee.config;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.Writer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -15,15 +18,29 @@ public abstract class ConfigurationProvider
providers.put( YamlConfiguration.class, new YamlConfiguration() ); providers.put( YamlConfiguration.class, new YamlConfiguration() );
} }
public ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider) public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider)
{ {
return providers.get( provider ); return providers.get( provider );
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
public abstract Configuration load(File file); public abstract void save(Configuration config, File file) throws IOException;
public abstract void save(Configuration config, Writer writer);
public abstract Configuration load(File file) throws IOException;
public abstract Configuration load(File file, Configuration defaults) throws IOException;
public abstract Configuration load(Reader reader); public abstract Configuration load(Reader reader);
public abstract Configuration load(Reader reader, Configuration defaults);
public abstract Configuration load(InputStream is);
public abstract Configuration load(InputStream is, Configuration defaults);
public abstract Configuration load(String string); public abstract Configuration load(String string);
public abstract Configuration load(String string, Configuration defaults);
} }

View File

@@ -1,14 +1,20 @@
package net.md_5.bungee.config; package net.md_5.bungee.config;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class YamlConfiguration extends ConfigurationProvider public class YamlConfiguration extends ConfigurationProvider
{ {
@@ -24,30 +30,86 @@ public class YamlConfiguration extends ConfigurationProvider
}; };
@Override @Override
public Configuration load(File file) public void save(Configuration config, File file) throws IOException
{ {
try ( FileReader reader = new FileReader( file ) ) try ( FileWriter writer = new FileWriter( file ) )
{ {
return load( reader ); save( config, writer );
} catch ( IOException ex ) }
{ }
return null;
@Override
public void save(Configuration config, Writer writer)
{
yaml.get().dump( config.self, writer );
}
@Override
public Configuration load(File file) throws IOException
{
return load( file, null );
}
@Override
public Configuration load(File file, Configuration defaults) throws IOException
{
try ( FileReader reader = new FileReader( file ) )
{
return load( reader, defaults );
} }
} }
@Override @Override
@SuppressWarnings("unchecked")
public Configuration load(Reader reader) public Configuration load(Reader reader)
{ {
Configuration conf = new Configuration( (Map<String, Object>) yaml.get().loadAs( reader, Map.class ), null ); return load( reader, null );
return conf;
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Configuration load(Reader reader, Configuration defaults)
{
Map<String, Object> map = yaml.get().loadAs( reader, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
@Override
public Configuration load(InputStream is)
{
return load( is, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(InputStream is, Configuration defaults)
{
Map<String, Object> map = yaml.get().loadAs( is, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
@Override
public Configuration load(String string) public Configuration load(String string)
{ {
Configuration conf = new Configuration( (Map<String, Object>) yaml.get().loadAs( string, Map.class ), null ); return load( string, null );
return conf; }
@Override
@SuppressWarnings("unchecked")
public Configuration load(String string, Configuration defaults)
{
Map<String, Object> map = yaml.get().loadAs( string, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
} }
} }

View File

@@ -0,0 +1,81 @@
package net.md_5.bungee.config;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
public class YamlConfigurationTest
{
private String document = ""
+ "receipt: Oz-Ware Purchase Invoice\n"
+ "date: 2012-08-06\n"
+ "customer:\n"
+ " given: Dorothy\n"
+ " family: Gale\n"
+ "\n"
+ "items:\n"
+ " - part_no: A4786\n"
+ " descrip: Water Bucket (Filled)\n"
+ " price: 1.47\n"
+ " quantity: 4\n"
+ "\n"
+ " - part_no: E1628\n"
+ " descrip: High Heeled \"Ruby\" Slippers\n"
+ " size: 8\n"
+ " price: 100.27\n"
+ " quantity: 1\n"
+ "\n"
+ "bill-to: &id001\n"
+ " street: |\n"
+ " 123 Tornado Alley\n"
+ " Suite 16\n"
+ " city: East Centerville\n"
+ " state: KS\n"
+ "\n"
+ "ship-to: *id001\n"
+ "\n"
+ "specialDelivery: >\n"
+ " Follow the Yellow Brick\n"
+ " Road to the Emerald City.\n"
+ " Pay no attention to the\n"
+ " man behind the curtain.";
@Test
public void testConfig() throws Exception
{
Configuration conf = ConfigurationProvider.getProvider( YamlConfiguration.class ).load( document );
testSection( conf );
StringWriter sw = new StringWriter();
ConfigurationProvider.getProvider( YamlConfiguration.class ).save( conf, sw );
// Check nulls were saved, see #1094
Assert.assertFalse( "Config contains null", sw.toString().contains( "null" ) );
conf = ConfigurationProvider.getProvider( YamlConfiguration.class ).load( new StringReader( sw.toString() ) );
conf.set( "receipt", "Oz-Ware Purchase Invoice" ); // Add it back
testSection( conf );
}
private void testSection(Configuration conf)
{
Assert.assertEquals( "receipt", "Oz-Ware Purchase Invoice", conf.getString( "receipt" ) );
// Assert.assertEquals( "date", "2012-08-06", conf.get( "date" ).toString() );
Configuration customer = conf.getSection( "customer" );
Assert.assertEquals( "customer.given", "Dorothy", customer.getString( "given" ) );
Assert.assertEquals( "customer.given", "Dorothy", conf.getString( "customer.given" ) );
List items = conf.getList( "items" );
Map item = (Map) items.get( 0 );
Assert.assertEquals( "items[0].part_no", "A4786", item.get( "part_no" ) );
conf.set( "receipt", null );
Assert.assertEquals( null, conf.get( "receipt" ) );
Assert.assertEquals( "foo", conf.get( "receipt", "foo" ) );
}
}

View File

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

View File

@@ -9,8 +9,9 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -18,8 +19,8 @@ public class EventBus
{ {
private final Map<Class<?>, Map<Byte, Map<Object, Method[]>>> byListenerAndPriority = new HashMap<>(); private final Map<Class<?>, Map<Byte, Map<Object, Method[]>>> byListenerAndPriority = new HashMap<>();
private final Map<Class<?>, EventHandlerMethod[]> byEventBaked = new HashMap<>(); private final Map<Class<?>, EventHandlerMethod[]> byEventBaked = new ConcurrentHashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock lock = new ReentrantLock();
private final Logger logger; private final Logger logger;
public EventBus() public EventBus()
@@ -29,37 +30,31 @@ public class EventBus
public EventBus(Logger logger) public EventBus(Logger logger)
{ {
this.logger = ( logger == null ) ? Logger.getGlobal() : logger; this.logger = ( logger == null ) ? Logger.getLogger( Logger.GLOBAL_LOGGER_NAME ) : logger;
} }
public void post(Object event) public void post(Object event)
{ {
lock.readLock().lock(); EventHandlerMethod[] handlers = byEventBaked.get( event.getClass() );
try
if ( handlers != null )
{ {
EventHandlerMethod[] handlers = byEventBaked.get( event.getClass() ); for ( EventHandlerMethod method : handlers )
if ( handlers != null )
{ {
for ( EventHandlerMethod method : handlers ) try
{ {
try method.invoke( event );
{ } catch ( IllegalAccessException ex )
method.invoke( event ); {
} catch ( IllegalAccessException ex ) throw new Error( "Method became inaccessible: " + event, ex );
{ } catch ( IllegalArgumentException ex )
throw new Error( "Method became inaccessible: " + event, ex ); {
} catch ( IllegalArgumentException ex ) throw new Error( "Method rejected target/argument: " + event, ex );
{ } catch ( InvocationTargetException ex )
throw new Error( "Method rejected target/argument: " + event, ex ); {
} catch ( InvocationTargetException ex ) logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
{
logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
}
} }
} }
} finally
{
lock.readLock().unlock();
} }
} }
@@ -101,7 +96,7 @@ public class EventBus
public void register(Object listener) public void register(Object listener)
{ {
Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener ); Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
lock.writeLock().lock(); lock.lock();
try try
{ {
for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() ) for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
@@ -127,14 +122,14 @@ public class EventBus
} }
} finally } finally
{ {
lock.writeLock().unlock(); lock.unlock();
} }
} }
public void unregister(Object listener) public void unregister(Object listener)
{ {
Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener ); Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
lock.writeLock().lock(); lock.lock();
try try
{ {
for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() ) for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
@@ -163,7 +158,7 @@ public class EventBus
} }
} finally } finally
{ {
lock.writeLock().unlock(); lock.unlock();
} }
} }
@@ -178,7 +173,11 @@ public class EventBus
if ( handlersByPriority != null ) if ( handlersByPriority != null )
{ {
List<EventHandlerMethod> handlersList = new ArrayList<>( handlersByPriority.size() * 2 ); List<EventHandlerMethod> handlersList = new ArrayList<>( handlersByPriority.size() * 2 );
for ( byte value = Byte.MIN_VALUE; value < Byte.MAX_VALUE; value++ )
// Either I'm really tired, or the only way we can iterate between Byte.MIN_VALUE and Byte.MAX_VALUE inclusively,
// with only a byte on the stack is by using a do {} while() format loop.
byte value = Byte.MIN_VALUE;
do
{ {
Map<Object, Method[]> handlersByListener = handlersByPriority.get( value ); Map<Object, Method[]> handlersByListener = handlersByPriority.get( value );
if ( handlersByListener != null ) if ( handlersByListener != null )
@@ -192,11 +191,11 @@ public class EventBus
} }
} }
} }
} } while ( value++ < Byte.MAX_VALUE );
byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) ); byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) );
} else } else
{ {
byEventBaked.put( eventClass, null ); byEventBaked.remove( eventClass );
} }
} }
} }

View File

@@ -8,7 +8,7 @@ public class EventPriorityTest
{ {
private final EventBus bus = new EventBus(); private final EventBus bus = new EventBus();
private final CountDownLatch latch = new CountDownLatch( 5 ); private final CountDownLatch latch = new CountDownLatch( 7 );
@Test @Test
public void testPriority() public void testPriority()
@@ -19,22 +19,36 @@ public class EventPriorityTest
Assert.assertEquals( 0, latch.getCount() ); Assert.assertEquals( 0, latch.getCount() );
} }
@EventHandler(priority = Byte.MIN_VALUE)
public void onMinPriority(PriorityTestEvent event)
{
Assert.assertEquals( 7, latch.getCount() );
latch.countDown();
}
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onLowestPriority(PriorityTestEvent event) public void onLowestPriority(PriorityTestEvent event)
{ {
Assert.assertEquals( 5, latch.getCount() ); Assert.assertEquals( 6, latch.getCount() );
latch.countDown(); latch.countDown();
} }
@EventHandler @EventHandler
public void onNormalPriority(PriorityTestEvent event) public void onNormalPriority(PriorityTestEvent event)
{ {
Assert.assertEquals( 3, latch.getCount() ); Assert.assertEquals( 4, latch.getCount() );
latch.countDown(); latch.countDown();
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void onHighestPriority(PriorityTestEvent event) public void onHighestPriority(PriorityTestEvent event)
{
Assert.assertEquals( 2, latch.getCount() );
latch.countDown();
}
@EventHandler(priority = Byte.MAX_VALUE)
public void onMaxPriority(PriorityTestEvent event)
{ {
Assert.assertEquals( 1, latch.getCount() ); Assert.assertEquals( 1, latch.getCount() );
latch.countDown(); latch.countDown();
@@ -50,14 +64,14 @@ public class EventPriorityTest
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH)
public void onHighPriority(PriorityTestEvent event) public void onHighPriority(PriorityTestEvent event)
{ {
Assert.assertEquals( 2, latch.getCount() ); Assert.assertEquals( 3, latch.getCount() );
latch.countDown(); latch.countDown();
} }
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
public void onLowPriority(PriorityTestEvent event) public void onLowPriority(PriorityTestEvent event)
{ {
Assert.assertEquals( 4, latch.getCount() ); Assert.assertEquals( 5, latch.getCount() );
latch.countDown(); latch.countDown();
} }
} }

View File

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

20
module/cmd-alert/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-alert</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_alert</name>
<description>Provides the alert and alertraw commands</description>
</project>

View File

@@ -1,9 +1,8 @@
package net.md_5.bungee.command; package net.md_5.bungee.module.cmd.alert;
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.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Command;
public class CommandAlert extends Command public class CommandAlert extends Command

View File

@@ -0,0 +1,55 @@
package net.md_5.bungee.module.cmd.alert;
import com.google.common.base.Joiner;
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.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.chat.ComponentSerializer;
public class CommandAlertRaw extends Command
{
public CommandAlertRaw()
{
super( "alertraw", "bungeecord.command.alert" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ChatColor.RED + "You must supply a message." );
} else
{
String message = Joiner.on( ' ' ).join( args );
try
{
ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) );
} catch ( Exception e )
{
Throwable error = e;
while ( error.getCause() != null )
{
error = error.getCause();
}
if ( sender instanceof ProxiedPlayer )
{
sender.sendMessage(
new ComponentBuilder( "An error occurred while parsing your message. (Hover for details)" ).
color( ChatColor.RED ).underlined( true ).
event( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( error.getMessage() ).color( ChatColor.RED ).create() ) ).
create() );
} else
{
sender.sendMessage( new ComponentBuilder( "An error occurred while parsing your message: " ).color( ChatColor.RED ).append( error.getMessage() ).create() );
}
}
}
}
}

View File

@@ -0,0 +1,14 @@
package net.md_5.bungee.module.cmd.alert;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginAlert extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandAlert() );
getProxy().getPluginManager().registerCommand( this, new CommandAlertRaw() );
}
}

View File

@@ -0,0 +1,5 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.alert.PluginAlert
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

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

20
module/cmd-find/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-find</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_find</name>
<description>Provides the find command</description>
</project>

View File

@@ -1,10 +1,10 @@
package net.md_5.bungee.command; package net.md_5.bungee.module.cmd.find;
import java.util.Collections;
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.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.command.PlayerCommand;
public class CommandFind extends PlayerCommand public class CommandFind extends PlayerCommand
{ {
@@ -32,10 +32,4 @@ public class CommandFind extends PlayerCommand
} }
} }
} }
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
return ( args.length == 0 ) ? super.onTabComplete( sender, args ) : Collections.EMPTY_LIST;
}
} }

View File

@@ -0,0 +1,13 @@
package net.md_5.bungee.module.cmd.find;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginFind extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandFind() );
}
}

View File

@@ -0,0 +1,5 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.find.PluginFind
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

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

20
module/cmd-list/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-list</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_list</name>
<description>Provides the glist command</description>
</project>

View File

@@ -1,7 +1,6 @@
package net.md_5.bungee.command; package net.md_5.bungee.module.cmd.list;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import net.md_5.bungee.Util; import net.md_5.bungee.Util;
@@ -33,31 +32,16 @@ public class CommandList extends Command
continue; continue;
} }
Collection<ProxiedPlayer> serverPlayers = server.getPlayers();
StringBuilder message = new StringBuilder();
message.append( ChatColor.GREEN );
message.append( "[" );
message.append( server.getName() );
message.append( "] " );
message.append( ChatColor.YELLOW );
message.append( "(" );
message.append( serverPlayers.size() );
message.append( "): " );
message.append( ChatColor.RESET );
List<String> players = new ArrayList<>(); List<String> players = new ArrayList<>();
for ( ProxiedPlayer player : serverPlayers ) for ( ProxiedPlayer player : server.getPlayers() )
{ {
players.add( player.getDisplayName() ); players.add( player.getDisplayName() );
} }
Collections.sort( players, String.CASE_INSENSITIVE_ORDER ); Collections.sort( players, String.CASE_INSENSITIVE_ORDER );
message.append( Util.format( players, ChatColor.RESET + ", " ) ); sender.sendMessage( ProxyServer.getInstance().getTranslation( "command_list", server.getName(), server.getPlayers().size(), Util.format( players, ChatColor.RESET + ", " ) ) );
sender.sendMessage( message.toString() );
} }
sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players" ) + ProxyServer.getInstance().getOnlineCount() ); sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players", ProxyServer.getInstance().getOnlineCount() ) );
} }
} }

View File

@@ -0,0 +1,13 @@
package net.md_5.bungee.module.cmd.list;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginList extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandList() );
}
}

View File

@@ -0,0 +1,5 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.list.PluginList
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

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

20
module/cmd-send/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-send</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_send</name>
<description>Provides the gsend command</description>
</project>

View File

@@ -0,0 +1,130 @@
package net.md_5.bungee.module.cmd.send;
import com.google.common.collect.ImmutableSet;
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.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class CommandSend extends Command implements TabExecutor
{
public CommandSend()
{
super( "send", "bungeecord.command.send" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length != 2 )
{
sender.sendMessage( ChatColor.RED + "Not enough arguments, usage: /send <server|player|all|current> <target>" );
return;
}
ServerInfo target = ProxyServer.getInstance().getServerInfo( args[1] );
if ( target == null )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
return;
}
if ( args[0].equalsIgnoreCase( "all" ) )
{
for ( ProxiedPlayer p : ProxyServer.getInstance().getPlayers() )
{
summon( p, target, sender );
}
} else if ( args[0].equalsIgnoreCase( "current" ) )
{
if ( !( sender instanceof ProxiedPlayer ) )
{
sender.sendMessage( ChatColor.RED + "Only in game players can use this command" );
return;
}
ProxiedPlayer player = (ProxiedPlayer) sender;
for ( ProxiedPlayer p : player.getServer().getInfo().getPlayers() )
{
summon( p, target, sender );
}
} else
{
// If we use a server name, send the entire server. This takes priority over players.
ServerInfo serverTarget = ProxyServer.getInstance().getServerInfo( args[0] );
if ( serverTarget != null )
{
for ( ProxiedPlayer p : serverTarget.getPlayers() )
{
summon( p, target, sender );
}
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null )
{
sender.sendMessage( ChatColor.RED + "That player is not online" );
return;
}
summon( player, target, sender );
}
}
sender.sendMessage( ChatColor.GREEN + "Successfully summoned player(s)" );
}
private void summon(ProxiedPlayer player, ServerInfo target, CommandSender sender)
{
if ( player.getServer() != null && !player.getServer().getInfo().equals( target ) )
{
player.connect( target );
player.sendMessage( ChatColor.GOLD + "Summoned to " + target.getName() + " by " + sender.getName() );
}
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
if ( args.length > 2 || args.length == 0 )
{
return ImmutableSet.of();
}
Set<String> matches = new HashSet<>();
if ( args.length == 1 )
{
String search = args[0].toLowerCase();
for ( ProxiedPlayer player : ProxyServer.getInstance().getPlayers() )
{
if ( player.getName().toLowerCase().startsWith( search ) )
{
matches.add( player.getName() );
}
}
if ( "all".startsWith( search ) )
{
matches.add( "all" );
}
if ( "current".startsWith( search ) )
{
matches.add( "current" );
}
} else
{
String search = args[1].toLowerCase();
for ( String server : ProxyServer.getInstance().getServers().keySet() )
{
if ( server.toLowerCase().startsWith( search ) )
{
matches.add( server );
}
}
}
return matches;
}
}

View File

@@ -0,0 +1,13 @@
package net.md_5.bungee.module.cmd.send;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginSend extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandSend() );
}
}

View File

@@ -0,0 +1,5 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.send.PluginSend
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

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

20
module/cmd-server/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.8-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-server</artifactId>
<version>1.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_server</name>
<description>Provides the server command</description>
</project>

View File

@@ -1,4 +1,4 @@
package net.md_5.bungee.command; package net.md_5.bungee.module.cmd.server;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@@ -11,6 +11,11 @@ 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.Command; import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor; import net.md_5.bungee.api.plugin.TabExecutor;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
/** /**
* Command to list and switch a player between available servers. * Command to list and switch a player between available servers.
@@ -34,22 +39,26 @@ public class CommandServer extends Command implements TabExecutor
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() ); player.sendMessage( ProxyServer.getInstance().getTranslation( "current_server", player.getServer().getInfo().getName() ) );
TextComponent serverList = new TextComponent( ProxyServer.getInstance().getTranslation( "server_list" ) );
StringBuilder serverList = new StringBuilder(); serverList.setColor( ChatColor.GOLD );
boolean first = true;
for ( ServerInfo server : servers.values() ) for ( ServerInfo server : servers.values() )
{ {
if ( server.canAccess( player ) ) if ( server.canAccess( player ) )
{ {
serverList.append( server.getName() ); TextComponent serverTextComponent = new TextComponent( first ? server.getName() : ", " + server.getName() );
serverList.append( ", " ); int count = server.getPlayers().size();
serverTextComponent.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder( count + ( count == 1 ? " player" : " players" ) + "\n" )
.append( "Click to connect to the server" ).italic( true )
.create() ) );
serverTextComponent.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) );
serverList.addExtra( serverTextComponent );
first = false;
} }
} }
if ( serverList.length() != 0 ) player.sendMessage( serverList );
{
serverList.setLength( serverList.length() - 2 );
}
player.sendMessage( ProxyServer.getInstance().getTranslation( "server_list" ) + serverList.toString() );
} else } else
{ {
ServerInfo server = servers.get( args[0] ); ServerInfo server = servers.get( args[0] );
@@ -67,14 +76,16 @@ public class CommandServer extends Command implements TabExecutor
} }
@Override @Override
public Iterable<String> onTabComplete(final CommandSender sender, String[] args) public Iterable<String> onTabComplete(final CommandSender sender, final String[] args)
{ {
return ( args.length != 0 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServers().values(), new Predicate<ServerInfo>() return ( args.length > 1 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServers().values(), new Predicate<ServerInfo>()
{ {
private final String lower = ( args.length == 0 ) ? "" : args[0].toLowerCase();
@Override @Override
public boolean apply(ServerInfo input) public boolean apply(ServerInfo input)
{ {
return input.canAccess( sender ); return input.getName().toLowerCase().startsWith( lower ) && input.canAccess( sender );
} }
} ), new Function<ServerInfo, String>() } ), new Function<ServerInfo, String>()
{ {

View File

@@ -0,0 +1,13 @@
package net.md_5.bungee.module.cmd.server;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginServer extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandServer() );
}
}

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