2
0
mirror of https://github.com/proxiodev/RedisBungee.git synced 2026-05-03 11:40:29 +00:00

283 Commits

Author SHA1 Message Date
4221ebb892 Update readme to reflect the new project name 2026-03-18 21:16:24 +04:00
d93076eb81 maintenance: jitpack and github action move to java 21 2026-03-17 00:35:29 +04:00
1d2c11c538 maintenance: velocity require java 21 now for 3.5.0 2026-03-17 00:31:36 +04:00
e495b11587 maintenance: fix javadocs of bungeecord and update to 5 instead of 4 2026-03-17 00:28:08 +04:00
b250776c82 maintenance: remove java @Nullable parameter from Configuration class 2026-03-17 00:18:01 +04:00
13cfe8db13 maintenance: move to blossom v2 and use indragit to get git info 2026-03-17 00:17:12 +04:00
d30c70c8c4 maintenance: update versions
blossom update to 2v include indragit
2026-03-17 00:15:36 +04:00
3aa76384c3 maintenance: update gradle wrapper 2026-03-17 00:14:10 +04:00
92c965bfc6 readd leagacy methods to bungeecord only 2025-02-24 23:20:02 +04:00
1fb429ea77 finishup bungeecord, move to mini message as serialzier 2025-02-22 19:04:06 +04:00
338297192c reimpl kick api using serialized messages instead, Move language to own module, depenedcies fixes 2025-02-22 17:57:06 +04:00
1d3bd7e101 remove depercated apis | begin removing adventure api from the main api 2025-02-22 15:43:49 +04:00
96c0dff8c1 debug command introduction 2025-02-22 15:05:43 +04:00
Pierre-Olivier GOIN
23aeb81308 database address isn't the same in player manager (#127)
Maintain the same name in the Redis database as in the ProxyDataManager.

redis-bungee -> redisbungee
2025-02-22 11:08:40 +04:00
Pierre-Olivier GOIN
c84b987b9e Duplicated RB-Velocity in Gradle Settings (#128) 2025-02-22 11:05:49 +04:00
8177260991 make platforms can't see each other when using same redis server and network id 2025-02-21 19:44:52 +04:00
287f037774 Remove generics used for events in PlayerData 2025-01-28 20:14:45 +04:00
c633f1a106 properly cache last online and its invalidation 2025-01-28 20:14:45 +04:00
9fd1da5f92 fix depenedcies being copied 2025-01-28 20:14:45 +04:00
0050575aff update okhttp 2025-01-28 20:14:45 +04:00
6eab4ef602 move gradle plugins to libs file 2025-01-28 20:14:44 +04:00
12acc16376 build system improvement & update adventure 2025-01-28 20:14:44 +04:00
8b9af8838d Fix methods of Bungeecord listener being private
seems like i forgot to check the bungeecord impl correctly.
2025-01-28 20:14:44 +04:00
Dawid Sawicki
81bf06e2df Allow to customize ping events priority via handle-motd-order (#108)
Currently, the bungee implementation is using NORMAL priority for the
ping event handler, while the velocity implementation is using LAST.
Regardless of which choice may be the better one, this is an
inconsistency that this patch addresses by using NORMAL as the default
for both platforms.

Additionally to addressing the inconsistency, this patch adds a new
config option `handle-motd-order` which uses velocity's event priority
nomenclature to allow configuring the behavior of the MOTD handling on
both platforms.

In cases where there is a MOTD plugin that incorrectly overrides a
player count using the local player count, one may choose to use order
LAST to override the value back to the global player count.

In cases where there is a MOTD plugin that relies on a player count
value from the ping response, one may choose to use order FIRST to make
sure the response will have the correct global player count.

Fixes https://github.com/ProxioDev/ValioBungee/issues/107
2025-01-28 20:14:44 +04:00
41b1dab8cc update velocity runner 2025-01-28 20:14:44 +04:00
a437db32b8 update gradle, update depends 2025-01-28 20:14:44 +04:00
2bd79f628e runWaterfall & Waterfall proxy EOL 2025-01-28 20:14:44 +04:00
93c1cd8e4c 0.13.0-SNAPSHOT 2025-01-28 20:14:44 +04:00
f27d54beb8 0.12.6 2025-01-28 12:09:06 +04:00
91ea0b08dc relocate adventure api in bungeecord due collison with other plugins 2025-01-28 12:08:10 +04:00
199c1c7135 0.12.5 2024-09-28 12:34:23 +04:00
dab5f26e2c fix event double firing in velocity 2024-09-26 19:24:38 +04:00
c622bc7b63 move destroyProxyMembers to correct place
fixes null network id
2024-09-26 19:10:32 +04:00
881691a92d gradle wrapper upgrade, update bungeecord 2024-09-26 19:09:51 +04:00
079606c9da include new branches name in github file 2024-09-26 16:08:43 +04:00
ea54a0bc49 Update gradle.yml 2024-09-26 16:05:17 +04:00
69e91c3e42 check if player is really on the proxy when connecting
this prevents logged in error if somehow proxy shutdowns at weird time
2024-09-26 13:43:33 +04:00
Joël | NoPermission
d8704c8a8f Fixed null when fetching server id. (#118)
(cherry picked from commit 219a4ab360)
2024-09-26 12:18:46 +04:00
Efe Kurban
981d42d4a8 Added null-check for server keys (#106)
Fixes https://github.com/ProxioDev/ValioBungee/issues/105

(cherry picked from commit be0c6be2aa)
2024-09-26 12:18:24 +04:00
e0bca62cdb reintroduce 1 hour cache 2024-05-18 15:02:14 +04:00
md5nake
9ebfafbeef Invalidate serversToPlayersCache on player updates (#103)
This closes #102
2024-05-18 14:59:24 +04:00
70eebdc9ec Revert "reintroduce 1 hour cache, remove servers to player cache due changes can happen fast."
This reverts commit e85e18dad8.
2024-05-18 14:58:02 +04:00
e85e18dad8 reintroduce 1 hour cache, remove servers to player cache due changes can happen fast. 2024-05-18 14:46:23 +04:00
995c9045df 0.12.4 | fix send command to proxies not executing on sender proxy 2024-05-16 02:57:49 +04:00
2485150ddc 0.12.3 2024-05-15 22:08:18 +04:00
32735466d6 Deprecate old apis for removal in 0.13.0 2024-05-14 20:52:45 +04:00
e8715e5399 ignore IllegalStateException thrown by ServerConnection class in velocity 2024-05-14 19:23:09 +04:00
71287055b4 fix javadocs x2 2024-05-13 02:43:47 +04:00
8b48736bc1 fix javadocs 2024-05-13 02:38:58 +04:00
a2e6aff4c2 remove java docs from proxy impl 2024-05-13 02:33:56 +04:00
7c1c1183cf update gitignore 2024-05-13 02:27:44 +04:00
7f35b64d93 stupid git 2024-05-13 02:00:31 +04:00
3dc3d80045 update github workflow 2024-05-13 01:56:38 +04:00
765e6fe122 redo module system 2024-05-13 01:54:50 +04:00
e8514b3e8b 0.12.3 snapshot 2024-05-13 01:02:29 +04:00
e1d401639e even more oops: why caches are set to 1 hour ._. 2024-05-07 20:42:33 +04:00
f8c304d441 oops: remove cache of last-online
i must forgotten to remove it during development
2024-05-07 20:37:16 +04:00
e6b789229c bump version 0.12.2 2024-05-06 21:30:55 +04:00
025b555457 maven publish commands aswell 2024-05-06 21:27:37 +04:00
7029552c02 clarify compatibility mode error 2024-05-06 15:24:10 +04:00
62007992a7 bump 0.12.2-snapshot 2024-05-06 15:20:34 +04:00
8a6d97e923 bump to 0.12.1 2024-04-28 23:33:07 +04:00
a65b1cdf5c fix: adventure api version was replaced by guava 2024-04-28 23:32:08 +04:00
25441a5122 update .github/workflow/gradle.yml branch name 2024-04-28 23:18:01 +04:00
b9c7c31c09 oops: forgot to cache uuids/name of joined player 2024-04-28 23:16:19 +04:00
27358d2f5f change big uuid cache message to include the '/rb clean' 2024-04-28 23:16:18 +04:00
c736f39e7f bump version to 0.12.1-snapshot 2024-04-28 23:16:18 +04:00
7b90a34fae Update README.md 2024-04-28 20:36:09 +04:00
1593c2d628 0.12.0 (#86)
## NOTES
data system shouldn't effect anybody, unless you do any direct query to
Redis query data, you should adapt the changes, by viewing classes
`ProxyDataManager` and `PlayerDataManager`

# Changes 
* RedisBungee is compiled with `java 17` now, Due java 11 support is
ending at end of September
* config version is now `2` which will reset your config if older
version
* Adventure API is included inside RedisBungee API
* new Language infrastructure for RedisBungee built-in messages #85
*commands not included yet*
* New data system which replaces Redis PubSub with Redis Streams *see
below*
* Ability to connect player to last server they where on using an config
option #84
* new environment variable `REDISBUNGEE_PROXY_ID` which can be set
before launch
* new environment variable `REDISBUNGEE_NETWORK_ID` which can be set
before launch
* RedisBungee requires redis version 6.2 or above  #88 
* Better command system
https://github.com/ProxioDev/RedisBungee/issues/93

## New data system
Due limitation of Redis PubSub in Cluster environment, Internals of
RedisBungee were changed to support Redis Streams
- Network Ids
  - networks ids used to group network proxies
    - example having 'test' network and 'main' network
- Networks in the same redis server / cluster share the same UUID cache
- Heartbeat system:
- RedisBungee old heartbeat system used hastset on redisbungee to store
the current unix time of the proxy to check what every proxy died or
not, now instead we publish the heartbeat using unix time, and online
count to proxy which proxy store it in their memory, which allow the
`get number of online players` to be faster than pooling whole list in
old data system.

- PubSub
- since redisbungee was initially designed with pubsub in mind,
registration no longer required now for event to fire, see the api
changes below.

## Commands System
* rewritten using [acf lib](https://github.com/aikar/commands) to be
platform independent
* new command `/rb` or `/redisbungee` with sub commands `help`, `info`,
'clean', 'show'.

* 'rb'
  * '/rb' and '/rb info'

![image](https://github.com/ProxioDev/RedisBungee/assets/34905970/70796ab0-b5fd-4578-8c93-c976e517df95)

  * '/rb show'
  

![image](https://github.com/ProxioDev/RedisBungee/assets/34905970/56332c37-701f-43e0-946b-6894b845fab3)

* configuration to disable or override each command from legacy to new
introduced one `/rb`
```yaml

# For redis bungee legacy commands
# either can be run using '/rbl glist' for example
# or if 'install' is set to true '/glist' can be used.
# 'install' also overrides the proxy installed commands
#
# In legacy commands each command got it own permissions since they had it own permission pre new command system,
# so it's also applied to subcommands in '/rbl'.
commands:
  # Permission redisbungee.legacy.use
  redisbungee-legacy:
    enabled: false
    subcommands:
        # Permission redisbungee.command.glist
        glist:
          enabled: false
          install: false
        # Permission redisbungee.command.find
        find:
          enabled: false
          install: false
        # Permission redisbungee.command.lastseen
        lastseen:
          enabled: false
          install: false
        # Permission redisbungee.command.ip
        ip:
          enabled: false
          install: false
        # Permission redisbungee.command.pproxy
        pproxy:
          enabled: false
          install: false
        # Permission redisbungee.command.sendtoall
        sendtoall:
          enabled: false
          install: false
        # Permission redisbungee.command.serverid
        serverid:
          enabled: false
          install: false
        # Permission redisbungee.command.serverids
        serverids:
          enabled: false
          install: false
       # Permission redisbungee.command.plist
        plist:
          enabled: false
          install: false
  # Permission redisbungee.command.use
  redisbungee:
    enabled: true
```

## API changes
- Kick api Deprecated: 
  - `kickPlayer(String playerName, String message) `
  - `kickPlayer(UUID playerUUID, String message) `

- newer where added using adventure api:
  - `kickPlayer(String playerName, Component message) `
  - `kickPlayer(UUID playerUUID, Component message) `

-  PubSub registration api Deprecated:
```java
/**
     * Register (a) PubSub channel(s), so that you may handle PubSubMessageEvent for it.
     *
     * @param channels the channels to register
     * @since 0.3
     * @deprecated No longer required
     */
    @Deprecated
    public final void registerPubSubChannels(String... channels) {
    }

    /**
     * Unregister (a) PubSub channel(s).
     *
     * @param channels the channels to unregister
     * @since 0.3
     * @deprecated No longer required
     */
    @Deprecated
    public final void unregisterPubSubChannels(String... channels) {
    }

```
# Contributors

* `summoncraft.us` for running this branch in production
* @SrBedrock for providing [Brazilian
Portuguese](https://en.wikipedia.org/wiki/Brazilian_Portuguese)
translation #87
 
# issues
closes #84 
closes #88 
closes #92 
closes #81
closes #93

---------

Signed-off-by: mohammed jasem alaajel <xrambad@gmail.com>
Co-authored-by: ThiagoROX <51332006+SrBedrock@users.noreply.github.com>
2024-04-28 15:29:53 +04:00
5c4de82714 jitpack java 17] 2024-04-12 22:28:25 +04:00
9e48e472a6 0.11.4: maintenance update 2024-04-12 22:08:44 +04:00
b04e13136b github worker java 17 2024-04-12 22:05:30 +04:00
8821d3995c rename github workflow 2024-04-12 21:49:54 +04:00
9218e6ad42 update jedis 2024-04-12 21:42:32 +04:00
fa6bcf7cb8 update to 3.3.0-SNAPSHOT velocity 2024-04-12 19:34:20 +04:00
830b930394 credit source of ISSUE_TEMPLATE 2024-04-12 18:56:20 +04:00
450b0ee396 new issue template 2024-04-12 18:55:51 +04:00
dd4cc2a5bb remove matrix support 2023-12-27 02:07:33 +04:00
fd3aa51288 bump version 0.11.3 (#83) 2023-07-18 04:53:10 +04:00
Daniël Voort
99941c733f Use PlayerDB.co for name lookups (#82)
I noticed that currently name lookups for UUIDs that are not cached, are
not supported in RedisBungee due to Mojang removing name history.

When looking at the usages, RedisBungee internally only uses the current
name (So no need for full name history), so I moved name lookups to
[PlayerDB](https://playerdb.co/), which does not have API rate limits
unlike Mojang's API.

closes #59
2023-07-18 04:48:20 +04:00
7fb9c4689e 0.11.2 (#77)
- update gradle wrapper

- include a copy of RedisBungee LICENSE inside API resources folder.

- Fix depends issues on Older versions of Bungeecord proxy *travertine*

- Bump version 0.11.2
2023-06-03 15:30:48 +04:00
5066a18dd7 uncomment disable-kick-when-online in the config and make it false by default (#75)
closes #74
2023-05-02 20:14:58 +04:00
265933f36e fix Velocity plugin startup / shutdown issues, java docs notes for some classes and logs for shutdown / startup (#73)
closes #71
2023-04-25 11:09:45 +04:00
mohammed Alteniji
9a583369e8 oops forgot to fix repo for gradle kotlin dsl 2023-04-16 23:02:36 +04:00
mohammed Alteniji
d9e5a21c14 Update README.md 2023-04-16 22:58:41 +04:00
mohammed Alteniji
4f43c98c87 reorder config.yml of redisbungee 2023-04-15 21:29:06 +04:00
26b58252eb oops remove password warn from the username 2023-04-13 03:06:16 +04:00
mohammed Alteniji
73879640e5 add ability to use redisbungee with acl username (#69) 2023-04-12 22:40:01 +04:00
mohammed Alteniji
c5040c9348 Update README.md 2023-04-12 20:26:06 +04:00
mohammed Alteniji
9ca72ff921 Update README.md 2023-04-10 01:56:35 +04:00
9050264b4d make velocity ping event execute as Last to prevent motd plugins changing online players 2023-04-10 01:48:20 +04:00
mohammed Alteniji
5b5f748cc9 fix wrong maven publishing error 2023-04-06 19:26:37 +04:00
mohammed Alteniji
47127c8520 Javadocs links update (#67) 2023-03-25 19:46:22 +04:00
mohammed Alteniji
b857bdb771 update to gradle (#66) 2023-03-23 15:42:59 +04:00
0f0f707ef7 bump version 2023-03-17 12:23:46 +04:00
441a12bb36 fix error being thrown getServerFor in Bungeecord/Velocity in the api closes #64
when plugins messing around with proxy internals (eg: limboapi) the field server in redis data of the player set to null which can become problematic for velocity due checking of null in velocity but i applied the same stuff to the bungeecord version aswell and document it in the javadocs
2023-03-17 12:15:10 +04:00
冰砚炽
0534970368 Fix PlayerList not returning server name when it is not ALL (#62) 2023-01-31 12:40:06 +04:00
20f9143ea5 bump version 2022-12-31 07:26:05 +04:00
1a2459b64e deprecated NameFetcher api 2022-12-31 07:23:35 +04:00
AlessioDP
c3888c8f65 Fix NullPointerException due plugin instance for jedis tasks in UUIDTranslator/AbstractDataManager (#57) 2022-12-25 06:26:53 +04:00
c8362a44ec add config option to restore old kick behavior pre 0.9.0 2022-11-27 12:29:13 +04:00
31e461a11c change url of big uuid cache 2022-11-27 12:10:29 +04:00
a9ea04c2c0 missed location were it wasn't using the constant 2022-11-24 01:41:47 +04:00
ddfc689c2d expose CachedUUIDEntry Class 2022-11-16 11:32:20 +04:00
ae6961ef24 bump version 2022-11-16 08:22:57 +04:00
8318bcd1bf update some logs in configloader 2022-11-16 08:22:30 +04:00
0b9fd6d7ff Make sure server field is withen map than calling redis seperatly 2022-11-14 08:45:55 +04:00
a526298d1c change one of the comments in PlayerUtils 2022-11-14 08:42:54 +04:00
c69b1e214e add missing final keyword to PROXY_TIMEOUT var 2022-11-14 08:40:46 +04:00
e5f0075a58 add messages config, change some jedis/redis depercated apis, bump to 0.9.0, new logging from another locations handling. 2022-11-11 20:30:16 +04:00
748bc13568 Fix ssl connections on PooledConnections
useSSL in configloader wasn't passing it to the ConnectionProvider
2022-11-05 15:44:51 +04:00
5e3ce725de Include some changes to ssl notes
and little change for compatiblity pool
2022-11-05 15:43:23 +04:00
92bb0030de move PlayerUtils code from platforms to the api 2022-11-02 08:38:25 +04:00
d8c21edc7a Bump version to 0.8.1 2022-11-02 07:58:13 +04:00
87a2b93537 update jitpack location 2022-11-01 10:13:36 +04:00
0f5d5b2440 Fixed Typos caused JedisPooled to be JedisCluster withen the abstract api 2022-11-01 09:59:08 +04:00
21f543581c add jitpack.yml
should fix the velocity not compiling on jitpack
2022-10-31 15:17:52 +04:00
bc266ae1fa Update javadocs for Jedis Cluster/Pooled instances 2022-10-30 00:10:56 +04:00
8dc42d071a Add copyright header to source code 2022-10-30 00:02:09 +04:00
71b959936e Move cluster summoner to Provider cluster 2022-10-25 15:52:50 +04:00
28419b3168 remove git versions 2022-10-25 08:18:30 +04:00
a382a298a1 Make summoner create customized JedisPooled that can't be closed 2022-10-25 08:04:48 +04:00
4d58ee1742 add todo list for later into config loader 2022-10-19 07:46:59 +04:00
af4e180b2c fix nullpointer in JedisPooledSummoner
Jedis pool when null it calls shutdown so added an check to check if its null
2022-10-17 08:27:55 +04:00
c1ad902bd3 upgrade jedis 2022-10-15 18:13:44 +04:00
74ed18e9b3 Make sure to run LoginEvent Last fixes #48 2022-09-14 08:09:43 +04:00
a51ff98909 update readme 2022-08-02 17:52:34 +04:00
80a4791d12 api changes in events, move Listener serialization into Util class 2022-07-30 22:36:29 +04:00
fdd537b276 fix javadocs again, add getServerFor in velocity 2022-07-27 19:06:57 +04:00
17897bc112 make RedisBungeeAPI class platform dependant and make Abstract version of it 2022-07-27 17:43:51 +04:00
576bcc39d2 update readme 2022-07-26 20:52:42 +04:00
508125023e update readme 2022-07-26 20:46:32 +04:00
43b2d20e39 update readme 2022-07-26 20:45:39 +04:00
64af12044e Change Plugin instance of Bungeecord to RedisBungee to maintain old plugin compatibility 2022-07-26 18:42:29 +04:00
2ae9b5d480 add log option, check connection for JedisPooled 2022-07-26 17:55:16 +04:00
d77e909e7d block when exhausted, new config options for the compatibility pool max connections 2022-07-26 17:52:00 +04:00
ee76fa0b3d Change Jedis -> JedisPooled, and make tasks use UnifiedJedis
since JedisCluster, JedisPooled are Childern of UnifiedJedis
2022-07-26 17:48:00 +04:00
mohammed Alteniji
f303f2c202 Update README.md 2022-07-26 15:43:56 +04:00
mohammed Alteniji
4e46dc5536 Update README.md 2022-07-26 15:41:57 +04:00
mohammed Alteniji
b58e503ec7 Update README.md 2022-07-26 15:38:01 +04:00
mohammed Alteniji
b16a7d4cbc Update README.md 2022-07-26 15:36:28 +04:00
5a7001a68c remove else when checking if password is null 2022-07-26 15:14:14 +04:00
81a8fd218e move PlayerUtils and change the name 2022-07-26 12:49:44 +04:00
92f5e04edf change method naming of updateProxiesIds 2022-07-26 12:42:42 +04:00
e7b241edd6 git rid of lua system 2022-07-26 12:40:14 +04:00
86c6e9464d internal changes 2022-07-26 10:58:00 +04:00
1828cd854c add config option for later use 2022-07-25 20:04:21 +04:00
6910c96f67 fix mess up in API 2022-07-25 19:13:25 +04:00
8f602407b5 fix java docs again 2022-07-25 18:49:49 +04:00
2c4fc00ec3 single class for loading config 2022-07-25 16:49:57 +04:00
11e867730c this has to be false, if absent 2022-07-23 10:31:12 +04:00
830077282d fix java docs 2022-07-23 08:24:03 +04:00
2b2ae88587 change RuntimeException to IllegalStateException 2022-07-23 08:21:36 +04:00
9ceccb6dd2 fix #38
somehow pull request #39 forgotten to add thrid Legacy channel
2022-07-22 23:52:15 +04:00
dea384a203 change wrong default max connection from 8 to 10 2022-07-22 17:08:51 +04:00
e4012a8d7b change config in readme, move config-version below 2022-07-22 16:12:56 +04:00
2c8c5fc1cf as new config, check if ye wana load legacy commands 2022-07-22 16:10:06 +04:00
5c6cf405fa config changes 2022-07-22 15:12:32 +04:00
0408e2923b update to config handling, fix null pointer in pubsub handler, config changes
rename config in jar, config api, fixed null pointer in pubsub listener, use map allow muiltable port and hosts for the cluster
2022-07-22 12:29:39 +04:00
5d1167c20f change original author name, and include it in velocity version 2022-07-21 16:37:32 +04:00
79694fcbb2 fix some typos in readme 2022-07-21 16:26:16 +04:00
1d889f28b9 update readme 2022-07-21 16:23:07 +04:00
f274301036 add firePayload for cleanup as its used in integerty check 2022-07-21 09:36:10 +04:00
c787c76eca fix null being passed into jedis on ServerConnectedEvent 2022-07-20 13:25:26 +04:00
9da5845da3 move Heartbeat task into its own class 2022-07-20 13:21:24 +04:00
4d3f1a551e General improvements, remove player cleanup from RedisUtils class and bug fixes
fixed bug where PlayerNetoworkJoin is not fireing, and also fixed wrong arguments being passed on server change network event, Moved Payload creation to it own Util class, Remove player creation from RedisUtils class and move it to GenericPlayerUtils, and also renamed Bungeecord/Velocity Player Util classes to <PLATFORM>PlayerUtils.java, removed the use of reflection to create event instances
2022-07-20 12:39:10 +04:00
17e6e12c24 oops, bungeecord code was not updated to handle kick if player inside same proxy 2022-07-19 15:43:58 +04:00
c207b4a0a2 oops, removed leftover debug stuff 2022-07-19 15:36:42 +04:00
c848126aa7 Implement network kick 2022-07-19 15:30:45 +04:00
f722b8c9d3 fix glist command when UUID to name translation fails by returning the uuid 2022-07-19 07:41:21 +04:00
d806bc2d41 reduce integrity check to run from 1 min to 30 seconds 2022-07-19 07:21:17 +04:00
2e8342f5c3 serverToPlayers was getting injected with null values as a server 2022-07-19 07:19:32 +04:00
SirSalad
c125137a74 Upload other artifacts (#41) 2022-07-19 04:30:39 +04:00
aff185a85b last bug was caused by confusion due Proxy Called server in some cases Which resulted in new API methods and some refactor 2022-07-17 15:38:00 +04:00
482dfc5141 oops, fixed bug where player servers would return the proxy than server 2022-07-17 15:14:46 +04:00
6a6e303334 updated config, added supported for velocity 2022-07-17 09:51:24 +04:00
a9600a7d8a check whatever password is empty or not 2022-07-17 09:14:39 +04:00
36136d0c0f remove leftover debug from cluster testing and handle config correctly 2022-07-17 08:31:55 +04:00
319b5eb553 pom file relocation was changed by refactor undoing.. 2022-07-17 07:44:00 +04:00
38de948c6b updated bungeecord to support Redis cluster 2022-07-17 07:41:18 +04:00
019bb30c09 remove lua files and do it in java, due Redis not supporting lua quite well in cluster mode, some scripts were added recently will be kept 2022-07-17 07:12:57 +04:00
f1f74b6456 check connection in JedisClusterSummoner too 2022-07-17 05:40:32 +04:00
b0ab5e3cb4 readd support for redis versions below 6, change method of the summoner 2022-07-17 05:25:50 +04:00
b214e3dad7 change internal package to api 2022-07-16 16:50:09 +04:00
81a2af8179 add JedisCLuster methods to the api 2022-07-16 16:32:10 +04:00
b910fa5eb6 add new lua scripts for cluster info/time requesting 2022-07-16 10:14:06 +04:00
b15b9dbb96 create constructor with plugin parameter in RedisClass 2022-07-16 09:39:36 +04:00
44f9a0945d API converted to support RedisCluster 2022-07-16 09:18:33 +04:00
e986d5f1fb change something again in readme 2022-07-16 07:09:04 +04:00
ce93bd5e07 change section of usage 2022-07-16 07:06:30 +04:00
870e6113db readme: fix refernce to the closed pull request 2022-07-16 06:49:10 +04:00
86b0904546 readme: add disclaimer to support version of velocity, fix typo in velocity setup 2022-07-16 06:48:00 +04:00
f4dbb00991 change config option 2022-07-16 03:00:51 +04:00
b6e4badc05 readd removed methods to maintain old plugins, prepare for redis cluster mode 2022-07-16 02:58:48 +04:00
78ae6ff7b6 update readme javadocs section 2022-07-16 02:33:58 +04:00
87aaf22073 add to makeJavaDocs 2022-07-16 02:22:46 +04:00
a74e4b6a15 change pom vars due deprecation 2022-07-16 02:21:20 +04:00
d6d8c30343 fix java docs 2022-07-16 02:15:34 +04:00
933fceba9d add velocity maven setup, update bungeecord section 2022-07-16 02:12:05 +04:00
a8b369dde3 Revert "add builder helper to handle compile errors in velocity version template plugin"
This reverts commit 414f29e99c.
2022-07-16 00:37:47 +04:00
414f29e99c add builder helper to handle compile errors in velocity version template plugin 2022-07-15 11:00:04 +04:00
Adrian
21f6cdeaeb re-implement the commands, right usage of logger, general improvements to the listener (#39)
Co-authored-by: mohammed jasem alaajel <xrambad@gmail.com>
2022-07-15 10:52:53 +04:00
5f07ba6b66 fix the worker file 2022-07-15 10:13:08 +04:00
673b966dda Revert "remove workflows as it stoped working months ago due unknown reason"
This reverts commit 69d435d455.
2022-07-15 10:11:44 +04:00
69d435d455 remove workflows as it stoped working months ago due unknown reason 2022-07-15 10:05:01 +04:00
3a8baae56f change versioning scheme on both velocity + bungeecord 2022-07-15 09:57:30 +04:00
803ae36d00 for some reason last commit did not include the subscribe as i should added it 2022-07-15 05:38:30 +04:00
ceef1f5184 rename listener classes depending on the platform, readd plugin message back #38 2022-07-15 03:05:53 +04:00
cd4f2aded4 Revert "fix bungeecord incorrect channel names"
This reverts commit d8a220bcc2.
2022-07-15 01:43:22 +04:00
d8a220bcc2 fix bungeecord incorrect channel names 2022-07-15 01:35:54 +04:00
mohammed Alteniji
c3d23a3601 Update bug_report.md 2022-07-11 02:16:13 +04:00
mohammed Alteniji
ea600c5b44 Update README.md 2022-07-09 23:53:50 +04:00
86f2d89cf4 due velocity including some libs by default, had to move depends to api than the parent, and exlude it from being included in shade 2022-07-09 14:43:31 +04:00
70d1aadd56 remove no support 2022-07-08 08:19:50 +04:00
183c227b77 update readme again, intellji markdown preview is bad :/ 2022-07-08 08:17:15 +04:00
640b26067a update readme 2022-07-08 08:15:42 +04:00
660bb239bb update readme 2022-07-08 08:14:22 +04:00
dd9c9e368d update readme 2022-07-08 08:07:50 +04:00
76787455d8 make velocity init on constructor than init method, change runables to lambdas and some generic removeals with supressing unchecked for eval lua 2022-07-08 06:47:50 +04:00
2c11cb179a rename internal method 2022-07-08 03:23:44 +04:00
9f05bd3438 more changes 2022-07-08 02:39:05 +04:00
mohammed Alteniji
c04a911fbe Merge pull request #36 from Booquefius/patch-1 2022-07-07 05:10:34 +04:00
0311a893c7 remove leftover of the removed method javadocs 2022-07-07 04:49:52 +04:00
5a51d5c1b3 oops removed weird copy 2022-07-07 04:41:45 +04:00
afac7a3d51 fixed the config for velocity, revert 2022-07-07 04:16:09 +04:00
bdda99bc81 added Velocity support (NOT TESTED), it will not compile yet due config impl not done yet 2022-07-07 02:24:08 +04:00
ea665fd70f add new generic for velocity 2022-07-07 00:13:11 +04:00
f7285ff4f1 removed get JedisPool, refactored jedis summoners, new SinglePoolJedisSummoner, start version 0.8.0 2022-07-06 23:05:35 +04:00
Booquefius
28d1667fe9 typo 2022-07-03 19:33:44 -04:00
mohammed Alteniji
839c8cd615 Update README.md 2022-07-03 17:31:38 +04:00
mohammed Alteniji
83dff6b280 Update README.md 2022-07-01 01:25:39 +04:00
mohammed Alteniji
0b868ffd6f Update README.md 2022-07-01 01:24:48 +04:00
mohammed Alteniji
d7c8544b99 Update README.md 2022-06-30 15:03:16 +04:00
2391692dd3 bump version to 0.7.3 2022-06-29 18:16:33 +04:00
39c45b3eab change password field in the config 2022-06-29 18:16:16 +04:00
b3bc51b96f FIX mistake in pubsub listener as its resending spam to pubsub 2022-06-29 18:11:19 +04:00
08c4901f47 removed .png file, updated gitignore 2022-06-29 17:47:25 +04:00
a39c4654fb center 2022-06-17 06:10:29 +04:00
2ceac5a079 move supported redis versions up 2022-06-17 06:03:56 +04:00
814dabbb6a update supported redis versions 2022-06-17 06:02:20 +04:00
mohammed Alteniji
1a76c260b8 add fork into the title 2022-06-17 05:08:29 +04:00
24c9407358 remove useless line 2022-06-15 08:10:22 +04:00
eee36923c1 readd commands after i forgotten about them 2022-06-15 06:32:22 +04:00
6e02ab70db remove not needed BungeEvents submodule and merge it to bungee plugin 2022-06-15 06:05:30 +04:00
mohammed Alteniji
9493576067 Merge pull request #30 from Simonsator/develop
Use the correct jitpack dependency in the Readme
2022-05-27 17:05:34 +04:00
mohammed Alteniji
02c9c3c75a Update README.md 2022-05-27 17:05:01 +04:00
Simonsator
0326c4490a Use the correct jitpack dependency
The previously mentioned dependency does not exist. "RedisBungee-Bungee" iwith the group id "com.github.limework.redisbungee" is the correct one
2022-05-26 10:27:28 +02:00
d34db3da44 update readme 2022-05-26 05:02:31 +04:00
5e18c4adb5 update readme 2022-05-26 04:50:08 +04:00
ade1604987 update readme, fix complation, 0.7.2 2022-05-26 00:53:25 +04:00
7b2db2899e update gitignore 2022-05-26 00:42:40 +04:00
bdda7c13c9 update readme, fix relocation 2022-05-25 12:50:26 +04:00
mohammed Alteniji
9696726fb5 Update README.md 2022-05-25 09:11:19 +04:00
86eeacbb1c disable relocation 2022-04-14 21:41:45 +04:00
43fe50a87f javadocs last changes hopefully 2022-04-14 16:26:33 +04:00
dba8dc8ab7 .gitignore 2022-04-14 16:07:52 +04:00
8bfefc1ab0 seperate events 2022-04-14 15:59:02 +04:00
2bc30ce5f3 update git ignore 2022-04-14 15:54:13 +04:00
070e416fac more java docs stuff + added jedis pool method and deprecate it 2022-04-14 14:34:56 +04:00
102f8591ab java docs 2022-04-14 14:16:34 +04:00
4d0222d2ae added some javadocs info + added jedis summoner class for future release 2022-04-14 14:12:47 +04:00
e53f8a02ba add glist command *reset will be added later 2022-04-14 01:56:17 +04:00
08c7d89749 revert payloads due mass errors 2022-04-14 01:40:16 +04:00
d3889d8bd9 forgot to implement some methods 2022-04-14 01:16:31 +04:00
baab263355 add the updated config 2022-04-13 22:26:36 +04:00
df92d15743 comments were removed accdenitly 2022-04-13 22:23:15 +04:00
3461bbcd1b java docs 2022-04-13 22:19:42 +04:00
7de457b6fa finished but untest 2022-04-13 22:17:38 +04:00
9f09ed21f1 more progress 2022-04-13 20:22:07 +04:00
17fdeda147 more progress 2022-04-13 20:08:46 +04:00
61dec7f03c seperate the internals from bungeecord api for easily supporting platform | progress 60% 2022-04-13 17:15:08 +04:00
165ba84791 update depends 2022-04-13 12:50:58 +04:00
b683d5e226 remove mvn wrapper 2022-04-13 12:29:12 +04:00
b27ddb6cf2 update readme 2022-04-13 12:28:10 +04:00
7c8748e262 update readme 2022-04-13 12:22:35 +04:00
mohammed Alteniji
2df0b9fc60 Update README.md 2022-03-22 16:57:07 +04:00
mohammed Alteniji
bd7d0f4d66 Update README.md 2022-03-22 15:28:41 +04:00
45b4ec65df #24 2021-12-20 05:21:53 +04:00
mohammed Alteniji
bdd02043ea Update README.md 2021-11-16 15:03:16 +04:00
mohammed jasem alaajel
e8439950e3 Update README.md 2021-09-19 19:46:54 +04:00
mohammed jasem alaajel
fbed68e1ab Update README.md 2021-09-19 19:43:25 +04:00
mohammed jasem alaajel
a006c343a7 change redis version check method. 2021-08-09 14:16:07 +04:00
mohammed jasem alaajel
c9798e2371 supresss unused warnings 2021-08-09 14:05:02 +04:00
132 changed files with 7098 additions and 3171 deletions

52
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
# used https://github.com/PaperMC/Paper/blob/master/.github/ISSUE_TEMPLATE/behavior-bug-or-plugin-incompatibility.yml as template
name: Bug or incompatibility
description: report issues within RedisBungee
labels: [ "waiting" ]
body:
- type: textarea
attributes:
label: intended behavior
description: what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: what the behavior you actually saw
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: either a video, or way you want to present it.
validations:
required: true
- type: textarea
attributes:
label: Velocity or Bungeecord Versions
description: |
Please provide Proxy version for either Bungeecord or Velocity
validations:
required: true
- type: textarea
attributes:
label: RedisBungee Version & Redis Version
description: |
RedisBungee version is Different from Redis Version!
good example:
Redis Version: 7.2
RedisBungee Version: 0.12.0
in-case you used development branch you can use git commit hash Instead of the version
validations:
required: true
- type: textarea
attributes:
label: Other
description: |
extra information that we might need to know?
validations:
required: false

View File

@@ -1,32 +0,0 @@
---
name: Bug report
about: if you find a bug please report it here...
title: ''
labels: waiting.....
assignees: ham1255
---
**Describe the bug**
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Redis version? it should be at least 6 and above.**
**Bungeecord version or (the bungee fork name eg: waterfall) and your plugins**
**console logs?**
please provide any errors if there any.

View File

@@ -1,10 +0,0 @@
---
name: Question
about: ask your questions here
title: ''
labels: question
assignees: GovindasOM, ham1255
---
## What is your question?

10
.github/ISSUE_TEMPLATE/question.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# used https://github.com/PaperMC/Paper/blob/master/.github/ISSUE_TEMPLATE/behavior-bug-or-plugin-incompatibility.yml as template
name: Ask Question
description: Ask any questions to the developers
labels: [ "question"]
body:
- type: textarea
attributes:
label: Your question?
validations:
required: true

42
.github/workflows/gradle.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: RedisBungee Build
on:
push:
branches: [ stable, develop ]
pull_request:
branches: [ stable, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
java-version: '21'
distribution: 'adopt'
- name: Build with gradle
run: ./gradlew shadowJar
- name: Upload Bungee
uses: actions/upload-artifact@v4.4.0
with:
# Artifact name
name: RedisBungee-Bungee
# Destination path
path: proxies/bungeecord/build/libs/*
- name: Upload Velocity
uses: actions/upload-artifact@v4.4.0
with:
name: RedisBungee-Velocity
path: proxies/velocity/build/libs/*
- name: Upload API
uses: actions/upload-artifact@v4.4.0
with:
name: RedisBungee-API
path: api/build/libs/*

View File

@@ -1,32 +0,0 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: RedisBungee Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'adopt'
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Download a Build Artifact
uses: actions/upload-artifact@v2.2.3
with:
# Artifact name
name: Redis_JAR
# Destination path
path: target/RedisBungee*

47
.gitignore vendored
View File

@@ -1,36 +1,49 @@
# Eclipse stuff # Eclipse stuff
/.classpath .classpath
/.project .project
/.settings .settings
# random files
.png
.jpg
# netbeans # netbeans
/nbproject nbproject
/nbactions.xml nbactions.xml
/nb-configuration.xml nb-configuration.xml
# we use maven! # we use maven!
/build.xml build.xml
# maven # maven
/target target
/dependency-reduced-pom.xml dependency-reduced-pom.xml
*/target
*/dependency-reduced-pom.xml
# vim # vim
.*.sw[a-p] .*.sw[a-p]
# various other potential build files # various other potential build files
/build build
/bin bin
/dist dist
/manifest.mf manifest.mf
# Mac filesystem dust # Mac filesystem dust
/.DS_Store .DS_Store
# intellij # intellij
*.iml *.iml
*.ipr *.ipr
*.iws *.iws
.idea/ .idea/
*.versionBackup
*.versionsBackup
# gradle
.gradle
# java docs
javadoc
# run-server folders
proxies/*/run

View File

@@ -1,117 +0,0 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

Binary file not shown.

View File

@@ -1,2 +0,0 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

View File

@@ -1,77 +1,32 @@
# Limework fork of RedisBungee # ValioBungee: The RedisBungee Limework's Fork
[![RedisBungee Build](https://github.com/Limework/RedisBungee/actions/workflows/maven.yml/badge.svg)](https://github.com/Limework/RedisBungee/actions/workflows/maven.yml) [![](https://jitpack.io/v/limework/redisbungee.svg)](https://jitpack.io/#limework/redisbungee)
Spigot link: [click](https://www.spigotmc.org/resources/redisbungee.87700/) The original project of RedisBungee is no longer maintained, so we have forked the plugin.
RedisBungee uses [Redis](https://redis.io) with Java client [Jedis](https://github.com/redis/jedis/)
to Synchronize players data between [BungeeCord](https://github.com/SpigotMC/BungeeCord)
or [Velocity*](https://github.com/PaperMC/Velocity) proxies
## Why different name?
Because from our current understanding we cant use Redis name due trademark restrictions.
so we settled with valiobungee. But it doesnt not effect any internals naming schemes like plugin id and so on.
The maintainer of RedisBungee has became inactive, so we have taken the development of the plugin. ## Downloads
RedisBungee uses [Redis](https://redis.io) to Synchronize data between [BungeeCord](https://github.com/SpigotMC/BungeeCord) proxies [![](https://raw.githubusercontent.com/Prospector/badges/master/modrinth-badge-72h-padded.png)](https://modrinth.com/plugin/redisbungee)
## Notice: about older versions of redis than redis 6.0 ## Wiki
As of now Version 0.6.4 is only supporting redis 6.0 and above! https://github.com/ProxioDev/ValioBungee/wiki
## Compiling ## Support
Now you can use Maven without installing it using [Maven wrappe](https://github.com/takari/maven-wrapper) :) open an issue with question button
RedisBungee is distributed as a [maven](https://maven.apache.org) project. ## License
To compile and installing to in your local Maven repository:
git clone https://github.com/Limework/RedisBungee.git .
mvnw clean install
mvnw package
If you have deb maven installed, you can use the `mvn` command instead.
And use it in your pom file.
<dependency>
<groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee</artifactId>
<version>0.6.5-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
Or if you want to use the jitpack maven server
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
And use it in your pom file.
<dependency>
<groupId>com.github.limework</groupId>
<artifactId>redisbungee</artifactId>
<version>0.6.4</version>
</dependency>
## Javadocs
Hosted on limework website. Version 0.6.0 ~~(note: any version 0.6.* will not have API changes.)~~
https://limework.net/JavaDocs/RedisBungee/
Note: we might add more api into RedisBungeeApi Class but we wont remove the old ones to
keep old plugins working....
## Configuration
**REDISBUNGEE REQUIRES A REDIS SERVER**, preferably with reasonably low latency. The default [config](https://github.com/limework/RedisBungee/blob/master/src/main/resources/example_config.yml) is saved when the plugin first starts.
## License!
This project is distributed under Eclipse Public License 1.0 This project is distributed under Eclipse Public License 1.0
You can find it [here](https://github.com/Limework/RedisBungee/blob/master/LICENSE) You can find it [here](https://github.com/proxiodev/RedisBungee/blob/master/LICENSE)
You can find the original RedisBungee by minecrafter [here](https://github.com/minecrafter/RedisBungee) or spigot page [here](https://www.spigotmc.org/resources/redisbungee.13494/) You can find the original RedisBungee is by [astei](https://github.com/astei) and project can be
found [here](https://github.com/minecrafter/RedisBungee) or spigot
page [here, but its no longer available](https://www.spigotmc.org/resources/redisbungee.13494/)
## YourKit
YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/) and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).
![YourKit](https://www.yourkit.com/images/yklogo.png)

76
api/build.gradle.kts Normal file
View File

@@ -0,0 +1,76 @@
import java.io.ByteArrayOutputStream
plugins {
`java-library`
`maven-publish`
alias(libs.plugins.blossom)
alias(libs.plugins.indragit)
}
dependencies {
api(libs.guava)
api(libs.jedis)
api(libs.okhttp)
api(libs.configurateV3)
api(libs.caffeine)
}
description = "RedisBungee interfaces"
sourceSets {
main {
blossom {
javaSources {
property("version", "$version")
property("git-commit", indraGit.commit().toString())
}
}
}
}
java {
withJavadocJar()
withSourcesJar()
}
tasks {
// thanks again for paper too
withType<Javadoc> {
val options = options as StandardJavadocDocletOptions
options.use()
options.isDocFilesSubDirs = true
val jedisVersion = libs.jedis.get().version
val configurateVersion = libs.configurateV3.get().version
val guavaVersion = libs.guava.get().version
val adventureVersion = libs.adventure.api.get().version
options.links(
"https://configurate.aoeu.xyz/$configurateVersion/apidocs/", // configurate
"https://javadoc.io/doc/redis.clients/jedis/$jedisVersion/", // jedis
"https://guava.dev/releases/$guavaVersion/api/docs/", // guava
"https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine",
"https://jd.advntr.dev/api/$adventureVersion"
)
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
filteringCharset = Charsets.UTF_8.name()
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}

View File

@@ -1,35 +1,45 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import lombok.NonNull; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import net.md_5.bungee.api.config.ServerInfo; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.*; import java.util.*;
/** /**
* This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}. * This abstract class is extended by platform plugin to provide some platform specific methods.
* overall its general contains all methods needed by external usage.
* *
* @author tuxed * @author Ham1255
* @since 0.2.3 * @since 0.8.0
*/ */
public class RedisBungeeAPI { @SuppressWarnings("unused")
private final RedisBungee plugin; public abstract class AbstractRedisBungeeAPI {
private final List<String> reservedChannels; protected final RedisBungeePlugin<?> plugin;
private static RedisBungeeAPI redisBungeeApi; private static AbstractRedisBungeeAPI abstractRedisBungeeAPI;
RedisBungeeAPI(RedisBungee plugin) { public AbstractRedisBungeeAPI(RedisBungeePlugin<?> plugin) {
// this does make sure that no one can replace first initiated API class.
if (abstractRedisBungeeAPI == null) {
abstractRedisBungeeAPI = this;
}
this.plugin = plugin; this.plugin = plugin;
redisBungeeApi = this;
this.reservedChannels = ImmutableList.of(
"redisbungee-allservers",
"redisbungee-" + RedisBungee.getConfiguration().getServerId(),
"redisbungee-data"
);
} }
/** /**
@@ -38,7 +48,7 @@ public class RedisBungeeAPI {
* @return a count of all players found * @return a count of all players found
*/ */
public final int getPlayerCount() { public final int getPlayerCount() {
return plugin.getCount(); return plugin.proxyDataManager().totalNetworkPlayers();
} }
/** /**
@@ -49,19 +59,19 @@ public class RedisBungeeAPI {
* @return the last time a player was on, if online returns a 0 * @return the last time a player was on, if online returns a 0
*/ */
public final long getLastOnline(@NonNull UUID player) { public final long getLastOnline(@NonNull UUID player) {
return plugin.getDataManager().getLastOnline(player); return plugin.playerDataManager().getLastOnline(player);
} }
/** /**
* Get the server where the specified player is playing. This function also deals with the case of local players * Get the server where the specified player is playing. This function also deals with the case of local players
* as well, and will return local information on them. * as well, and will return local information on them.
* *
* @param player a player name * @param player a player uuid
* @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on. * @return a String name for the server the player is on. Can be Null if plugins is doing weird stuff to the proxy internals
*/ */
public final ServerInfo getServerFor(@NonNull UUID player) { @Nullable
String server = plugin.getDataManager().getServer(player); public final String getServerNameFor(@NonNull UUID player) {
return plugin.getProxy().getServerInfo(server); return plugin.playerDataManager().getServerFor(player);
} }
/** /**
@@ -72,7 +82,7 @@ public class RedisBungeeAPI {
* @return a Set with all players found * @return a Set with all players found
*/ */
public final Set<UUID> getPlayersOnline() { public final Set<UUID> getPlayersOnline() {
return plugin.getPlayers(); return plugin.proxyDataManager().networkPlayers();
} }
/** /**
@@ -93,11 +103,11 @@ public class RedisBungeeAPI {
/** /**
* Get a full list of players on all servers. * Get a full list of players on all servers.
* *
* @return a immutable Multimap with all players found on this server * @return a immutable Multimap with all players found on this network
* @since 0.2.5 * @since 0.2.5
*/ */
public final Multimap<String, UUID> getServerToPlayers() { public final Multimap<String, UUID> getServerToPlayers() {
return plugin.serversToPlayers(); return plugin.playerDataManager().serversToPlayers();
} }
/** /**
@@ -113,11 +123,11 @@ public class RedisBungeeAPI {
/** /**
* Get a list of players on the specified proxy. * Get a list of players on the specified proxy.
* *
* @param server a server name * @param proxyID proxy id
* @return a Set with all UUIDs found on this proxy * @return a Set with all UUIDs found on this proxy
*/ */
public final Set<UUID> getPlayersOnProxy(@NonNull String server) { public final Set<UUID> getPlayersOnProxy(@NonNull String proxyID) {
return plugin.getPlayersOnProxy(server); return plugin.proxyDataManager().getPlayersOn(proxyID);
} }
/** /**
@@ -138,7 +148,7 @@ public class RedisBungeeAPI {
* @since 0.2.4 * @since 0.2.4
*/ */
public final InetAddress getPlayerIp(@NonNull UUID player) { public final InetAddress getPlayerIp(@NonNull UUID player) {
return plugin.getDataManager().getIp(player); return plugin.playerDataManager().getIpFor(player);
} }
/** /**
@@ -149,7 +159,7 @@ public class RedisBungeeAPI {
* @since 0.3.3 * @since 0.3.3
*/ */
public final String getProxy(@NonNull UUID player) { public final String getProxy(@NonNull UUID player) {
return plugin.getDataManager().getProxy(player); return plugin.playerDataManager().getProxyFor(player);
} }
/** /**
@@ -160,7 +170,7 @@ public class RedisBungeeAPI {
* @since 0.2.5 * @since 0.2.5
*/ */
public final void sendProxyCommand(@NonNull String command) { public final void sendProxyCommand(@NonNull String command) {
plugin.sendProxyCommand("allservers", command); sendProxyCommand("allservers", command);
} }
/** /**
@@ -168,69 +178,47 @@ public class RedisBungeeAPI {
* *
* @param proxyId a proxy ID * @param proxyId a proxy ID
* @param command the command to send and execute * @param command the command to send and execute
* @see #getServerId() * @see #getProxyId()
* @see #getAllServers() * @see #getAllProxies()
* @since 0.2.5 * @since 0.2.5
*/ */
public final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) { public final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) {
plugin.sendProxyCommand(proxyId, command); plugin.proxyDataManager().sendCommandTo(proxyId, command);
} }
/** /**
* Sends a message to a PubSub channel. The channel has to be subscribed to on this, or another redisbungee instance for {@link com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent} to fire. * Sends a message to a PubSub channel which makes PubSubMessageEvent fire.
* <p>
* Note: Since 0.12.0 registering a channel api is no longer required
* *
* @param channel The PubSub channel * @param channel The PubSub channel
* @param message the message body to send * @param message the message body to send
* @since 0.3.3 * @since 0.3.3
*/ */
public final void sendChannelMessage(@NonNull String channel, @NonNull String message) { public final void sendChannelMessage(@NonNull String channel, @NonNull String message) {
plugin.sendChannelMessage(channel, message); plugin.proxyDataManager().sendChannelMessage(channel, message);
} }
/** /**
* Get the current BungeeCord server ID for this server. * Get the current BungeeCord / Velocity proxy ID for this server.
* *
* @return the current server ID * @return the current server ID
* @see #getAllServers() * @see #getAllProxies()
* @since 0.2.5 * @since 0.8.0
*/ */
public final String getServerId() { public final String getProxyId() {
return RedisBungee.getConfiguration().getServerId(); return plugin.proxyDataManager().proxyId();
} }
/** /**
* Get all the linked proxies in this network. * Get all the linked proxies in this network.
* *
* @return the list of all proxies * @return the list of all proxies
* @see #getServerId() * @see #getProxyId()
* @since 0.2.5 * @since 0.8.0
*/ */
public final List<String> getAllServers() { public final List<String> getAllProxies() {
return plugin.getServerIds(); return plugin.proxyDataManager().proxiesIds();
}
/**
* Register (a) PubSub channel(s), so that you may handle {@link com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent} for it.
*
* @param channels the channels to register
* @since 0.3
*/
public final void registerPubSubChannels(String... channels) {
RedisBungee.getPubSubListener().addChannel(channels);
}
/**
* Unregister (a) PubSub channel(s).
*
* @param channels the channels to unregister
* @since 0.3
*/
public final void unregisterPubSubChannels(String... channels) {
for (String channel : channels) {
Preconditions.checkArgument(!reservedChannels.contains(channel), "attempting to unregister internal channel");
}
RedisBungee.getPubSubListener().removeChannel(channels);
} }
/** /**
@@ -293,32 +281,59 @@ public class RedisBungeeAPI {
* *
* @param name the UUID to fetch the name for * @param name the UUID to fetch the name for
* @param expensiveLookups whether or not to perform potentially expensive lookups * @param expensiveLookups whether or not to perform potentially expensive lookups
* @return the UUID for the name * @return the {@link UUID} for the name
* @since 0.3.2 * @since 0.3.2
*/ */
public final UUID getUuidFromName(@NonNull String name, boolean expensiveLookups) { public final UUID getUuidFromName(@NonNull String name, boolean expensiveLookups) {
return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups); return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups);
} }
/** /**
* This gets Redis Bungee Jedis pool * returns Summoner class responsible for Single Jedis {@link redis.clients.jedis.JedisPooled} with {@link JedisPool}, Cluster Jedis {@link redis.clients.jedis.JedisCluster} handling
* *
* @return {@link JedisPool} * @return {@link Summoner}
* @since 0.6.5 * @since 0.8.0
*/ */
public JedisPool getJedisPool() { public Summoner<?> getSummoner() {
return this.plugin.getPool(); return this.plugin.getSummoner();
} }
/** /**
* This alternative to {@link RedisBungee#getApi()} * Kicks a player from the network using miniMessage
* which now deprecated. but to maintain old plugins compatibility it won't be removed. * calls {@link #getUuidFromName(String)} to get uuid
* * <a href="https://docs.advntr.dev/minimessage/format.html">...</a>
* @return the API instance. * @param playerName player name
* @since 0.6.5 * @param miniMessage kick message that player will see on kick using minimessage as format
* @since 0.13.0
*/ */
public static RedisBungeeAPI getRedisBungeeApi() { public void kickPlayer(String playerName, String miniMessage) {
return redisBungeeApi; kickPlayer(getUuidFromName(playerName), miniMessage);
} }
/**
* Kicks a player from the network
* <a href="https://docs.advntr.dev/minimessage/format.html">...</a>
* @param player player uuid
* @param miniMessage kick message that player will see on kick using minimessage as format
* @since 0.13.0
*/
public void kickPlayer(UUID player, String miniMessage) {
plugin.playerDataManager().serializedPlayerKick(player, miniMessage);
}
/**
* shows what mode is RedisBungee is on
* Basically what every redis mode is used like cluster or single instance.
*
* @return {@link RedisBungeeMode}
* @since 0.8.0
*/
public RedisBungeeMode getMode() {
return this.plugin.getRedisBungeeMode();
}
public static AbstractRedisBungeeAPI getAbstractRedisBungeeAPI() {
return abstractRedisBungeeAPI;
}
} }

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
public class Constants {
public final static String VERSION = "{{ version }}";
public final static String GIT_COMMIT = "{{ git-commit }}";
public static String getGithubCommitLink() {
return "https://github.com/ProxioDev/ValioBungee/commit/" + GIT_COMMIT;
}
}

View File

@@ -0,0 +1,286 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.common.net.InetAddresses;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask;
import org.json.JSONObject;
import redis.clients.jedis.ClusterPipeline;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
import redis.clients.jedis.UnifiedJedis;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.TimeUnit;
public abstract class PlayerDataManager<P> {
protected final RedisBungeePlugin<P> plugin;
private final Object SERVERS_TO_PLAYERS_KEY = new Object();
private final UnifiedJedis unifiedJedis;
private final String proxyId;
private final String networkId;
private final LoadingCache<UUID, String> serverCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getServerFromRedis);
private final LoadingCache<UUID, String> lastServerCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastServerFromRedis);
private final LoadingCache<UUID, String> proxyCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getProxyFromRedis);
private final LoadingCache<UUID, InetAddress> ipCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getIpAddressFromRedis);
private final LoadingCache<UUID, Long> lastOnlineCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastOnlineFromRedis);
private final LoadingCache<Object, Multimap<String, UUID>> serverToPlayersCache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(this::serversToPlayersBuilder);
public PlayerDataManager(RedisBungeePlugin<P> plugin) {
this.plugin = plugin;
this.unifiedJedis = plugin.proxyDataManager().unifiedJedis();
this.proxyId = plugin.proxyDataManager().proxyId();
this.networkId = plugin.proxyDataManager().networkId();
}
// handle network wide
// server change
// public void onPlayerChangedServerNetworkEvent
// public void onNetworkPlayerQuit
// public void onNetworkPlayerJoin
// local events
// public void onPubSubMessageEvent
// public void onServerConnectedEvent
// public void onLoginEvent
// public void onDisconnectEvent
protected void handleNetworkPlayerServerChange(IPlayerChangedServerNetworkEvent event) {
this.serverCache.invalidate(event.getUuid());
this.lastServerCache.invalidate(event.getUuid());
//TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache in-place without querying redis. That would be a lot more efficient.
this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY);
}
protected void handleNetworkPlayerQuit(IPlayerLeftNetworkEvent event) {
this.proxyCache.invalidate(event.getUuid());
this.serverCache.invalidate(event.getUuid());
this.ipCache.invalidate(event.getUuid());
this.lastOnlineCache.invalidate(event.getUuid());
//TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache in-place without querying redis. That would be a lot more efficient.
this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY);
}
protected void handleNetworkPlayerJoin(IPlayerJoinedNetworkEvent event) {
this.proxyCache.invalidate(event.getUuid());
this.serverCache.invalidate(event.getUuid());
this.ipCache.invalidate(event.getUuid());
this.lastOnlineCache.invalidate(event.getUuid());
//TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache in-place without querying redis. That would be a lot more efficient.
this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY);
}
protected void handlePubSubMessageEvent(IPubSubMessageEvent event) {
switch (event.getChannel()) {
case "redisbungee-serverchange" -> {
JSONObject data = new JSONObject(event.getMessage());
UUID uuid = UUID.fromString(data.getString("uuid"));
String from = null;
if (data.has("from")) from = data.getString("from");
String to = data.getString("to");
plugin.fireEvent(plugin.createPlayerChangedServerNetworkEvent(uuid, from, to));
}
case "redisbungee-player-join" -> {
JSONObject data = new JSONObject(event.getMessage());
UUID uuid = UUID.fromString(data.getString("uuid"));
plugin.fireEvent(plugin.createPlayerJoinedNetworkEvent(uuid));
}
case "redisbungee-player-leave" -> {
JSONObject data = new JSONObject(event.getMessage());
UUID uuid = UUID.fromString(data.getString("uuid"));
plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
}
case "redisbungee-player-kick" -> {
JSONObject data = new JSONObject(event.getMessage());
UUID uuid = UUID.fromString(data.getString("uuid"));
String message = data.getString("serialized-message");
handleSerializedKick(uuid, message);
}
}
}
protected void playerChangedServer(UUID uuid, String from, String to) {
JSONObject data = new JSONObject();
data.put("proxy", this.proxyId);
data.put("uuid", uuid);
data.put("from", from);
data.put("to", to);
plugin.proxyDataManager().sendChannelMessage("redisbungee-serverchange", data.toString());
plugin.fireEvent(plugin.createPlayerChangedServerNetworkEvent(uuid, from, to));
handleServerChangeRedis(uuid, to);
}
// must check if player is on the local proxy
// https://docs.advntr.dev/minimessage/index.html
// implemented downstream in Velocity and Bungeecord
protected abstract boolean handleSerializedKick(UUID player, String serializedMiniMessage);
// https://docs.advntr.dev/minimessage/index.html
// implemented downstream in Velocity and Bungeecord
// called by kickPlayer in each impl of this class `NOT OVERRIDE`
public void serializedPlayerKick(UUID player, String serializedMiniMessage) {
JSONObject data = new JSONObject();
data.put("proxy", this.proxyId);
data.put("uuid", player);
data.put("serialized-message", serializedMiniMessage);
if (!handleSerializedKick(player, serializedMiniMessage))
plugin.proxyDataManager().sendChannelMessage("redisbungee-player-kick", data.toString());
}
private void handleServerChangeRedis(UUID uuid, String server) {
Map<String, String> data = new HashMap<>();
data.put("server", server);
data.put("last-server", server);
unifiedJedis.hset("redisbungee::" + this.networkId + "::player::" + uuid + "::data", data);
}
protected void addPlayer(final UUID uuid, final String name, final InetAddress inetAddress) {
Map<String, String> redisData = new HashMap<>();
redisData.put("last-online", String.valueOf(0));
redisData.put("proxy", this.proxyId);
redisData.put("ip", inetAddress.getHostAddress());
unifiedJedis.hset("redisbungee::" + this.networkId + "::player::" + uuid + "::data", redisData);
plugin.getUuidTranslator().persistInfo(name, uuid, this.unifiedJedis);
JSONObject data = new JSONObject();
data.put("proxy", this.proxyId);
data.put("uuid", uuid);
plugin.proxyDataManager().sendChannelMessage("redisbungee-player-join", data.toString());
plugin.fireEvent(plugin.createPlayerJoinedNetworkEvent(uuid));
this.plugin.proxyDataManager().addPlayer(uuid);
}
protected void removePlayer(UUID uuid) {
unifiedJedis.hset("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-online", String.valueOf(System.currentTimeMillis()));
unifiedJedis.hdel("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server", "proxy", "ip");
JSONObject data = new JSONObject();
data.put("proxy", this.proxyId);
data.put("uuid", uuid);
plugin.proxyDataManager().sendChannelMessage("redisbungee-player-leave", data.toString());
plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
this.plugin.proxyDataManager().removePlayer(uuid);
}
protected String getProxyFromRedis(UUID uuid) {
return unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "proxy");
}
protected String getServerFromRedis(UUID uuid) {
return unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server");
}
protected String getLastServerFromRedis(UUID uuid) {
return unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-server");
}
protected InetAddress getIpAddressFromRedis(UUID uuid) {
String ip = unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "ip");
if (ip == null) return null;
return InetAddresses.forString(ip);
}
protected long getLastOnlineFromRedis(UUID uuid) {
String unixString = unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-online");
if (unixString == null) return -1;
return Long.parseLong(unixString);
}
public String getLastServerFor(UUID uuid) {
return this.lastServerCache.get(uuid);
}
public String getServerFor(UUID uuid) {
return this.serverCache.get(uuid);
}
public String getProxyFor(UUID uuid) {
return this.proxyCache.get(uuid);
}
public InetAddress getIpFor(UUID uuid) {
return this.ipCache.get(uuid);
}
public long getLastOnline(UUID uuid) {
return this.lastOnlineCache.get(uuid);
}
public Multimap<String, UUID> serversToPlayers() {
return this.serverToPlayersCache.get(SERVERS_TO_PLAYERS_KEY);
}
protected Multimap<String, UUID> serversToPlayersBuilder(Object o) {
try {
return new RedisPipelineTask<Multimap<String, UUID>>(plugin) {
private final Set<UUID> uuids = plugin.proxyDataManager().networkPlayers();
private final ImmutableMultimap.Builder<String, UUID> builder = ImmutableMultimap.builder();
@Override
public Multimap<String, UUID> doPooledPipeline(Pipeline pipeline) {
HashMap<UUID, Response<String>> responses = new HashMap<>();
for (UUID uuid : uuids) {
Optional.ofNullable(pipeline.hget("redisbungee::" + networkId + "::player::" + uuid + "::data", "server")).ifPresent(stringResponse -> {
responses.put(uuid, stringResponse);
});
}
pipeline.sync();
responses.forEach((uuid, response) -> {
String key = response.get();
if (key == null) return;
builder.put(key, uuid);
});
return builder.build();
}
@Override
public Multimap<String, UUID> clusterPipeline(ClusterPipeline pipeline) {
HashMap<UUID, Response<String>> responses = new HashMap<>();
for (UUID uuid : uuids) {
Optional.ofNullable(pipeline.hget("redisbungee::" + networkId + "::player::" + uuid + "::data", "server")).ifPresent(stringResponse -> {
responses.put(uuid, stringResponse);
});
}
pipeline.sync();
responses.forEach((uuid, response) -> {
String key = response.get();
if (key == null) return;
builder.put(key, uuid);
});
return builder.build();
}
}.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,408 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
import com.imaginarycode.minecraft.redisbungee.api.payloads.gson.AbstractPayloadSerializer;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.DeathPayloadSerializer;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.HeartbeatPayloadSerializer;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.PubSubPayloadSerializer;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.RunCommandPayloadSerializer;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask;
import com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil;
import redis.clients.jedis.*;
import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadParams;
import redis.clients.jedis.resps.StreamEntry;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.google.common.base.Preconditions.checkArgument;
public abstract class ProxyDataManager implements Runnable {
private static final int MAX_ENTRIES = 10000;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final UnifiedJedis unifiedJedis;
// data:
// Proxy id, heartbeat (unix epoch from instant), players as int
private final ConcurrentHashMap<String, HeartbeatPayload.HeartbeatData> heartbeats = new ConcurrentHashMap<>();
private final String networkId;
private final String proxyId;
private final String STREAM_ID;
// This different from proxy id, just to detect if there is duplicate proxy using same proxy id
private final UUID dataManagerUUID = UUID.randomUUID();
protected final RedisBungeePlugin<?> plugin;
private final Gson gson = new GsonBuilder().registerTypeAdapter(AbstractPayload.class, new AbstractPayloadSerializer()).registerTypeAdapter(HeartbeatPayload.class, new HeartbeatPayloadSerializer()).registerTypeAdapter(DeathPayload.class, new DeathPayloadSerializer()).registerTypeAdapter(PubSubPayload.class, new PubSubPayloadSerializer()).registerTypeAdapter(RunCommandPayload.class, new RunCommandPayloadSerializer()).create();
public ProxyDataManager(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
this.proxyId = this.plugin.configuration().getProxyId();
this.unifiedJedis = plugin.getSummoner().obtainResource();
this.networkId = plugin.configuration().networkId();
this.STREAM_ID = "network-" + this.networkId + "-redisbungee-stream";
this.destroyProxyMembers();
}
public abstract Set<UUID> getLocalOnlineUUIDs();
public Set<UUID> getPlayersOn(String proxyId) {
checkArgument(proxiesIds().contains(proxyId), proxyId + " is not a valid proxy ID");
if (proxyId.equals(this.proxyId)) return this.getLocalOnlineUUIDs();
if (!this.heartbeats.containsKey(proxyId)) {
return new HashSet<>(); // return empty hashset or null?
}
return getProxyMembers(proxyId);
}
// this skip checking if proxy is and its package private
// due proxy shutdown shenanigans
public boolean isPlayerTrulyOnProxy(String proxyId, UUID uuid) {
return unifiedJedis.sismember("redisbungee::" + this.networkId + "::proxies::" + proxyId + "::online-players", uuid.toString());
}
public List<String> proxiesIds() {
return Collections.list(this.heartbeats.keys());
}
public synchronized void sendCommandTo(String proxyToRun, String command) {
if (isClosed()) return;
if (proxyToRun.equals("allservers") || proxyToRun.equals(this.proxyId())) {
handlePlatformCommandExecution(command);
}
publishPayload(new RunCommandPayload(this.proxyId, proxyToRun, command));
}
public synchronized void sendChannelMessage(String channel, String message) {
if (isClosed()) return;
publishPayload(new PubSubPayload(this.proxyId, channel, message));
}
// call every 1 second
public synchronized void publishHeartbeat() {
if (isClosed()) return;
HeartbeatPayload.HeartbeatData heartbeatData = new HeartbeatPayload.HeartbeatData(Instant.now().getEpochSecond(), this.getLocalOnlineUUIDs().size());
this.heartbeats.put(this.proxyId(), heartbeatData);
publishPayload(new HeartbeatPayload(this.proxyId, heartbeatData));
}
public Set<UUID> networkPlayers() {
try {
return new RedisPipelineTask<Set<UUID>>(this.plugin) {
@Override
public Set<UUID> doPooledPipeline(Pipeline pipeline) {
HashSet<Response<Set<String>>> responses = new HashSet<>();
for (String proxyId : proxiesIds()) {
responses.add(pipeline.smembers("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players"));
}
pipeline.sync();
HashSet<UUID> uuids = new HashSet<>();
for (Response<Set<String>> response : responses) {
for (String stringUUID : response.get()) {
uuids.add(UUID.fromString(stringUUID));
}
}
return uuids;
}
@Override
public Set<UUID> clusterPipeline(ClusterPipeline pipeline) {
HashSet<Response<Set<String>>> responses = new HashSet<>();
for (String proxyId : proxiesIds()) {
responses.add(pipeline.smembers("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players"));
}
pipeline.sync();
HashSet<UUID> uuids = new HashSet<>();
for (Response<Set<String>> response : responses) {
for (String stringUUID : response.get()) {
uuids.add(UUID.fromString(stringUUID));
}
}
return uuids;
}
}.call();
} catch (Exception e) {
throw new RuntimeException("unable to get network players", e);
}
}
public int totalNetworkPlayers() {
int players = 0;
for (HeartbeatPayload.HeartbeatData value : this.heartbeats.values()) {
players += value.players();
}
return players;
}
public Map<String, Integer> eachProxyCount() {
ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder();
heartbeats.forEach((proxy, data) -> builder.put(proxy, data.players()));
return builder.build();
}
// Call on close
private synchronized void publishDeath() {
publishPayload(new DeathPayload(this.proxyId));
}
private void publishPayload(AbstractPayload payload) {
Map<String, String> data = new HashMap<>();
data.put("payload", gson.toJson(payload));
data.put("data-manager-uuid", this.dataManagerUUID.toString());
data.put("class", payload.getClassName());
this.unifiedJedis.xadd(STREAM_ID, XAddParams.xAddParams().maxLen(MAX_ENTRIES).id(StreamEntryID.NEW_ENTRY), data);
}
private void handleHeartBeat(HeartbeatPayload payload) {
String id = payload.senderProxy();
if (!heartbeats.containsKey(id)) {
plugin.logInfo("Proxy {} has connected", id);
}
heartbeats.put(id, payload.data());
}
// call every 1 minutes
public void correctionTask() {
// let's check this proxy players
Set<UUID> localOnlineUUIDs = getLocalOnlineUUIDs();
Set<UUID> storedRedisUuids = getProxyMembers(this.proxyId);
if (!localOnlineUUIDs.equals(storedRedisUuids)) {
plugin.logWarn("De-synced players set detected correcting....");
Set<UUID> add = new HashSet<>(localOnlineUUIDs);
Set<UUID> remove = new HashSet<>(storedRedisUuids);
add.removeAll(storedRedisUuids);
remove.removeAll(localOnlineUUIDs);
for (UUID uuid : add) {
plugin.logWarn("found {} that isn't in the set, adding it to the Corrected set", uuid);
}
for (UUID uuid : remove) {
plugin.logWarn("found {} that does not belong to this proxy removing it from the corrected set", uuid);
}
try {
new RedisPipelineTask<Void>(plugin) {
@Override
public Void doPooledPipeline(Pipeline pipeline) {
Set<String> removeString = new HashSet<>();
for (UUID uuid : remove) {
removeString.add(uuid.toString());
}
Set<String> addString = new HashSet<>();
for (UUID uuid : add) {
addString.add(uuid.toString());
}
pipeline.srem("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{}));
pipeline.sadd("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", addString.toArray(new String[]{}));
pipeline.sync();
return null;
}
@Override
public Void clusterPipeline(ClusterPipeline pipeline) {
Set<String> removeString = new HashSet<>();
for (UUID uuid : remove) {
removeString.add(uuid.toString());
}
Set<String> addString = new HashSet<>();
for (UUID uuid : add) {
addString.add(uuid.toString());
}
pipeline.srem("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{}));
pipeline.sadd("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", addString.toArray(new String[]{}));
pipeline.sync();
return null;
}
}.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
plugin.logInfo("Player set has been corrected!");
}
// handle dead proxies "THAT" Didn't send death payload but considered dead due TIMEOUT ~30 seconds
final Set<String> deadProxies = new HashSet<>();
for (Map.Entry<String, HeartbeatPayload.HeartbeatData> stringHeartbeatDataEntry : this.heartbeats.entrySet()) {
String id = stringHeartbeatDataEntry.getKey();
long heartbeat = stringHeartbeatDataEntry.getValue().heartbeat();
if (Instant.now().getEpochSecond() - heartbeat > RedisUtil.PROXY_TIMEOUT) {
deadProxies.add(id);
cleanProxy(id);
}
}
try {
new RedisPipelineTask<Void>(plugin) {
@Override
public Void doPooledPipeline(Pipeline pipeline) {
for (String deadProxy : deadProxies) {
pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
}
pipeline.sync();
return null;
}
@Override
public Void clusterPipeline(ClusterPipeline pipeline) {
for (String deadProxy : deadProxies) {
pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
}
pipeline.sync();
return null;
}
}.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void handleProxyDeath(DeathPayload payload) {
cleanProxy(payload.senderProxy());
}
private void cleanProxy(String id) {
if (id.equals(this.proxyId())) {
return;
}
for (UUID uuid : getProxyMembers(id)) plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
this.heartbeats.remove(id);
plugin.logInfo("Proxy {} has disconnected", id);
}
private void handleChannelMessage(PubSubPayload payload) {
String channel = payload.channel();
String message = payload.message();
this.plugin.fireEvent(this.plugin.createPubSubEvent(channel, message));
}
protected abstract void handlePlatformCommandExecution(String command);
private void handleCommand(RunCommandPayload payload) {
String proxyToRun = payload.proxyToRun();
String command = payload.command();
if (proxyToRun.equals("allservers") || proxyToRun.equals(this.proxyId())) {
handlePlatformCommandExecution(command);
}
}
public void addPlayer(UUID uuid) {
this.unifiedJedis.sadd("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players", uuid.toString());
}
public void removePlayer(UUID uuid) {
this.unifiedJedis.srem("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players", uuid.toString());
}
private void destroyProxyMembers() {
unifiedJedis.del("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players");
}
private Set<UUID> getProxyMembers(String proxyId) {
Set<String> uuidsStrings = unifiedJedis.smembers("redisbungee::" + this.networkId + "::proxies::" + proxyId + "::online-players");
HashSet<UUID> uuids = new HashSet<>();
for (String proxyMember : uuidsStrings) {
uuids.add(UUID.fromString(proxyMember));
}
return uuids;
}
private StreamEntryID lastStreamEntryID;
// polling from stream
@Override
public void run() {
while (!isClosed()) {
try {
List<java.util.Map.Entry<String, List<StreamEntry>>> data = unifiedJedis.xread(XReadParams.xReadParams().block(0), Collections.singletonMap(STREAM_ID, lastStreamEntryID != null ? lastStreamEntryID : StreamEntryID.LAST_ENTRY));
for (Map.Entry<String, List<StreamEntry>> datum : data) {
for (StreamEntry streamEntry : datum.getValue()) {
this.lastStreamEntryID = streamEntry.getID();
String payloadData = streamEntry.getFields().get("payload");
String clazz = streamEntry.getFields().get("class");
UUID payloadDataManagerUUID = UUID.fromString(streamEntry.getFields().get("data-manager-uuid"));
AbstractPayload unknownPayload = (AbstractPayload) gson.fromJson(payloadData, Class.forName(clazz));
if (unknownPayload.senderProxy().equals(this.proxyId)) {
if (!payloadDataManagerUUID.equals(this.dataManagerUUID)) {
plugin.logWarn("detected other proxy is using same ID! {} this can cause issues, please shutdown this proxy and change the id!", this.proxyId);
}
continue;
}
if (unknownPayload instanceof HeartbeatPayload payload) {
handleHeartBeat(payload);
} else if (unknownPayload instanceof DeathPayload payload) {
handleProxyDeath(payload);
} else if (unknownPayload instanceof RunCommandPayload payload) {
handleCommand(payload);
} else if (unknownPayload instanceof PubSubPayload payload) {
handleChannelMessage(payload);
} else {
plugin.logWarn("got unknown data manager payload: {}", unknownPayload.getClassName());
}
}
}
} catch (Exception e) {
this.plugin.logFatal("an error has occurred in the stream", e);
try {
Thread.sleep(5000);
} catch (InterruptedException ignored) {
}
}
}
}
public void close() {
closed.set(true);
this.publishDeath();
this.heartbeats.clear();
this.destroyProxyMembers();
}
public boolean isClosed() {
return closed.get();
}
public String proxyId() {
return proxyId;
}
public UnifiedJedis unifiedJedis() {
return unifiedJedis;
}
public String networkId() {
return networkId;
}
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api;
public enum RedisBungeeMode {
SINGLE, CLUSTER
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.events.EventsPlatform;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* This Class has all internal methods needed by every redis bungee plugin, and it can be used to implement another platforms than bungeecord or another forks of RedisBungee
* <p>
* Reason this is interface because some proxies implementations require the user to extend class for plugins for example bungeecord.
*
* @author Ham1255
* @since 0.7.0
*/
public interface RedisBungeePlugin<P> extends EventsPlatform {
default void initialize() {
}
default void stop() {
}
void logInfo(String msg);
void logInfo(String format, Object... object);
void logWarn(String msg);
void logWarn(String format, Object... object);
void logFatal(String msg);
void logFatal(String format, Throwable throwable);
RedisBungeeConfiguration configuration();
Summoner<?> getSummoner();
RedisBungeeMode getRedisBungeeMode();
AbstractRedisBungeeAPI getAbstractRedisBungeeApi();
ProxyDataManager proxyDataManager();
PlayerDataManager<P> playerDataManager();
UUIDTranslator getUuidTranslator();
boolean isOnlineMode();
P getPlayer(UUID uuid);
P getPlayer(String name);
UUID getPlayerUUID(String player);
String getPlayerName(UUID player);
String getPlayerServerName(P player);
boolean isPlayerOnAServer(P player);
InetAddress getPlayerIp(P player);
void executeAsync(Runnable runnable);
void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time);
String platformId();
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.config;
public enum HandleMotdOrder {
FIRST,
NORMAL,
LAST
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.config;
import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
import java.util.List;
public class RedisBungeeConfiguration {
private final String proxyId;
private final List<InetAddress> exemptAddresses;
private final boolean kickWhenOnline;
private final boolean handleReconnectToLastServer;
private final boolean handleMotd;
private final HandleMotdOrder handleMotdOrder;
private final CommandsConfiguration commandsConfiguration;
private final String networkId;
public RedisBungeeConfiguration(String networkId, String proxyId, List<String> exemptAddresses, boolean kickWhenOnline, boolean handleReconnectToLastServer, boolean handleMotd, HandleMotdOrder handleMotdOrder, CommandsConfiguration commandsConfiguration) {
this.proxyId = proxyId;
ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder();
for (String s : exemptAddresses) {
addressBuilder.add(InetAddresses.forString(s));
}
this.exemptAddresses = addressBuilder.build();
this.kickWhenOnline = kickWhenOnline;
this.handleReconnectToLastServer = handleReconnectToLastServer;
this.handleMotd = handleMotd;
this.handleMotdOrder = handleMotdOrder;
this.commandsConfiguration = commandsConfiguration;
this.networkId = networkId;
}
public String getProxyId() {
return proxyId;
}
public List<InetAddress> getExemptAddresses() {
return exemptAddresses;
}
public boolean kickWhenOnline() {
return kickWhenOnline;
}
public boolean handleMotd() {
return this.handleMotd;
}
public HandleMotdOrder handleMotdOrder() {
return handleMotdOrder;
}
public boolean handleReconnectToLastServer() {
return this.handleReconnectToLastServer;
}
public record CommandsConfiguration(boolean redisbungeeEnabled, boolean redisbungeeLegacyEnabled,
LegacySubCommandsConfiguration legacySubCommandsConfiguration) {
}
public record LegacySubCommandsConfiguration(boolean findEnabled, boolean glistEnabled, boolean ipEnabled,
boolean lastseenEnabled, boolean plistEnabled, boolean pproxyEnabled,
boolean sendtoallEnabled, boolean serveridEnabled,
boolean serveridsEnabled, boolean installFind, boolean installGlist, boolean installIp,
boolean installLastseen, boolean installPlist, boolean installPproxy,
boolean installSendtoall, boolean installServerid,
boolean installServerids) {
}
public CommandsConfiguration commandsConfiguration() {
return commandsConfiguration;
}
public String networkId() {
return networkId;
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.config.loaders;
import com.google.common.reflect.TypeToken;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.objectmapping.ObjectMappingException;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.*;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import redis.clients.jedis.providers.PooledConnectionProvider;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
public interface ConfigLoader extends GenericConfigLoader {
int CONFIG_VERSION = 2;
default void loadConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException {
Path configFile = createConfigFile(dataFolder, "config.yml", "config.yml");
final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(configFile).build();
ConfigurationNode node = yamlConfigurationFileLoader.load();
if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
handleOldConfig(dataFolder, "config.yml", "config.yml");
node = yamlConfigurationFileLoader.load();
}
final boolean useSSL = node.getNode("useSSL").getBoolean(false);
final boolean kickWhenOnline = node.getNode("kick-when-online").getBoolean(true);
String redisPassword = node.getNode("redis-password").getString("");
String redisUsername = node.getNode("redis-username").getString("");
String networkId = node.getNode("network-id").getString("main");
String proxyId = node.getNode("proxy-id").getString("proxy-1");
final int maxConnections = node.getNode("max-redis-connections").getInt(10);
List<String> exemptAddresses;
try {
exemptAddresses = node.getNode("exempt-ip-addresses").getList(TypeToken.of(String.class));
} catch (ObjectMappingException e) {
exemptAddresses = Collections.emptyList();
}
// check redis password
if ((redisPassword.isEmpty() || redisPassword.equals("none"))) {
redisPassword = null;
plugin.logWarn("password is empty");
}
if ((redisUsername.isEmpty() || redisUsername.equals("none"))) {
redisUsername = null;
}
// env var
String proxyIdFromEnv = System.getenv("REDISBUNGEE_PROXY_ID");
if (proxyIdFromEnv != null) {
plugin.logInfo("Overriding current configured proxy id {} and been set to {} by Environment variable REDISBUNGEE_PROXY_ID", proxyId, proxyIdFromEnv);
proxyId = proxyIdFromEnv;
}
String networkIdFromEnv = System.getenv("REDISBUNGEE_NETWORK_ID");
if (networkIdFromEnv != null) {
plugin.logInfo("Overriding current configured network id {} and been set to {} by Environment variable REDISBUNGEE_NETWORK_ID", networkId, networkIdFromEnv);
networkId = networkIdFromEnv;
}
// Configuration sanity checks.
if (proxyId == null || proxyId.isEmpty()) {
String genId = UUID.randomUUID().toString();
plugin.logInfo("Generated proxy id " + genId + " and saving it to config.");
node.getNode("proxy-id").setValue(genId);
yamlConfigurationFileLoader.save(node);
proxyId = genId;
plugin.logInfo("proxy id was generated: " + proxyId);
} else {
plugin.logInfo("Loaded proxy id " + proxyId);
}
if (networkId.isEmpty()) {
networkId = "main";
plugin.logWarn("network id was empty and replaced with 'main'");
}
plugin.logInfo("Loaded network id " + networkId);
// TO avoid proxies from different platforms from seeing each other.
networkId = plugin.platformId() + "-" + networkId;
plugin.logInfo("Platform is {} so network id is now is {}", plugin.platformId(), networkId);
boolean reconnectToLastServer = node.getNode("reconnect-to-last-server").getBoolean();
boolean handleMotd = node.getNode("handle-motd").getBoolean(true);
plugin.logInfo("handle reconnect to last server: {}", reconnectToLastServer);
plugin.logInfo("handle motd: {}", handleMotd);
HandleMotdOrder handleMotdOrder = HandleMotdOrder.NORMAL;
String handleMotdOrderName = node.getNode("handle-motd-priority").getString();
if (handleMotdOrderName != null) {
try {
handleMotdOrder = HandleMotdOrder.valueOf(handleMotdOrderName.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
plugin.logWarn("handle motd order value '{}' is unsupported (allowed: {})", handleMotdOrderName, HandleMotdOrder.values());
}
}
plugin.logInfo("handle motd order: {}", handleMotdOrder);
// commands
boolean redisBungeeEnabled = node.getNode("commands", "redisbungee", "enabled").getBoolean(true);
boolean redisBungeeLegacyEnabled =node.getNode("commands", "redisbungee-legacy", "enabled").getBoolean(false);
boolean glistEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "enabled").getBoolean(false);
boolean findEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "enabled").getBoolean(false);
boolean lastseenEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "enabled").getBoolean(false);
boolean ipEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "enabled").getBoolean(false);
boolean pproxyEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "enabled").getBoolean(false);
boolean sendToAllEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "enabled").getBoolean(false);
boolean serverIdEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "enabled").getBoolean(false);
boolean serverIdsEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "enabled").getBoolean(false);
boolean pListEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "enabled").getBoolean(false);
boolean installGlist = node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "install").getBoolean(false);
boolean installFind = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "install").getBoolean(false);
boolean installLastseen = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "install").getBoolean(false);
boolean installIp = node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "install").getBoolean(false);
boolean installPproxy = node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "install").getBoolean(false);
boolean installSendToAll = node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "install").getBoolean(false);
boolean installServerid = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "install").getBoolean(false);
boolean installServerIds = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "install").getBoolean(false);
boolean installPlist = node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "install").getBoolean(false);
RedisBungeeConfiguration configuration = new RedisBungeeConfiguration(networkId, proxyId, exemptAddresses, kickWhenOnline, reconnectToLastServer, handleMotd, handleMotdOrder,
new RedisBungeeConfiguration.CommandsConfiguration(
redisBungeeEnabled, redisBungeeLegacyEnabled,
new RedisBungeeConfiguration.LegacySubCommandsConfiguration(
findEnabled, glistEnabled, ipEnabled,
lastseenEnabled, pListEnabled, pproxyEnabled,
sendToAllEnabled, serverIdEnabled, serverIdsEnabled,
installFind, installGlist, installIp,
installLastseen, installPlist, installPproxy,
installSendToAll, installServerid, installServerIds)
));
Summoner<?> summoner;
RedisBungeeMode redisBungeeMode;
if (useSSL) {
plugin.logInfo("Using ssl");
}
if (node.getNode("cluster-mode-enabled").getBoolean(false)) {
plugin.logInfo("RedisBungee MODE: CLUSTER");
Set<HostAndPort> hostAndPortSet = new HashSet<>();
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxConnections);
poolConfig.setBlockWhenExhausted(true);
node.getNode("redis-cluster-servers").getChildrenList().forEach((childNode) -> {
Map<Object, ? extends ConfigurationNode> hostAndPort = childNode.getChildrenMap();
String host = hostAndPort.get("host").getString();
int port = hostAndPort.get("port").getInt();
hostAndPortSet.add(new HostAndPort(host, port));
});
plugin.logInfo(hostAndPortSet.size() + " cluster nodes were specified");
if (hostAndPortSet.isEmpty()) {
throw new RuntimeException("No redis cluster servers specified");
}
summoner = new JedisClusterSummoner(new ClusterConnectionProvider(hostAndPortSet, DefaultJedisClientConfig.builder().user(redisUsername).password(redisPassword).ssl(useSSL).socketTimeoutMillis(5000).timeoutMillis(10000).build(), poolConfig));
redisBungeeMode = RedisBungeeMode.CLUSTER;
} else {
plugin.logInfo("RedisBungee MODE: SINGLE");
final String redisServer = node.getNode("redis-server").getString("127.0.0.1");
final int redisPort = node.getNode("redis-port").getInt(6379);
if (redisServer != null && redisServer.isEmpty()) {
throw new RuntimeException("No redis server specified");
}
JedisPool jedisPool = null;
if (node.getNode("enable-jedis-pool-compatibility").getBoolean(false)) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(node.getNode("compatibility-max-connections").getInt(3));
config.setBlockWhenExhausted(true);
jedisPool = new JedisPool(config, redisServer, redisPort, 5000, redisUsername, redisPassword, useSSL);
plugin.logInfo("Compatibility JedisPool was created");
}
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxConnections);
poolConfig.setBlockWhenExhausted(true);
summoner = new JedisPooledSummoner(new PooledConnectionProvider(new ConnectionFactory(new HostAndPort(redisServer, redisPort), DefaultJedisClientConfig.builder().user(redisUsername).timeoutMillis(5000).ssl(useSSL).password(redisPassword).build()), poolConfig), jedisPool);
redisBungeeMode = RedisBungeeMode.SINGLE;
}
plugin.logInfo("Successfully connected to Redis.");
onConfigLoad(configuration, summoner, redisBungeeMode);
}
void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.config.loaders;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
public interface GenericConfigLoader {
// CHANGES on every reboot
String RANDOM_OLD = "backup-" + Instant.now().getEpochSecond();
default Path createConfigFile(Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException {
if (Files.notExists(dataFolder)) {
Files.createDirectory(dataFolder);
}
Path file = dataFolder.resolve(configFile);
if (Files.notExists(file) && defaultResourceID != null) {
try (InputStream in = getClass().getClassLoader().getResourceAsStream(defaultResourceID)) {
Files.createFile(file);
assert in != null;
Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
}
}
return file;
}
default void handleOldConfig(Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException {
Path oldConfigFolder = dataFolder.resolve("old_config");
if (Files.notExists(oldConfigFolder)) {
Files.createDirectory(oldConfigFolder);
}
Path randomStoreConfigDirectory = oldConfigFolder.resolve(RANDOM_OLD);
if (Files.notExists(randomStoreConfigDirectory)) {
Files.createDirectory(randomStoreConfigDirectory);
}
Path oldConfigPath = dataFolder.resolve(configFile);
Files.move(oldConfigPath, randomStoreConfigDirectory.resolve(configFile));
createConfigFile(dataFolder, configFile, defaultResourceID);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID;
/**
* Since each platform have their own events' implementation for example Bungeecord events extends Event while velocity don't
*
* @author Ham1255
* @since 0.7.0
*/
public interface EventsPlatform {
IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server);
IPlayerJoinedNetworkEvent createPlayerJoinedNetworkEvent(UUID uuid);
IPlayerLeftNetworkEvent createPlayerLeftNetworkEvent(UUID uuid);
IPubSubMessageEvent createPubSubEvent(String channel, String message);
void fireEvent(Object event);
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID;
public interface IPlayerChangedServerNetworkEvent extends RedisBungeeEvent {
UUID getUuid();
String getServer();
String getPreviousServer();
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID;
public interface IPlayerJoinedNetworkEvent extends RedisBungeeEvent {
UUID getUuid();
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID;
public interface IPlayerLeftNetworkEvent extends RedisBungeeEvent {
UUID getUuid();
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.events;
public interface IPubSubMessageEvent extends RedisBungeeEvent {
String getChannel();
String getMessage();
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.events;
interface RedisBungeeEvent {
}

View File

@@ -0,0 +1,24 @@
package com.imaginarycode.minecraft.redisbungee.api.payloads;
public abstract class AbstractPayload {
private final String senderProxy;
public AbstractPayload(String proxyId) {
this.senderProxy = proxyId;
}
public AbstractPayload(String senderProxy, String className) {
this.senderProxy = senderProxy;
}
public String senderProxy() {
return senderProxy;
}
public String getClassName() {
return getClass().getName();
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
import java.lang.reflect.Type;
public class AbstractPayloadSerializer implements JsonSerializer<AbstractPayload>, JsonDeserializer<AbstractPayload> {
@Override
public AbstractPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
return new AbstractPayload(jsonObject.get("proxy").getAsString(), jsonObject.get("class").getAsString()) {
};
}
@Override
public JsonElement serialize(AbstractPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
return jsonObject;
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class DeathPayload extends AbstractPayload {
public DeathPayload(String proxyId) {
super(proxyId);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class HeartbeatPayload extends AbstractPayload {
public record HeartbeatData(long heartbeat, int players) {
}
private final HeartbeatData data;
public HeartbeatPayload(String proxyId, HeartbeatData data) {
super(proxyId);
this.data = data;
}
public HeartbeatData data() {
return data;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class PubSubPayload extends AbstractPayload {
private final String channel;
private final String message;
public PubSubPayload(String proxyId, String channel, String message) {
super(proxyId);
this.channel = channel;
this.message = message;
}
public String channel() {
return channel;
}
public String message() {
return message;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class RunCommandPayload extends AbstractPayload {
private final String proxyToRun;
private final String command;
public RunCommandPayload(String proxyId, String proxyToRun, String command) {
super(proxyId);
this.proxyToRun = proxyToRun;
this.command = command;
}
public String proxyToRun() {
return proxyToRun;
}
public String command() {
return command;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload;
import java.lang.reflect.Type;
public class DeathPayloadSerializer implements JsonSerializer<DeathPayload>, JsonDeserializer<DeathPayload> {
private static final Gson gson = new Gson();
@Override
public DeathPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString();
return new DeathPayload(senderProxy);
}
@Override
public JsonElement serialize(DeathPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
return jsonObject;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload;
import java.lang.reflect.Type;
public class HeartbeatPayloadSerializer implements JsonSerializer<HeartbeatPayload>, JsonDeserializer<HeartbeatPayload> {
@Override
public HeartbeatPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString();
long heartbeat = jsonObject.get("heartbeat").getAsLong();
int players = jsonObject.get("players").getAsInt();
return new HeartbeatPayload(senderProxy, new HeartbeatPayload.HeartbeatData(heartbeat, players));
}
@Override
public JsonElement serialize(HeartbeatPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("heartbeat", new JsonPrimitive(src.data().heartbeat()));
jsonObject.add("players", new JsonPrimitive(src.data().players()));
return jsonObject;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload;
import java.lang.reflect.Type;
public class PubSubPayloadSerializer implements JsonSerializer<PubSubPayload>, JsonDeserializer<PubSubPayload> {
private static final Gson gson = new Gson();
@Override
public PubSubPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString();
String channel = jsonObject.get("channel").getAsString();
String message = jsonObject.get("message").getAsString();
return new PubSubPayload(senderProxy, channel, message);
}
@Override
public JsonElement serialize(PubSubPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("channel", new JsonPrimitive(src.channel()));
jsonObject.add("message", context.serialize(src.message()));
return jsonObject;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload;
import java.lang.reflect.Type;
public class RunCommandPayloadSerializer implements JsonSerializer<RunCommandPayload>, JsonDeserializer<RunCommandPayload> {
@Override
public RunCommandPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString();
String proxyToRun = jsonObject.get("proxy-to-run").getAsString();
String command = jsonObject.get("command").getAsString();
return new RunCommandPayload(senderProxy, proxyToRun, command);
}
@Override
public JsonElement serialize(RunCommandPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("proxy-to-run", new JsonPrimitive(src.proxyToRun()));
jsonObject.add("command", context.serialize(src.command()));
return jsonObject;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.io.IOException;
import java.time.Duration;
public class JedisClusterSummoner implements Summoner<JedisCluster> {
private final ClusterConnectionProvider clusterConnectionProvider;
public JedisClusterSummoner(ClusterConnectionProvider clusterConnectionProvider) {
this.clusterConnectionProvider = clusterConnectionProvider;
// test the connection
JedisCluster jedisCluster = obtainResource();
jedisCluster.set("random_data", "0");
jedisCluster.del("random_data");
}
@Override
public void close() throws IOException {
this.clusterConnectionProvider.close();
}
@Override
public JedisCluster obtainResource() {
return new NotClosableJedisCluster(this.clusterConnectionProvider, 60, Duration.ofSeconds(10));
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.PooledConnectionProvider;
import java.io.IOException;
public class JedisPooledSummoner implements Summoner<JedisPooled> {
private final PooledConnectionProvider connectionProvider;
private final JedisPool jedisPool;
public JedisPooledSummoner(PooledConnectionProvider connectionProvider, JedisPool jedisPool) {
this.connectionProvider = connectionProvider;
this.jedisPool = jedisPool;
// test connections
if (jedisPool != null) {
try (Jedis jedis = this.jedisPool.getResource()) {
// Test the connection to make sure configuration is right
jedis.ping();
}
}
final JedisPooled jedisPooled = this.obtainResource();
jedisPooled.set("random_data", "0");
jedisPooled.del("random_data");
}
@Override
public JedisPooled obtainResource() {
// create UnClosable JedisPool *disposable*
return new NotClosableJedisPooled(this.connectionProvider);
}
public JedisPool getCompatibilityJedisPool() {
return this.jedisPool;
}
@Override
public void close() throws IOException {
if (this.jedisPool != null) {
this.jedisPool.close();
}
this.connectionProvider.close();
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.time.Duration;
public class NotClosableJedisCluster extends JedisCluster {
NotClosableJedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
super(provider, maxAttempts, maxTotalRetriesDuration);
}
@Override
public void close() {
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.PooledConnectionProvider;
public class NotClosableJedisPooled extends JedisPooled {
NotClosableJedisPooled(PooledConnectionProvider provider) {
super(provider);
}
@Override
public void close() {
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.UnifiedJedis;
import java.io.Closeable;
/**
* This class intended for future release to support redis sentinel or redis clusters
*
* @author Ham1255
* @since 0.7.0
*/
public interface Summoner<P extends UnifiedJedis> extends Closeable {
P obtainResource();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import redis.clients.jedis.*;
public abstract class RedisPipelineTask<T> extends RedisTask<T> {
public RedisPipelineTask(AbstractRedisBungeeAPI api) {
super(api);
}
public RedisPipelineTask(RedisBungeePlugin<?> plugin) {
super(plugin);
}
@Override
public T unifiedJedisTask(UnifiedJedis unifiedJedis) {
if (unifiedJedis instanceof JedisPooled pooled) {
try (Pipeline pipeline = pooled.pipelined()) {
return doPooledPipeline(pipeline);
}
} else if (unifiedJedis instanceof JedisCluster jedisCluster) {
try (ClusterPipeline pipeline = jedisCluster.pipelined()) {
return clusterPipeline(pipeline);
}
}
return null;
}
public abstract T doPooledPipeline(Pipeline pipeline);
public abstract T clusterPipeline(ClusterPipeline pipeline);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import redis.clients.jedis.UnifiedJedis;
import java.util.concurrent.Callable;
/**
* Since Jedis now have UnifiedJedis which basically extended by cluster / single connections classes
* can help us to have shared code.
*/
public abstract class RedisTask<V> implements Runnable, Callable<V> {
protected final Summoner<?> summoner;
protected final RedisBungeeMode mode;
@Override
public V call() throws Exception {
return this.execute();
}
public RedisTask(AbstractRedisBungeeAPI api) {
this.summoner = api.getSummoner();
this.mode = api.getMode();
}
public RedisTask(RedisBungeePlugin<?> plugin) {
this.summoner = plugin.getSummoner();
this.mode = plugin.getRedisBungeeMode();
}
public abstract V unifiedJedisTask(UnifiedJedis unifiedJedis);
@Override
public void run() {
this.execute();
}
public V execute() {
// JedisCluster, JedisPooled in fact is just UnifiedJedis does not need new instance since its single instance anyway.
if (mode == RedisBungeeMode.SINGLE) {
JedisPooledSummoner jedisSummoner = (JedisPooledSummoner) summoner;
return this.unifiedJedisTask(jedisSummoner.obtainResource());
} else if (mode == RedisBungeeMode.CLUSTER) {
JedisClusterSummoner jedisClusterSummoner = (JedisClusterSummoner) summoner;
return this.unifiedJedisTask(jedisClusterSummoner.obtainResource());
}
return null;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.CachedUUIDEntry;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.exceptions.JedisException;
import java.util.ArrayList;
public class UUIDCleanupTask extends RedisTask<Void>{
private final Gson gson = new Gson();
private final RedisBungeePlugin<?> plugin;
public UUIDCleanupTask(RedisBungeePlugin<?> plugin) {
super(plugin);
this.plugin = plugin;
}
// this code is inspired from https://github.com/minecrafter/redisbungeeclean
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
try {
final long number = unifiedJedis.hlen("uuid-cache");
plugin.logInfo("Found {} entries", number);
ArrayList<String> fieldsToRemove = new ArrayList<>();
unifiedJedis.hgetAll("uuid-cache").forEach((field, data) -> {
CachedUUIDEntry cachedUUIDEntry = gson.fromJson(data, CachedUUIDEntry.class);
if (cachedUUIDEntry.expired()) {
fieldsToRemove.add(field);
}
});
if (!fieldsToRemove.isEmpty()) {
unifiedJedis.hdel("uuid-cache", fieldsToRemove.toArray(new String[0]));
}
plugin.logInfo("deleted {} entries", fieldsToRemove.size());
} catch (JedisException e) {
plugin.logFatal("There was an error fetching information", e);
}
return null;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.UnifiedJedis;
public class InitialUtils {
public static void checkRedisVersion(RedisBungeePlugin<?> plugin) {
new RedisTask<Void>(plugin) {
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
// This is more portable than INFO <section>
String info = new String((byte[]) unifiedJedis.sendCommand(Protocol.Command.INFO));
for (String s : info.split("\r\n")) {
if (s.startsWith("redis_version:")) {
String version = s.split(":")[1];
plugin.logInfo("Redis server version: " + version);
if (!RedisUtil.isRedisVersionRight(version)) {
plugin.logFatal("Your version of Redis (" + version + ") is not at least version " + RedisUtil.MAJOR_VERSION + "." + RedisUtil.MINOR_VERSION + " RedisBungee requires a newer version of Redis.");
throw new RuntimeException("Unsupported Redis version detected");
}
long uuidCacheSize = unifiedJedis.hlen("uuid-cache");
if (uuidCacheSize > 750000) {
plugin.logInfo("Looks like you have a really big UUID cache! Run '/rb clean' to remove expired cache entries");
}
break;
}
}
return null;
}
}.execute();
}
}

View File

@@ -0,0 +1,31 @@
package com.imaginarycode.minecraft.redisbungee.api.util;
import com.google.common.annotations.VisibleForTesting;
@VisibleForTesting
public class RedisUtil {
public final static int PROXY_TIMEOUT = 30;
public static final int MAJOR_VERSION = 6;
public static final int MINOR_VERSION = 2;
public static boolean isRedisVersionRight(String redisVersion) {
String[] args = redisVersion.split("\\.");
if (args.length < 2) {
return false;
}
int major = Integer.parseInt(args[0]);
int minor = Integer.parseInt(args[1]);
if (major > MAJOR_VERSION) return true;
return major == MAJOR_VERSION && minor >= MINOR_VERSION;
}
// Ham1255: i am keeping this if some plugin uses this *IF*
@Deprecated
public static boolean canUseLua(String redisVersion) {
// Need to use >=3 to use Lua optimizations.
return isRedisVersionRight(redisVersion);
}
}

View File

@@ -0,0 +1,40 @@
package com.imaginarycode.minecraft.redisbungee.api.util.serialize;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.io.ByteArrayDataOutput;
import java.util.Collection;
import java.util.Map;
public class MultiMapSerialization {
public static void serializeMultiset(Multiset<String> collection, ByteArrayDataOutput output) {
output.writeInt(collection.elementSet().size());
for (Multiset.Entry<String> entry : collection.entrySet()) {
output.writeUTF(entry.getElement());
output.writeInt(entry.getCount());
}
}
@SuppressWarnings("SameParameterValue")
public static void serializeMultimap(Multimap<String, String> collection, boolean includeNames, ByteArrayDataOutput output) {
output.writeInt(collection.keySet().size());
for (Map.Entry<String, Collection<String>> entry : collection.asMap().entrySet()) {
output.writeUTF(entry.getKey());
if (includeNames) {
serializeCollection(entry.getValue(), output);
} else {
output.writeInt(entry.getValue().size());
}
}
}
public static void serializeCollection(Collection<?> collection, ByteArrayDataOutput output) {
output.writeInt(collection.size());
for (Object o : collection) {
output.writeUTF(o.toString());
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import java.util.Calendar;
import java.util.UUID;
public record CachedUUIDEntry(String name, UUID uuid, Calendar expiry) {
public boolean expired() {
return Calendar.getInstance().after(expiry);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class NameFetcher {
private static final OkHttpClient httpClient = new OkHttpClient();
private static final Gson gson = new Gson();
public static List<String> nameHistoryFromUuid(UUID uuid) throws IOException {
String name = getName(uuid);
if (name == null) return Collections.emptyList();
return Collections.singletonList(name);
}
public static String getName(UUID uuid) throws IOException {
String url = "https://playerdb.co/api/player/minecraft/" + uuid.toString();
Request request = new Request.Builder()
.addHeader("User-Agent", "RedisBungee-ProxioDev")
.url(url)
.get()
.build();
ResponseBody body = httpClient.newCall(request).execute().body();
String response = body.string();
body.close();
JsonObject json = gson.fromJson(response, JsonObject.class);
if (!json.has("success") || !json.get("success").getAsBoolean()) return null;
if (!json.has("data")) return null;
JsonObject data = json.getAsJsonObject("data");
if (!data.has("player")) return null;
JsonObject player = data.getAsJsonObject("player");
if (!player.has("username")) return null;
return player.get("username").getAsString();
}
}

View File

@@ -1,9 +1,18 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid; /*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.google.gson.Gson;
import com.squareup.okhttp.*; import okhttp3.*;
import lombok.Setter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -18,9 +27,8 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
private static final MediaType JSON = MediaType.parse("application/json"); private static final MediaType JSON = MediaType.parse("application/json");
private final List<String> names; private final List<String> names;
private final boolean rateLimiting; private final boolean rateLimiting;
private static final Gson gson = new Gson();
@Setter private static final OkHttpClient httpClient = new OkHttpClient();
private static OkHttpClient httpClient;
private UUIDFetcher(List<String> names, boolean rateLimiting) { private UUIDFetcher(List<String> names, boolean rateLimiting) {
this.names = ImmutableList.copyOf(names); this.names = ImmutableList.copyOf(names);
@@ -39,12 +47,12 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
Map<String, UUID> uuidMap = new HashMap<>(); Map<String, UUID> uuidMap = new HashMap<>();
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++) { for (int i = 0; i < requests; i++) {
String body = RedisBungee.getGson().toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); String body = gson.toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size())));
Request request = new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build(); Request request = new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build();
ResponseBody responseBody = httpClient.newCall(request).execute().body(); ResponseBody responseBody = httpClient.newCall(request).execute().body();
String response = responseBody.string(); String response = responseBody.string();
responseBody.close(); responseBody.close();
Profile[] array = RedisBungee.getGson().fromJson(response, Profile[].class); Profile[] array = gson.fromJson(response, Profile[].class);
for (Profile profile : array) { for (Profile profile : array) {
UUID uuid = UUIDFetcher.getUUID(profile.id); UUID uuid = UUIDFetcher.getUUID(profile.id);
uuidMap.put(profile.name, uuid); uuidMap.put(profile.name, uuid);

View File

@@ -0,0 +1,208 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.exceptions.JedisException;
import java.util.Calendar;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
public final class UUIDTranslator {
private static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");
private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}");
private final RedisBungeePlugin<?> plugin;
private final Map<String, CachedUUIDEntry> nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4);
private final Map<UUID, CachedUUIDEntry> uuidToNameMap = new ConcurrentHashMap<>(128, 0.5f, 4);
private static final Gson gson = new Gson();
public UUIDTranslator(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
private void addToMaps(String name, UUID uuid) {
// This is why I like LocalDate...
// Cache the entry for three days.
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 3);
// Create the entry and populate the local maps
CachedUUIDEntry entry = new CachedUUIDEntry(name, uuid, calendar);
nameToUuidMap.put(name.toLowerCase(), entry);
uuidToNameMap.put(uuid, entry);
}
public UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (plugin.getPlayer(player) != null)
return plugin.getPlayerUUID(player);
// Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase());
if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired())
return cachedUUIDEntry.uuid();
else
nameToUuidMap.remove(player);
}
// Check if we can exit early
if (UUID_PATTERN.matcher(player).find()) {
return UUID.fromString(player);
}
if (MOJANGIAN_UUID_PATTERN.matcher(player).find()) {
// Reconstruct the UUID
return UUIDFetcher.getUUID(player);
}
// If we are in offline mode, UUID generation is simple.
// We don't even have to cache the UUID, since this is easy to recalculate.
if (!plugin.isOnlineMode()) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
}
RedisTask<UUID> redisTask = new RedisTask<UUID>(plugin) {
@Override
public UUID unifiedJedisTask(UnifiedJedis unifiedJedis) {
String stored = unifiedJedis.hget("uuid-cache", player.toLowerCase());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
unifiedJedis.hdel("uuid-cache", player.toLowerCase());
// Doesn't hurt to also remove the UUID entry as well.
unifiedJedis.hdel("uuid-cache", entry.uuid().toString());
} else {
nameToUuidMap.put(player.toLowerCase(), entry);
uuidToNameMap.put(entry.uuid(), entry);
return entry.uuid();
}
}
// That didn't work. Let's ask Mojang.
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
} catch (Exception e) {
plugin.logFatal("Unable to fetch UUID from Mojang for " + player);
return null;
}
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(player)) {
persistInfo(entry.getKey(), entry.getValue(), unifiedJedis);
return entry.getValue();
}
}
return null;
}
};
// Let's try Redis.
try {
return redisTask.execute();
} catch (JedisException e) {
plugin.logFatal("Unable to fetch UUID for " + player);
}
return null; // Nope, game over!
}
public String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (plugin.getPlayer(player) != null)
return plugin.getPlayerName(player);
// Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player);
if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired())
return cachedUUIDEntry.name();
else
uuidToNameMap.remove(player);
}
RedisTask<String> redisTask = new RedisTask<String>(plugin) {
@Override
public String unifiedJedisTask(UnifiedJedis unifiedJedis) {
String stored = unifiedJedis.hget("uuid-cache", player.toString());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
unifiedJedis.hdel("uuid-cache", player.toString());
// Doesn't hurt to also remove the named entry as well.
// TODO: Since UUIDs are fixed, we could look up the name and see if the UUID matches.
unifiedJedis.hdel("uuid-cache", entry.name());
} else {
nameToUuidMap.put(entry.name().toLowerCase(), entry);
uuidToNameMap.put(player, entry);
return entry.name();
}
}
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
// That didn't work. Let's ask PlayerDB.
String name;
try {
name = NameFetcher.getName(player);
} catch (Exception e) {
plugin.logFatal("Unable to fetch name from PlayerDB for " + player);
return null;
}
if (name != null) {
persistInfo(name, player, unifiedJedis);
return name;
}
return null;
}
};
// Okay, it wasn't locally cached. Let's try Redis.
try {
return redisTask.execute();
} catch (JedisException e) {
plugin.logFatal("Unable to fetch name for " + player);
return null;
}
}
public void persistInfo(String name, UUID uuid, UnifiedJedis unifiedJedis) {
addToMaps(name, uuid);
String json = gson.toJson(uuidToNameMap.get(uuid));
unifiedJedis.hset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
}
}

View File

@@ -0,0 +1,203 @@
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and documentation
distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and are
distributed by that particular Contributor. A Contribution 'originates'
from a Contributor if it was added to the Program by such Contributor
itself or anyone acting on such Contributor's behalf. Contributions do not
include additions to the Program which: (i) are separate modules of
software distributed in conjunction with the Program under their own
license agreement, and (ii) are not derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.
"Program" means the Contributions distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free copyright license to
reproduce, prepare derivative works of, publicly display, publicly
perform, distribute and sublicense the Contribution of such Contributor,
if any, and such derivative works, in source code and object code form.
b) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free patent license under
Licensed Patents to make, use, sell, offer to sell, import and otherwise
transfer the Contribution of such Contributor, if any, in source code and
object code form. This patent license shall apply to the combination of
the Contribution and the Program if, at the time the Contribution is
added by the Contributor, such addition of the Contribution causes such
combination to be covered by the Licensed Patents. The patent license
shall not apply to any other combinations which include the Contribution.
No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the licenses
to its Contributions set forth herein, no assurances are provided by any
Contributor that the Program does not infringe the patent or other
intellectual property rights of any other entity. Each Contributor
disclaims any liability to Recipient for claims brought by any other
entity based on infringement of intellectual property rights or
otherwise. As a condition to exercising the rights and licenses granted
hereunder, each Recipient hereby assumes sole responsibility to secure
any other intellectual property rights needed, if any. For example, if a
third party patent license is required to allow Recipient to distribute
the Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright
license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under
its own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties
and conditions, express and implied, including warranties or
conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for
damages, including direct, indirect, special, incidental and
consequential damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are
offered by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable
manner on or through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its Contribution,
if
any, in a manner that reasonably allows subsequent Recipients to identify the
originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with
respect to end users, business partners and the like. While this license is
intended to facilitate the commercial use of the Program, the Contributor who
includes the Program in a commercial product offering should do so in a manner
which does not create potential liability for other Contributors. Therefore,
if a Contributor includes the Program in a commercial product offering, such
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
every other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits and
other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such Commercial
Contributor in connection with its distribution of the Program in a commercial
product offering. The obligations in this section do not apply to any claims
or Losses relating to any actual or alleged intellectual property
infringement. In order to qualify, an Indemnified Contributor must:
a) promptly notify the Commercial Contributor in writing of such claim, and
b) allow the Commercial Contributor to control, and cooperate with the
Commercial Contributor in, the defense and any related settlement
negotiations. The Indemnified Contributor may participate in any such claim at
its own expense.
For example, a Contributor might include the Program in a commercial product
offering, Product X. That Contributor is then a Commercial Contributor. If
that Commercial Contributor then makes performance claims, or offers
warranties related to Product X, those performance claims and warranties are
such Commercial Contributor's responsibility alone. Under this section, the
Commercial Contributor would have to defend claims against the other
Contributors related to those performance claims and warranties, and if a
court requires any other Contributor to pay any damages as a result, the
Commercial Contributor must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
Recipient is solely responsible for determining the appropriateness of using
and distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to the
risks and costs of program errors, compliance with applicable laws, damage to
or loss of data, programs or equipment, and unavailability or interruption of
operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of the
remainder of the terms of this Agreement, and without further action by the
parties hereto, such provision shall be reformed to the minimum extent
necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
(excluding combinations of the Program with other software or hardware)
infringes such Recipient's patent(s), then such Recipient's rights granted
under Section 2(b) shall terminate as of the date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipient's rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipient's obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue
and survive.
Everyone is permitted to copy and distribute copies of this Agreement, but in
order to avoid inconsistency the Agreement is copyrighted and may only be
modified in the following manner. The Agreement Steward reserves the right to
publish new versions (including revisions) of this Agreement from time to
time. No one other than the Agreement Steward has the right to modify this
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
Eclipse Foundation may assign the responsibility to serve as the Agreement
Steward to a suitable separate entity. Each new version of the Agreement will
be given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version of the
Agreement is published, Contributor may elect to distribute the Program
(including its Contributions) under the new version. Except as expressly
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
licenses to the intellectual property of any Contributor under this Agreement,
whether expressly, by implication, estoppel or otherwise. All rights in the
Program not expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to this
Agreement will bring a legal action under this Agreement more than one year
after the cause of action arose. Each party waives its rights to a jury trial in
any resulting litigation.

View File

@@ -0,0 +1,151 @@
# RedisBungee configuration file.
# Notice:
# Redis 7.2.4 is last free and open source Redis version after license change
# https://download.redis.io/releases/redis-7.2.4.tar.gz which you have to compile yourself,
# unless your package manager still provide it.
# Here is The alternatives
# - 'ValKey' By linux foundation https://valkey.io/download/
# - 'KeyDB' by Snapchat inc https://docs.keydb.dev/docs/download/
# The 'Redis', 'ValKey', 'KeyDB' server you will use.
# these settings are ignored when cluster mode is enabled.
redis-server: 127.0.0.1
redis-port: 6379
# Cluster Mode
# enabling this option will enable cluster mode.
cluster-mode-enabled: false
# FORMAT:
# redis-cluster-servers:
# - host: 127.0.0.1`
# port: 2020
# - host: 127.0.0.1
# port: 2021
# - host: 127.0.0.1
# port: 2021
# you can set single server and Jedis will automatically discover cluster nodes,
# but might fail if this single redis node is down when Proxy startup, its recommended put the all the nodes
redis-cluster-servers:
- host: 127.0.0.1
port: 6379
# OPTIONAL: if your redis uses acl usernames set the username here. leave empty for no username.
redis-username: ""
# OPTIONAL but recommended: If your Redis server uses AUTH, set the required password.
redis-password: ""
# Maximum connections that will be maintained to the Redis server.
# The default is 10. This setting should be left as-is unless you have some wildly
# inefficient plugins or a lot of players.
max-redis-connections: 10
# since redis can support ssl by version 6 you can use SSL/TLS in redis bungee too!
# but there is more configuration needed to work see https://github.com/ProxioDev/RedisBungee/issues/18
# Keep note that SSL/TLS connections will decrease redis performance so use it when needed.
useSSL: false
# An identifier for this network, which helps to separate redisbungee instances on same redis instance.
# You can use environment variable 'REDISBUNGEE_NETWORK_ID' to override
# Depending on the platform bungeecord or velocity system will append platform id to the network id
# to prevent proxies from different platforms from seeing each other. since 0.13.0
network-id: "main"
# An identifier for this BungeeCord / Velocity instance. Will randomly generate if leaving it blank.
# You can set Environment variable 'REDISBUNGEE_PROXY_ID' to override
proxy-id: "proxy-1"
# since RedisBungee Internally now uses UnifiedJedis instead of Jedis, JedisPool.
# which will break compatibility with old plugins that uses RedisBungee JedisPool
# so to mitigate this issue, RedisBungee will create an JedisPool for compatibility reasons.
# disabled by default
# Automatically disabled when cluster mode is enabled
enable-jedis-pool-compatibility: false
# max connections for the compatibility pool
compatibility-max-connections: 3
# restore old login behavior before 0.9.0 update
# enabled by default
# when true: when player login and there is old player with same uuid it will get disconnected as result and new player will log in
# when false: when a player login but login will fail because old player is still connected.
kick-when-online: true
# enabled by default
# this option tells RedisBungee handle motd and set online count, when motd is requested
# you can disable this when you want to handle motd yourself, use RedisBungee api to get total players when needed :)
handle-motd: true
# MOTD plugins compatibility setting
# Allowed values: FIRST, NORMAL, LAST
# This option enables RedisBungee to manage various interactions between other plugins and the online player count,
# which is dynamically updated to a global player count in ping responses if the handle-motd option is enabled.
# If you encounter issues with other plugins accessing or modifying the player count, try using a value of FIRST or LAST.
handle-motd-order: NORMAL
# A list of IP addresses for which RedisBungee will not modify the response for, useful for automatic
# restart scripts.
# Automatically disabled if handle-motd is disabled.
exempt-ip-addresses: []
# disabled by default
# RedisBungee will attempt to connect player to last server that was stored.
reconnect-to-last-server: false
# For redis bungee legacy commands
# either can be run using '/rbl glist' for example
# or if 'install' is set to true '/glist' can be used.
# 'install' also overrides the proxy installed commands
#
# In legacy commands each command got it own permissions since they had it own permission pre new command system,
# so it's also applied to subcommands in '/rbl'.
commands:
# Permission redisbungee.legacy.use
redisbungee-legacy:
enabled: false
subcommands:
# Permission redisbungee.command.glist
glist:
enabled: false
install: false
# Permission redisbungee.command.find
find:
enabled: false
install: false
# Permission redisbungee.command.lastseen
lastseen:
enabled: false
install: false
# Permission redisbungee.command.ip
ip:
enabled: false
install: false
# Permission redisbungee.command.pproxy
pproxy:
enabled: false
install: false
# Permission redisbungee.command.sendtoall
sendtoall:
enabled: false
install: false
# Permission redisbungee.command.serverid
serverid:
enabled: false
install: false
# Permission redisbungee.command.serverids
serverids:
enabled: false
install: false
# Permission redisbungee.command.plist
plist:
enabled: false
install: false
# Permission redisbungee.command.use
redisbungee:
enabled: true
# Config version DO NOT CHANGE!!!!
config-version: 2

26
commands/build.gradle.kts Normal file
View File

@@ -0,0 +1,26 @@
plugins {
`java-library`
}
dependencies {
compileOnly(project(":RedisBungee-API"))
implementation(libs.acf.core)
compileOnly(libs.adventure.api)
compileOnly(libs.adventure.miniMessage)
}
description = "RedisBungee common commands"
tasks {
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
filteringCharset = Charsets.UTF_8.name()
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandContexts;
import co.aikar.commands.CommandManager;
import co.aikar.commands.InvalidCommandArgument;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.legacy.LegacyRedisBungeeCommands;
import java.util.UUID;
public class CommandLoader {
public static void initCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
registerContexts(commandManager);
var commandsConfiguration = plugin.configuration().commandsConfiguration();
if (commandsConfiguration.redisbungeeEnabled()) {
commandManager.registerCommand(new CommandRedisBungee(plugin));
}
if (commandsConfiguration.redisbungeeLegacyEnabled()) {
commandManager.registerCommand(new LegacyRedisBungeeCommands(commandManager,plugin));
}
commandManager.registerCommand(new CommandRedisBungeeDebug(plugin));
}
private static void registerContexts(CommandManager<?, ?, ?, ?, ?, ?> commandManager) {
CommandContexts<?> commandContexts = commandManager.getCommandContexts();
commandContexts.registerContext(UUID.class, c -> {
String uuidString = c.popFirstArg();
try {
return UUID.fromString(uuidString);
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("invaild uuid");
}
});
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.annotation.*;
import com.google.common.primitives.Ints;
import com.imaginarycode.minecraft.redisbungee.Constants;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import com.imaginarycode.minecraft.redisbungee.commands.utils.StopperUUIDCleanupTask;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@CommandAlias("rb|redisbungee")
@CommandPermission("redisbungee.command.use")
@Description("Main command")
public class CommandRedisBungee extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin;
public CommandRedisBungee(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
@Default
@Subcommand("info|version|git")
@Description("information about current redisbungee build")
public void info(CommandIssuer issuer) {
final String message = """
<color:aqua>This proxy is running RedisBungee Limework's fork
<color:gold>========================================
<color:aqua>RedisBungee version: <color:green><version>
<color:aqua>Commit: <color:green><commit>
<color:gold>========================================
<color:gold>run /rb help for more commands""";
sendMessage(
issuer,
MiniMessage.miniMessage()
.deserialize(
message,
Placeholder.component("version", Component.text(Constants.VERSION)),
Placeholder.component(
"commit",
Component.text(Constants.GIT_COMMIT.substring(0, 8))
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink()))
.hoverEvent(HoverEvent.showText(Component.text("Click me to open: " + Constants.getGithubCommitLink())))
)));
}
// <color:aqua>......: <color:green>......
@HelpCommand
@Description("shows the help page")
public void help(CommandIssuer issuer) {
final String barFormat = "<color:gold>========================================";
final String commandFormat = "<color:aqua>/rb <sub-command>: <color:green><description>";
TextComponent.Builder message = Component.text();
message.append(MiniMessage.miniMessage().deserialize(barFormat));
getSubCommands().forEach((subCommand, registeredCommand) -> {
String[] split = registeredCommand.getCommand().split(" ");
if (split.length > 1 && subCommand.equalsIgnoreCase(split[1])) {
message.appendNewline().append(MiniMessage.miniMessage().deserialize(commandFormat, Placeholder.component("sub-command", Component.text(subCommand)),
Placeholder.component("description", MiniMessage.miniMessage().deserialize(registeredCommand.getHelpText()))
));
}
});
message.appendNewline().append(MiniMessage.miniMessage().deserialize(barFormat));
sendMessage(issuer, message.build());
}
@Subcommand("clean")
@Description("cleans up the uuid cache<color:red> <bold>WARNING...</bold> <color:white>command above could cause performance issues")
@Private
public void cleanUp(CommandIssuer issuer) {
if (StopperUUIDCleanupTask.isRunning) {
sendMessage(issuer,
Component.text("cleanup is currently running!").color(NamedTextColor.RED));
return;
}
sendMessage(issuer,
Component.text("cleanup is Starting, you should see the output status in the proxy console").color(NamedTextColor.GOLD));
plugin.executeAsync(new StopperUUIDCleanupTask(plugin));
}
private List<Map.Entry<String, Integer>> subListProxies(List<Map.Entry<String, Integer>> data, final int currentPage, final int pageSize) {
return data.subList(((currentPage * pageSize) - pageSize), Ints.constrainToRange(currentPage * pageSize, 0, data.size()));
}
@Subcommand("show")
@Description("Shows proxies in this network")
public void showProxies(CommandIssuer issuer, String[] args) {
final String closer = "<color:gold>========================================";
final String pageTop = "<color:yellow>Page: <color:green><current>/<max> <color:yellow>Network ID: <color:green><network> <color:yellow>Proxies online: <color:green><proxies>";
final String proxy = "<color:yellow><proxy><here> : <color:green><players> online";
final String proxyHere = " (#) ";
final String nextPage = ">>>>>";
final String previousPage = "<<<<< ";
final String pageInvalid = "<color:red>invalid page";
final String noProxies = "<color:red>No proxies were found :(";
final int pageSize = 16;
int currentPage;
if (args.length > 0) {
try {
currentPage = Integer.parseInt(args[0]);
if (currentPage < 1) currentPage = 1;
} catch (NumberFormatException e) {
sendMessage(issuer, MiniMessage.miniMessage().deserialize(pageInvalid));
return;
}
} else currentPage = 1;
var data = new ArrayList<>(plugin.proxyDataManager().eachProxyCount().entrySet());
// there is no way this runs because there is always an heartbeat.
// if not could be some shenanigans done by devs :P
if (data.isEmpty()) {
sendMessage(issuer, MiniMessage.miniMessage().deserialize(noProxies));
return;
}
// compute the total pages
int maxPages = (int) Math.ceil(data.size() / (double) pageSize);
if (currentPage > maxPages) currentPage = maxPages;
var subList = subListProxies(data, currentPage, pageSize);
TextComponent.Builder builder = Component.text();
builder.append(MiniMessage.miniMessage().deserialize(closer)).appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(pageTop,
Placeholder.component("current", Component.text(currentPage)),
Placeholder.component("max", Component.text(maxPages)),
Placeholder.component("network", Component.text(plugin.proxyDataManager().networkId())),
Placeholder.component("proxies", Component.text(data.size()))
)).appendNewline();
int left = pageSize;
for (Map.Entry<String, Integer> entrySet : subList) {
builder.append(MiniMessage.miniMessage().deserialize(proxy,
Placeholder.component("proxy", Component.text(entrySet.getKey())),
Placeholder.component("here", Component.text(plugin.proxyDataManager().proxyId().equals(entrySet.getKey()) ? proxyHere : "")),
Placeholder.component("players", Component.text(entrySet.getValue()))
)).appendNewline();
left--;
}
while(left > 0) {
builder.appendNewline();
left--;
}
if (currentPage > 1) {
builder.append(MiniMessage.miniMessage().deserialize(previousPage)
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage - 1))));
} else {
builder.append(MiniMessage.miniMessage().deserialize(previousPage).color(NamedTextColor.GRAY));
}
if (subList.size() == pageSize && !subListProxies(data, currentPage + 1, pageSize).isEmpty()) {
builder.append(MiniMessage.miniMessage().deserialize(nextPage)
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage + 1))));
} else {
builder.append(MiniMessage.miniMessage().deserialize(nextPage).color(NamedTextColor.GRAY));
}
builder.appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(closer));
sendMessage(issuer, builder.build());
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.*;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import java.util.UUID;
@CommandAlias("rbd|redisbungeedebug")
@CommandPermission("redisbungee.command.debug.use")
@Description("debug commands")
public class CommandRedisBungeeDebug extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin;
public CommandRedisBungeeDebug(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
@Subcommand("kickByName")
@Description("kicks a player from the network by name")
@Private
public void kick(CommandIssuer issuer, String playerName) {
plugin.playerDataManager().serializedPlayerKick(plugin.getUuidTranslator().getTranslatedUuid(playerName, false), "kicked using redisbungee api using name");
}
@Subcommand("kickByUUID")
@Description("kicks a player from the network by UUID")
@Private
public void kick(CommandIssuer issuer, UUID uuid) {
plugin.playerDataManager().serializedPlayerKick(uuid, "kicked using redisbungee api using uuid");
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("find|rfind")
@CommandPermission("redisbungee.command.find")
public class CommandFind extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandFind(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void find(CommandIssuer issuer, String[] args) {
rootCommand.find(issuer, args);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("glist|rglist")
@CommandPermission("redisbungee.command.glist")
public class CommandGList extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandGList(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void gList(CommandIssuer issuer, String[] args) {
rootCommand.gList(issuer, args);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("ip|playerip|rip|rplayerip")
@CommandPermission("redisbungee.command.ip")
public class CommandIp extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandIp(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void ip(CommandIssuer issuer, String[] args) {
this.rootCommand.ip(issuer, args);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("lastseen|rlastseend")
@CommandPermission("redisbungee.command.lastseen")
public class CommandLastSeen extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandLastSeen(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void lastSeen(CommandIssuer issuer, String[] args) {
this.rootCommand.lastSeen(issuer,args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("pproxy")
@CommandPermission("redisbungee.command.pproxy")
public class CommandPProxy extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandPProxy(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void playerProxy(CommandIssuer issuer, String[] args) {
this.rootCommand.playerProxy(issuer,args);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("plist|rplist")
@CommandPermission("redisbungee.command.plist")
public class CommandPlist extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandPlist(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void playerList(CommandIssuer issuer, String[] args) {
this.rootCommand.playerList(issuer, args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("sendtoall|rsendtoall")
@CommandPermission("redisbungee.command.sendtoall")
public class CommandSendToAll extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandSendToAll(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void sendToAll(CommandIssuer issuer, String[] args) {
this.rootCommand.sendToAll(issuer, args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("serverid|rserverid")
@CommandPermission("redisbungee.command.serverid")
public class CommandServerId extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandServerId(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void serverId(CommandIssuer issuer) {
this.rootCommand.serverId(issuer);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("serverids|rserverids")
@CommandPermission("redisbungee.command.serverids")
public class CommandServerIds extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandServerIds(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void serverIds(CommandIssuer issuer) {
this.rootCommand.serverIds(issuer);
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.CommandManager;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Subcommand;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
@CommandAlias("rbl|redisbungeelegacy")
@CommandPermission("redisbungee.legacy.use")
public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin;
public LegacyRedisBungeeCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
var commands = plugin.configuration().commandsConfiguration().legacySubCommandsConfiguration();
if (!plugin.configuration().commandsConfiguration().redisbungeeLegacyEnabled()) throw new IllegalStateException("someone tried to init me while disabled!");
if (commands == null) throw new NullPointerException("commands config is null!!");
if (commands.installGlist()) commandManager.registerCommand(new CommandGList(this));
if (commands.installFind()) commandManager.registerCommand(new CommandFind(this));
if (commands.installIp()) commandManager.registerCommand(new CommandIp(this));
if (commands.installLastseen()) commandManager.registerCommand(new CommandLastSeen(this));
if (commands.installPlist()) commandManager.registerCommand(new CommandPlist(this));
if (commands.installPproxy()) commandManager.registerCommand(new CommandPProxy(this));
if (commands.installSendtoall()) commandManager.registerCommand(new CommandSendToAll(this));
if (commands.installServerid()) commandManager.registerCommand(new CommandServerId(this));
if (commands.installServerids()) commandManager.registerCommand(new CommandServerIds(this));
}
private static final Component NO_PLAYER_SPECIFIED =
Component.text("You must specify a player name.", NamedTextColor.RED);
private static final Component PLAYER_NOT_FOUND =
Component.text("No such player found.", NamedTextColor.RED);
private static final Component NO_COMMAND_SPECIFIED =
Component.text("You must specify a command to be run.", NamedTextColor.RED);
private static String playerPlural(int num) {
return num == 1 ? num + " player is" : num + " players are";
}
@Subcommand("glist")
@CommandPermission("redisbungee.command.glist")
public void gList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
int count = plugin.getAbstractRedisBungeeApi().getPlayerCount();
Component playersOnline = Component.text(playerPlural(count) + " currently online.", NamedTextColor.YELLOW);
if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
// if for any reason UUID translation fails just return the uuid as name, to make command finish executing.
String playerName = plugin.getUuidTranslator().getNameFromUuid(value, false);
human.put(key, playerName != null ? playerName : value.toString());
});
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
Component serverName = Component.text("[" + server + "] ", NamedTextColor.GREEN);
Component serverCount = Component.text("(" + serverToPlayers.get(server).size() + "): ", NamedTextColor.YELLOW);
Component serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers));
}
sendMessage(issuer, playersOnline);
} else {
sendMessage(issuer, playersOnline);
sendMessage(issuer, Component.text("To see all players online, use /glist showall.", NamedTextColor.YELLOW));
}
});
}
@Subcommand("find")
@CommandPermission("redisbungee.command.find")
public void find(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
String proxyId = plugin.playerDataManager().getProxyFor(uuid);
if (proxyId != null) {
String serverId = plugin.playerDataManager().getServerFor(uuid);
Component message = Component.text(args[0] + " is on proxy " + proxyId + " on server " + serverId +".", NamedTextColor.BLUE);
sendMessage(issuer, message);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
}
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("lastseen")
@CommandPermission("redisbungee.command.lastseen")
public void lastSeen(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
long secs = plugin.getAbstractRedisBungeeApi().getLastOnline(uuid);
TextComponent.Builder message = Component.text();
if (secs == 0) {
message.color(NamedTextColor.GREEN);
message.content(args[0] + " is currently online.");
} else if (secs != -1) {
message.color(NamedTextColor.BLUE);
message.content(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else {
message.color(NamedTextColor.RED);
message.content(args[0] + " has never been online.");
}
sendMessage(issuer, message.build());
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("ip")
@CommandPermission("redisbungee.command.ip")
public void ip(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid);
if (ia != null) {
TextComponent message = Component.text(args[0] + " is connected from " + ia.toString() + ".", NamedTextColor.GREEN);
sendMessage(issuer, message);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
}
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("pproxy")
@CommandPermission("redisbungee.command.pproxy")
public void playerProxy(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid);
if (proxy != null) {
TextComponent message = Component.text(args[0] + " is connected to " + proxy + ".", NamedTextColor.GREEN);
sendMessage(issuer, message);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
}
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("sendtoall")
@CommandPermission("redisbungee.command.sendtoall")
public void sendToAll(CommandIssuer issuer, String[] args) {
if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args);
plugin.getAbstractRedisBungeeApi().sendProxyCommand(command);
TextComponent message = Component.text("Sent the command /" + command + " to all proxies.", NamedTextColor.GREEN);
sendMessage(issuer, message);
} else {
sendMessage(issuer, NO_COMMAND_SPECIFIED);
}
}
@Subcommand("serverid")
@CommandPermission("redisbungee.command.serverid")
public void serverId(CommandIssuer issuer) {
sendMessage(issuer, Component.text("You are on " + plugin.getAbstractRedisBungeeApi().getProxyId() + ".", NamedTextColor.YELLOW));
}
@Subcommand("serverids")
@CommandPermission("redisbungee.command.serverids")
public void serverIds(CommandIssuer issuer) {
sendMessage(issuer, Component.text("All Proxies IDs: " + Joiner.on(", ").join(plugin.getAbstractRedisBungeeApi().getAllProxies()), NamedTextColor.YELLOW));
}
@Subcommand("plist")
@CommandPermission("redisbungee.command.plist")
public void playerList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) {
sendMessage(issuer, Component.text(proxy + " is not a valid proxy. See /serverids for valid proxies.", NamedTextColor.RED));
return;
}
Set<UUID> players = plugin.getAbstractRedisBungeeApi().getPlayersOnProxy(proxy);
Component playersOnline = Component.text(playerPlural(players.size()) + " currently on proxy " + proxy + ".", NamedTextColor.YELLOW);
if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
if (players.contains(value)) {
human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false));
}
});
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = Component.text("[" + server + "] ", NamedTextColor.RED);
TextComponent serverCount = Component.text("(" + human.get(server).size() + "): ", NamedTextColor.YELLOW);
TextComponent serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers));
}
sendMessage(issuer, playersOnline);
} else {
sendMessage(issuer, playersOnline);
sendMessage(issuer, Component.text("To see all players online, use /plist " + proxy + " showall.", NamedTextColor.YELLOW));
}
});
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.utils;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandIssuer;
import net.kyori.adventure.text.Component;
/**
* this just dumb class that wraps the adventure stuff into base command
*/
public abstract class AdventureBaseCommand extends BaseCommand {
protected void sendMessage(CommandIssuer issuer, Component component) {
CommandPlatformHelper.getPlatformHelper().sendMessage(issuer, component);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.utils;
import co.aikar.commands.CommandIssuer;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import net.kyori.adventure.text.Component;
public abstract class CommandPlatformHelper {
private static CommandPlatformHelper SINGLETON;
public abstract void sendMessage(CommandIssuer issuer, Component component);
public static void init(CommandPlatformHelper platformHelper) {
if (SINGLETON != null) {
throw new IllegalStateException("tried to re init Platform Helper");
}
SINGLETON = platformHelper;
}
public static CommandPlatformHelper getPlatformHelper() {
return SINGLETON;
}
}

View File

@@ -0,0 +1,25 @@
package com.imaginarycode.minecraft.redisbungee.commands.utils;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.tasks.UUIDCleanupTask;
import redis.clients.jedis.UnifiedJedis;
public class StopperUUIDCleanupTask extends UUIDCleanupTask {
public static boolean isRunning = false;
public StopperUUIDCleanupTask(RedisBungeePlugin<?> plugin) {
super(plugin);
}
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
isRunning = true;
try {
super.unifiedJedisTask(unifiedJedis);
} catch (Exception ignored) {}
isRunning = false;
return null;
}
}

7
copyright_print.txt Normal file
View File

@@ -0,0 +1,7 @@
Copyright (c) 2013-present RedisBungee contributors
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html

2
gradle.properties Normal file
View File

@@ -0,0 +1,2 @@
group=com.imaginarycode.minecraft
version=0.13.0-SNAPSHOT

36
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,36 @@
[versions]
guava = "33.5.0-jre"
jedis = "5.2.0"
okhttp = "4.12.0"
configurateV3 = "3.7.3"
caffeine = "3.2.3"
adventure = "4.26.1"
adventure-bungeecord-platform = "4.4.1"
acf = "e2005dd62d"
bungeecordApi = "1.21-R0.5-SNAPSHOT"
velocity = "3.5.0-SNAPSHOT"
[plugins]
blossom = { id = "net.kyori.blossom", version = "2.2.0" }
indragit = {id = "net.kyori.indra.git", version="4.0.0"}
shadow = { id = "com.gradleup.shadow", version = "9.3.1" }
run-velocity = { id = "xyz.jpenilla.run-velocity", version = "2.3.1" }
[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
jedis = { module = "redis.clients:jedis", version.ref = "jedis" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
configurateV3 = { module = "org.spongepowered:configurate-yaml", version.ref = "configurateV3" }
caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" }
adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure" }
adventure-miniMessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
acf-core = { module = "com.github.ProxioDev.commands:acf-core", version.ref = "acf" }
acf-bungeecord = { module = "com.github.ProxioDev.commands:acf-bungee", version.ref = "acf" }
acf-velocity = { module = "com.github.ProxioDev.commands:acf-velocity", version.ref = "acf" }
platform-bungeecord = { module = "net.md-5:bungeecord-api", version.ref = "bungeecordApi" }
adventure-platforms-bungeecord = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventure-bungeecord-platform" }
platform-velocity = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

251
gradlew vendored Executable file
View File

@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

94
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
jitpack.yml Normal file
View File

@@ -0,0 +1,2 @@
jdk:
- openjdk21

49
lang/build.gradle.kts Normal file
View File

@@ -0,0 +1,49 @@
plugins {
`java-library`
`maven-publish`
}
dependencies {
compileOnly(project(":RedisBungee-API"))
compileOnly(libs.adventure.api)
compileOnly(libs.adventure.miniMessage)
}
description = "RedisBungee languages"
java {
withJavadocJar()
withSourcesJar()
}
tasks {
// thanks again for paper too
withType<Javadoc> {
val options = options as StandardJavadocDocletOptions
options.use()
options.isDocFilesSubDirs = true
val adventureVersion = libs.adventure.api.get().version
options.links(
"https://jd.advntr.dev/api/$adventureVersion"
)
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
filteringCharset = Charsets.UTF_8.name()
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package net.limework.valiobungee.config.lang;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.GenericConfigLoader;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Locale;
public interface LangConfigLoader extends GenericConfigLoader {
int CONFIG_VERSION = 1;
default void loadLangConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException {
Path configFile = createConfigFile(dataFolder, "lang.yml", "lang.yml");
final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(configFile).build();
ConfigurationNode node = yamlConfigurationFileLoader.load();
if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
handleOldConfig(dataFolder, "lang.yml", "lang.yml");
node = yamlConfigurationFileLoader.load();
}
// MINI message serializer
MiniMessage miniMessage = MiniMessage.miniMessage();
Component prefix = miniMessage.deserialize(node.getNode("prefix").getString("<color:red>[<color:yellow>Redis<color:red>Bungee]"));
Locale defaultLocale = Locale.forLanguageTag(node.getNode("default-locale").getString("en-us"));
boolean useClientLocale = node.getNode("use-client-locale").getBoolean(true);
LangConfiguration.Messages messages = new LangConfiguration.Messages(defaultLocale);
node.getNode("messages").getChildrenMap().forEach((key, childNode) -> childNode.getChildrenMap().forEach((childKey, childChildNode) -> {
messages.register(key.toString(), Locale.forLanguageTag(childKey.toString()), childChildNode.getString());
}));
messages.test(defaultLocale);
onLangConfigLoad(new LangConfiguration(prefix, defaultLocale, useClientLocale, messages));
}
void onLangConfigLoad(LangConfiguration langConfiguration);
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package net.limework.valiobungee.config.lang;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* This language support implementation is temporarily
* until I come up with better system but for now we will use Maps instead :/
* Todo: possible usage of adventure api
*/
public class LangConfiguration {
private interface RegistrableMessages {
void register(String id, Locale locale, String miniMessage);
void test(Locale locale);
default void throwError(Locale locale, String where) {
throw new IllegalStateException("Language system in `" + where + "` found missing entries for " + locale.toString());
}
}
public static class Messages implements RegistrableMessages{
private final Map<Locale, Component> LOGGED_IN_FROM_OTHER_LOCATION;
private final Map<Locale, Component> ALREADY_LOGGED_IN;
private final Map<Locale, String> SERVER_CONNECTING;
private final Map<Locale, String> SERVER_NOT_FOUND;
private final Locale defaultLocale;
public Messages(Locale defaultLocale) {
LOGGED_IN_FROM_OTHER_LOCATION = new HashMap<>();
ALREADY_LOGGED_IN = new HashMap<>();
SERVER_CONNECTING = new HashMap<>();
SERVER_NOT_FOUND = new HashMap<>();
this.defaultLocale = defaultLocale;
}
public void register(String id, Locale locale, String miniMessage) {
switch (id) {
case "server-not-found" -> SERVER_NOT_FOUND.put(locale, miniMessage);
case "server-connecting" -> SERVER_CONNECTING.put(locale, miniMessage);
case "logged-in-other-location" -> LOGGED_IN_FROM_OTHER_LOCATION.put(locale, MiniMessage.miniMessage().deserialize(miniMessage));
case "already-logged-in" -> ALREADY_LOGGED_IN.put(locale, MiniMessage.miniMessage().deserialize(miniMessage));
}
}
public Component alreadyLoggedIn(Locale locale) {
if (ALREADY_LOGGED_IN.containsKey(locale)) return ALREADY_LOGGED_IN.get(locale);
return ALREADY_LOGGED_IN.get(defaultLocale);
}
// there is no way to know whats client locale during login so just default to use default locale MESSAGES.
public Component alreadyLoggedIn() {
return this.alreadyLoggedIn(this.defaultLocale);
}
public Component loggedInFromOtherLocation(Locale locale) {
if (LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale)) return LOGGED_IN_FROM_OTHER_LOCATION.get(locale);
return LOGGED_IN_FROM_OTHER_LOCATION.get(defaultLocale);
}
// there is no way to know what's client locale during login so just default to use default locale MESSAGES.
public Component loggedInFromOtherLocation() {
return this.loggedInFromOtherLocation(this.defaultLocale);
}
public Component serverConnecting(Locale locale, String server) {
String miniMessage;
if (SERVER_CONNECTING.containsKey(locale)) {
miniMessage = SERVER_CONNECTING.get(locale);
} else {
miniMessage = SERVER_CONNECTING.get(defaultLocale);
}
return MiniMessage.miniMessage().deserialize(miniMessage, Placeholder.parsed("server", server));
}
public Component serverConnecting(String server) {
return this.serverConnecting(this.defaultLocale, server);
}
public Component serverNotFound(Locale locale, String server) {
String miniMessage;
if (SERVER_NOT_FOUND.containsKey(locale)) {
miniMessage = SERVER_NOT_FOUND.get(locale);
} else {
miniMessage = SERVER_NOT_FOUND.get(defaultLocale);
}
return MiniMessage.miniMessage().deserialize(miniMessage, Placeholder.parsed("server", server));
}
public Component serverNotFound(String server) {
return this.serverNotFound(this.defaultLocale, server);
}
// tests locale if set CORRECTLY or just throw if not
public void test(Locale locale) {
if (!(LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale) && ALREADY_LOGGED_IN.containsKey(locale) && SERVER_CONNECTING.containsKey(locale) && SERVER_NOT_FOUND.containsKey(locale))) {
throwError(locale, "messages");
}
}
}
private final Component redisBungeePrefix;
private final Locale defaultLanguage;
private final boolean useClientLanguage;
private final Messages messages;
public LangConfiguration(Component redisBungeePrefix, Locale defaultLanguage, boolean useClientLanguage, Messages messages) {
this.redisBungeePrefix = redisBungeePrefix;
this.defaultLanguage = defaultLanguage;
this.useClientLanguage = useClientLanguage;
this.messages = messages;
}
public Component redisBungeePrefix() {
return redisBungeePrefix;
}
public Locale defaultLanguage() {
return defaultLanguage;
}
public boolean useClientLanguage() {
return useClientLanguage;
}
public Messages messages() {
return messages;
}
}

View File

@@ -0,0 +1,55 @@
# this config file is for messages / Languages
# use MiniMessage format https://docs.advntr.dev/minimessage/format.html
# for colors etc... Legacy chat color is not supported.
# Language codes used in minecraft from the minecraft wiki
# example: en-us for american english and ar-sa for arabic
# all codes can be obtained from link below
# from the colum Locale Code -> In-game
# NOTE: minecraft wiki shows languages like this `en_us` in config it should be `en-us`
# https://minecraft.wiki/w/Language
# example:
# lets assume we want to add arabic language.
# messages:
# logged-in-other-location:
# en-us: "<color:red>You logged in from another location!"
# ar-sa: "<color:red>لقد اتصلت من مكان اخر"
# RedisBungee Prefix if ever used.
prefix: "<color:red>[<color:yellow>Redis<color:red>Bungee]"
# en-us is american English, Which is the default language used when a language for a message isn't defined.
# Warning: IF THE set default locale wasn't defined in the config for all messages, plugin will not load.
# set the Default locale
default-locale: en-us
# send language based on client sent settings
# if you don't have languages configured For client Language
# it will default to language that has been set above
# NOTE: due minecraft protocol not sending player settings during login,
# some of the messages like logged-in-other-location will
# skip translation and use default locale that has been set in default-locale.
use-client-locale: true
# messages that are used during login, and connecting to Last server
messages:
logged-in-other-location:
en-us: "<color:red>You logged in from another location!"
pt-br: "<color:red>Você está logado em outra localização!"
already-logged-in:
en-us: "<color:red>You are already logged in!"
pt-br: "<color:red>Você já está logado!"
server-not-found:
# placeholder <server> displays server name in the message.
en-us: "<color:red>unable to connect you to the last server, because server <server> was not found."
pt-br: "<color:red>falha ao conectar você ao último servidor, porque o servidor <server> não foi encontrado."
server-connecting:
# placeholder <server> displays server name in the message.
en-us: "<color:green>Connecting you to <server>..."
pt-br: "<color:green>Conectando você a <server>..."
# DO NOT CHANGE!!!!!
config-version: 1

310
mvnw vendored
View File

@@ -1,310 +0,0 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

182
mvnw.cmd vendored
View File

@@ -1,182 +0,0 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

130
pom.xml
View File

@@ -1,130 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>
<groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee</artifactId>
<version>0.6.5</version>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<inceptionYear>2013</inceptionYear>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>redis.clients.jedis</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.jedis
</shadedPattern>
</relocation>
<relocation>
<pattern>redis.clients.util</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.jedisutil
</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons.pool</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.commonspool
</shadedPattern>
</relocation>
<relocation>
<pattern>com.squareup.okhttp</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.okhttp
</shadedPattern>
</relocation>
<relocation>
<pattern>okio</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.okio
</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<source>8</source>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.10.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.7.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,61 @@
plugins {
java
alias(libs.plugins.shadow)
}
dependencies {
implementation(project(":RedisBungee-Bungee"))
compileOnly(libs.platform.bungeecord)
implementation(libs.adventure.platforms.bungeecord)
implementation(libs.adventure.miniMessage)
implementation(libs.acf.bungeecord)
implementation(project(":RedisBungee-Commands"))
implementation(project(":RedisBungee-Lang"))
}
description = "RedisBungee Bungeecord implementation"
java {
withSourcesJar()
}
tasks {
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
processResources {
filteringCharset = Charsets.UTF_8.name()
filesMatching("plugin.yml") {
filter {
it.replace("*{redisbungee.version}*", "$version", false)
}
}
}
shadowJar {
relocate("redis.clients.jedis", "com.imaginarycode.minecraft.redisbungee.internal.jedis")
relocate("redis.clients.util", "com.imaginarycode.minecraft.redisbungee.internal.jedisutil")
relocate("org.apache.commons.pool", "com.imaginarycode.minecraft.redisbungee.internal.commonspool")
relocate("okhttp3", "com.imaginarycode.minecraft.redisbungee.internal.okhttp3")
relocate("kotlin", "com.imaginarycode.minecraft.redisbungee.internal.kotlin")
relocate("okio", "com.imaginarycode.minecraft.redisbungee.internal.okio")
relocate("org.json", "com.imaginarycode.minecraft.redisbungee.internal.json")
// configurate shade
relocate("ninja.leaping.configurate", "com.imaginarycode.minecraft.redisbungee.internal.configurate")
relocate("org.yaml", "com.imaginarycode.minecraft.redisbungee.internal.yml")
relocate("com.google.common", "com.imaginarycode.minecraft.redisbungee.internal.com.google.common")
relocate("com.google.errorprone", "com.imaginarycode.minecraft.redisbungee.internal.com.google.errorprone")
relocate("com.google.gson", "com.imaginarycode.minecraft.redisbungee.internal.com.google.gson")
relocate("com.google.j2objc", "com.imaginarycode.minecraft.redisbungee.internal.com.google.j2objc")
relocate("com.google.thirdparty", "com.imaginarycode.minecraft.redisbungee.internal.com.google.thirdparty")
relocate("com.github.benmanes.caffeine", "com.imaginarycode.minecraft.redisbungee.internal.caffeine")
// acf shade
relocate("co.aikar.commands", "com.imaginarycode.minecraft.redisbungee.internal.acf.commands")
// adventure :/
relocate("net.kyori", "com.imaginarycode.minecraft.redisbungee.internal.net.kyori")
}
}

View File

@@ -0,0 +1,47 @@
plugins {
`java-library`
`maven-publish`
}
dependencies {
api(project(":RedisBungee-API"))
compileOnly(libs.adventure.platforms.bungeecord)
compileOnly(libs.platform.bungeecord)
}
description = "RedisBungee Bungeecord API"
java {
withJavadocJar()
withSourcesJar()
}
tasks {
withType<Javadoc> {
dependsOn(project(":RedisBungee-API").getTasksByName("javadoc", false))
val options = options as StandardJavadocDocletOptions
options.use()
options.isDocFilesSubDirs = true
options.links(
"https://hub.spigotmc.org/jenkins/job/BungeeCord/ws/api/target/reports/apidocs", // bungeecord api
)
val apiDocs = File(rootProject.projectDir, "api/build/docs/javadoc")
options.linksOffline("https://ci.limework.net/ValioBungee/api/build/docs/javadoc", apiDocs.path)
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
import net.kyori.adventure.text.Component;
import java.util.UUID;
// this class used to redirect calls to keep the implementation and api separate
public interface ApiPlatformSupport {
void kickPlayer(UUID player, Component message);
}

View File

@@ -0,0 +1,163 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.UUID;
/**
* This platform class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungeeAPI#getRedisBungeeApi()}
* or somehow you got the Plugin instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}.
*
* @author tuxed
* @since 0.2.3 | updated 0.8.0
*/
public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
private static RedisBungeeAPI redisBungeeApi;
private static final BungeeComponentSerializer BUNGEE_COMPONENT_SERIALIZER = BungeeComponentSerializer.get();
public RedisBungeeAPI(RedisBungeePlugin<?> plugin) {
super(plugin);
if (redisBungeeApi == null) {
redisBungeeApi = this;
}
}
/**
* Get the server where the specified player is playing. This function also deals with the case of local players
* as well, and will return local information on them.
*
* @param player a player uuid
* @return {@link ServerInfo} Can be null if proxy can't find it.
* @see #getServerNameFor(UUID)
*/
@Nullable
public final ServerInfo getServerFor(@NonNull UUID player) {
String serverName = this.getServerNameFor(player);
if (serverName == null) return null;
return ((Plugin) this.plugin).getProxy().getServerInfo(serverName);
}
/**
* Kicks a player from the network
* calls {@link #getUuidFromName(String)} to get uuid
*
* @param playerName player name
* @param message kick message that player will see on kick
* @since 0.13.0
*/
public void kickPlayer(String playerName, BaseComponent[] message) {
kickPlayer(getUuidFromName(playerName), message);
}
/**
* Kicks a player from the network
*
* @param player player uuid
* @param message kick message that player will see on kick
* @since 0.13.0
*/
public void kickPlayer(UUID player, BaseComponent[] message) {
kickPlayer(player, BUNGEE_COMPONENT_SERIALIZER.deserialize(message));
}
/**
* Kicks a player from the network
* calls {@link #getUuidFromName(String)} to get uuid
*
* @param playerName player name
* @param message kick message that player will see on kick
* @since 0.12.0
*/
public void kickPlayer(String playerName, Component message) {
kickPlayer(getUuidFromName(playerName), message);
}
/**
* Kicks a player from the network
*
* @param player player uuid
* @param message kick message that player will see on kick
* @since 0.12.0
*/
public void kickPlayer(UUID player, Component message) {
((ApiPlatformSupport) this.plugin).kickPlayer(player, message);
}
/**
* Get the current BungeeCord / Velocity proxy ID for this server.
*
* @return the current server ID
* @see #getAllServers()
* @since 0.2.5
* @deprecated to avoid confusion between A server and A proxy see #getProxyId()
*/
@Deprecated(forRemoval = true)
public final String getServerId() {
return getProxyId();
}
/**
* Get all the linked proxies in this network.
*
* @return the list of all proxies
* @see #getServerId()
* @since 0.2.5
* @deprecated to avoid confusion between A server and A proxy see see {@link #getAllProxies()}
*/
@Deprecated(forRemoval = true)
public final List<String> getAllServers() {
return getAllProxies();
}
/**
* Register (a) PubSub channel(s), so that you may handle PubSubMessageEvent for it.
*
* @param channels the channels to register
* @since 0.3
* @deprecated No longer required
*/
@Deprecated(forRemoval = true)
public final void registerPubSubChannels(String... channels) {
}
/**
* Unregister (a) PubSub channel(s).
*
* @param channels the channels to unregister
* @since 0.3
* @deprecated No longer required
*/
@Deprecated(forRemoval = true)
public final void unregisterPubSubChannels(String... channels) {
}
/**
* Api instance
*
* @return the API instance.
* @since 0.6.5
*/
public static RedisBungeeAPI getRedisBungeeApi() {
return redisBungeeApi;
}
}

View File

@@ -1,6 +1,16 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
@@ -14,8 +24,7 @@ import java.util.UUID;
* *
* @since 0.3.4 * @since 0.3.4
*/ */
@ToString public class PlayerChangedServerNetworkEvent extends Event implements IPlayerChangedServerNetworkEvent {
public class PlayerChangedServerNetworkEvent extends Event {
private final UUID uuid; private final UUID uuid;
private final String previousServer; private final String previousServer;
private final String server; private final String server;
@@ -26,14 +35,17 @@ public class PlayerChangedServerNetworkEvent extends Event {
this.server = server; this.server = server;
} }
@Override
public UUID getUuid() { public UUID getUuid() {
return uuid; return uuid;
} }
@Override
public String getServer() { public String getServer() {
return server; return server;
} }
@Override
public String getPreviousServer() { public String getPreviousServer() {
return previousServer; return previousServer;
} }

View File

@@ -1,6 +1,16 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
@@ -14,14 +24,14 @@ import java.util.UUID;
* *
* @since 0.3.4 * @since 0.3.4
*/ */
@ToString public class PlayerJoinedNetworkEvent extends Event implements IPlayerJoinedNetworkEvent {
public class PlayerJoinedNetworkEvent extends Event {
private final UUID uuid; private final UUID uuid;
public PlayerJoinedNetworkEvent(UUID uuid) { public PlayerJoinedNetworkEvent(UUID uuid) {
this.uuid = uuid; this.uuid = uuid;
} }
@Override
public UUID getUuid() { public UUID getUuid() {
return uuid; return uuid;
} }

View File

@@ -1,6 +1,16 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
@@ -14,14 +24,14 @@ import java.util.UUID;
* *
* @since 0.3.4 * @since 0.3.4
*/ */
@ToString public class PlayerLeftNetworkEvent extends Event implements IPlayerLeftNetworkEvent {
public class PlayerLeftNetworkEvent extends Event {
private final UUID uuid; private final UUID uuid;
public PlayerLeftNetworkEvent(UUID uuid) { public PlayerLeftNetworkEvent(UUID uuid) {
this.uuid = uuid; this.uuid = uuid;
} }
@Override
public UUID getUuid() { public UUID getUuid() {
return uuid; return uuid;
} }

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import net.md_5.bungee.api.plugin.Event;
/**
* This event is posted when a PubSub message is received.
* <p>
* <strong>Warning</strong>: This event is fired in a separate thread!
*
* @since 0.2.6
*/
public class PubSubMessageEvent extends Event implements IPubSubMessageEvent {
private final String channel;
private final String message;
public PubSubMessageEvent(String channel, String message) {
this.channel = channel;
this.message = message;
}
@Override
public String getChannel() {
return channel;
}
@Override
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,17 @@
package com.imaginarycode.minecraft.redisbungee;
import co.aikar.commands.BungeeCommandIssuer;
import co.aikar.commands.CommandIssuer;
import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
public class BungeeCommandPlatformHelper extends CommandPlatformHelper {
@Override
public void sendMessage(CommandIssuer issuer, Component component) {
BungeeCommandIssuer bIssuer = (BungeeCommandIssuer) issuer;
bIssuer.getIssuer().sendMessage(BungeeComponentSerializer.get().serialize(component));
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> implements Listener {
private final RedisBungee bPlugin;
public BungeePlayerDataManager(RedisBungee plugin) {
super(plugin);
bPlugin = plugin;
}
@EventHandler
public void onPlayerChangedServerNetworkEvent(PlayerChangedServerNetworkEvent event) {
super.handleNetworkPlayerServerChange(event);
}
@EventHandler
public void onNetworkPlayerQuit(PlayerLeftNetworkEvent event) {
super.handleNetworkPlayerQuit(event);
}
@EventHandler
public void onNetworkPlayerJoin(PlayerJoinedNetworkEvent event) {
super.handleNetworkPlayerJoin(event);
}
@EventHandler
public void onPubSubMessageEvent(PubSubMessageEvent event) {
super.handlePubSubMessageEvent(event);
}
@EventHandler
public void onServerConnectedEvent(ServerConnectedEvent event) {
final String currentServer = event.getServer().getInfo().getName();
final String oldServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
}
private final BungeeComponentSerializer BUNGEE_COMPONENT_SERIALIZER = BungeeComponentSerializer.get();
private final static MiniMessage MINI_MESSAGE_SERIALIZER = MiniMessage.miniMessage();
@Override
public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) {
ProxiedPlayer player = plugin.getPlayer(uuid);
if (player == null) return false;
// decode the adventure component
if (serializedMiniMessage == null) {
// kick the player too even if the message is invalid
player.disconnect(BUNGEE_COMPONENT_SERIALIZER.serialize(Component.empty()));
plugin.logWarn("unable to decode serialized adventure component because its empty or null");
} else {
Component message = MINI_MESSAGE_SERIALIZER.deserialize(serializedMiniMessage);
player.disconnect(BUNGEE_COMPONENT_SERIALIZER.serialize(message));
}
return true;
}
public void kickPlayer(UUID player, Component message) {
serializedPlayerKick(player, MINI_MESSAGE_SERIALIZER.serialize(message));
}
@EventHandler
public void onLoginEvent(LoginEvent event) {
event.registerIntent((Plugin) plugin);
// check if online
if (getLastOnline(event.getConnection().getUniqueId()) == 0) {
// because something can go wrong and proxy somehow does not update player data correctly on shutdown
// we have to check proxy if it has the player
String proxyId = getProxyFor(event.getConnection().getUniqueId());
if (proxyId == null || !plugin.proxyDataManager().isPlayerTrulyOnProxy(proxyId, event.getConnection().getUniqueId())) {
event.completeIntent((Plugin) plugin);
} else {
if (plugin.configuration().kickWhenOnline()) {
kickPlayer(event.getConnection().getUniqueId(), bPlugin.langConfiguration().messages().loggedInFromOtherLocation());
// wait 3 seconds before releasing the event
plugin.executeAsyncAfter(() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
} else {
event.setCancelled(true);
event.setCancelReason(BungeeComponentSerializer.get().serialize(bPlugin.langConfiguration().messages().alreadyLoggedIn()));
event.completeIntent((Plugin) plugin);
}
}
} else {
event.completeIntent((Plugin) plugin);
}
}
@EventHandler
public void onLoginEvent(PostLoginEvent event) {
super.addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getName(), event.getPlayer().getAddress().getAddress());
}
@EventHandler
public void onDisconnectEvent(PlayerDisconnectEvent event) {
super.removePlayer(event.getPlayer().getUniqueId());
}
}

View File

@@ -0,0 +1,369 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
import co.aikar.commands.BungeeCommandManager;
import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.api.util.InitialUtils;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import com.imaginarycode.minecraft.redisbungee.commands.CommandLoader;
import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import net.kyori.adventure.text.Component;
import net.limework.valiobungee.config.lang.LangConfigLoader;
import net.limework.valiobungee.config.lang.LangConfiguration;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisPool;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.logging.Level;
public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlayer>, ConfigLoader, LangConfigLoader, ApiPlatformSupport {
private static RedisBungeeAPI apiStatic;
private AbstractRedisBungeeAPI api;
private RedisBungeeMode redisBungeeMode;
private ProxyDataManager proxyDataManager;
private BungeePlayerDataManager playerDataManager;
private ScheduledTask heartbeatTask;
private ScheduledTask cleanupTask;
private Summoner<?> summoner;
private UUIDTranslator uuidTranslator;
private RedisBungeeConfiguration configuration;
private LangConfiguration langConfiguration;
private BungeeCommandManager commandManager;
private final Logger logger = LoggerFactory.getLogger("RedisBungee");
@Override
public RedisBungeeConfiguration configuration() {
return this.configuration;
}
public LangConfiguration langConfiguration() {
return this.langConfiguration;
}
@Override
public AbstractRedisBungeeAPI getAbstractRedisBungeeApi() {
return this.api;
}
@Override
public ProxyDataManager proxyDataManager() {
return this.proxyDataManager;
}
@Override
public PlayerDataManager<ProxiedPlayer> playerDataManager() {
return this.playerDataManager;
}
@Override
public UUIDTranslator getUuidTranslator() {
return this.uuidTranslator;
}
@Override
public void fireEvent(Object event) {
this.getProxy().getPluginManager().callEvent((Event) event);
}
@Override
public boolean isOnlineMode() {
return this.getProxy().getConfig().isOnlineMode();
}
@Override
public void logInfo(String msg) {
this.logger.info(msg);
}
@Override
public void logInfo(String format, Object... object) {
this.logger.info(format, object);
}
@Override
public void logWarn(String msg) {
this.logger.warn(msg);
}
@Override
public void logWarn(String format, Object... object) {
this.logger.warn(format, object);
}
@Override
public void logFatal(String msg) {
this.logger.error(msg);
}
@Override
public void logFatal(String format, Throwable throwable) {
this.logger.error(format, throwable);
}
@Override
public ProxiedPlayer getPlayer(UUID uuid) {
return this.getProxy().getPlayer(uuid);
}
@Override
public ProxiedPlayer getPlayer(String name) {
return this.getProxy().getPlayer(name);
}
@Override
public UUID getPlayerUUID(String player) {
return this.getProxy().getPlayer(player).getUniqueId();
}
@Override
public String getPlayerName(UUID player) {
return this.getProxy().getPlayer(player).getName();
}
@Override
public String getPlayerServerName(ProxiedPlayer player) {
return player.getServer().getInfo().getName();
}
@Override
public boolean isPlayerOnAServer(ProxiedPlayer player) {
return player.getServer() != null;
}
@Override
public InetAddress getPlayerIp(ProxiedPlayer player) {
return player.getAddress().getAddress();
}
@Override
public void initialize() {
logInfo("Initializing RedisBungee.....");
logInfo("Version: {}", Constants.VERSION);
ThreadFactory factory = ((ThreadPoolExecutor) getExecutorService()).getThreadFactory();
ScheduledExecutorService service = Executors.newScheduledThreadPool(24, factory);
try {
Field field = Plugin.class.getDeclaredField("service");
field.setAccessible(true);
ExecutorService builtinService = (ExecutorService) field.get(this);
field.set(this, service);
builtinService.shutdownNow();
} catch (IllegalAccessException | NoSuchFieldException e) {
getLogger().log(Level.WARNING, "Can't replace BungeeCord thread pool with our own");
getLogger().log(Level.WARNING, "skipping replacement.....");
}
try {
loadConfig(this, getDataFolder().toPath());
loadLangConfig(this, getDataFolder().toPath());
} catch (IOException e) {
throw new RuntimeException("Unable to load/save config", e);
}
// init the proxy data manager
this.proxyDataManager = new ProxyDataManager(this) {
@Override
public Set<UUID> getLocalOnlineUUIDs() {
HashSet<UUID> uuids = new HashSet<>();
ProxyServer.getInstance().getPlayers().forEach((proxiedPlayer) -> uuids.add(proxiedPlayer.getUniqueId()));
return uuids;
}
@Override
protected void handlePlatformCommandExecution(String command) {
logInfo("Dispatching {}", command);
ProxyServer.getInstance().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), command);
}
};
this.playerDataManager = new BungeePlayerDataManager(this);
getProxy().getPluginManager().registerListener(this, this.playerDataManager);
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this));
// start listening
getProxy().getScheduler().runAsync(this, proxyDataManager);
// heartbeat
this.heartbeatTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.publishHeartbeat(), 0, 1, TimeUnit.SECONDS);
// cleanup
this.cleanupTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.correctionTask(), 0, 60, TimeUnit.SECONDS);
// init the http lib
InitialUtils.checkRedisVersion(this);
uuidTranslator = new UUIDTranslator(this);
// register plugin messages channel.
getProxy().registerChannel("legacy:redisbungee");
getProxy().registerChannel("RedisBungee");
// init the api
this.api = new RedisBungeeAPI(this);
apiStatic = (RedisBungeeAPI) this.api;
// commands
CommandPlatformHelper.init(new BungeeCommandPlatformHelper());
this.commandManager = new BungeeCommandManager(this);
CommandLoader.initCommands(this.commandManager, this);
logInfo("RedisBungee initialized successfully ");
}
@Override
public void stop() {
logInfo("Turning off redis connections.....");
getProxy().getPluginManager().unregisterListeners(this);
if (this.cleanupTask != null) {
this.cleanupTask.cancel();
}
if (heartbeatTask != null) {
heartbeatTask.cancel();
}
try {
this.proxyDataManager.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
this.summoner.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
if (this.commandManager != null) {
this.commandManager.unregisterCommands();
}
logInfo("RedisBungee shutdown successfully");
}
@Override
public Summoner<?> getSummoner() {
return this.summoner;
}
@Override
public RedisBungeeMode getRedisBungeeMode() {
return this.redisBungeeMode;
}
@Override
public void executeAsync(Runnable runnable) {
this.getProxy().getScheduler().runAsync(this, runnable);
}
@Override
public void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time) {
this.getProxy().getScheduler().schedule(this, runnable, time, timeUnit);
}
@Override
public void onEnable() {
initialize();
}
@Override
public void onDisable() {
stop();
}
@Override
public IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server) {
return new PlayerChangedServerNetworkEvent(uuid, previousServer, server);
}
@Override
public IPlayerJoinedNetworkEvent createPlayerJoinedNetworkEvent(UUID uuid) {
return new PlayerJoinedNetworkEvent(uuid);
}
@Override
public IPlayerLeftNetworkEvent createPlayerLeftNetworkEvent(UUID uuid) {
return new PlayerLeftNetworkEvent(uuid);
}
@Override
public IPubSubMessageEvent createPubSubEvent(String channel, String message) {
return new PubSubMessageEvent(channel, message);
}
@Override
public void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
this.configuration = configuration;
this.redisBungeeMode = mode;
this.summoner = summoner;
}
@Override
public void onLangConfigLoad(LangConfiguration langConfiguration) {
this.langConfiguration = langConfiguration;
}
@Override
public String platformId() {
return "bungeecord";
}
@Override
public void kickPlayer(UUID player, Component message) {
this.playerDataManager.kickPlayer(player, message);
}
/**
* This returns an instance of {@link RedisBungeeAPI}
*
* @return the {@link AbstractRedisBungeeAPI} object instance.
* @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} this class intended to for old plugins that no longer updated.
*/
@Deprecated
public static RedisBungeeAPI getApi() {
return apiStatic;
}
@Deprecated
public JedisPool getPool() {
if (api.getMode() == RedisBungeeMode.SINGLE) {
JedisPool jedisPool = ((JedisPooledSummoner) getSummoner()).getCompatibilityJedisPool();
if (jedisPool == null) {
throw new IllegalStateException("JedisPool compatibility mode is disabled, Please enable it in the RedisBungee config.yml");
}
return jedisPool;
} else {
throw new IllegalStateException("Mode is not " + RedisBungeeMode.SINGLE);
}
}
}

View File

@@ -1,24 +1,31 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
/**
* This class is the CommandSender that RedisBungee uses to dispatch commands to BungeeCord.
* <p>
* It inherits all permissions of the console command sender. Sending messages and modifying permissions are no-ops.
*
* @author tuxed
* @since 0.2.3
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RedisBungeeCommandSender implements CommandSender { public class RedisBungeeCommandSender implements CommandSender {
static final RedisBungeeCommandSender instance = new RedisBungeeCommandSender(); private static final RedisBungeeCommandSender singleton;
static {
singleton = new RedisBungeeCommandSender();
}
public static RedisBungeeCommandSender getSingleton() {
return singleton;
}
@Override @Override
public String getName() { public String getName() {
@@ -27,37 +34,37 @@ public class RedisBungeeCommandSender implements CommandSender {
@Override @Override
public void sendMessage(String s) { public void sendMessage(String s) {
// no-op
} }
@Override @Override
public void sendMessages(String... strings) { public void sendMessages(String... strings) {
// no-op
} }
@Override @Override
public void sendMessage(BaseComponent... baseComponents) { public void sendMessage(BaseComponent... baseComponents) {
// no-op
} }
@Override @Override
public void sendMessage(BaseComponent baseComponent) { public void sendMessage(BaseComponent baseComponent) {
// no-op
} }
@Override @Override
public Collection<String> getGroups() { public Collection<String> getGroups() {
return Collections.emptySet(); return null;
} }
@Override @Override
public void addGroups(String... strings) { public void addGroups(String... strings) {
// no-op
} }
@Override @Override
public void removeGroups(String... strings) { public void removeGroups(String... strings) {
// no-op
} }
@Override @Override
@@ -67,7 +74,7 @@ public class RedisBungeeCommandSender implements CommandSender {
@Override @Override
public void setPermission(String s, boolean b) { public void setPermission(String s, boolean b) {
// no-op
} }
@Override @Override

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.AbstractReconnectHandler;
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.connection.Server;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.util.*;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.*;
public class RedisBungeeListener implements Listener {
private final RedisBungee plugin;
public RedisBungeeListener(RedisBungee plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPingFirst(ProxyPingEvent event) {
if (plugin.configuration().handleMotdOrder() != HandleMotdOrder.FIRST) {
return;
}
onPing0(event);
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPingNormal(ProxyPingEvent event) {
if (plugin.configuration().handleMotdOrder() != HandleMotdOrder.NORMAL) {
return;
}
onPing0(event);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPingLast(ProxyPingEvent event) {
if (plugin.configuration().handleMotdOrder() != HandleMotdOrder.LAST) {
return;
}
onPing0(event);
}
private void onPing0(ProxyPingEvent event) {
if (!plugin.configuration().handleMotd()) return;
if (plugin.configuration().getExemptAddresses().contains(event.getConnection().getAddress().getAddress())) return;
ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection());
if (forced != null && event.getConnection().getListener().isPingPassthrough()) return;
event.getResponse().getPlayers().setOnline(plugin.proxyDataManager().totalNetworkPlayers());
}
@SuppressWarnings("UnstableApiUsage")
@EventHandler
public void onPluginMessage(PluginMessageEvent event) {
if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) {
final String currentChannel = event.getTag();
final byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
plugin.executeAsync(() -> {
ByteArrayDataInput in = ByteStreams.newDataInput(data);
String subchannel = in.readUTF();
ByteArrayDataOutput out = ByteStreams.newDataOutput();
String type;
switch (subchannel) {
case "PlayerList" -> {
out.writeUTF("PlayerList");
Set<UUID> original = Collections.emptySet();
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
original = plugin.proxyDataManager().networkPlayers();
} else {
out.writeUTF(type);
try {
original = plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type);
} catch (IllegalArgumentException ignored) {
}
}
Set<String> players = new HashSet<>();
for (UUID uuid : original)
players.add(plugin.getUuidTranslator().getNameFromUuid(uuid, false));
out.writeUTF(Joiner.on(',').join(players));
}
case "PlayerCount" -> {
out.writeUTF("PlayerCount");
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
out.writeInt(plugin.proxyDataManager().totalNetworkPlayers());
} else {
out.writeUTF(type);
try {
out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) {
out.writeInt(0);
}
}
}
case "LastOnline" -> {
String user = in.readUTF();
out.writeUTF("LastOnline");
out.writeUTF(user);
out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
}
case "ServerPlayers" -> {
String type1 = in.readUTF();
out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
boolean includesUsers;
switch (type1) {
case "COUNT" -> includesUsers = false;
case "PLAYERS" -> includesUsers = true;
default -> {
// TODO: Should I raise an error?
return;
}
}
out.writeUTF(type1);
if (includesUsers) {
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : multimap.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
serializeMultimap(human, true, out);
} else {
serializeMultiset(multimap.keys(), out);
}
}
case "Proxy" -> {
out.writeUTF("Proxy");
out.writeUTF(plugin.configuration().getProxyId());
}
case "PlayerProxy" -> {
String username = in.readUTF();
out.writeUTF("PlayerProxy");
out.writeUTF(username);
out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
}
default -> {
return;
}
}
((Server) event.getSender()).sendData(currentChannel, out.toByteArray());
});
}
}
@EventHandler
public void onServerConnectEvent(ServerConnectEvent event) {
if (event.getReason() == ServerConnectEvent.Reason.JOIN_PROXY && plugin.configuration().handleReconnectToLastServer()) {
ProxiedPlayer player = event.getPlayer();
String lastServer = plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId());
if (lastServer == null) return;
player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverConnecting(player.getLocale(), lastServer)));
ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(lastServer);
if (serverInfo == null) {
player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverNotFound(player.getLocale(), lastServer)));
return;
}
event.setTarget(serverInfo);
}
}
}

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