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

355 Commits

Author SHA1 Message Date
c97de82970 player manager 2026-04-04 10:40:46 +04:00
f58a212782 more changes 2026-04-03 19:19:44 +04:00
42e8a861a3 make the api the plugin 2026-04-03 19:19:44 +04:00
c6a45e6b0c setup velocity, ValioBungee api 2026-04-03 19:19:44 +04:00
81491398e9 make introduce libs of redisbungee back 2026-04-03 19:19:44 +04:00
9cb81330e9 make an api version 2026-04-03 19:13:34 +04:00
f1b744bfbe fix license notices 2026-04-03 19:13:34 +04:00
b855764e19 implement the ProxyManager
untested
2026-04-03 19:13:34 +04:00
5e4b151d44 rewrite init 2026-04-03 19:13:30 +04:00
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
mohammed jasem alaajel
5caabc8aa4 prepare for a release 2021-07-31 18:15:02 +04:00
mohammed jasem alaajel
9b4a49f66a don't display exception when can't replace the executor service 2021-07-31 17:07:42 +04:00
mohammed jasem alaajel
785b23ecfb fix java docs issue 2021-07-30 02:20:39 +04:00
mohammed jasem alaajel
d8e0ea71a2 oops 2021-07-30 02:13:39 +04:00
mohammed jasem alaajel
ac66899f98 new api functions 2021-07-30 02:12:01 +04:00
mohammed jasem alaajel
a8673c563b change version 2021-07-30 02:09:53 +04:00
mohammed jasem alaajel
8a4dd53e58 bump depends versions in pom.xml 2021-07-30 02:00:32 +04:00
mohammed jasem alaajel
85711c5688 changed ReadMe 2021-07-22 02:22:11 +04:00
mohammed jasem alaajel
86ebc9c0d2 Fix: Player being added into the Redis database if LoginEvent was cancelled..... (#16) 2021-07-22 02:06:36 +04:00
mohammed jasem alaajel
49ac1ba9ef bump jedis version 2021-07-21 20:50:42 +04:00
mohammed jasem alaajel
d29e51b4a6 Update README.md 2021-07-21 16:33:06 +04:00
mohammed jasem alaajel
1620df961b bumped version 2021-07-21 16:20:48 +04:00
foss-mc
ede9a7cba9 Allow unsuccessful replacement of BungeeCord thread pool (#15)
* Allow unsuccessful replacement of BungeeCord thread pool

Port https://github.com/minecrafter/RedisBungee/pull/76

Some BungeeCord forks have different implementation of thread pool and RedisBungee won't load in such case

* Update RedisBungee.java
2021-07-21 16:13:25 +04:00
mohammed jasem alaajel
701c87db02 add another warning 2021-07-20 17:11:13 +04:00
mohammed jasem alaajel
0ac5a5eebb add warning 2021-07-20 17:08:17 +04:00
mohammed jasem alaajel
0758afeba1 update README.md 2021-07-20 14:55:54 +04:00
mohammed jasem alaajel
9227224248 fix the test checks 2021-07-20 14:52:03 +04:00
mohammed jasem alaajel
ba9ca03e1b oops its should be redis 6.0 not 6.2 2021-07-20 14:49:45 +04:00
williamd5
6c35f23e16 readme.md git clone in current dir, fix link formatting (#13)
* git clone in current dir, fix link formatting

No need to make the user clone into a new dir, then move into this new dir. They will most likely already have an empty dir already created or one with this name might already exist, e.g. a fork repository.

* Update README.md
2021-07-04 17:07:45 +04:00
mohammed jasem alaajel
16720487f0 Update issue templates 2021-06-24 03:58:50 +04:00
mohammed jasem alaajel
6391d8d92c Update issue templates 2021-06-24 03:57:52 +04:00
mohammed jasem alaajel
51115746c7 older versions or redis than (6.2) is no longer supported 2021-06-24 03:54:44 +04:00
mohammed jasem alaajel
f53829481a updated the notice 2021-06-24 03:54:06 +04:00
mohammed jasem alaajel
9441624c09 added java 16 support 2021-06-24 03:35:05 +04:00
mohammed jasem alaajel
4fa152b65f Update README.md 2021-06-22 04:07:02 +04:00
mohammed jasem alaajel
0e9388568a Update README.md 2021-06-22 04:04:53 +04:00
mohammed jasem alaajel
f5a2362b68 Update issue templates 2021-06-22 04:03:30 +04:00
mohammed jasem alaajel
513c1ae2ea Forgotten a word 2021-06-22 02:45:27 +04:00
mohammed jasem alaajel
2a8ab73c2b Update README.md 2021-06-22 01:33:33 +04:00
mohammed jasem alaajel
02a117228a Update README.md 2021-06-12 14:49:29 +04:00
mohammed jasem alaajel
3d1fd57c89 Update README.md 2021-05-31 05:33:00 +04:00
mohammed jasem alaajel
334c17d0d8 Update README.md 2021-05-24 00:45:13 +04:00
mohammed jasem alaajel
5ec5b7af9e Update README.md 2021-05-22 18:17:28 +04:00
mohammed jasem alaajel
ad18d168a8 Update README.md 2021-05-22 18:15:32 +04:00
mohammed jasem alaajel
96793e81c5 code clean up and lambda etc 2021-05-21 16:51:44 +04:00
mohammed jasem alaajel
39b3c03604 fixed the placeholder api problem 2021-05-21 03:34:37 +04:00
weihao
373e1c16d4 Persistent random server id generation (#5)
* Allow random server id

* Assign and save random server id

* Add logging for server id

* Remove unused

* Update notes

* updated readme

* fixed bug from last update

* updated config

* fixed typo of in one of the authors name

Co-authored-by: mohammed jasem alaajel <34905970+ham1255@users.noreply.github.com>
2021-05-21 01:06:34 +04:00
mohammed jasem alaajel
56042af4eb Update README.md 2021-05-19 16:58:45 +04:00
mohammed jasem alaajel
8df8d96ced add random ids! 2021-05-17 22:42:57 +04:00
mohammed jasem alaajel
4c1ffa2b01 changed this to warn instead of severe 2021-05-17 04:11:09 +04:00
mohammed jasem alaajel
02caf2da5e Removed Limework maven server 2021-05-17 00:53:42 +04:00
mohammed jasem alaajel
a005b46467 Update README.md 2021-05-16 21:59:04 +04:00
mohammed jasem alaajel
65abb29558 Update README.md 2021-05-16 21:56:55 +04:00
mohammed jasem alaajel
5169d873a8 Update maven.yml 2021-05-16 21:54:14 +04:00
mohammed jasem alaajel
f0fbaab0b1 Changed default config redirection 2021-05-14 15:55:33 +04:00
mohammed jasem alaajel
4091da2ed8 Update README.md 2021-05-14 15:15:45 +04:00
mohammed jasem alaajel
86bef752de Update README.md 2021-05-14 15:12:07 +04:00
Govindas
17e203d7be small mistake 2021-05-14 10:53:00 +00:00
Govindas
ab8db5570c Update README.md 2021-05-14 10:52:12 +00:00
Govindas
571a318969 Update README.md 2021-05-14 10:51:41 +00:00
mohammed jasem alaajel
f07eed220d Update README.md 2021-05-13 23:12:40 +04:00
mohammed jasem alaajel
76721cd5fd fix to spamming seconds behind (#2)
* fix to spamming seconds behind

* changes the message

* bumped version
2021-05-13 22:41:53 +04:00
mohammed jasem alaajel
f8ed847857 Last commit on the action workflow sorry :/ 2021-05-13 22:16:38 +04:00
mohammed jasem alaajel
bfdbe69b97 Update maven.yml 2021-05-13 22:14:20 +04:00
mohammed jasem alaajel
536686e92f Update maven.yml 2021-05-13 22:10:29 +04:00
mohammed jasem alaajel
78ee62a98a Create maven.yml 2021-04-27 19:52:17 +04:00
Govindas
01f63f0f58 Update bungeecord api to working one 2021-03-28 17:53:23 +03:00
mohammed jasem alaajel
28640ab859 changed version to without snapshot due bug in nexus i think 2021-02-06 16:50:02 +04:00
mohammed jasem alaajel
e72126a15b Update README.md 2021-01-10 23:38:14 +04:00
mohammed jasem alaajel
386b02ed30 Update example_config.yml 2021-01-10 23:23:30 +04:00
mohammed jasem alaajel
40e8831ab8 Update README.md 2021-01-10 22:22:57 +04:00
mohammed jasem alaajel
aca5203962 Update README.md 2021-01-08 00:20:37 +04:00
mohammed jasem alaajel
3512895ad8 fixed some typos in readme! and java docs added 2021-01-08 00:09:43 +04:00
74 changed files with 2858 additions and 3602 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

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: redisbungee/proxies/bungeecord/build/libs/*
- name: Upload Velocity
uses: actions/upload-artifact@v4.4.0
with:
name: RedisBungee-Velocity
path: redisbungee/proxies/velocity/build/libs/*
- name: Upload API
uses: actions/upload-artifact@v4.4.0
with:
name: RedisBungee-API
path: redisbungee/api/build/libs/*

48
.gitignore vendored
View File

@@ -1,36 +1,50 @@
# 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
**/run/
!src/**/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

819
LICENSE
View File

@@ -1,203 +1,674 @@
Eclipse Public License - v 1.0 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM Everyone is permitted to copy and distribute verbatim copies
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. of this license document, but changing it is not allowed.
1. DEFINITIONS Preamble
"Contribution" means: The GNU General Public License is a free, copyleft license for
software and other kinds of works.
a) in the case of the initial Contributor, the initial code and documentation The licenses for most software and other practical works are designed
distributed under this Agreement, and to take away your freedom to share and change the works. By contrast,
b) in the case of each subsequent Contributor: the GNU General Public License is intended to guarantee your freedom to
i) changes to the Program, and share and change all versions of a program--to make sure it remains free
ii) additions to the Program; software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
where such changes and/or additions to the Program originate from and are When we speak of free software, we are referring to freedom, not
distributed by that particular Contributor. A Contribution 'originates' price. Our General Public Licenses are designed to make sure that you
from a Contributor if it was added to the Program by such Contributor have the freedom to distribute copies of free software (and charge for
itself or anyone acting on such Contributor's behalf. Contributions do not them if you wish), that you receive source code or can get it if you
include additions to the Program which: (i) are separate modules of want it, that you can change the software or use pieces of it in new
software distributed in conjunction with the Program under their own free programs, and that you know you can do these things.
license agreement, and (ii) are not derivative works of the Program.
"Contributor" means any person or entity that distributes the Program. To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
"Licensed Patents" mean patent claims licensable by a Contributor which are For example, if you distribute copies of such a program, whether
necessarily infringed by the use or sale of its Contribution alone or when gratis or for a fee, you must pass on to the recipients the same
combined with the Program. freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
"Program" means the Contributions distributed in accordance with this Developers that use the GNU GPL protect your rights with two steps:
Agreement. (1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
"Recipient" means anyone who receives the Program under this Agreement, For the developers' and authors' protection, the GPL clearly explains
including all Contributors. that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
2. GRANT OF RIGHTS Some devices are designed to deny users access to install or run
a) Subject to the terms of this Agreement, each Contributor hereby grants modified versions of the software inside them, although the manufacturer
Recipient a non-exclusive, worldwide, royalty-free copyright license to can do so. This is fundamentally incompatible with the aim of
reproduce, prepare derivative works of, publicly display, publicly protecting users' freedom to change the software. The systematic
perform, distribute and sublicense the Contribution of such Contributor, pattern of such abuse occurs in the area of products for individuals to
if any, and such derivative works, in source code and object code form. use, which is precisely where it is most unacceptable. Therefore, we
b) Subject to the terms of this Agreement, each Contributor hereby grants have designed this version of the GPL to prohibit the practice for those
Recipient a non-exclusive, worldwide, royalty-free patent license under products. If such problems arise substantially in other domains, we
Licensed Patents to make, use, sell, offer to sell, import and otherwise stand ready to extend this provision to those domains in future versions
transfer the Contribution of such Contributor, if any, in source code and of the GPL, as needed to protect the freedom of users.
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 Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
A Contributor may choose to distribute the Program in object code form under The precise terms and conditions for copying, distribution and
its own license agreement, provided that: modification follow.
a) it complies with the terms and conditions of this Agreement; and TERMS AND CONDITIONS
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: 0. Definitions.
a) it must be made available under this Agreement; and "This License" refers to version 3 of the GNU General Public License.
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, "Copyright" also means copyright-like laws that apply to other kinds of
if works, such as semiconductor masks.
any, in a manner that reasonably allows subsequent Recipients to identify the
originator of the Contribution.
4. COMMERCIAL DISTRIBUTION "The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
Commercial distributors of software may accept certain responsibilities with To "modify" a work means to copy from or adapt all or part of the work
respect to end users, business partners and the like. While this license is in a fashion requiring copyright permission, other than the making of an
intended to facilitate the commercial use of the Program, the Contributor who exact copy. The resulting work is called a "modified version" of the
includes the Program in a commercial product offering should do so in a manner earlier work or a work "based on" the earlier work.
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 A "covered work" means either the unmodified Program or a work based
offering, Product X. That Contributor is then a Commercial Contributor. If on the Program.
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 To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN To "convey" a work means any kind of propagation that enables other
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR parties to make or receive copies. Mere interaction with a user through
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, a computer network, with no transfer of a copy, is not conveying.
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 An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 1. Source Code.
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 The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
If any provision of this Agreement is invalid or unenforceable under A "Standard Interface" means an interface that either is an official
applicable law, it shall not affect the validity or enforceability of the standard defined by a recognized standards body, or, in the case of
remainder of the terms of this Agreement, and without further action by the interfaces specified for a particular programming language, one that
parties hereto, such provision shall be reformed to the minimum extent is widely used among developers working in that language.
necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including a The "System Libraries" of an executable work include anything, other
cross-claim or counterclaim in a lawsuit) alleging that the Program itself than the work as a whole, that (a) is included in the normal form of
(excluding combinations of the Program with other software or hardware) packaging a Major Component, but which is not part of that Major
infringes such Recipient's patent(s), then such Recipient's rights granted Component, and (b) serves only to enable use of the work with that
under Section 2(b) shall terminate as of the date such litigation is filed. Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
All Recipient's rights under this Agreement shall terminate if it fails to The "Corresponding Source" for a work in object code form means all
comply with any of the material terms or conditions of this Agreement and does the source code needed to generate, install, and (for an executable
not cure such failure in a reasonable period of time after becoming aware of work) run the object code and to modify the work, including scripts to
such noncompliance. If all Recipient's rights under this Agreement terminate, control those activities. However, it does not include the work's
Recipient agrees to cease use and distribution of the Program as soon as System Libraries, or general-purpose tools or generally available free
reasonably practicable. However, Recipient's obligations under this Agreement programs which are used unmodified in performing those activities but
and any licenses granted by Recipient relating to the Program shall continue which are not part of the work. For example, Corresponding Source
and survive. includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
Everyone is permitted to copy and distribute copies of this Agreement, but in The Corresponding Source need not include anything that users
order to avoid inconsistency the Agreement is copyrighted and may only be can regenerate automatically from other parts of the Corresponding
modified in the following manner. The Agreement Steward reserves the right to Source.
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 The Corresponding Source for a work in source code form is that
intellectual property laws of the United States of America. No party to this same work.
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 2. Basic Permissions.
any resulting litigation.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,38 +1,30 @@
# Limework fork of RedisBungee # ValioBungee: The RedisBungee Limework's rewrite
this fork was made due maintainer of redisBungee became unactive so we took the place to develop it! ## 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.
RedisBungee bridges [Redis](http://redis.io) and BungeeCord together. ## Downloads
RedisBungee was used on thechunk server which we think was shutdown due website not loading... [![](https://raw.githubusercontent.com/Prospector/badges/master/modrinth-badge-72h-padded.png)](https://modrinth.com/plugin/redisbungee)
this will be deployed soon on [Govindas limework!](https://Limework.net) ## Wiki
~~This is the solution deployed on [The Chunk](http://thechunk.net) to make sure our multi-Bungee setup flows smoothly together.~~ https://github.com/ProxioDev/ValioBungee/wiki
## Compiling ## Support
Now you can use maven without installing it using Maven wrapper https://github.com/takari/maven-wrapper :) open an issue with question button
RedisBungee is distributed as a [maven](http://maven.apache.org) project. To compile it and install it in your local Maven repository: ## License
git clone https://github.com/Limework/RedisBungee.git This project is distributed under GPLv3
cd RedisBungee
mvnw clean install
## Javadocs With API exception as Apache License v2
will be availale soon! With Original RedisBungee compatiblity layer as EPLv1
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/)
## Configuration
**REDISBUNGEE REQUIRES A REDIS SERVER**, preferably with reasonably low latency. The default [config](https://github.com/minecrafter/RedisBungee/blob/master/src/main/resources/example_config.yml) is saved when the plugin first starts.
## License!
this project is distruped using Eclipse Public License 1.0
which you can find it [here](https://github.com/Limework/RedisBungee/blob/master/LICENSE)
you can find the orginal redisBungee by minecrafter [here](https://github.com/minecrafter/RedisBungee) or spigot page [here](https://www.spigotmc.org/resources/redisbungee.13494/)

202
api/LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) 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. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

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

@@ -0,0 +1,23 @@
plugins {
`maven-publish`
}
description = "Api functions for valiobungee"
dependencies {
testImplementation(libs.testing.juipter)
}
tasks.test {
useJUnitPlatform()
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}

15
api/copyright_header.txt Normal file
View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/
package net.limework.valiobungee.api;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import net.limework.valiobungee.api.entity.NetworkPlayer;
import net.limework.valiobungee.api.entity.NetworkProxy;
/**
* The api interface
*
* @author Ham1255
* @since 1.0.0
*/
public interface ValioBungeeAPI {
/**
* @param uuid of player
* @return {@link Optional} if empty player is offline and if present player on the network
*/
Optional<NetworkPlayer> getNetworkPlayer(UUID uuid);
/**
* @param id the proxy id
* @return {@link Optional} if empty the proxy doesn't exist if present it's an online proxy
*/
Optional<NetworkProxy> getNetworkProxy(String id);
/**
* @return the local proxy
*/
NetworkProxy getLocalProxy();
/**
* @return Returns the network proxies that is currently online including the local
*/
Set<NetworkProxy> getNetworkProxies();
/**
* @return the local players empty if no players
*/
Set<NetworkPlayer> getLocalProxyPlayers();
/**
* @return the network players empty if no players
*/
Set<NetworkPlayer> getNetworkPlayers();
/**
* @return Version based on the build
*/
String getVersion();
/**
* @return GIT COMMIT based on the build
*/
String getGitCommit();
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/
package net.limework.valiobungee.api.entity;
import java.util.UUID;
/**
* Network player.
*
* @author Ham1255
* @since 1.0.0
*/
public interface NetworkPlayer {
/**
* @return proxy player on
*/
NetworkProxy getProxy();
/**
* @return true when player is on the same proxy
*/
boolean isLocal();
/**
* This used when we already had the object but player went offline since getting a player as
* {@link java.util.Optional} we check if its present if not means offline see {@link
* net.limework.valiobungee.api.ValioBungeeAPI#getNetworkPlayer(UUID)}
*
* @return true when a player is online on the network
*/
boolean isOnline();
/**
* @return player unique id
*/
UUID getUniqueId();
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/
package net.limework.valiobungee.api.entity;
import java.util.Set;
/**
* Proxy is an object for online proxy in a network
*
* @author Ham1255
* @since 1.0.0
*/
public interface NetworkProxy {
/**
* @return return the proxy id of this proxy
*/
String proxyId();
/**
* @return online players number in this proxy
*/
int onlinePlayerCount();
/**
* @return Returns set of players in this proxy
*/
Set<NetworkPlayer> getProxyPlayers();
/**
* @return returns true if this NetworkProxy is the same proxy
*/
boolean isMe();
}

View File

@@ -0,0 +1,33 @@
plugins {
`maven-publish`
}
dependencies {
compileOnly(libs.platform.velocity)
api(project(":valiobungee-api"))
}
description = "ValioBungee Velocity API"
tasks {
withType<Javadoc> {
dependsOn(project(":valiobungee-api").getTasksByName("javadoc", false))
val options = options as StandardJavadocDocletOptions
options.use()
options.isDocFilesSubDirs = true
options.links(
"https://jd.papermc.io/velocity/3.5.0/", // velocity api
)
val apiDocs = File(rootProject.projectDir, "api/build/docs/javadoc")
//options.linksOffline("https://ci.limework.net/ValioBungee/api/build/docs/javadoc", apiDocs.path)
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/
package net.limework.valiobungee.velocity.api;
import net.limework.valiobungee.api.ValioBungeeAPI;
/**
* Valiobungee velocity specific API
*
* @author Ham1255
* @since 1.0.0
*/
public interface VelocityValioBungeeAPI extends ValioBungeeAPI {}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/
package net.limework.valiobungee.velocity.api.entities;
import com.velocitypowered.api.proxy.Player;
import java.util.Optional;
import net.limework.valiobungee.api.entity.NetworkPlayer;
/**
* Velocity Network player. hold specific stuff for velocity platform
*
* @author Ham1255
* @since 1.0.0
*/
public interface VelocityNetworkPlayer extends NetworkPlayer {
/**
* @return an optional that contains a player if he is on the same proxy
*/
Optional<Player> getHandle();
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* 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.
*/
package net.limework.valiobungee.velocity.api.entities;
import net.limework.valiobungee.api.entity.NetworkProxy;
/**
* Proxy is an object for online proxy in a velocity network
*
* @author Ham1255
* @since 1.0.0
*/
public interface VelocityNetworkProxy extends NetworkProxy {}

48
build.gradle.kts Normal file
View File

@@ -0,0 +1,48 @@
plugins {
`java-library`
alias { libs.plugins.spotless } apply false
}
subprojects {
apply { plugin("com.diffplug.spotless") }
apply { plugin("java-library") }
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
withJavadocJar()
withSourcesJar()
}
tasks {
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
filteringCharset = Charsets.UTF_8.name()
}
}
extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> {
var redisBungeeProjects = sequenceOf("RedisBungee-API", "RedisBungee-Lang", "RedisBungee-Commands", "RedisBungee-Bungee", "RedisBungee-Proxy-Bungee", "RedisBungee-Velocity", "RedisBungee-Proxy-Velocity")
var apiProjects = sequenceOf("valiobungee-api", "valiobungee-velocity-api")
java {
removeUnusedImports()
googleJavaFormat()
if (apiProjects.contains(project.name)) {
licenseHeaderFile(rootProject.file("api/copyright_header.txt"))
} else if (redisBungeeProjects.contains(project.name)) {
licenseHeaderFile(rootProject.file("redisbungee/copyright_header.txt"))
} else {
licenseHeaderFile(rootProject.file("copyright_header.txt"))
}
if (project.name == "valiobungee-core") {
targetExclude("**/net/limework/valiobungee/core/proto/**")
}
}
}
}

18
copyright_header.txt Normal file
View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/

40
core/build.gradle.kts Normal file
View File

@@ -0,0 +1,40 @@
plugins {
alias(libs.plugins.blossom)
alias(libs.plugins.indragit)
alias(libs.plugins.protobuf)
}
description = "Core functions for valiobungee"
sourceSets {
main {
blossom {
javaSources {
property("version", version.toString())
property("git", indraGit.commit().get().name)
}
}
}
}
dependencies {
api(project(":valiobungee-api"))
api(libs.protobuf)
api(libs.caffeine)
api(libs.slf4j)
testImplementation(libs.testing.juipter)
testImplementation(libs.testing.slf4j.simple)
}
tasks.test {
useJUnitPlatform()
}
protobuf {
protoc {
artifact = libs.protoc.get().toString()
}
}

View File

@@ -0,0 +1,9 @@
dependencies {
compileOnly(project(":valiobungee-core"))
api(libs.redisson)
}
description = "ValioBungee Redisson implementation"

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core;
public class ConstantVariables {
public static final String VERSION = "{{ version }}";
public static final String GIT_COMMIT = "{{ git }}";
public static String getGithubCommitLink() {
return "https://github.com/ProxioDev/ValioBungee/commit/" + GIT_COMMIT;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import net.limework.valiobungee.api.entity.NetworkPlayer;
public abstract class PlayerManager {
protected final ValioBungeePlatform platform;
public PlayerManager(ValioBungeePlatform platform) {
this.platform = platform;
}
public abstract Optional<NetworkPlayer> getNetworkPlayer(UUID uuid);
public abstract Set<NetworkPlayer> getNetworkPlayers();
public abstract boolean isOnline(UUID uuid);
public abstract void handleJoin(UUID uuid);
public abstract void handleQuit(UUID uuid);
public abstract void correctionTask();
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.limework.valiobungee.api.entity.NetworkProxy;
import net.limework.valiobungee.core.proto.messages.*;
import net.limework.valiobungee.core.util.logging.LogProviderFactory;
import org.slf4j.Logger;
/**
* This abstract class is responsible for proxy discovery and count online players as its cached
* locally Why abstract? Because it allows us to develop alternative implementation to use other
* software than valkey or redis
*/
public abstract class ProxyNetworkManager {
protected final UUID proxyManagerId = UUID.randomUUID();
protected final ValioBungeePlatform platform;
public ProxyNetworkManager(ValioBungeePlatform platform) {
this.platform = platform;
}
protected final Logger log = LogProviderFactory.get();
// proxy info class here stores the ProxyManager ID hence we use another cache for online players
// reason we use caffeine cache it allows us to auto remove entries without need for scheduled
// tasks in the proxies when a proxy disappears without sending death message
private final Cache<String, Heartbeat> heartbeats =
Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(30))
.removalListener(
(RemovalListener<String, Heartbeat>)
(key, value, cause) -> {
if (cause == RemovalCause.EXPIRED) {
assert value != null;
log.warn("proxy {} has disconnected but did not send death message", key);
}
})
.build();
protected void handleProxyHeartBeat(Heartbeat payload) {
if (!this.heartbeats.asMap().containsKey(payload.getSender().getProxyId()))
log.info("Proxy {} has connected!", payload.getSender().getProxyId());
this.heartbeats.put(payload.getSender().getProxyId(), payload);
}
protected void handleProxyDeath(Death payload) {
this.heartbeats.invalidate(payload.getSender().getProxyId());
log.info("Proxy {} has disconnected", payload.getSender().getProxyId());
}
public int onlinePlayersCount() {
// reason we + local online players because our proxy is not inside it own heartbeat cache
return heartbeats.asMap().values().stream().mapToInt(Heartbeat::getOnlinePlayersCount).sum()
+ platform.localOnlinePlayers();
}
public int onlinePlayersCount(String proxyId) {
if (proxyId.equals(this.platform.proxyId())) {
return platform.localOnlinePlayers();
} else {
if (!heartbeats.asMap().containsKey(proxyId)) return 0; // don't error?
return heartbeats.asMap().get(proxyId).getOnlinePlayersCount();
}
}
protected ProxyInfo createProxyInfo() {
return ProxyInfo.newBuilder()
.setProxyId(platform.proxyId())
.setProxyManagerId(this.proxyManagerId.toString())
.build();
}
protected Death createDeathPayload() {
return Death.newBuilder().setSender(createProxyInfo()).setReason(DeathReason.SHUTDOWN).build();
}
protected Heartbeat createHeartbeatPayload() {
return Heartbeat.newBuilder()
.setSender(createProxyInfo())
.setOnlinePlayersCount(platform.localOnlinePlayers())
.build();
}
// return self only if no other proxies
public Set<NetworkProxy> getNetworkProxies() {
return Stream.concat(
this.heartbeats.asMap().values().stream()
.map(h -> platform.proxyPlatformCreator(h.getSender().getProxyId())),
Stream.of(platform.getLocalProxy()))
.collect(Collectors.toSet());
}
public Optional<NetworkProxy> getNetworkProxy(String id) {
if (platform.networkId().equals(id)) return Optional.of(platform.proxyPlatformCreator(id));
if (!this.heartbeats.asMap().containsKey(id)) return Optional.empty();
return Optional.of(
platform.proxyPlatformCreator(this.heartbeats.asMap().get(id).getSender().getProxyId()));
}
protected abstract void publishDeathPayload();
protected abstract void publishHeartbeatPayload();
public abstract void init();
public abstract void close();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core;
import net.limework.valiobungee.api.ValioBungeeAPI;
import net.limework.valiobungee.api.entity.NetworkProxy;
public interface ValioBungeePlatform extends ValioBungeeAPI {
int localOnlinePlayers();
String platformProxyVendor();
String networkId();
String proxyId();
ProxyNetworkManager proxyNetworkManager();
PlayerManager playerManager();
NetworkProxy proxyPlatformCreator(String id);
@Override
default String getGitCommit() {
return ConstantVariables.GIT_COMMIT;
}
@Override
default String getVersion() {
return ConstantVariables.VERSION;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core.api.entities;
import java.util.Objects;
import java.util.UUID;
import net.limework.valiobungee.api.entity.NetworkPlayer;
import net.limework.valiobungee.api.entity.NetworkProxy;
import net.limework.valiobungee.core.ValioBungeePlatform;
public abstract class AbstractNetworkPlayer implements NetworkPlayer {
protected final ValioBungeePlatform platform;
protected final UUID uuid;
protected final NetworkProxy proxy;
public AbstractNetworkPlayer(ValioBungeePlatform platform, UUID uuid, NetworkProxy proxy) {
this.platform = platform;
this.uuid = uuid;
this.proxy = proxy;
}
@Override
public UUID getUniqueId() {
return this.uuid;
}
@Override
public NetworkProxy getProxy() {
return this.proxy;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof AbstractNetworkPlayer that)) return false;
return Objects.equals(uuid, that.uuid) && Objects.equals(proxy, that.proxy);
}
@Override
public int hashCode() {
return Objects.hash(uuid, proxy);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core.api.entities;
import java.util.Objects;
import net.limework.valiobungee.api.entity.NetworkProxy;
import net.limework.valiobungee.core.ValioBungeePlatform;
public abstract class AbstractNetworkProxy implements NetworkProxy {
private final ValioBungeePlatform platform;
private final String proxyId;
public AbstractNetworkProxy(ValioBungeePlatform platform, String proxyId) {
this.platform = platform;
this.proxyId = proxyId;
}
@Override
public String proxyId() {
return this.proxyId;
}
@Override
public int onlinePlayerCount() {
return this.platform.proxyNetworkManager().onlinePlayersCount(this.proxyId);
}
@Override
public boolean isMe() {
return this.platform.proxyId().equals(proxyId);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof AbstractNetworkProxy that)) return false;
return Objects.equals(proxyId, that.proxyId);
}
@Override
public int hashCode() {
return Objects.hashCode(proxyId);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core.util.logging;
import org.slf4j.Logger;
public class LogProviderFactory {
private static Logger instance;
public static void register(Logger logger) {
if (instance != null) throw new IllegalStateException("Logger already registered");
instance = logger;
}
public static Logger get() {
if (instance == null) throw new IllegalStateException("No logger registered");
return instance;
}
}

View File

@@ -0,0 +1,28 @@
syntax = "proto3";
package net.limework.valiobungee.core.proto.messages;
option java_multiple_files = true;
message ProxyInfo {
string proxy_id = 1; // UNIQUE ID
string proxy_manager_id = 2; // ProxyManager id. changes every reboot, used to detect duplicate proxy names
}
// heartbeat ._.?
message Heartbeat {
ProxyInfo sender = 1;
int32 online_players_count = 2;
}
// death
enum DeathReason {
UNSPECIFIED = 0; // TESTING ONLY
RELOAD = 1; // plugin reload
SHUTDOWN = 2; // proxy shutdown
}
message Death {
ProxyInfo sender = 1; // proxy that died
DeathReason reason = 2;
}

View File

@@ -0,0 +1,9 @@
dependencies {
compileOnly(project(":valiobungee-core"))
}
description = "ValioBungee standalone `offline` implementation"

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import net.limework.valiobungee.api.entity.NetworkPlayer;
public class StandalonePlayerManager extends PlayerManager {
public StandalonePlayerManager(ValioBungeePlatform platform) {
super(platform);
}
@Override
public Optional<NetworkPlayer> getNetworkPlayer(UUID uuid) {
return platform.getLocalProxyPlayers().stream()
.filter(p -> p.getUniqueId().equals(uuid))
.findFirst();
}
@Override
public Set<NetworkPlayer> getNetworkPlayers() {
return platform.getLocalProxyPlayers();
}
@Override
public boolean isOnline(UUID uuid) {
return false;
}
@Override
public void handleJoin(UUID uuid) {}
@Override
public void handleQuit(UUID uuid) {}
@Override
public void correctionTask() {}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.core;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import net.limework.valiobungee.core.proto.messages.Death;
import net.limework.valiobungee.core.proto.messages.DeathReason;
import net.limework.valiobungee.core.proto.messages.Heartbeat;
import net.limework.valiobungee.core.proto.messages.ProxyInfo;
/** this class is used to debug the abstract class */
public class StandaloneProxyNetworkManager extends ProxyNetworkManager {
public StandaloneProxyNetworkManager(ValioBungeePlatform platform) {
super(platform);
}
@Override
protected void publishDeathPayload() {}
@Override
protected void publishHeartbeatPayload() {}
private final AtomicBoolean shutdown = new AtomicBoolean(false);
private void makeFakeProxiesRunning() {
// fake 12 proxies
String fakePrefix = "fake-proxy-";
for (int i = 0; i < 6; i++) {
String prefix = fakePrefix + i;
Heartbeat heartbeat =
Heartbeat.newBuilder()
.setSender(ProxyInfo.newBuilder().setProxyId(prefix).build())
.setOnlinePlayersCount(i)
.build();
Thread.ofVirtual()
.start(
() -> {
while (!shutdown.get()) {
handleProxyHeartBeat(heartbeat);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
handleProxyDeath(
Death.newBuilder()
.setReason(DeathReason.SHUTDOWN)
.setSender(heartbeat.getSender())
.build());
});
}
}
private void makeFakeProxiesDisappearing() {
// fake 12 proxies
String fakePrefix = "fake-disappear-proxy-";
for (int i = 0; i < 2; i++) {
String prefix = fakePrefix + i;
Heartbeat heartbeat =
Heartbeat.newBuilder()
.setSender(ProxyInfo.newBuilder().setProxyId(prefix).build())
.setOnlinePlayersCount(i)
.build();
handleProxyHeartBeat(heartbeat);
}
}
private void makeFakeProxiesShuttingDown() {
// fake 12 proxies
String fakePrefix = "fake-shutdown-proxy-";
for (int i = 0; i < 2; i++) {
String prefix = fakePrefix + i;
Heartbeat heartbeat =
Heartbeat.newBuilder()
.setSender(ProxyInfo.newBuilder().setProxyId(prefix).build())
.setOnlinePlayersCount(i)
.build();
Thread.ofVirtual()
.start(
() -> {
int li = 0;
int liMax = 60 + ThreadLocalRandom.current().nextInt(20);
while (!shutdown.get() && li <= liMax) {
handleProxyHeartBeat(heartbeat);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
li++;
}
handleProxyDeath(
Death.newBuilder()
.setReason(DeathReason.SHUTDOWN)
.setSender(heartbeat.getSender())
.build());
});
}
}
@Override
public void init() {
makeFakeProxiesRunning();
makeFakeProxiesDisappearing();
makeFakeProxiesShuttingDown();
}
@Override
public void close() {
shutdown.set(true);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
}

5
gradle.properties Normal file
View File

@@ -0,0 +1,5 @@
group=net.limework
version=1.0.0-SNAPSHOT
redisbungee-group=com.imaginarycode.minecraft
redisbungee-version=0.13.0-SNAPSHOT

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

@@ -0,0 +1,53 @@
[versions]
protobuf-plugin = "0.9.5"
protobuf = "3.25.8" # needed for reference to be used for protoc
slf4j = "2.0.17"
guava = "33.5.0-jre"
jedis = "5.2.0"
okhttp = "4.12.0"
configurateV3 = "3.7.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 = "net.kyori.blossom:2.2.0"
indragit = "net.kyori.indra.git:4.0.0"
shadow = "com.gradleup.shadow:9.3.1"
spotless = "com.diffplug.spotless:8.2.0"
protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
run-velocity = { id = "xyz.jpenilla.run-velocity", version = "3.0.2" }
[libraries]
# protobuf
protobuf = { group = "com.google.protobuf", name = "protobuf-java", version.ref = "protobuf" }
protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
# valiobungee
redisson = "org.redisson:redisson:4.3.0"
caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.3"
# logging
slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
# testing
testing-juipter = "org.junit.jupiter:junit-jupiter:6.0.3"
testing-slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
# redisbungee speific
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" }
# minecraft speific
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

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%

126
pom.xml
View File

@@ -1,126 +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-SNAPSHOT</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>2.9.1</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.4.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.16-R0.4-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</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.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

57
settings.gradle.kts Normal file
View File

@@ -0,0 +1,57 @@
@file:Suppress("UnstableApiUsage")
pluginManagement {
repositories {
gradlePluginPortal()
}
}
rootProject.name = "ValioBungee"
fun configureRootProjects(name: String) {
val projectName = ":valiobungee-$name"
configureProject(projectName, name)
}
fun configureAPISubProject(name: String) {
val projectName = ":valiobungee-$name-api"
configureProject(projectName, "api/$name")
}
fun configureCoreSubProject(name: String) {
val projectName = ":valiobungee-core-$name"
configureProject(projectName, "core/$name")
}
fun configureProject(name: String, path: String) {
include(name)
project(name).projectDir = file(path)
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://jitpack.io")
}
}
// main project stuff
sequenceOf("api", "core", "velocity").forEach { configureRootProjects(it) }
// core data implementations
sequenceOf("redisson", "standalone").forEach { configureCoreSubProject(it) }
// api
sequenceOf("velocity").forEach { configureAPISubProject(it) }
// RedisBunggee Project
// configureProject(":RedisBungee-API", "redisbungee/api")
// configureProject(":RedisBungee-Lang", "redisbungee/lang")
// configureProject(":RedisBungee-Commands", "redisbungee/commands")
// configureProject(":RedisBungee-Bungee", "redisbungee/proxies/bungeecord/bungeecord-api")
// configureProject(":RedisBungee-Proxy-Bungee", "redisbungee/proxies/bungeecord")
// configureProject(":RedisBungee-Velocity", "redisbungee/proxies/velocity/velocity-api")
// configureProject(":RedisBungee-Proxy-Velocity", "redisbungee/proxies/velocity")

View File

@@ -1,258 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
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 lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import redis.clients.jedis.Jedis;
import java.net.InetAddress;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
/**
* This class manages all the data that RedisBungee fetches from Redis, along with updates to that data.
*
* @since 0.3.3
*/
public class DataManager implements Listener {
private final RedisBungee plugin;
private final Cache<UUID, String> serverCache = createCache();
private final Cache<UUID, String> proxyCache = createCache();
private final Cache<UUID, InetAddress> ipCache = createCache();
private final Cache<UUID, Long> lastOnlineCache = createCache();
public DataManager(RedisBungee plugin) {
this.plugin = plugin;
}
private static <K, V> Cache<K, V> createCache() {
// TODO: Allow customization via cache specification, ala ServerListPlus
return CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build();
}
private final JsonParser parser = new JsonParser();
public String getServer(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
if (player != null)
return player.getServer() != null ? player.getServer().getInfo().getName() : null;
try {
return serverCache.get(uuid, new Callable<String>() {
@Override
public String call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) {
return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "server"), "user not found");
}
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
return null; // HACK
plugin.getLogger().log(Level.SEVERE, "Unable to get server", e);
throw new RuntimeException("Unable to get server for " + uuid, e);
}
}
public String getProxy(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
if (player != null)
return RedisBungee.getConfiguration().getServerId();
try {
return proxyCache.get(uuid, new Callable<String>() {
@Override
public String call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) {
return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "proxy"), "user not found");
}
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
return null; // HACK
plugin.getLogger().log(Level.SEVERE, "Unable to get proxy", e);
throw new RuntimeException("Unable to get proxy for " + uuid, e);
}
}
public InetAddress getIp(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
if (player != null)
return player.getAddress().getAddress();
try {
return ipCache.get(uuid, new Callable<InetAddress>() {
@Override
public InetAddress call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) {
String result = tmpRsc.hget("player:" + uuid, "ip");
if (result == null)
throw new NullPointerException("user not found");
return InetAddresses.forString(result);
}
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
return null; // HACK
plugin.getLogger().log(Level.SEVERE, "Unable to get IP", e);
throw new RuntimeException("Unable to get IP for " + uuid, e);
}
}
public long getLastOnline(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
if (player != null)
return 0;
try {
return lastOnlineCache.get(uuid, new Callable<Long>() {
@Override
public Long call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) {
String result = tmpRsc.hget("player:" + uuid, "online");
return result == null ? -1 : Long.valueOf(result);
}
}
});
} catch (ExecutionException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to get last time online", e);
throw new RuntimeException("Unable to get last time online for " + uuid, e);
}
}
private void invalidate(UUID uuid) {
ipCache.invalidate(uuid);
lastOnlineCache.invalidate(uuid);
serverCache.invalidate(uuid);
proxyCache.invalidate(uuid);
}
@EventHandler
public void onPostLogin(PostLoginEvent event) {
// Invalidate all entries related to this player, since they now lie.
invalidate(event.getPlayer().getUniqueId());
}
@EventHandler
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
// Invalidate all entries related to this player, since they now lie.
invalidate(event.getPlayer().getUniqueId());
}
@EventHandler
public void onPubSubMessage(PubSubMessageEvent event) {
if (!event.getChannel().equals("redisbungee-data"))
return;
// Partially deserialize the message so we can look at the action
JsonObject jsonObject = parser.parse(event.getMessage()).getAsJsonObject();
String source = jsonObject.get("source").getAsString();
if (source.equals(RedisBungee.getConfiguration().getServerId()))
return;
DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString());
switch (action) {
case JOIN:
final DataManagerMessage<LoginPayload> message1 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<LoginPayload>>() {
}.getType());
proxyCache.put(message1.getTarget(), message1.getSource());
lastOnlineCache.put(message1.getTarget(), (long) 0);
ipCache.put(message1.getTarget(), message1.getPayload().getAddress());
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
plugin.getProxy().getPluginManager().callEvent(new PlayerJoinedNetworkEvent(message1.getTarget()));
}
});
break;
case LEAVE:
final DataManagerMessage<LogoutPayload> message2 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<LogoutPayload>>() {
}.getType());
invalidate(message2.getTarget());
lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp());
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
plugin.getProxy().getPluginManager().callEvent(new PlayerLeftNetworkEvent(message2.getTarget()));
}
});
break;
case SERVER_CHANGE:
final DataManagerMessage<ServerChangePayload> message3 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<ServerChangePayload>>() {
}.getType());
serverCache.put(message3.getTarget(), message3.getPayload().getServer());
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
plugin.getProxy().getPluginManager().callEvent(new PlayerChangedServerNetworkEvent(message3.getTarget(), message3.getPayload().getOldServer(), message3.getPayload().getServer()));
}
});
break;
}
}
@Getter
@RequiredArgsConstructor
static class DataManagerMessage<T> {
private final UUID target;
private final String source = RedisBungee.getApi().getServerId();
private final Action action; // for future use!
private final T payload;
enum Action {
JOIN,
LEAVE,
SERVER_CHANGE
}
}
@Getter
@RequiredArgsConstructor
static class LoginPayload {
private final InetAddress address;
}
@Getter
@RequiredArgsConstructor
static class ServerChangePayload {
private final String server;
private final String oldServer;
}
@Getter
@RequiredArgsConstructor
static class LogoutPayload {
private final long timestamp;
}
}

View File

@@ -1,569 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.*;
import com.google.common.io.ByteStreams;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.util.*;
import com.imaginarycode.minecraft.redisbungee.util.uuid.NameFetcher;
import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDFetcher;
import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDTranslator;
import com.squareup.okhttp.Dispatcher;
import com.squareup.okhttp.OkHttpClient;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import static com.google.common.base.Preconditions.checkArgument;
/**
* The RedisBungee plugin.
* <p>
* The only function of interest is {@link #getApi()}, which exposes some functions in this class.
*/
public final class RedisBungee extends Plugin {
@Getter
private static Gson gson = new Gson();
private static RedisBungeeAPI api;
@Getter(AccessLevel.PACKAGE)
private static PubSubListener psl = null;
@Getter
private JedisPool pool;
@Getter
private UUIDTranslator uuidTranslator;
@Getter(AccessLevel.PACKAGE)
private static RedisBungeeConfiguration configuration;
@Getter
private DataManager dataManager;
@Getter
private static OkHttpClient httpClient;
private volatile List<String> serverIds;
private final AtomicInteger nagAboutServers = new AtomicInteger();
private final AtomicInteger globalPlayerCount = new AtomicInteger();
private Future<?> integrityCheck;
private Future<?> heartbeatTask;
private boolean usingLua;
private LuaManager.Script serverToPlayersScript;
private LuaManager.Script getPlayerCountScript;
private static final Object SERVER_TO_PLAYERS_KEY = new Object();
private final Cache<Object, Multimap<String, UUID>> serverToPlayersCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
/**
* Fetch the {@link RedisBungeeAPI} object created on plugin start.
*
* @return the {@link RedisBungeeAPI} object
*/
public static RedisBungeeAPI getApi() {
return api;
}
static PubSubListener getPubSubListener() {
return psl;
}
final List<String> getServerIds() {
return serverIds;
}
private List<String> getCurrentServerIds(boolean nag, boolean lagged) {
try (Jedis jedis = pool.getResource()) {
long time = getRedisTime(jedis.time());
int nagTime = 0;
if (nag) {
nagTime = nagAboutServers.decrementAndGet();
if (nagTime <= 0) {
nagAboutServers.set(10);
}
}
ImmutableList.Builder<String> servers = ImmutableList.builder();
Map<String, String> heartbeats = jedis.hgetAll("heartbeats");
for (Map.Entry<String, String> entry : heartbeats.entrySet()) {
try {
long stamp = Long.parseLong(entry.getValue());
if (lagged ? time >= stamp + 30 : time <= stamp + 30)
servers.add(entry.getKey());
else if (nag && nagTime <= 0) {
getLogger().severe(entry.getKey() + " is " + (time - stamp) + " seconds behind! (Time not synchronized or server down?)");
}
} catch (NumberFormatException ignored) {
}
}
return servers.build();
} catch (JedisConnectionException e) {
getLogger().log(Level.SEVERE, "Unable to fetch server IDs", e);
return Collections.singletonList(configuration.getServerId());
}
}
public Set<UUID> getPlayersOnProxy(String server) {
checkArgument(getServerIds().contains(server), server + " is not a valid proxy ID");
try (Jedis jedis = pool.getResource()) {
Set<String> users = jedis.smembers("proxy:" + server + ":usersOnline");
ImmutableSet.Builder<UUID> builder = ImmutableSet.builder();
for (String user : users) {
builder.add(UUID.fromString(user));
}
return builder.build();
}
}
final Multimap<String, UUID> serversToPlayers() {
try {
return serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, new Callable<Multimap<String, UUID>>() {
@Override
public Multimap<String, UUID> call() throws Exception {
Collection<String> data = (Collection<String>) serverToPlayersScript.eval(ImmutableList.<String>of(), getServerIds());
ImmutableMultimap.Builder<String, UUID> builder = ImmutableMultimap.builder();
String key = null;
for (String s : data) {
if (key == null) {
key = s;
continue;
}
builder.put(key, UUID.fromString(s));
key = null;
}
return builder.build();
}
});
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
final int getCount() {
return globalPlayerCount.get();
}
final int getCurrentCount() {
Long count = (Long) getPlayerCountScript.eval(ImmutableList.<String>of(), ImmutableList.<String>of());
return count.intValue();
}
private Set<String> getLocalPlayersAsUuidStrings() {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (ProxiedPlayer player : getProxy().getPlayers()) {
builder.add(player.getUniqueId().toString());
}
return builder.build();
}
final Set<UUID> getPlayers() {
ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
if (pool != null) {
try (Jedis rsc = pool.getResource()) {
List<String> keys = new ArrayList<>();
for (String i : getServerIds()) {
keys.add("proxy:" + i + ":usersOnline");
}
if (!keys.isEmpty()) {
Set<String> users = rsc.sunion(keys.toArray(new String[keys.size()]));
if (users != null && !users.isEmpty()) {
for (String user : users) {
try {
setBuilder = setBuilder.add(UUID.fromString(user));
} catch (IllegalArgumentException ignored) {
}
}
}
}
} catch (JedisConnectionException e) {
// Redis server has disappeared!
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
throw new RuntimeException("Unable to get all players online", e);
}
}
return setBuilder.build();
}
final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) {
checkArgument(getServerIds().contains(proxyId) || proxyId.equals("allservers"), "proxyId is invalid");
sendChannelMessage("redisbungee-" + proxyId, command);
}
final void sendChannelMessage(String channel, String message) {
try (Jedis jedis = pool.getResource()) {
jedis.publish(channel, message);
} catch (JedisConnectionException e) {
// Redis server has disappeared!
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
throw new RuntimeException("Unable to publish channel message", e);
}
}
private long getRedisTime(List<String> timeRes) {
return Long.parseLong(timeRes.get(0));
}
@Override
public void onEnable() {
ThreadFactory factory = ((ThreadPoolExecutor) getExecutorService()).getThreadFactory();
getExecutorService().shutdownNow();
ScheduledExecutorService service;
try {
Field field = Plugin.class.getDeclaredField("service");
field.setAccessible(true);
field.set(this, service = Executors.newScheduledThreadPool(24, factory));
} catch (Exception e) {
throw new RuntimeException("Can't replace BungeeCord thread pool with our own", e);
}
try {
loadConfig();
} catch (IOException e) {
throw new RuntimeException("Unable to load/save config", e);
} catch (JedisConnectionException e) {
throw new RuntimeException("Unable to connect to your Redis server!", e);
}
if (pool != null) {
try (Jedis tmpRsc = pool.getResource()) {
// This is more portable than INFO <section>
String info = tmpRsc.info();
for (String s : info.split("\r\n")) {
if (s.startsWith("redis_version:")) {
String version = s.split(":")[1];
if (!(usingLua = RedisUtil.canUseLua(version))) {
getLogger().warning("Your version of Redis (" + version + ") is not at least version 2.6. RedisBungee requires a newer version of Redis.");
throw new RuntimeException("Unsupported Redis version detected");
} else {
LuaManager manager = new LuaManager(this);
serverToPlayersScript = manager.createScript(IOUtil.readInputStreamAsString(getResourceAsStream("lua/server_to_players.lua")));
getPlayerCountScript = manager.createScript(IOUtil.readInputStreamAsString(getResourceAsStream("lua/get_player_count.lua")));
}
break;
}
}
tmpRsc.hset("heartbeats", configuration.getServerId(), tmpRsc.time().get(0));
long uuidCacheSize = tmpRsc.hlen("uuid-cache");
if (uuidCacheSize > 750000) {
getLogger().info("Looks like you have a really big UUID cache! Run https://www.spigotmc.org/resources/redisbungeecleaner.8505/ as soon as possible.");
}
}
serverIds = getCurrentServerIds(true, false);
uuidTranslator = new UUIDTranslator(this);
heartbeatTask = service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try (Jedis rsc = pool.getResource()) {
long redisTime = getRedisTime(rsc.time());
rsc.hset("heartbeats", configuration.getServerId(), String.valueOf(redisTime));
} catch (JedisConnectionException e) {
// Redis server has disappeared!
getLogger().log(Level.SEVERE, "Unable to update heartbeat - did your Redis server go away?", e);
return;
}
try {
serverIds = getCurrentServerIds(true, false);
globalPlayerCount.set(getCurrentCount());
} catch (Throwable e) {
getLogger().log(Level.SEVERE, "Unable to update data - did your Redis server go away?", e);
}
}
}, 0, 3, TimeUnit.SECONDS);
dataManager = new DataManager(this);
if (configuration.isRegisterBungeeCommands()) {
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.GlistCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.FindCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.LastSeenCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.IpCommand(this));
}
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.SendToAll(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerId(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerIds());
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlayerProxyCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlistCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.DebugCommand(this));
api = new RedisBungeeAPI(this);
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this, configuration.getExemptAddresses()));
getProxy().getPluginManager().registerListener(this, dataManager);
psl = new PubSubListener();
getProxy().getScheduler().runAsync(this, psl);
integrityCheck = service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try (Jedis tmpRsc = pool.getResource()) {
Set<String> players = getLocalPlayersAsUuidStrings();
Set<String> playersInRedis = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline");
List<String> lagged = getCurrentServerIds(false, true);
// Clean up lagged players.
for (String s : lagged) {
Set<String> laggedPlayers = tmpRsc.smembers("proxy:" + s + ":usersOnline");
tmpRsc.del("proxy:" + s + ":usersOnline");
if (!laggedPlayers.isEmpty()) {
getLogger().info("Cleaning up lagged proxy " + s + " (" + laggedPlayers.size() + " players)...");
for (String laggedPlayer : laggedPlayers) {
RedisUtil.cleanUpPlayer(laggedPlayer, tmpRsc);
}
}
}
Set<String> absentLocally = new HashSet<>(playersInRedis);
absentLocally.removeAll(players);
Set<String> absentInRedis = new HashSet<>(players);
absentInRedis.removeAll(playersInRedis);
for (String member : absentLocally) {
boolean found = false;
for (String proxyId : getServerIds()) {
if (proxyId.equals(configuration.getServerId())) continue;
if (tmpRsc.sismember("proxy:" + proxyId + ":usersOnline", member)) {
// Just clean up the set.
found = true;
break;
}
}
if (!found) {
RedisUtil.cleanUpPlayer(member, tmpRsc);
getLogger().warning("Player found in set that was not found locally and globally: " + member);
} else {
tmpRsc.srem("proxy:" + configuration.getServerId() + ":usersOnline", member);
getLogger().warning("Player found in set that was not found locally, but is on another proxy: " + member);
}
}
Pipeline pipeline = tmpRsc.pipelined();
for (String player : absentInRedis) {
// Player not online according to Redis but not BungeeCord.
getLogger().warning("Player " + player + " is on the proxy but not in Redis.");
ProxiedPlayer proxiedPlayer = ProxyServer.getInstance().getPlayer(UUID.fromString(player));
if (proxiedPlayer == null)
continue; // We'll deal with it later.
RedisUtil.createPlayer(proxiedPlayer, pipeline, true);
}
pipeline.sync();
} catch (Throwable e) {
getLogger().log(Level.SEVERE, "Unable to fix up stored player data", e);
}
}
}, 0, 1, TimeUnit.MINUTES);
}
getProxy().registerChannel("legacy:redisbungee");
getProxy().registerChannel("RedisBungee");
}
@Override
public void onDisable() {
if (pool != null) {
// Poison the PubSub listener
psl.poison();
integrityCheck.cancel(true);
heartbeatTask.cancel(true);
getProxy().getPluginManager().unregisterListeners(this);
try (Jedis tmpRsc = pool.getResource()) {
tmpRsc.hdel("heartbeats", configuration.getServerId());
if (tmpRsc.scard("proxy:" + configuration.getServerId() + ":usersOnline") > 0) {
Set<String> players = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline");
for (String member : players)
RedisUtil.cleanUpPlayer(member, tmpRsc);
}
}
pool.destroy();
}
}
private void loadConfig() throws IOException, JedisConnectionException {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
File file = new File(getDataFolder(), "config.yml");
if (!file.exists()) {
file.createNewFile();
try (InputStream in = getResourceAsStream("example_config.yml");
OutputStream out = new FileOutputStream(file)) {
ByteStreams.copy(in, out);
}
}
final Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file);
final String redisServer = configuration.getString("redis-server", "localhost");
final int redisPort = configuration.getInt("redis-port", 6379);
final boolean useSSL = configuration.getBoolean("useSSL");
String redisPassword = configuration.getString("redis-password");
String serverId = configuration.getString("server-id");
if (redisPassword != null && (redisPassword.isEmpty() || redisPassword.equals("none"))) {
redisPassword = null;
}
// Configuration sanity checks.
if (serverId == null || serverId.isEmpty()) {
throw new RuntimeException("server-id is not specified in the configuration or is empty");
}
if (redisServer != null && !redisServer.isEmpty()) {
final String finalRedisPassword = redisPassword;
FutureTask<JedisPool> task = new FutureTask<>(new Callable<JedisPool>() {
@Override
public JedisPool call() throws Exception {
// Create the pool...
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(configuration.getInt("max-redis-connections", 8));
return new JedisPool(config, redisServer, redisPort, 0, finalRedisPassword, useSSL);
}
});
getProxy().getScheduler().runAsync(this, task);
try {
pool = task.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Unable to create Redis pool", e);
}
// Test the connection
try (Jedis rsc = pool.getResource()) {
rsc.ping();
// If that worked, now we can check for an existing, alive Bungee:
File crashFile = new File(getDataFolder(), "restarted_from_crash.txt");
if (crashFile.exists()) {
crashFile.delete();
} else if (rsc.hexists("heartbeats", serverId)) {
try {
long value = Long.parseLong(rsc.hget("heartbeats", serverId));
long redisTime = getRedisTime(rsc.time());
if (redisTime < value + 20) {
getLogger().severe("You have launched a possible impostor BungeeCord instance. Another instance is already running.");
getLogger().severe("For data consistency reasons, RedisBungee will now disable itself.");
getLogger().severe("If this instance is coming up from a crash, create a file in your RedisBungee plugins directory with the name 'restarted_from_crash.txt' and RedisBungee will not perform this check.");
throw new RuntimeException("Possible impostor instance!");
}
} catch (NumberFormatException ignored) {
}
}
FutureTask<Void> task2 = new FutureTask<>(new Callable<Void>() {
@Override
public Void call() throws Exception {
httpClient = new OkHttpClient();
Dispatcher dispatcher = new Dispatcher(getExecutorService());
httpClient.setDispatcher(dispatcher);
NameFetcher.setHttpClient(httpClient);
UUIDFetcher.setHttpClient(httpClient);
RedisBungee.configuration = new RedisBungeeConfiguration(RedisBungee.this.getPool(), configuration);
return null;
}
});
getProxy().getScheduler().runAsync(this, task2);
try {
task2.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Unable to create HTTP client", e);
}
getLogger().log(Level.INFO, "Successfully connected to Redis.");
} catch (JedisConnectionException e) {
pool.destroy();
pool = null;
throw e;
}
} else {
throw new RuntimeException("No redis server specified!");
}
}
@NoArgsConstructor(access = AccessLevel.PRIVATE)
class PubSubListener implements Runnable {
private JedisPubSubHandler jpsh;
private Set<String> addedChannels = new HashSet<String>();
@Override
public void run() {
boolean broken = false;
try (Jedis rsc = pool.getResource()) {
try {
jpsh = new JedisPubSubHandler();
addedChannels.add("redisbungee-" + configuration.getServerId());
addedChannels.add("redisbungee-allservers");
addedChannels.add("redisbungee-data");
rsc.subscribe(jpsh, addedChannels.toArray(new String[0]));
} catch (Exception e) {
// FIXME: Extremely ugly hack
// Attempt to unsubscribe this instance and try again.
getLogger().log(Level.INFO, "PubSub error, attempting to recover.", e);
try {
jpsh.unsubscribe();
} catch (Exception e1) {
/* This may fail with
- java.net.SocketException: Broken pipe
- redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance
*/
}
broken = true;
}
} catch (JedisConnectionException e) {
getLogger().log(Level.INFO, "PubSub error, attempting to recover in 5 secs.");
getProxy().getScheduler().schedule(RedisBungee.this, PubSubListener.this, 5, TimeUnit.SECONDS);
}
if (broken) {
run();
}
}
public void addChannel(String... channel) {
addedChannels.addAll(Arrays.asList(channel));
jpsh.subscribe(channel);
}
public void removeChannel(String... channel) {
addedChannels.removeAll(Arrays.asList(channel));
jpsh.unsubscribe(channel);
}
public void poison() {
addedChannels.clear();
jpsh.unsubscribe();
}
}
private class JedisPubSubHandler extends JedisPubSub {
@Override
public void onMessage(final String s, final String s2) {
if (s2.trim().length() == 0) return;
getProxy().getScheduler().runAsync(RedisBungee.this, new Runnable() {
@Override
public void run() {
getProxy().getPluginManager().callEvent(new PubSubMessageEvent(s, s2));
}
});
}
}
}

View File

@@ -1,299 +0,0 @@
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.Multimap;
import lombok.NonNull;
import net.md_5.bungee.api.config.ServerInfo;
import java.net.InetAddress;
import java.util.*;
/**
* This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}.
*
* @author tuxed
* @since 0.2.3
*/
public class RedisBungeeAPI {
private final RedisBungee plugin;
private final List<String> reservedChannels;
RedisBungeeAPI(RedisBungee plugin) {
this.plugin = plugin;
this.reservedChannels = ImmutableList.of(
"redisbungee-allservers",
"redisbungee-" + RedisBungee.getConfiguration().getServerId(),
"redisbungee-data"
);
}
/**
* Get a combined count of all players on this network.
*
* @return a count of all players found
*/
public final int getPlayerCount() {
return plugin.getCount();
}
/**
* Get the last time a player was on. If the player is currently online, this will return 0. If the player has not been recorded,
* this will return -1. Otherwise it will return a value in milliseconds.
*
* @param player a player name
* @return the last time a player was on, if online returns a 0
*/
public final long getLastOnline(@NonNull UUID player) {
return plugin.getDataManager().getLastOnline(player);
}
/**
* 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 name
* @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on.
*/
public final ServerInfo getServerFor(@NonNull UUID player) {
String server = plugin.getDataManager().getServer(player);
return plugin.getProxy().getServerInfo(server);
}
/**
* Get a combined list of players on this network.
* <p>
* <strong>Note that this function returns an instance of {@link com.google.common.collect.ImmutableSet}.</strong>
*
* @return a Set with all players found
*/
public final Set<UUID> getPlayersOnline() {
return plugin.getPlayers();
}
/**
* Get a combined list of players on this network, as a collection of usernames.
*
* @return a Set with all players found
* @see #getNameFromUuid(java.util.UUID)
* @since 0.3
*/
public final Collection<String> getHumanPlayersOnline() {
Set<String> names = new HashSet<>();
for (UUID uuid : getPlayersOnline()) {
names.add(getNameFromUuid(uuid, false));
}
return names;
}
/**
* Get a full list of players on all servers.
*
* @return a immutable Multimap with all players found on this server
* @since 0.2.5
*/
public final Multimap<String, UUID> getServerToPlayers() {
return plugin.serversToPlayers();
}
/**
* Get a list of players on the server with the given name.
*
* @param server a server name
* @return a Set with all players found on this server
*/
public final Set<UUID> getPlayersOnServer(@NonNull String server) {
return ImmutableSet.copyOf(getServerToPlayers().get(server));
}
/**
* Get a list of players on the specified proxy.
*
* @param server a server name
* @return a Set with all UUIDs found on this proxy
*/
public final Set<UUID> getPlayersOnProxy(@NonNull String server) {
return plugin.getPlayersOnProxy(server);
}
/**
* Convenience method: Checks if the specified player is online.
*
* @param player a player name
* @return if the player is online
*/
public final boolean isPlayerOnline(@NonNull UUID player) {
return getLastOnline(player) == 0;
}
/**
* Get the {@link java.net.InetAddress} associated with this player.
*
* @param player the player to fetch the IP for
* @return an {@link java.net.InetAddress} if the player is online, null otherwise
* @since 0.2.4
*/
public final InetAddress getPlayerIp(@NonNull UUID player) {
return plugin.getDataManager().getIp(player);
}
/**
* Get the RedisBungee proxy ID this player is connected to.
*
* @param player the player to fetch the IP for
* @return the proxy the player is connected to, or null if they are offline
* @since 0.3.3
*/
public final String getProxy(@NonNull UUID player) {
return plugin.getDataManager().getProxy(player);
}
/**
* Sends a proxy command to all proxies.
*
* @param command the command to send and execute
* @see #sendProxyCommand(String, String)
* @since 0.2.5
*/
public final void sendProxyCommand(@NonNull String command) {
plugin.sendProxyCommand("allservers", command);
}
/**
* Sends a proxy command to the proxy with the given ID. "allservers" means all proxies.
*
* @param proxyId a proxy ID
* @param command the command to send and execute
* @see #getServerId()
* @see #getAllServers()
* @since 0.2.5
*/
public final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) {
plugin.sendProxyCommand(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.
*
* @param channel The PubSub channel
* @param message the message body to send
* @since 0.3.3
*/
public final void sendChannelMessage(@NonNull String channel, @NonNull String message) {
plugin.sendChannelMessage(channel, message);
}
/**
* Get the current BungeeCord server ID for this server.
*
* @return the current server ID
* @see #getAllServers()
* @since 0.2.5
*/
public final String getServerId() {
return RedisBungee.getConfiguration().getServerId();
}
/**
* Get all the linked proxies in this network.
*
* @return the list of all proxies
* @see #getServerId()
* @since 0.2.5
*/
public final List<String> getAllServers() {
return plugin.getServerIds();
}
/**
* 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);
}
/**
* Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function falls back to Mojang
* as a last resort, so calls <strong>may</strong> be blocking.
* <p>
* For the common use case of translating a list of UUIDs into names, use {@link #getHumanPlayersOnline()} instead.
* <p>
* If performance is a concern, use {@link #getNameFromUuid(java.util.UUID, boolean)} as this allows you to disable Mojang lookups.
*
* @param uuid the UUID to fetch the name for
* @return the name for the UUID
* @since 0.3
*/
public final String getNameFromUuid(@NonNull UUID uuid) {
return getNameFromUuid(uuid, true);
}
/**
* Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function can fall back to Mojang
* as a last resort if {@code expensiveLookups} is true, so calls <strong>may</strong> be blocking.
* <p>
* For the common use case of translating the list of online players into names, use {@link #getHumanPlayersOnline()}.
* <p>
* If performance is a concern, set {@code expensiveLookups} to false as this will disable lookups via Mojang.
*
* @param uuid the UUID to fetch the name for
* @param expensiveLookups whether or not to perform potentially expensive lookups
* @return the name for the UUID
* @since 0.3.2
*/
public final String getNameFromUuid(@NonNull UUID uuid, boolean expensiveLookups) {
return plugin.getUuidTranslator().getNameFromUuid(uuid, expensiveLookups);
}
/**
* Fetch a UUID from the specified name. Names are cached locally and in Redis. This function falls back to Mojang
* as a last resort, so calls <strong>may</strong> be blocking.
* <p>
* If performance is a concern, see {@link #getUuidFromName(String, boolean)}, which disables the following functions:
* <ul>
* <li>Searching local entries case-insensitively</li>
* <li>Searching Mojang</li>
* </ul>
*
* @param name the UUID to fetch the name for
* @return the UUID for the name
* @since 0.3
*/
public final UUID getUuidFromName(@NonNull String name) {
return getUuidFromName(name, true);
}
/**
* Fetch a UUID from the specified name. Names are cached locally and in Redis. This function falls back to Mojang
* as a last resort if {@code expensiveLookups} is true, so calls <strong>may</strong> be blocking.
* <p>
* If performance is a concern, set {@code expensiveLookups} to false to disable searching Mojang and searching for usernames
* case-insensitively.
*
* @param name the UUID to fetch the name for
* @param expensiveLookups whether or not to perform potentially expensive lookups
* @return the UUID for the name
* @since 0.3.2
*/
public final UUID getUuidFromName(@NonNull String name, boolean expensiveLookups) {
return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups);
}
}

View File

@@ -1,77 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import java.util.Collection;
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 {
static final RedisBungeeCommandSender instance = new RedisBungeeCommandSender();
@Override
public String getName() {
return "RedisBungee";
}
@Override
public void sendMessage(String s) {
// no-op
}
@Override
public void sendMessages(String... strings) {
// no-op
}
@Override
public void sendMessage(BaseComponent... baseComponents) {
// no-op
}
@Override
public void sendMessage(BaseComponent baseComponent) {
// no-op
}
@Override
public Collection<String> getGroups() {
return Collections.emptySet();
}
@Override
public void addGroups(String... strings) {
// no-op
}
@Override
public void removeGroups(String... strings) {
// no-op
}
@Override
public boolean hasPermission(String s) {
return true;
}
@Override
public void setPermission(String s, boolean b) {
// no-op
}
@Override
public Collection<String> getPermissions() {
return Collections.emptySet();
}
}

View File

@@ -1,356 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.plugin.Command;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
/**
* This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen.
* <p>
* All classes use the {@link RedisBungeeAPI}.
*
* @author tuxed
* @since 0.2.3
*/
class RedisBungeeCommands {
private static final BaseComponent[] NO_PLAYER_SPECIFIED =
new ComponentBuilder("You must specify a player name.").color(ChatColor.RED).create();
private static final BaseComponent[] PLAYER_NOT_FOUND =
new ComponentBuilder("No such player found.").color(ChatColor.RED).create();
private static final BaseComponent[] NO_COMMAND_SPECIFIED =
new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
private static String playerPlural(int num) {
return num == 1 ? num + " player is" : num + " players are";
}
public static class GlistCommand extends Command {
private final RedisBungee plugin;
GlistCommand(RedisBungee plugin) {
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
int count = RedisBungee.getApi().getPlayerCount();
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(count) + " currently online.").create();
if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = RedisBungee.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.GREEN);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
}
}
});
}
}
public static class FindCommand extends Command {
private final RedisBungee plugin;
FindCommand(RedisBungee plugin) {
super("find", "bungeecord.command.find", "rfind");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
ServerInfo si = RedisBungee.getApi().getServerFor(uuid);
if (si != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.BLUE);
message.setText(args[0] + " is on " + si.getName() + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class LastSeenCommand extends Command {
private final RedisBungee plugin;
LastSeenCommand(RedisBungee plugin) {
super("lastseen", "redisbungee.command.lastseen", "rlastseen");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
long secs = RedisBungee.getApi().getLastOnline(uuid);
TextComponent message = new TextComponent();
if (secs == 0) {
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is currently online.");
} else if (secs != -1) {
message.setColor(ChatColor.BLUE);
message.setText(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else {
message.setColor(ChatColor.RED);
message.setText(args[0] + " has never been online.");
}
sender.sendMessage(message);
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class IpCommand extends Command {
private final RedisBungee plugin;
IpCommand(RedisBungee plugin) {
super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
InetAddress ia = RedisBungee.getApi().getPlayerIp(uuid);
if (ia != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is connected from " + ia.toString() + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class PlayerProxyCommand extends Command {
private final RedisBungee plugin;
PlayerProxyCommand(RedisBungee plugin) {
super("pproxy", "redisbungee.command.pproxy");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
String proxy = RedisBungee.getApi().getProxy(uuid);
if (proxy != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is connected to " + proxy + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class SendToAll extends Command {
private final RedisBungee plugin;
SendToAll(RedisBungee plugin) {
super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args);
RedisBungee.getApi().sendProxyCommand(command);
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText("Sent the command /" + command + " to all proxies.");
sender.sendMessage(message);
} else {
sender.sendMessage(NO_COMMAND_SPECIFIED);
}
}
}
public static class ServerId extends Command {
private final RedisBungee plugin;
ServerId(RedisBungee plugin) {
super("serverid", "redisbungee.command.serverid", "rserverid");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
TextComponent textComponent = new TextComponent();
textComponent.setText("You are on " + RedisBungee.getApi().getServerId() + ".");
textComponent.setColor(ChatColor.YELLOW);
sender.sendMessage(textComponent);
}
}
public static class ServerIds extends Command {
public ServerIds() {
super("serverids", "redisbungee.command.serverids");
}
@Override
public void execute(CommandSender sender, String[] strings) {
TextComponent textComponent = new TextComponent();
textComponent.setText("All server IDs: " + Joiner.on(", ").join(RedisBungee.getApi().getAllServers()));
textComponent.setColor(ChatColor.YELLOW);
sender.sendMessage(textComponent);
}
}
public static class PlistCommand extends Command {
private final RedisBungee plugin;
PlistCommand(RedisBungee plugin) {
super("plist", "redisbungee.command.plist", "rplist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
String proxy = args.length >= 1 ? args[0] : RedisBungee.getConfiguration().getServerId();
if (!plugin.getServerIds().contains(proxy)) {
sender.sendMessage(new ComponentBuilder(proxy + " is not a valid proxy. See /serverids for valid proxies.").color(ChatColor.RED).create());
return;
}
Set<UUID> players = RedisBungee.getApi().getPlayersOnProxy(proxy);
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(players.size()) + " currently on proxy " + proxy + ".").create();
if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = RedisBungee.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
if (players.contains(entry.getValue())) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
}
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.RED);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + human.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /plist " + proxy + " showall.").color(ChatColor.YELLOW).create());
}
}
});
}
}
public static class DebugCommand extends Command {
private final RedisBungee plugin;
DebugCommand(RedisBungee plugin) {
super("rdebug", "redisbungee.command.debug");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
TextComponent poolActiveStat = new TextComponent("Currently active pool objects: " + plugin.getPool().getNumActive());
TextComponent poolIdleStat = new TextComponent("Currently idle pool objects: " + plugin.getPool().getNumIdle());
TextComponent poolWaitingStat = new TextComponent("Waiting on free objects: " + plugin.getPool().getNumWaiters());
sender.sendMessage(poolActiveStat);
sender.sendMessage(poolIdleStat);
sender.sendMessage(poolWaitingStat);
}
}
}

View File

@@ -1,36 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses;
import lombok.Getter;
import net.md_5.bungee.config.Configuration;
import redis.clients.jedis.JedisPool;
import java.net.InetAddress;
import java.util.List;
public class RedisBungeeConfiguration {
@Getter
private final JedisPool pool;
@Getter
private final String serverId;
@Getter
private final boolean registerBungeeCommands;
@Getter
private final List<InetAddress> exemptAddresses;
public RedisBungeeConfiguration(JedisPool pool, Configuration configuration) {
this.pool = pool;
this.serverId = configuration.getString("server-id");
this.registerBungeeCommands = configuration.getBoolean("register-bungee-commands", true);
List<String> stringified = configuration.getStringList("exempt-ip-addresses");
ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder();
for (String s : stringified) {
addressBuilder.add(InetAddresses.forString(s));
}
this.exemptAddresses = addressBuilder.build();
}
}

View File

@@ -1,291 +0,0 @@
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.collect.Multiset;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.util.RedisCallable;
import lombok.AllArgsConstructor;
import net.md_5.bungee.api.AbstractReconnectHandler;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
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.*;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.net.InetAddress;
import java.util.*;
@AllArgsConstructor
public class RedisBungeeListener implements Listener {
private static final BaseComponent[] ALREADY_LOGGED_IN =
new ComponentBuilder("You are already logged on to this server.").color(ChatColor.RED)
.append("\n\nIt may help to try logging in again in a few minutes.\nIf this does not resolve your issue, please contact staff.")
.color(ChatColor.GRAY)
.create();
private static final BaseComponent[] ONLINE_MODE_RECONNECT =
new ComponentBuilder("Whoops! You need to reconnect.").color(ChatColor.RED)
.append("\n\nWe found someone online using your username. They were kicked and you may reconnect.\nIf this does not work, please contact staff.")
.color(ChatColor.GRAY)
.create();
private final RedisBungee plugin;
private final List<InetAddress> exemptAddresses;
@EventHandler(priority = EventPriority.LOWEST)
public void onLogin(final LoginEvent event) {
event.registerIntent(plugin);
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) {
@Override
protected Void call(Jedis jedis) {
try {
if (event.isCancelled()) {
return null;
}
// We make sure they aren't trying to use an existing player's name.
// This is problematic for online-mode servers as they always disconnect old clients.
if (plugin.getProxy().getConfig().isOnlineMode()) {
ProxiedPlayer player = plugin.getProxy().getPlayer(event.getConnection().getName());
if (player != null) {
event.setCancelled(true);
// TODO: Make it accept a BaseComponent[] like everything else.
event.setCancelReason(TextComponent.toLegacyText(ONLINE_MODE_RECONNECT));
return null;
}
}
for (String s : plugin.getServerIds()) {
if (jedis.sismember("proxy:" + s + ":usersOnline", event.getConnection().getUniqueId().toString())) {
event.setCancelled(true);
// TODO: Make it accept a BaseComponent[] like everything else.
event.setCancelReason(TextComponent.toLegacyText(ALREADY_LOGGED_IN));
return null;
}
}
Pipeline pipeline = jedis.pipelined();
plugin.getUuidTranslator().persistInfo(event.getConnection().getName(), event.getConnection().getUniqueId(), pipeline);
RedisUtil.createPlayer(event.getConnection(), pipeline, false);
// We're not publishing, the API says we only publish at PostLoginEvent time.
pipeline.sync();
return null;
} finally {
event.completeIntent(plugin);
}
}
});
}
@EventHandler
public void onPostLogin(final PostLoginEvent event) {
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) {
@Override
protected Void call(Jedis jedis) {
jedis.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
event.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.JOIN,
new DataManager.LoginPayload(event.getPlayer().getAddress().getAddress()))));
return null;
}
});
}
@EventHandler
public void onPlayerDisconnect(final PlayerDisconnectEvent event) {
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) {
@Override
protected Void call(Jedis jedis) {
Pipeline pipeline = jedis.pipelined();
RedisUtil.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), pipeline);
pipeline.sync();
return null;
}
});
}
@EventHandler
public void onServerChange(final ServerConnectedEvent event) {
final String currentServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) {
@Override
protected Void call(Jedis jedis) {
jedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", event.getServer().getInfo().getName());
jedis.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
event.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.SERVER_CHANGE,
new DataManager.ServerChangePayload(event.getServer().getInfo().getName(), currentServer))));
return null;
}
});
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPing(final ProxyPingEvent event) {
if (exemptAddresses.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.getCount());
}
@EventHandler
public void onPluginMessage(final 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.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
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.getPlayers();
} else {
try {
original = RedisBungee.getApi().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));
break;
case "PlayerCount":
out.writeUTF("PlayerCount");
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
out.writeInt(plugin.getCount());
} else {
out.writeUTF(type);
try {
out.writeInt(RedisBungee.getApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) {
out.writeInt(0);
}
}
break;
case "LastOnline":
String user = in.readUTF();
out.writeUTF("LastOnline");
out.writeUTF(user);
out.writeLong(RedisBungee.getApi().getLastOnline(plugin.getUuidTranslator().getTranslatedUuid(user, true)));
break;
case "ServerPlayers":
String type1 = in.readUTF();
out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = RedisBungee.getApi().getServerToPlayers();
boolean includesUsers;
switch (type1) {
case "COUNT":
includesUsers = false;
break;
case "PLAYERS":
includesUsers = true;
break;
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);
}
break;
case "Proxy":
out.writeUTF("Proxy");
out.writeUTF(RedisBungee.getConfiguration().getServerId());
break;
case "PlayerProxy":
String username = in.readUTF();
out.writeUTF("PlayerProxy");
out.writeUTF(username);
out.writeUTF(RedisBungee.getApi().getProxy(plugin.getUuidTranslator().getTranslatedUuid(username, true)));
break;
default:
return;
}
((Server) event.getSender()).sendData(currentChannel, out.toByteArray());
}
});
}
}
private 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());
}
}
private 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());
}
}
}
private void serializeCollection(Collection<?> collection, ByteArrayDataOutput output) {
output.writeInt(collection.size());
for (Object o : collection) {
output.writeUTF(o.toString());
}
}
@EventHandler
public void onPubSubMessage(PubSubMessageEvent event) {
if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + RedisBungee.getApi().getServerId())) {
String message = event.getMessage();
if (message.startsWith("/"))
message = message.substring(1);
plugin.getLogger().info("Invoking command via PubSub: /" + message);
plugin.getProxy().getPluginManager().dispatchCommand(RedisBungeeCommandSender.instance, message);
}
}
}

View File

@@ -1,73 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.annotations.VisibleForTesting;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@VisibleForTesting
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RedisUtil {
protected static void createPlayer(ProxiedPlayer player, Pipeline pipeline, boolean fireEvent) {
createPlayer(player.getPendingConnection(), pipeline, fireEvent);
if (player.getServer() != null)
pipeline.hset("player:" + player.getUniqueId().toString(), "server", player.getServer().getInfo().getName());
}
protected static void createPlayer(PendingConnection connection, Pipeline pipeline, boolean fireEvent) {
Map<String, String> playerData = new HashMap<>(4);
playerData.put("online", "0");
playerData.put("ip", connection.getAddress().getAddress().getHostAddress());
playerData.put("proxy", RedisBungee.getConfiguration().getServerId());
pipeline.sadd("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", connection.getUniqueId().toString());
pipeline.hmset("player:" + connection.getUniqueId().toString(), playerData);
if (fireEvent) {
pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
connection.getUniqueId(), DataManager.DataManagerMessage.Action.JOIN,
new DataManager.LoginPayload(connection.getAddress().getAddress()))));
}
}
public static void cleanUpPlayer(String player, Jedis rsc) {
rsc.srem("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", player);
rsc.hdel("player:" + player, "server", "ip", "proxy");
long timestamp = System.currentTimeMillis();
rsc.hset("player:" + player, "online", String.valueOf(timestamp));
rsc.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
UUID.fromString(player), DataManager.DataManagerMessage.Action.LEAVE,
new DataManager.LogoutPayload(timestamp))));
}
public static void cleanUpPlayer(String player, Pipeline rsc) {
rsc.srem("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", player);
rsc.hdel("player:" + player, "server", "ip", "proxy");
long timestamp = System.currentTimeMillis();
rsc.hset("player:" + player, "online", String.valueOf(timestamp));
rsc.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
UUID.fromString(player), DataManager.DataManagerMessage.Action.LEAVE,
new DataManager.LogoutPayload(timestamp))));
}
public static boolean canUseLua(String redisVersion) {
// Need to use >=2.6 to use Lua optimizations.
String[] args = redisVersion.split("\\.");
if (args.length < 2) {
return false;
}
int major = Integer.parseInt(args[0]);
int minor = Integer.parseInt(args[1]);
return major >= 3 || (major == 2 && minor >= 6);
}
}

View File

@@ -1,40 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event;
import java.util.UUID;
/**
* This event is sent when a player connects to a new server. RedisBungee sends the event only when
* the proxy the player has been connected to is different than the local proxy.
* <p>
* This event corresponds to {@link net.md_5.bungee.api.event.ServerConnectedEvent}, and is fired
* asynchronously.
*
* @since 0.3.4
*/
@ToString
public class PlayerChangedServerNetworkEvent extends Event {
private final UUID uuid;
private final String previousServer;
private final String server;
public PlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server) {
this.uuid = uuid;
this.previousServer = previousServer;
this.server = server;
}
public UUID getUuid() {
return uuid;
}
public String getServer() {
return server;
}
public String getPreviousServer() {
return previousServer;
}
}

View File

@@ -1,28 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event;
import java.util.UUID;
/**
* This event is sent when a player joins the network. RedisBungee sends the event only when
* the proxy the player has been connected to is different than the local proxy.
* <p>
* This event corresponds to {@link net.md_5.bungee.api.event.PostLoginEvent}, and is fired
* asynchronously.
*
* @since 0.3.4
*/
@ToString
public class PlayerJoinedNetworkEvent extends Event {
private final UUID uuid;
public PlayerJoinedNetworkEvent(UUID uuid) {
this.uuid = uuid;
}
public UUID getUuid() {
return uuid;
}
}

View File

@@ -1,28 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event;
import java.util.UUID;
/**
* This event is sent when a player disconnects. RedisBungee sends the event only when
* the proxy the player has been connected to is different than the local proxy.
* <p>
* This event corresponds to {@link net.md_5.bungee.api.event.PlayerDisconnectEvent}, and is fired
* asynchronously.
*
* @since 0.3.4
*/
@ToString
public class PlayerLeftNetworkEvent extends Event {
private final UUID uuid;
public PlayerLeftNetworkEvent(UUID uuid) {
this.uuid = uuid;
}
public UUID getUuid() {
return uuid;
}
}

View File

@@ -1,27 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.events;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
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
*/
@RequiredArgsConstructor
@ToString
public class PubSubMessageEvent extends Event {
private final String channel;
private final String message;
public String getChannel() {
return channel;
}
public String getMessage() {
return message;
}
}

View File

@@ -1,22 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.util;
import com.google.common.io.ByteStreams;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IOUtil {
public static String readInputStreamAsString(InputStream is) {
String string;
try {
string = new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new AssertionError(e);
}
return string;
}
}

View File

@@ -1,44 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.util;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import lombok.RequiredArgsConstructor;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;
import java.util.List;
@RequiredArgsConstructor
public class LuaManager {
private final RedisBungee plugin;
public Script createScript(String script) {
try (Jedis jedis = plugin.getPool().getResource()) {
String hash = jedis.scriptLoad(script);
return new Script(script, hash);
}
}
@RequiredArgsConstructor
public class Script {
private final String script;
private final String hashed;
public Object eval(List<String> keys, List<String> args) {
Object data;
try (Jedis jedis = plugin.getPool().getResource()) {
try {
data = jedis.evalsha(hashed, keys, args);
} catch (JedisDataException e) {
if (e.getMessage().startsWith("NOSCRIPT")) {
data = jedis.eval(script, keys, args);
} else {
throw e;
}
}
}
return data;
}
}
}

View File

@@ -1,45 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.util;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import lombok.AllArgsConstructor;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.concurrent.Callable;
import java.util.logging.Level;
@AllArgsConstructor
public abstract class RedisCallable<T> implements Callable<T>, Runnable {
private final RedisBungee plugin;
@Override
public T call() {
return run(false);
}
public void run() {
call();
}
private T run(boolean retry) {
try (Jedis jedis = plugin.getPool().getResource()) {
return call(jedis);
} catch (JedisConnectionException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to get connection", e);
if (!retry) {
// Wait one second before retrying the task
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
throw new RuntimeException("task failed to run", e1);
}
return run(true);
}
}
throw new RuntimeException("task failed to run");
}
protected abstract T call(Jedis jedis);
}

View File

@@ -1,45 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid;
import com.google.gson.reflect.TypeToken;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.ResponseBody;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NameFetcher {
@Setter
private static OkHttpClient httpClient;
public static List<String> nameHistoryFromUuid(UUID uuid) throws IOException {
String url = "https://api.mojang.com/user/profiles/" + uuid.toString().replace("-", "") + "/names";
Request request = new Request.Builder().url(url).get().build();
ResponseBody body = httpClient.newCall(request).execute().body();
String response = body.string();
body.close();
Type listType = new TypeToken<List<Name>>() {
}.getType();
List<Name> names = RedisBungee.getGson().fromJson(response, listType);
List<String> humanNames = new ArrayList<>();
for (Name name : names) {
humanNames.add(name.name);
}
return humanNames;
}
public static class Name {
private String name;
private long changedToAt;
}
}

View File

@@ -1,63 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid;
import com.google.common.collect.ImmutableList;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import com.squareup.okhttp.*;
import lombok.Setter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
/* Credits to evilmidget38 for this class. I modified it to use Gson. */
public class UUIDFetcher implements Callable<Map<String, UUID>> {
private static final double PROFILES_PER_REQUEST = 100;
private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
private static final MediaType JSON = MediaType.parse("application/json");
private final List<String> names;
private final boolean rateLimiting;
@Setter
private static OkHttpClient httpClient;
private UUIDFetcher(List<String> names, boolean rateLimiting) {
this.names = ImmutableList.copyOf(names);
this.rateLimiting = rateLimiting;
}
public UUIDFetcher(List<String> names) {
this(names, true);
}
public static UUID getUUID(String id) {
return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32));
}
public Map<String, UUID> call() throws Exception {
Map<String, UUID> uuidMap = new HashMap<>();
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++) {
String body = RedisBungee.getGson().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();
ResponseBody responseBody = httpClient.newCall(request).execute().body();
String response = responseBody.string();
responseBody.close();
Profile[] array = RedisBungee.getGson().fromJson(response, Profile[].class);
for (Profile profile : array) {
UUID uuid = UUIDFetcher.getUUID(profile.id);
uuidMap.put(profile.name, uuid);
}
if (rateLimiting && i != requests - 1) {
Thread.sleep(100L);
}
}
return uuidMap;
}
private static class Profile {
String id;
String name;
}
}

View File

@@ -1,198 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ProxyServer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.regex.Pattern;
@RequiredArgsConstructor
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 RedisBungee 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 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 final UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (ProxyServer.getInstance().getPlayer(player) != null)
return ProxyServer.getInstance().getPlayer(player).getUniqueId();
// Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase());
if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired())
return cachedUUIDEntry.getUuid();
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.getProxy().getConfig().isOnlineMode()) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
}
// Let's try Redis.
try (Jedis jedis = plugin.getPool().getResource()) {
String stored = jedis.hget("uuid-cache", player.toLowerCase());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = RedisBungee.getGson().fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
jedis.hdel("uuid-cache", player.toLowerCase());
// Doesn't hurt to also remove the UUID entry as well.
jedis.hdel("uuid-cache", entry.getUuid().toString());
} else {
nameToUuidMap.put(player.toLowerCase(), entry);
uuidToNameMap.put(entry.getUuid(), entry);
return entry.getUuid();
}
}
// That didn't work. Let's ask Mojang.
if (!expensiveLookups || !ProxyServer.getInstance().getConfig().isOnlineMode())
return null;
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID from Mojang for " + player, e);
return null;
}
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(player)) {
persistInfo(entry.getKey(), entry.getValue(), jedis);
return entry.getValue();
}
}
} catch (JedisException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID for " + player, e);
}
return null; // Nope, game over!
}
public final String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (ProxyServer.getInstance().getPlayer(player) != null)
return ProxyServer.getInstance().getPlayer(player).getName();
// Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player);
if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired())
return cachedUUIDEntry.getName();
else
uuidToNameMap.remove(player);
}
// Okay, it wasn't locally cached. Let's try Redis.
try (Jedis jedis = plugin.getPool().getResource()) {
String stored = jedis.hget("uuid-cache", player.toString());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = RedisBungee.getGson().fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
jedis.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.
jedis.hdel("uuid-cache", entry.getName());
} else {
nameToUuidMap.put(entry.getName().toLowerCase(), entry);
uuidToNameMap.put(player, entry);
return entry.getName();
}
}
if (!expensiveLookups || !ProxyServer.getInstance().getConfig().isOnlineMode())
return null;
// That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane.
String name;
try {
List<String> nameHist = NameFetcher.nameHistoryFromUuid(player);
name = Iterables.getLast(nameHist, null);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch name from Mojang for " + player, e);
return null;
}
if (name != null) {
persistInfo(name, player, jedis);
return name;
}
return null;
} catch (JedisException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch name for " + player, e);
return null;
}
}
public final void persistInfo(String name, UUID uuid, Jedis jedis) {
addToMaps(name, uuid);
String json = RedisBungee.getGson().toJson(uuidToNameMap.get(uuid));
jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
}
public final void persistInfo(String name, UUID uuid, Pipeline jedis) {
addToMaps(name, uuid);
String json = RedisBungee.getGson().toJson(uuidToNameMap.get(uuid));
jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
}
@RequiredArgsConstructor
@Getter
private class CachedUUIDEntry {
private final String name;
private final UUID uuid;
private final Calendar expiry;
public boolean expired() {
return Calendar.getInstance().after(expiry);
}
}
}

View File

@@ -1,34 +0,0 @@
# RedisBungee configuration file.
# PLEASE READ THE WIKI: https://github.com/minecrafter/RedisBungee/wiki
# The Redis server you use.
# Get Redis from http://redis.io/
redis-server: 127.0.0.1
redis-port: 6379
# OPTIONAL: If your Redis server uses AUTH, set the password required.
redis-password: ""
# Maximum connections that will be maintained to the Redis server.
# The default is 8. This setting should be left as-is unless you have some wildly
# inefficient plugins or a lot of players.
max-redis-connections: 8
# since redis can support ssl by version 6 you can use ssl in redis bungee too! (
useSSL: false
# An identifier for this BungeeCord instance.
server-id: test1
# Whether or not RedisBungee should install its version of regular BungeeCord commands.
# Often, the RedisBungee commands are desired, but in some cases someone may wish to
# override the commands using another plugin.
#
# If you are just denying access to the commands, RedisBungee uses the default BungeeCord
# permissions - just deny them and access will be denied.
#
# Please note that with build 787+, most commands overridden by RedisBungee were moved to
# modules, and these must be disabled or overridden yourself.
register-bungee-commands: true
# A list of IP addresses for which RedisBungee will not modify the response for, useful for automatic
# restart scripts.
exempt-ip-addresses: []

View File

@@ -1,24 +0,0 @@
local c = redis.call
local curTime = c("TIME")
local time = tonumber(curTime[1])
local heartbeats = c("HGETALL", "heartbeats")
local total = 0
local key
for _, v in ipairs(heartbeats) do
if not key then
key = v
else
local n = tonumber(v)
if n then
if n + 30 >= time then
total = total + c("SCARD", "proxy:" .. key .. ":usersOnline")
end
end
key = nil
end
end
return total

View File

@@ -1,18 +0,0 @@
local call = redis.call
local ipairs = ipairs
local serverToData = {}
for _, proxy in ipairs(ARGV) do
local players = call("SMEMBERS", "proxy:" .. proxy .. ":usersOnline")
for _, player in ipairs(players) do
local server = call("HGET", "player:" .. player, "server")
if server then
local sz = #serverToData
serverToData[sz + 1] = server
serverToData[sz + 2] = player
end
end
end
return serverToData

View File

@@ -1,9 +0,0 @@
name: ${project.name}
main: com.imaginarycode.minecraft.redisbungee.RedisBungee
version: ${project.version}
author: Chunker and Govindas limework
authors:
- chunkr
- Govindas Limework
# This is used so that we can automatically override default BungeeCord behavior.
softDepends: ["cmd_find", "cmd_list"]

View File

@@ -1,17 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.test;
import com.imaginarycode.minecraft.redisbungee.RedisUtil;
import org.junit.Assert;
import org.junit.Test;
public class RedisUtilTest {
@Test
public void testRedisLuaCheck() {
Assert.assertTrue(RedisUtil.canUseLua("2.6.0"));
Assert.assertFalse(RedisUtil.canUseLua("2.2.12"));
Assert.assertFalse(RedisUtil.canUseLua("1.2.4"));
Assert.assertTrue(RedisUtil.canUseLua("2.8.4"));
Assert.assertTrue(RedisUtil.canUseLua("3.0.0"));
Assert.assertTrue(RedisUtil.canUseLua("3.2.1"));
}
}

View File

@@ -1,47 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.test;
import com.imaginarycode.minecraft.redisbungee.util.uuid.NameFetcher;
import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDFetcher;
import com.squareup.okhttp.OkHttpClient;
import org.junit.Test;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class UUIDNameTest {
private String[] uuidsToTest = {"68ec43f7234b41b48764dfb38b9ffe8c", "652a2bc4e8cd405db7b698156ee2dc09"};
private String[] namesToTest = {"vemacs"};
@Test
public void testUuidToName() throws IOException {
OkHttpClient httpClient = new OkHttpClient();
NameFetcher.setHttpClient(httpClient);
for (String uuid : uuidsToTest) {
List<String> names = NameFetcher.nameHistoryFromUuid(UUIDFetcher.getUUID(uuid));
String currentName = names.get(names.size() - 1);
System.out.println("Current name for UUID " + uuid + " is " + currentName);
}
}
@Test
public void testNameToUuid() throws IOException {
OkHttpClient httpClient = new OkHttpClient();
UUIDFetcher.setHttpClient(httpClient);
for (String name : namesToTest) {
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(name)).call();
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(name)) {
System.out.println("Current UUID for name " + name + " is " + entry.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

24
velocity/build.gradle.kts Normal file
View File

@@ -0,0 +1,24 @@
plugins {
java
alias(libs.plugins.shadow)
alias(libs.plugins.run.velocity)
}
dependencies {
implementation(project(":valiobungee-velocity-api"))
implementation(project(":valiobungee-core"))
implementation(project(":valiobungee-core-standalone"))
implementation(project(":valiobungee-core-redisson"))
compileOnly(libs.platform.velocity)
annotationProcessor(libs.platform.velocity)
}
description = "ValioBungee Velocity implementation"
tasks {
runVelocity {
velocityVersion(libs.versions.velocity.get())
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.velocity;
import net.limework.valiobungee.core.ValioBungeePlatform;
public class PlayerManagerListener {
private final ValioBungeePlatform platform;
public PlayerManagerListener(ValioBungeePlatform platform) {
this.platform = platform;
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.velocity;
import com.google.inject.Inject;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import net.limework.valiobungee.api.entity.NetworkPlayer;
import net.limework.valiobungee.api.entity.NetworkProxy;
import net.limework.valiobungee.core.*;
import net.limework.valiobungee.core.util.logging.LogProviderFactory;
import net.limework.valiobungee.velocity.api.VelocityValioBungeeAPI;
import net.limework.valiobungee.velocity.api.entities.ImplVelocityNetworkPlayer;
import net.limework.valiobungee.velocity.api.entities.ImplVelocityNetworkProxy;
import org.slf4j.Logger;
@Plugin(
id = "valiobungee",
name = "valiobungee",
version = ConstantVariables.VERSION,
url = "https://github.com/ProxioDev/ValioBungee",
authors = {"limework", "ProxioDev"})
public class VelocityValioBungeePlugin implements ValioBungeePlatform, VelocityValioBungeeAPI {
private final ProxyServer server;
private final Logger logger;
private final Path dataFolder;
private final ProxyNetworkManager proxyNetworkManager;
private final PlayerManager playerManager;
@Inject
public VelocityValioBungeePlugin(
ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
this.server = server;
this.logger = logger;
this.dataFolder = dataDirectory;
// init logging
LogProviderFactory.register(logger);
this.proxyNetworkManager = new StandaloneProxyNetworkManager(this);
this.playerManager = new StandalonePlayerManager(this);
}
@Subscribe(priority = Short.MAX_VALUE) // this really important so MAKE IT MAX
public void onProxyInitializeEvent(ProxyInitializeEvent event) {
logger.info(
"initializing ValioBungee for {} platform, version {}",
platformProxyVendor(),
ConstantVariables.VERSION);
this.proxyNetworkManager.init(); // init the heart beat system
this.server.getEventManager().register(this, new PlayerManagerListener(this));
this.server
.getScheduler()
.buildTask(this, this.playerManager::correctionTask)
.repeat(Duration.ofMinutes(30))
.schedule(); // the correction task
}
@Subscribe(priority = Short.MIN_VALUE) // this really import so Make it AT LOWEST
public void onProxyShutdownEvent(ProxyShutdownEvent event) {
this.proxyNetworkManager.close(); // shutdown the heartbeat system
}
@Override
public int localOnlinePlayers() {
return this.server.getPlayerCount();
}
@Override
public String platformProxyVendor() {
return "velocity";
}
@Override
public ProxyNetworkManager proxyNetworkManager() {
return this.proxyNetworkManager;
}
@Override
public PlayerManager playerManager() {
return this.playerManager;
}
@Override
public NetworkProxy proxyPlatformCreator(String id) {
return new ImplVelocityNetworkProxy(this, id);
}
private final String id = "test-ido-" + ThreadLocalRandom.current().nextInt(10);
@Override
public String proxyId() {
return id;
}
@Override
public String networkId() {
return "development";
}
@Override
public Optional<NetworkPlayer> getNetworkPlayer(UUID uuid) {
logger.warn("not implemented api call returned as Optional empty");
server.getAllPlayers();
return Optional.empty();
}
@Override
public Optional<NetworkProxy> getNetworkProxy(String id) {
return this.proxyNetworkManager.getNetworkProxy(id);
}
@Override
public NetworkProxy getLocalProxy() {
return new ImplVelocityNetworkProxy(this, proxyId());
}
@Override
public Set<NetworkProxy> getNetworkProxies() {
return proxyNetworkManager.getNetworkProxies();
}
@Override
public Set<NetworkPlayer> getLocalProxyPlayers() {
return this.server.getAllPlayers().stream()
.map(p -> new ImplVelocityNetworkPlayer(this, p.getUniqueId(), getLocalProxy(), p))
.collect(Collectors.toSet());
}
@Override
public Set<NetworkPlayer> getNetworkPlayers() {
logger.warn("not implemented api call returned as Optional empty");
return Set.of();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.velocity.api.entities;
import com.velocitypowered.api.proxy.Player;
import java.util.Optional;
import java.util.UUID;
import net.limework.valiobungee.api.entity.NetworkProxy;
import net.limework.valiobungee.core.ValioBungeePlatform;
import net.limework.valiobungee.core.api.entities.AbstractNetworkPlayer;
public class ImplVelocityNetworkPlayer extends AbstractNetworkPlayer
implements VelocityNetworkPlayer {
private final Player handle;
public ImplVelocityNetworkPlayer(
ValioBungeePlatform platform, UUID uuid, NetworkProxy proxy, Player handle) {
super(platform, uuid, proxy);
this.handle = handle;
}
@Override
public Optional<Player> getHandle() {
return Optional.ofNullable(handle);
}
@Override
public boolean isLocal() {
return handle != null && handle.isActive();
}
@Override
public boolean isOnline() {
if (isLocal()) {
return handle.isActive();
}
return platform.playerManager().isOnline(uuid);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2026 ValioBungee contributors
*
* This file is part of ValioBungee.
*
* ValioBungee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ValioBungee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ValioBungee. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
*/
package net.limework.valiobungee.velocity.api.entities;
import java.util.Set;
import net.limework.valiobungee.api.entity.NetworkPlayer;
import net.limework.valiobungee.core.api.entities.AbstractNetworkProxy;
import net.limework.valiobungee.velocity.VelocityValioBungeePlugin;
public class ImplVelocityNetworkProxy extends AbstractNetworkProxy implements VelocityNetworkProxy {
public ImplVelocityNetworkProxy(VelocityValioBungeePlugin platform, String proxyId) {
super(platform, proxyId);
}
@Override
public Set<NetworkPlayer> getProxyPlayers() {
return Set.of();
}
}