mirror of
https://github.com/kennytv/Maintenance.git
synced 2026-11-04 16:50:07 -05:00
Compare commits
154
Commits
4.0.0
...
localization
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbdf811ae5 | ||
|
|
0518f848e1 | ||
|
|
4ebfa8c750 | ||
|
|
ea51ea9c63 | ||
|
|
3049f17250 | ||
|
|
619da4abee | ||
|
|
bb838fd4dd | ||
|
|
695404ce5c | ||
|
|
fd30fa4da6 | ||
|
|
3aad351c7b | ||
|
|
95492fe0e1 | ||
|
|
11b8fd2861 | ||
|
|
b9fdc90f7f | ||
|
|
acc5d89263 | ||
|
|
46b9dc4e04 | ||
|
|
5d37138b64 | ||
|
|
83f3a850c5 | ||
|
|
b58cb9709b | ||
|
|
00f6b25a67 | ||
|
|
e5fd9e6f37 | ||
|
|
051d916f87 | ||
|
|
b1ace71d4b | ||
|
|
da6fef8906 | ||
|
|
f6ad07a115 | ||
|
|
9072e03f79 | ||
|
|
946c1a7f72 | ||
|
|
572fc63162 | ||
|
|
87aedfaa3b | ||
|
|
8aba97d60d | ||
|
|
04d85b9c3c | ||
|
|
0603a17b5e | ||
|
|
3cd689f15d | ||
|
|
6c28d31fec | ||
|
|
1988b0284b | ||
|
|
c7f3dc72d4 | ||
|
|
9767322376 | ||
|
|
0e50c39100 | ||
|
|
ad2228b9c4 | ||
|
|
90e880500a | ||
|
|
15c3d9c111 | ||
|
|
ec55606737 | ||
|
|
a7438ca297 | ||
|
|
65d68982f9 | ||
|
|
03f0368c10 | ||
|
|
c16637ee36 | ||
|
|
7034bb85f5 | ||
|
|
38b142e8c9 | ||
|
|
2af4929c3a | ||
|
|
035b720369 | ||
|
|
8a106e4b34 | ||
|
|
f4a34041b8 | ||
|
|
ceb812e025 | ||
|
|
5058e851d8 | ||
|
|
ba04bae7bf | ||
|
|
d1abdd496e | ||
|
|
956e7bb3ef | ||
|
|
171e1368f4 | ||
|
|
0fff12f368 | ||
|
|
a9d052eabb | ||
|
|
10c661e849 | ||
|
|
695f3872a0 | ||
|
|
05af3d3db2 | ||
|
|
f9396fa369 | ||
|
|
ddc43fb1eb | ||
|
|
5c51dd86b6 | ||
|
|
dcb5f64449 | ||
|
|
c0b991932c | ||
|
|
3b9ef8b006 | ||
|
|
c73e18acf1 | ||
|
|
55576dce4a | ||
|
|
aa1c77c230 | ||
|
|
72b5f38e63 | ||
|
|
3e8cc06aa7 | ||
|
|
0f375adce6 | ||
|
|
9c5e8182cc | ||
|
|
1b94594fc1 | ||
|
|
67bdc8261a | ||
|
|
e255529f78 | ||
|
|
5b7ab9fe42 | ||
|
|
cfa30cea79 | ||
|
|
c59b784e48 | ||
|
|
864fee66fa | ||
|
|
3c810d7ed3 | ||
|
|
e24806694e | ||
|
|
fa7effe8f3 | ||
|
|
26802cbe1a | ||
|
|
1cb35abccb | ||
|
|
1f2d0bba2a | ||
|
|
a7e577554c | ||
|
|
c0b3ea22b9 | ||
|
|
0adf767b5e | ||
|
|
3cc21504f0 | ||
|
|
cdebac351f | ||
|
|
8941b55362 | ||
|
|
fa225ad5f0 | ||
|
|
dea7fc9032 | ||
|
|
ced1651c05 | ||
|
|
6be27d05bb | ||
|
|
c39bb44e42 | ||
|
|
b71e1ce25d | ||
|
|
b5cfc47524 | ||
|
|
c32698fc76 | ||
|
|
b177c5e2f8 | ||
|
|
d0e0aff78e | ||
|
|
9f5ecd590e | ||
|
|
bc93f1f36d | ||
|
|
3c1c1e48fb | ||
|
|
c8e14e1d74 | ||
|
|
29300d5fe2 | ||
|
|
7da9365e23 | ||
|
|
ded84cd1c9 | ||
|
|
62cc71675b | ||
|
|
0752d3dc93 | ||
|
|
d352e44e50 | ||
|
|
83877a6c3a | ||
|
|
199eb2e67d | ||
|
|
0683450c2d | ||
|
|
dbdd7d18d0 | ||
|
|
cf4ae33ca7 | ||
|
|
ba8c574e79 | ||
|
|
225fe4b15a | ||
|
|
de203244c3 | ||
|
|
167065eef5 | ||
|
|
25503f6196 | ||
|
|
23bfe7ef8f | ||
|
|
31e721f443 | ||
|
|
158680e6ca | ||
|
|
b6c9c1bd5e | ||
|
|
370b9d9eb8 | ||
|
|
339136c1f2 | ||
|
|
4f0e158435 | ||
|
|
c6efc58242 | ||
|
|
6aed091356 | ||
|
|
d343c75eb6 | ||
|
|
9cbe2597d0 | ||
|
|
912d094d6d | ||
|
|
6d942f87ce | ||
|
|
30380604da | ||
|
|
f2c07f1fd4 | ||
|
|
36f14a6b30 | ||
|
|
871bd0882c | ||
|
|
fbc9fc47e2 | ||
|
|
ccd5eab475 | ||
|
|
5d0fbb310e | ||
|
|
09d2e03c0c | ||
|
|
57da60a669 | ||
|
|
63dafb05b6 | ||
|
|
d8e36d7ff6 | ||
|
|
39f966ba5d | ||
|
|
879f07e3e7 | ||
|
|
9cbead310a | ||
|
|
25375b184d | ||
|
|
eec5acd0ce | ||
|
|
14cef4d53b |
No files matched your search
+108
-4
@@ -1,13 +1,117 @@
|
||||
# Maintenance Changelog
|
||||
This file contains update logs for this project. The top may contain a `Unreleased` section, gathering update logs for a future update during development.
|
||||
|
||||
## 5.1.1 - October 17th 2026
|
||||
|
||||
### Changed
|
||||
- Velocity/Bungee: Timer commands now accept `global` as the server argument, with an optional mode: `/maintenance starttimer global [mode] <time>`, `/maintenance scheduletimer global [mode] <time> <duration>`, `/maintenance endtimer global <time>`
|
||||
- Timer durations now also accept days, e.g. `2d` or `1d12h`
|
||||
- Folia: Now supports timers
|
||||
|
||||
### Fixed
|
||||
- Folia: Fixed the update check stalling the global region thread
|
||||
- Velocity/Bungee: Fixed the `redis.enabled` confg key
|
||||
- Velocity/Bungee: `/maintenance reload` now keeps the maintenance status from Redis instead of overriding it with the local config
|
||||
- Velocity/Bungee: Fixed `/maintenance aborttimer` tab completion not suggesting servers with running timers
|
||||
|
||||
---
|
||||
## 5.1.0 - May 17th 2026
|
||||
|
||||
### Changed
|
||||
- Made Discord webhooks configurable separately from command messages, including a new `%TIMESTAMP%` parameter for Discord timestamps (e.g. `<t:%TIMESTAMP%:R>`)
|
||||
|
||||
### Fixed
|
||||
- Velocity/Bungee: Fixed proxied-maintenance-servers config loading
|
||||
---
|
||||
## 5.0.0 - April 17th 2026
|
||||
|
||||
This update removes *a lot* of legacy code, making the code-base a great bit more simple and a lot more performant.
|
||||
|
||||
### Requirements
|
||||
- The Paper part now **requires at least Paper 1.20.1** and no longer supports Spigot
|
||||
- The universal jar has been removed. Each platform now has its own jar (that is as small as possible)
|
||||
- Maintenance now **requires at least Java 21**
|
||||
|
||||
### Changed
|
||||
- **Added mode-based maintenance messages** for `ping-message`, `player-count-message`, and `player-list-hover-message`
|
||||
- You can have different MOTDs depending on the pre-configured mode, e.g. a generic `default` or one for `upcoming-release`.
|
||||
- Updated proxy/global toggle syntax: `/maintenance on [mode]`
|
||||
- For proxy servers: `/maintenance on global [mode]`, `/maintenance on <server> [mode]`
|
||||
- Other updated commands: `/maintenance motd [timer] [mode]`, `/maintenance setmotd [timer] [mode] ...`, `/maintenance removemotd [timer] [mode] ...`
|
||||
- **Added `webhook` config option for Discord webhooks** when the maintenance status changes, a timer is started or a join is blocked
|
||||
- **Added `%REASON%` variable to messages**, using the `active-reason` config option. This allows for dynamic MOTD and other messages via a quick command without modifying the messages themselves
|
||||
- Can be changed via the `/maintenance setreason <reason>` command
|
||||
- **Added `/maintenance status` command** on Paper/Sponge to display the current maintenance status as well as any running start or end timers.
|
||||
- Velocity/Bungee: **The existing timer command now displays start and end timers** for proxied servers.
|
||||
- Velocity/Bungee: **Replaced `mysql` section with `redis`**. This is a more suitable database for such reading and sharing of data and no longer means some server pings can take multiple seconds, if you have multiple proxies linked
|
||||
|
||||
### Fixed
|
||||
- Velocity/Bungee: Fixed the `/maintenance schedule` command for single servers having swapped time arguments
|
||||
---
|
||||
## 4.3.0 - September 17th 2024
|
||||
### Changed
|
||||
* Added alternative player count and player list hover message for when endtimers are running, similar to the existing ping messages setting. They are disabled by default and can be found in their `player-count-message` and `player-list-hover-message` config sections
|
||||
* All timer commands now accept durations like `2h30m`, `1h30m5s`, or `90s`. The default behavior without units will still be in minutes
|
||||
* The language defaults will *not* automatically update the command help messages
|
||||
* Slightly reorganized the configuration file (it will be automatically migrated on startup, but new sections will be slapped at the bottom of the file)
|
||||
* The `fallback` field now also accepts null values next to an empty array for disabling the feature
|
||||
* Updated language files from [Crowdin](https://crowdin.com/translate/maintenance)
|
||||
|
||||
### Fixed
|
||||
* Fixed the `/maintenance dump` command
|
||||
* Fixed the original player sample not being cleared in Velocity
|
||||
* Velocity: Fixed the console getting broadcasts twice
|
||||
* Velocity: Don't log legacy color codes to the console
|
||||
* Paper: Fixed the player count messages on Paper 1.21
|
||||
---
|
||||
## 4.2.1 - May 17th 2024
|
||||
### Changed
|
||||
* The proxied maintenance status is now also shown for forced hosts (thanks to alec-jensen)
|
||||
* Offline player fetching improvements (thanks to EpicPlayerA10)
|
||||
* Added the setting `fallback-to-offline-uuid` to use offline player uuids in maintenance whitelist commands (defaults to false)
|
||||
* Maintenance now skips the plugin remapping process on 1.20.5+ Paper servers
|
||||
* Small optimizations
|
||||
* Updated language files from [Crowdin](https://crowdin.com/translate/maintenance)
|
||||
* Massive thank you to all the contributors! These include: vortix22, xdalul, CodeZhangBorui, jesusskye, Doc94,
|
||||
Texider_, marvin2k0, leonard.bausenwein, pro.timeo.kerjean, meviper, troev5, Murka124, jhqwqmc, Pryzinho,
|
||||
raysetratyboy, Kolja07, and rikunightcore
|
||||
---
|
||||
## 4.2.0 - September 17th 2023
|
||||
### Changed
|
||||
* Velocity/Bungee: Added `commands-on-single-maintenance-enable` and `commands-on-single-maintenance-disable` config options to define commands to be executed after maintenance has been toggled on a proxied server
|
||||
* Commands inside the `all` list will be executed for any proxied server, but you can also define commands for specific servers by adding arrays with the server names as keys
|
||||
* You can use the placeholder `%SERVER%` in commands to be replaced with the server name
|
||||
* Start-, end-, and schedule timers for proxied servers now also include the server in the feedback message
|
||||
* Updated language files from [Crowdin](https://crowdin.com/translate/maintenance) (including new language files for Danish, Japanese, Korean, and Hungarian)
|
||||
---
|
||||
## 4.1.0 - April 17th 2023
|
||||
### Changed
|
||||
* Added config option `enable-playercounthovermessage` to be able to toggle the player count hover message
|
||||
* Removed platform specific command aliases (e.g. `maintenancevelocity`)
|
||||
* Translations
|
||||
* Moved translation editing to [Crowdin](https://crowdin.com/translate/maintenance)
|
||||
* Added Turkish translation file (thanks to Proomp)
|
||||
* Added Swedish translation file (thanks to Sup33r)
|
||||
* Added Ukrainian translation file (thanks to Mrlucke)
|
||||
* Added Japanese translation file (thanks to yu-solt)
|
||||
* Added bStats metrics to Velocity and Sponge modules
|
||||
|
||||
### Fixed
|
||||
* Fixed disabling the `enable-pingmessages` setting not working on Paper servers
|
||||
* Fixed variable replacement in messages with gradients
|
||||
* Fixed the message for cancelling proxied server timers not replacing the server argument
|
||||
* Setting `enable-pingmessages` to `false` no longer disables custom player and player hover messages
|
||||
---
|
||||
## 4.0.1 - April 17th 2022
|
||||
### Fixed
|
||||
* Fixed the config header sometimes breaking the config on upgrading/saving
|
||||
---
|
||||
## 4.0.0 - March 17th 2022
|
||||
### Changed
|
||||
* Added support for hex colors in messages and replaced the component parsing with adventure-minimessage
|
||||
* This means that section symbols (`§`) are no longer the intended/supported format
|
||||
* You can use the following page to edit and preview formatted text: https://webui.adventure.kyori.net/
|
||||
* You can find full documentation on the format (including normal colors and formatting, rgb, click/hover events, and more) here: https://docs.adventure.kyori.net/minimessage/format
|
||||
* You can use the following page to edit and preview formatted text: https://webui.advntr.dev/
|
||||
* You can find full documentation on the format (including normal colors and formatting, rgb, click/hover events, and more) here: https://docs.papermc.io/adventure/minimessage/format/
|
||||
* Your config and language file will be updated to the new format automatically, you won't have to change anything manually!
|
||||
* Note that the player count and player count hover messages do not support rgb colors
|
||||
* Updated Sponge module to SpongeAPI v8 (Minecraft 1.15+)
|
||||
@@ -61,7 +165,7 @@ This file contains update logs for this project. The top may contain a `Unreleas
|
||||
* Bungee/Velocity: Added language fields to choose custom kickmessages for individual proxied servers
|
||||
* See [**HERE**](https://github.com/KennyTV/Maintenance/wiki/Configuration#language-file) for an example setting
|
||||
* Added Chinese language file (thanks to Spigot user yeban)
|
||||
* Added Italian language file (thanks to @TheViperShow)
|
||||
* Added Italian language file
|
||||
* Updated Russian language file (thanks to Spigot user En_0t_S)
|
||||
* Removed automated config migration from 2.x->3.0
|
||||
* See [**HERE**](https://github.com/KennyTV/Maintenance/wiki/Updates#updating-from-2x) on how to do that manually - virtually noone should be using such old versions anymore
|
||||
@@ -269,4 +373,4 @@ This file contains update logs for this project. The top may contain a `Unreleas
|
||||
### Fixed
|
||||
* Fixed some permission and message bugs
|
||||
* Fixed problems with adding and removing players from the maintenance whitelist
|
||||
* Fixed update checks
|
||||
* Fixed update checks
|
||||
@@ -8,7 +8,7 @@ about: Report a bug to have it fixed
|
||||
**How does the problem occur/what are the steps to reproduce it?**
|
||||
|
||||
## Error
|
||||
**Is there an error? Save it via https://pastebin.com/ and link paste the link.**
|
||||
**Is there an error? Save it via https://mclo.gs/ and link paste the link.**
|
||||
|
||||
## Server information
|
||||
**Type '/maintenance dump' and paste the given link here**
|
||||
@@ -0,0 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
- package-ecosystem: "gradle"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
@@ -7,25 +7,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/cache@v2.1.7
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-maven-
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2.5.0
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v6
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v2.3.1
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: MaintenanceFiles
|
||||
name: Maintenance Files
|
||||
path: build/libs
|
||||
@@ -2,6 +2,7 @@
|
||||
build/
|
||||
.gradle
|
||||
bin/
|
||||
run/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
@@ -94,3 +95,4 @@ $RECYCLE.BIN/
|
||||
*.lnk
|
||||
!MaintenanceAPI.jar
|
||||
**/src/test/resources/
|
||||
!core/src/test/resources/dirty-config.yml
|
||||
@@ -1,11 +1,10 @@
|
||||
# Maintenance
|
||||
[](https://github.com/kennytv/Maintenance/releases)
|
||||

|
||||
[](https://github.com/kennytv/Maintenance/releases)
|
||||
[](https://crowdin.com/project/maintenance)
|
||||
[](https://discord.gg/vGCUzHq)
|
||||
[](https://twitter.com/kennytvn)
|
||||
|
||||
Maintenance is the most customizable free maintenance plugin for your Minecraft server you can find. It runs on Spigot, BungeeCord, Sponge, as well as Velocity.
|
||||
Maintenance is the most customizable free maintenance plugin for your Minecraft server you can find. It runs on Paper (also Spigot, but with limited functionality), BungeeCord, Sponge, as well as Velocity.
|
||||
|
||||
Its features include:
|
||||
* A custom motd as well as server icon, that will be shown during maintenance
|
||||
@@ -15,24 +14,26 @@ Its features include:
|
||||
* Nearly all messages are editable via the language file, given in a multitude of different languages
|
||||
* Features specifically for Bungee/Velocity
|
||||
* Only want to enable maintenance on a single server? You can also do so by using the `/maintenance <on/off> <servername>` command
|
||||
* Link multiple proxy instances through a MySQL database connection, so you don't have to change maintenance on each proxy by hand
|
||||
* Use PlaceholderAPI placeholders with the [**MaintenanceAddon**](https://github.com/kennytv/Maintenance/wiki/MaintenanceAddon) (only for Bungee)
|
||||
* Link multiple proxy instances through a Redis connection, so you don't have to change maintenance status or whitelisted players on each proxy by hand
|
||||
* Use PlaceholderAPI placeholders with the [**MaintenanceAddon**](https://hangar.papermc.io/kennytv/MaintenanceAddon) (for Velocity and Bungee)
|
||||
|
||||
A full list of commands, permissions and configuration options can be found in the wiki listed below.
|
||||
* [Hangar Downloads](https://hangar.papermc.io/kennytv/Maintenance)
|
||||
* [Wiki](https://github.com/kennytv/Maintenance/wiki) (configuration, permissions, API usage)
|
||||
* [Downloads](https://github.com/kennytv/Maintenance/releases) (GitHub)
|
||||
* [Changelogs](https://github.com/kennytv/Maintenance/blob/master/.github/CHANGELOG.md)
|
||||
* [Issue tracker/bug reports](https://github.com/kennytv/Maintenance/issues)
|
||||
* [Discord](https://discord.gg/vGCUzHq)
|
||||
* [PlaceholderAPI addon](https://github.com/kennytv/Maintenance/wiki/MaintenanceAddon)
|
||||
* [PlaceholderAPI addon](https://hangar.papermc.io/kennytv/MaintenanceAddon)
|
||||
|
||||
Other forum threads
|
||||
* [Spigot forums](https://www.spigotmc.org/resources/maintenance.40699/)
|
||||
* [Ore/Sponge Forums](https://ore.spongepowered.org/KennyTV/Maintenance)
|
||||
* [Velocity forums](https://forums.velocitypowered.com/t/maintenance/129)
|
||||
|
||||
## Localization
|
||||
The English base language file is localized using Crodwin. You can see and update translations here: https://crowdin.com/project/maintenance
|
||||
|
||||
Join [our Discord](https://discord.gg/vGCUzHq) if you want to get the proofreader or translator role on the project.
|
||||
Adding translations for languages not yet listed is always appreciated as well!
|
||||
|
||||
## Compiling
|
||||
To create a working jar yourself, simply clone the project and compile it with maven (by using `mvn clean package` in your IDE console).
|
||||
To create a working jar yourself, simply clone the project and compile it with Gradle (`gradlew build`). To compile, you need a JDK 21+ (although it targets 17).
|
||||
|
||||
## License
|
||||
This project is licensed under the [GNU General Public License v3](LICENSE.txt).
|
||||
@@ -1,24 +0,0 @@
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
// Shade and relocate adventure in an extra module, so that common/the rest can directly depend on a
|
||||
// relocated adventure without breaking native platform's adventure usage with project wide relocation
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.bundles.adventure) {
|
||||
exclude("net.kyori", "adventure-api")
|
||||
exclude("net.kyori", "adventure-bom")
|
||||
exclude("com.google.code.gson", "gson")
|
||||
}
|
||||
}
|
||||
|
||||
publishShadowJar()
|
||||
@@ -1,20 +0,0 @@
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.adventurePlatformBukkit) {
|
||||
exclude("net.kyori", "adventure-api")
|
||||
}
|
||||
}
|
||||
|
||||
publishShadowJar()
|
||||
@@ -1,20 +0,0 @@
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.adventurePlatformBungee) {
|
||||
exclude("net.kyori", "adventure-api")
|
||||
}
|
||||
}
|
||||
|
||||
publishShadowJar()
|
||||
Whitespace-only changes.
@@ -4,9 +4,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api(projects.maintenanceApi)
|
||||
compileOnly(projects.adventure.adventureApi) {
|
||||
targetConfiguration = "shadow"
|
||||
}
|
||||
compileOnly(libs.adventureApi)
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
+4
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -15,17 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.server;
|
||||
package eu.kennytv.maintenance.api.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class DummyServer implements Server {
|
||||
private final String name;
|
||||
|
||||
public DummyServer(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public record DummyServer(String name) implements Server {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -18,23 +18,26 @@
|
||||
package eu.kennytv.maintenance.api.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.api.Maintenance;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface MaintenanceProxy extends Maintenance {
|
||||
|
||||
/**
|
||||
* Enables/disables maintenance mode on a proxied server.
|
||||
* If enabled, all non-permitted players will be kicked.
|
||||
* If MySQL is enabled, it will also be written into the database.
|
||||
* If Redis is enabled, it will also be written into the database.
|
||||
*
|
||||
* @param server server to apply the maintenance status to
|
||||
* @param maintenance true to enable, false to disable maintenance mode
|
||||
* @return true if the mode was changed
|
||||
* @see #getServer(String)
|
||||
*/
|
||||
boolean setMaintenanceToServer(Server server, boolean maintenance);
|
||||
boolean setMaintenanceToServer(Server server, boolean maintenance, @Nullable String mode);
|
||||
|
||||
default boolean setMaintenanceToServer(final Server server, final boolean maintenance) {
|
||||
return setMaintenanceToServer(server, maintenance, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param server server to check
|
||||
@@ -59,6 +62,18 @@ public interface MaintenanceProxy extends Maintenance {
|
||||
@Nullable
|
||||
Server getServer(String server);
|
||||
|
||||
/**
|
||||
* Returns a wrapped {@link Server} object of a proxied server, or a dummy server if not registered.
|
||||
*
|
||||
* @param serverName name of the proxied server
|
||||
* @return wrapped server object, or a dummy if not registered
|
||||
* @see Server#isRegisteredServer()
|
||||
*/
|
||||
default Server getServerOrDummy(final String serverName) {
|
||||
final Server server = getServer(serverName);
|
||||
return server != null ? server : new DummyServer(serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered proxied servers.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.api.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
/**
|
||||
* Wrapper for a proxied server.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -18,6 +18,7 @@
|
||||
package eu.kennytv.maintenance.api;
|
||||
|
||||
import eu.kennytv.maintenance.api.event.manager.EventManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Maintenance {
|
||||
|
||||
@@ -25,13 +26,18 @@ public interface Maintenance {
|
||||
* Enables/disables maintenance mode.
|
||||
* If enabled, all non-permitted players will be kicked.
|
||||
* <p>
|
||||
* If using the BungeeCord version and MySQL is enabled,
|
||||
* If using the BungeeCord version and Redis is enabled,
|
||||
* it will also be written into the database.
|
||||
* </p>
|
||||
*
|
||||
* @param maintenance true to enable, false to disable maintenance mode
|
||||
* @param mode mode, used for custom messages
|
||||
*/
|
||||
void setMaintenance(boolean maintenance);
|
||||
void setMaintenance(boolean maintenance, @Nullable String mode);
|
||||
|
||||
default void setMaintenance(boolean maintenance) {
|
||||
setMaintenance(maintenance, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if maintenance is currently enabled
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,16 +19,39 @@ package eu.kennytv.maintenance.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Settings {
|
||||
|
||||
/**
|
||||
* Note that the value might need a few seconds to update, if you're on BungeeCord with MySQL enabled
|
||||
* Returns whether maintenance is currently enabled.
|
||||
*
|
||||
* @return true if maintenance is currently enabled
|
||||
*/
|
||||
boolean isMaintenance();
|
||||
|
||||
/**
|
||||
* Returns the current active maintenance mode, or null is set to default.
|
||||
* Used when getting MOTD/player count messages.
|
||||
*
|
||||
* @return the current active maintenance mode, or null if set to default
|
||||
*/
|
||||
@Nullable String activeMode();
|
||||
|
||||
/**
|
||||
* Returns the reason for the current active mode.
|
||||
*
|
||||
* @return variable reason for the current active mode, or empty if no reason is set
|
||||
*/
|
||||
String activeReason();
|
||||
|
||||
/**
|
||||
* Sets the reason for the current active mode, used in %REASON% message placeholders.
|
||||
*
|
||||
* @param reason reason to set for the current active mode, or null to remove the reason
|
||||
*/
|
||||
void setActiveReason(@Nullable String reason);
|
||||
|
||||
/**
|
||||
* @return true if custom ping messages during maintenance should be used
|
||||
*/
|
||||
@@ -73,8 +96,8 @@ public interface Settings {
|
||||
*
|
||||
* @param name name of the player to remove with original lower-/uppercase letters
|
||||
* @return true if removing the player was successful
|
||||
* @deprecated if a player changes their name, the list will still contain the old one
|
||||
* @see #removeWhitelistedPlayer(UUID)
|
||||
* @deprecated if a player changes their name, the list will still contain the old one
|
||||
*/
|
||||
@Deprecated
|
||||
boolean removeWhitelistedPlayer(String name);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -22,17 +22,5 @@ import eu.kennytv.maintenance.api.event.manager.MaintenanceEvent;
|
||||
/**
|
||||
* Notification event fired when maintenance mode has been changed.
|
||||
*/
|
||||
public final class MaintenanceChangedEvent implements MaintenanceEvent {
|
||||
private final boolean maintenance;
|
||||
|
||||
public MaintenanceChangedEvent(final boolean maintenance) {
|
||||
this.maintenance = maintenance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if maintenance has been enabled, false otherwise
|
||||
*/
|
||||
public boolean isMaintenance() {
|
||||
return maintenance;
|
||||
}
|
||||
public record MaintenanceChangedEvent(boolean isMaintenance) implements MaintenanceEvent {
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -8,5 +8,6 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// version must be manually kept in sync with the one in root project settings.gradle.kts
|
||||
implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.6.1")
|
||||
implementation("com.gradleup.nmcp:com.gradleup.nmcp.gradle.plugin:1.6.2")
|
||||
}
|
||||
@@ -6,12 +6,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
fun Project.publishShadowJar() {
|
||||
configurePublication {
|
||||
artifact(tasks["shadowJar"])
|
||||
artifact(tasks["sourcesJar"])
|
||||
artifact(tasks["javadocJar"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,14 +30,7 @@ private fun Project.configurePublication(configurer: MavenPublication.() -> Unit
|
||||
}
|
||||
|
||||
fun Project.latestCommitHash(): String {
|
||||
val byteOut = ByteArrayOutputStream()
|
||||
exec {
|
||||
return providers.exec {
|
||||
commandLine = listOf("git", "rev-parse", "--short", "HEAD")
|
||||
standardOutput = byteOut
|
||||
}
|
||||
return byteOut.toString(Charsets.UTF_8.name()).trim()
|
||||
}
|
||||
|
||||
fun JavaPluginExtension.javaTarget(version: Int) {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(version))
|
||||
}.standardOutput.asBytes.get().toString(Charsets.UTF_8).trim()
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
id("com.gradleup.nmcp")
|
||||
}
|
||||
|
||||
tasks {
|
||||
// Variable replacements
|
||||
processResources {
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml")) {
|
||||
expand("version" to project.version, "description" to project.description)
|
||||
val ver = project.version.toString()
|
||||
val desc = project.description
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml", "META-INF/sponge_plugins.json")) {
|
||||
expand(mapOf("version" to ver, "description" to desc))
|
||||
}
|
||||
}
|
||||
javadoc {
|
||||
@@ -18,11 +22,21 @@ tasks {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
javaTarget(8)
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications)
|
||||
}
|
||||
|
||||
publishing {
|
||||
@@ -30,5 +44,27 @@ publishing {
|
||||
groupId = rootProject.group as String
|
||||
artifactId = project.name
|
||||
version = rootProject.version as String
|
||||
pom {
|
||||
name.set("Maintenance")
|
||||
description.set("Paper plugin to enable maintenance mode on your Minecraft server.")
|
||||
url.set("https://github.com/kennytv/Maintenance")
|
||||
scm {
|
||||
url.set("https://github.com/kennytv/Maintenance")
|
||||
connection.set("scm:git:https://github.com/kennytv/Maintenance.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com/kennytv/Maintenance.git")
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name.set("GNU GPLv3")
|
||||
url.set("https://www.gnu.org/licenses/gpl-3.0.html")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("kennytv")
|
||||
name.set("Nassim Jahnke")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import java.util.*
|
||||
|
||||
plugins {
|
||||
id("maintenance.shadow-conventions")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
archiveFileName.set("Maintenance-${project.name.substringAfter("maintenance-").capitalize()}-${project.version}.jar")
|
||||
archiveFileName.set("Maintenance-${project.name.substringAfter("maintenance-").replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}-${project.version}.jar")
|
||||
destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs"))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.kotlin.dsl.named
|
||||
|
||||
plugins {
|
||||
id("maintenance.base-conventions")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -24,5 +23,10 @@ tasks {
|
||||
publishShadowJar()
|
||||
|
||||
fun ShadowJar.configureRelocations() {
|
||||
relocate("com.zaxxer.hikari", "eu.kennytv.maintenance.lib.hikari")
|
||||
relocate("org.bstats", "eu.kennytv.maintenance.lib.bstats")
|
||||
|
||||
// Proxy
|
||||
relocate("redis.client", "eu.kennytv.maintenance.lib.redis.client")
|
||||
relocate("org.json", "eu.kennytv.maintenance.lib.json")
|
||||
relocate("org.apache.commons.pool2", "eu.kennytv.maintenance.lib.apache.commons.pool2")
|
||||
}
|
||||
+26
-11
@@ -1,8 +1,10 @@
|
||||
import org.gradle.api.plugins.JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME
|
||||
import org.gradle.api.plugins.JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME
|
||||
|
||||
plugins {
|
||||
base
|
||||
id("maintenance.build-logic")
|
||||
id("com.gradleup.nmcp.aggregation") version "1.6.2"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -12,30 +14,43 @@ allprojects {
|
||||
}
|
||||
|
||||
val platforms = setOf(
|
||||
projects.maintenanceSpigot,
|
||||
projects.maintenancePaper,
|
||||
projects.maintenanceBungee,
|
||||
projects.maintenanceSponge,
|
||||
projects.maintenanceVelocity
|
||||
).map { it.dependencyProject }
|
||||
).map { it.path }
|
||||
|
||||
val special = setOf(
|
||||
projects.maintenance,
|
||||
projects.maintenanceApi,
|
||||
projects.maintenanceApiProxy,
|
||||
projects.adventure,
|
||||
projects.adventure.adventureApi,
|
||||
projects.adventure.adventurePlatformBukkit,
|
||||
projects.adventure.adventurePlatformBungee,
|
||||
).map { it.dependencyProject }
|
||||
projects.maintenanceApiProxy
|
||||
).map { it.path }
|
||||
|
||||
subprojects {
|
||||
when (this) {
|
||||
when (path) {
|
||||
in platforms -> plugins.apply("maintenance.platform-conventions")
|
||||
in special -> plugins.apply("maintenance.base-conventions")
|
||||
else -> plugins.apply("maintenance.standard-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
TEST_IMPLEMENTATION_CONFIGURATION_NAME("org.junit.jupiter:junit-jupiter-engine:5.8.2")
|
||||
TEST_IMPLEMENTATION_CONFIGURATION_NAME(rootProject.libs.bundles.junit)
|
||||
TEST_IMPLEMENTATION_CONFIGURATION_NAME(rootProject.libs.snakeyaml)
|
||||
TEST_RUNTIME_ONLY_CONFIGURATION_NAME("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
allprojects {
|
||||
nmcpAggregation(project(path))
|
||||
}
|
||||
}
|
||||
|
||||
nmcpAggregation {
|
||||
centralPortal {
|
||||
username = System.getenv("MAVEN_CENTRAL_USERNAME")
|
||||
password = System.getenv("MAVEN_CENTRAL_PASSWORD")
|
||||
publishingType = "USER_MANAGED"
|
||||
publicationName = "maintenance:$version"
|
||||
publishingTimeout = java.time.Duration.ZERO
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -1,7 +1,19 @@
|
||||
dependencies {
|
||||
implementation(projects.maintenanceCoreProxy)
|
||||
implementation(projects.adventure.adventurePlatformBungee) {
|
||||
targetConfiguration = "shadow"
|
||||
}
|
||||
implementation(libs.bstatsBungee)
|
||||
compileOnly(libs.bungee)
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.bundles.adventureBungee) {
|
||||
exclude("com.google.code.gson", "gson")
|
||||
exclude("org.slf4j", "slf4j-api")
|
||||
exclude("net.kyori", "adventure-bom")
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -22,7 +22,6 @@ import eu.kennytv.maintenance.bungee.command.MaintenanceBungeeCommand;
|
||||
import eu.kennytv.maintenance.bungee.command.MaintenanceBungeeCommandBase;
|
||||
import eu.kennytv.maintenance.bungee.listener.ProxyPingListener;
|
||||
import eu.kennytv.maintenance.bungee.listener.ServerConnectListener;
|
||||
import eu.kennytv.maintenance.bungee.metrics.MetricsLite;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeSenderInfo;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeServer;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeTask;
|
||||
@@ -37,23 +36,6 @@ import eu.kennytv.maintenance.core.proxy.util.ProxyOfflineSenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -61,10 +43,22 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.imageio.ImageIO;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
private final MaintenanceBungeeBase plugin;
|
||||
@@ -88,7 +82,7 @@ public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
pm.registerCommand(plugin, new MaintenanceBungeeCommandBase(commandManager));
|
||||
|
||||
continueLastEndtimer();
|
||||
new MetricsLite(plugin);
|
||||
new Metrics(plugin, 742);
|
||||
|
||||
final Plugin serverListPlus = pm.getPlugin("ServerListPlus");
|
||||
if (serverListPlus != null) {
|
||||
@@ -108,18 +102,6 @@ public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendUpdateNotification(final SenderInfo sender) {
|
||||
final TextComponent tc1 = new TextComponent(TextComponent.fromLegacyText(getPrefix()));
|
||||
final TextComponent tc2 = new TextComponent(TextComponent.fromLegacyText("§cDownload it at: §6https://www.spigotmc.org/resources/maintenance.40699/"));
|
||||
final TextComponent click = new TextComponent(TextComponent.fromLegacyText(" §7§l§o(CLICK ME)"));
|
||||
click.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.spigotmc.org/resources/maintenance.40699/"));
|
||||
click.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(new ComponentBuilder("§aDownload the latest version").create())));
|
||||
tc1.addExtra(tc2);
|
||||
tc1.addExtra(click);
|
||||
((BungeeSenderInfo) sender).sendMessage(tc1);
|
||||
}
|
||||
|
||||
public boolean isMaintenance(final ServerInfo serverInfo) {
|
||||
return settingsProxy.isMaintenance(serverInfo.getName());
|
||||
}
|
||||
@@ -136,9 +118,9 @@ public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
@Override
|
||||
protected void kickPlayers(final Server server, final Server fallback) {
|
||||
// Kick players from a proxied server
|
||||
final ServerInfo fallbackServer = fallback != null ? ((BungeeServer) fallback).getServer() : null;
|
||||
final ServerInfo fallbackServer = fallback != null ? ((BungeeServer) fallback).server() : null;
|
||||
final boolean checkForFallback = fallbackServer != null && !isMaintenance(fallback);
|
||||
for (final ProxiedPlayer player : ((BungeeServer) server).getServer().getPlayers()) {
|
||||
for (final ProxiedPlayer player : ((BungeeServer) server).server().getPlayers()) {
|
||||
if (!hasPermission(player, "bypass") && !settingsProxy.isWhitelisted(player.getUniqueId())) {
|
||||
if (checkForFallback && fallbackServer.canAccess(player)) {
|
||||
audiences.player(player).sendMessage(settingsProxy.getMessage("singleMaintenanceActivated", "%SERVER%", server.getName()));
|
||||
@@ -155,7 +137,7 @@ public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
@Override
|
||||
protected void kickPlayersTo(final Server server) {
|
||||
// Kick all players to a single waiting server
|
||||
final ServerInfo serverInfo = ((BungeeServer) server).getServer();
|
||||
final ServerInfo serverInfo = ((BungeeServer) server).server();
|
||||
// Notifications done in global method
|
||||
for (final ProxiedPlayer player : getProxy().getPlayers()) {
|
||||
if (hasPermission(player, "bypass") || settingsProxy.isWhitelisted(player.getUniqueId())) continue;
|
||||
@@ -176,28 +158,26 @@ public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOfflinePlayer(final String name, final Consumer<@Nullable SenderInfo> consumer) {
|
||||
public CompletableFuture<@Nullable SenderInfo> getOfflinePlayer(final String name) {
|
||||
final ProxiedPlayer player = getProxy().getPlayer(name);
|
||||
if (player != null) {
|
||||
consumer.accept(new BungeeSenderInfo(player));
|
||||
return;
|
||||
return CompletableFuture.completedFuture(new BungeeSenderInfo(player));
|
||||
}
|
||||
|
||||
getProxy().getScheduler().runAsync(plugin, () -> {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
final ProfileLookup profile = doUUIDLookup(name);
|
||||
consumer.accept(new ProxyOfflineSenderInfo(profile.getUuid(), profile.getName()));
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
consumer.accept(null);
|
||||
return new ProxyOfflineSenderInfo(profile.uuid(), profile.name());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOfflinePlayer(final UUID uuid, final Consumer<@Nullable SenderInfo> consumer) {
|
||||
public CompletableFuture<@Nullable SenderInfo> getOfflinePlayer(final UUID uuid) {
|
||||
final ProxiedPlayer player = getProxy().getPlayer(uuid);
|
||||
consumer.accept(player != null ? new BungeeSenderInfo(player) : null);
|
||||
return CompletableFuture.completedFuture(player != null ? new BungeeSenderInfo(player) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,7 +195,7 @@ public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getServerNameOf(final SenderInfo sender) {
|
||||
final ProxiedPlayer player = getProxy().getPlayer(sender.getUuid());
|
||||
final ProxiedPlayer player = getProxy().getPlayer(sender.uuid());
|
||||
if (player == null || player.getServer() == null) return null;
|
||||
return player.getServer().getInfo().getName();
|
||||
}
|
||||
|
||||
+5
-28
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -18,19 +18,14 @@
|
||||
package eu.kennytv.maintenance.bungee.command;
|
||||
|
||||
import eu.kennytv.maintenance.bungee.MaintenanceBungeePlugin;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeSenderInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.command.MaintenanceProxyCommand;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public final class MaintenanceBungeeCommand extends MaintenanceProxyCommand {
|
||||
private final MaintenanceBungeePlugin plugin;
|
||||
@@ -41,36 +36,18 @@ public final class MaintenanceBungeeCommand extends MaintenanceProxyCommand {
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendUpdateMessage(final SenderInfo sender) {
|
||||
final TextComponent tc = new TextComponent("§6× §8[§aUpdate§8]");
|
||||
tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/maintenance forceupdate"));
|
||||
tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText("§aClick here to update the plugin")));
|
||||
tc.addExtra(" §8< §7Or use the command §c/maintenance forceupdate");
|
||||
((BungeeSenderInfo) sender).sendMessage(tc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServersCompletion(final String s) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final Map.Entry<String, ServerInfo> entry : plugin.getProxy().getServers().entrySet()) {
|
||||
final String serverName = entry.getValue().getName();
|
||||
if (entry.getKey().toLowerCase().startsWith(s) && !plugin.getSettingsProxy().getMaintenanceServers().contains(serverName)) {
|
||||
if (entry.getKey().toLowerCase(Locale.ROOT).startsWith(s) && !plugin.getSettingsProxy().getMaintenanceServers().contains(serverName)) {
|
||||
list.add(serverName);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDumpMessage(final SenderInfo sender, final String url) {
|
||||
final BungeeSenderInfo bungeeSender = ((BungeeSenderInfo) sender);
|
||||
final TextComponent clickText = new TextComponent(plugin.getPrefix() + "§7Click here to copy the link");
|
||||
clickText.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, url));
|
||||
clickText.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText("§aClick here to copy the link")));
|
||||
bungeeSender.sendMessage(clickText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPlayersCompletion() {
|
||||
final List<String> list = new ArrayList<>();
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -27,7 +27,7 @@ public final class MaintenanceBungeeCommandBase extends Command implements TabEx
|
||||
private final MaintenanceCommand command;
|
||||
|
||||
public MaintenanceBungeeCommandBase(final MaintenanceCommand command) {
|
||||
super("maintenance", "maintenance.command", "maintenancebungee", "mt");
|
||||
super("maintenance", "maintenance.command", "mt");
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
|
||||
+22
-17
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -36,28 +36,33 @@ public final class ProxyPingListener implements Listener {
|
||||
|
||||
@EventHandler(priority = 80)
|
||||
public void proxyPing(final ProxyPingEvent event) {
|
||||
if (!settings.isMaintenance() || !settings.isEnablePingMessages()) return;
|
||||
if (!settings.isMaintenance()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ServerPing ping = event.getResponse();
|
||||
ServerPing.Players players = ping.getPlayers();
|
||||
if (players == null) {
|
||||
ping.setPlayers(players = new ServerPing.Players(0, 0, null));
|
||||
}
|
||||
|
||||
if (settings.hasCustomPlayerCountMessage()) {
|
||||
ping.setVersion(new ServerPing.Protocol(settings.getPlayerCountMessage()
|
||||
.replace("%ONLINE%", Integer.toString(players.getOnline()))
|
||||
.replace("%MAX%", Integer.toString(players.getMax())), 1));
|
||||
ping.setVersion(new ServerPing.Protocol(settings.getLegacyParsedPlayerCountMessage(), 1));
|
||||
}
|
||||
|
||||
ping.setDescriptionComponent(ComponentUtil.toBadComponents(settings.getRandomPingMessage()));
|
||||
|
||||
final String[] lines = settings.getPlayerCountHoverLines();
|
||||
final ServerPing.PlayerInfo[] samplePlayers = new ServerPing.PlayerInfo[lines.length];
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
samplePlayers[i] = new ServerPing.PlayerInfo(lines[i], "");
|
||||
if (settings.isEnablePingMessages()) {
|
||||
ping.setDescriptionComponent(ComponentUtil.toBadComponents(settings.getRandomPingMessage()));
|
||||
}
|
||||
|
||||
if (settings.hasCustomPlayerCountHoverMessage()) {
|
||||
ServerPing.Players players = ping.getPlayers();
|
||||
if (players == null) {
|
||||
players = new ServerPing.Players(0, 0, null);
|
||||
ping.setPlayers(players);
|
||||
}
|
||||
|
||||
final String[] lines = settings.getLegacyParsedPlayerCountHoverLines();
|
||||
final ServerPing.PlayerInfo[] samplePlayers = new ServerPing.PlayerInfo[lines.length];
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
samplePlayers[i] = new ServerPing.PlayerInfo(lines[i], "");
|
||||
}
|
||||
players.setSample(samplePlayers);
|
||||
}
|
||||
players.setSample(samplePlayers);
|
||||
|
||||
if (settings.hasCustomIcon() && plugin.getFavicon() != null) {
|
||||
ping.setFavicon(plugin.getFavicon());
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -24,7 +24,7 @@ import eu.kennytv.maintenance.bungee.util.BungeeServer;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.listener.ProxyJoinListenerBase;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ServerConnectResult;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
@@ -48,13 +48,13 @@ public final class ServerConnectListener extends ProxyJoinListenerBase implement
|
||||
final BungeeSenderInfo player = new BungeeSenderInfo(event.getPlayer());
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getLogger().info("Join permission check for " + event.getPlayer().getName()
|
||||
+ " - Permission: " + player.hasMaintenancePermission("bypass") + ", whitelist: " + settings.isWhitelisted(player.getUuid()));
|
||||
+ " - Permission: " + player.hasMaintenancePermission("bypass") + ", whitelist: " + settings.isWhitelisted(player.uuid()));
|
||||
}
|
||||
|
||||
if (shouldKick(player)) {
|
||||
final Server waitingServer = shouldConnectToWaitingServer(player);
|
||||
if (waitingServer != null) {
|
||||
event.setTarget(((BungeeServer) waitingServer).getServer());
|
||||
event.setTarget(((BungeeServer) waitingServer).server());
|
||||
player.send(settings.getMessage("sentToWaitingServer"));
|
||||
|
||||
if (plugin.isDebug()) {
|
||||
@@ -67,7 +67,7 @@ public final class ServerConnectListener extends ProxyJoinListenerBase implement
|
||||
|
||||
player.disconnect(settings.getKickMessage());
|
||||
if (settings.isJoinNotifications()) {
|
||||
broadcastJoinNotification(player.getName());
|
||||
broadcastJoinNotification(player.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public final class ServerConnectListener extends ProxyJoinListenerBase implement
|
||||
final BungeeSenderInfo player = new BungeeSenderInfo(proxiedPlayer);
|
||||
final ServerConnectResult connectResult = serverConnect(player, new BungeeServer(event.getTarget()), normalServerConnect);
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getLogger().info("Connectresult for " + player.getName() + " to " + event.getTarget().getName() + ": " + connectResult);
|
||||
plugin.getLogger().info("Connectresult for " + player.name() + " to " + event.getTarget().getName() + ": " + connectResult);
|
||||
}
|
||||
|
||||
if (connectResult.isCancelled()) {
|
||||
@@ -94,7 +94,7 @@ public final class ServerConnectListener extends ProxyJoinListenerBase implement
|
||||
player.disconnect(settings.getKickMessage());
|
||||
}
|
||||
} else if (connectResult.getTarget() != null) {
|
||||
event.setTarget(((BungeeServer) connectResult.getTarget()).getServer());
|
||||
event.setTarget(((BungeeServer) connectResult.getTarget()).server());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public final class ServerConnectListener extends ProxyJoinListenerBase implement
|
||||
|
||||
@Override
|
||||
protected void broadcastJoinNotification(final String name, final Server server) {
|
||||
sendJoinMessage(((BungeeServer) server).getServer().getPlayers(), name);
|
||||
sendJoinMessage(((BungeeServer) server).server().getPlayers(), name);
|
||||
}
|
||||
|
||||
private void sendJoinMessage(final Iterable<ProxiedPlayer> players, final String name) {
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
package eu.kennytv.maintenance.bungee.metrics;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
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 javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public final class MetricsLite {
|
||||
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
private static final int PLUGIN_ID = 742;
|
||||
private static final String URL = "https://bStats.org/submitData/bungeecord";
|
||||
private final Plugin plugin;
|
||||
private boolean enabled;
|
||||
private String serverUUID;
|
||||
private boolean logFailedRequests;
|
||||
private static boolean logSentData;
|
||||
private static boolean logResponseStatusText;
|
||||
private static final List<Object> knownMetricsInstances = new ArrayList<>();
|
||||
|
||||
public MetricsLite(final Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
try {
|
||||
loadConfig();
|
||||
} catch (final IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to load bStats config!", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Class<?> usedMetricsClass = getFirstBStatsClass();
|
||||
if (usedMetricsClass == null) {
|
||||
return;
|
||||
}
|
||||
if (usedMetricsClass == getClass()) {
|
||||
linkMetrics(this);
|
||||
startSubmitting();
|
||||
} else {
|
||||
try {
|
||||
usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null, this);
|
||||
} catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to link to first metrics class " + usedMetricsClass.getName() + "!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public static void linkMetrics(final Object metrics) {
|
||||
knownMetricsInstances.add(metrics);
|
||||
}
|
||||
|
||||
public JsonObject getPluginData() {
|
||||
final JsonObject data = new JsonObject();
|
||||
|
||||
//final String pluginName = plugin.getDescription().getName();
|
||||
final String pluginName = "MaintenanceBungee"; // I cut off the suffix, not sure how bStats would like the name change
|
||||
final String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.addProperty("pluginName", pluginName);
|
||||
data.addProperty("id", PLUGIN_ID);
|
||||
data.addProperty("pluginVersion", pluginVersion);
|
||||
|
||||
final JsonArray customCharts = new JsonArray();
|
||||
data.add("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
plugin.getProxy().getScheduler().schedule(plugin, this::submitData, 2, 30, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
private JsonObject getServerData() {
|
||||
final int playerAmount = Math.min(plugin.getProxy().getOnlineCount(), 500);
|
||||
final int onlineMode = plugin.getProxy().getConfig().isOnlineMode() ? 1 : 0;
|
||||
final String bungeecordVersion = plugin.getProxy().getVersion();
|
||||
final int managedServers = plugin.getProxy().getServers().size();
|
||||
|
||||
final String javaVersion = System.getProperty("java.version");
|
||||
final String osName = System.getProperty("os.name");
|
||||
final String osArch = System.getProperty("os.arch");
|
||||
final String osVersion = System.getProperty("os.version");
|
||||
final int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
final JsonObject data = new JsonObject();
|
||||
|
||||
data.addProperty("serverUUID", serverUUID);
|
||||
|
||||
data.addProperty("playerAmount", playerAmount);
|
||||
data.addProperty("managedServers", managedServers);
|
||||
data.addProperty("onlineMode", onlineMode);
|
||||
data.addProperty("bungeecordVersion", bungeecordVersion);
|
||||
|
||||
data.addProperty("javaVersion", javaVersion);
|
||||
data.addProperty("osName", osName);
|
||||
data.addProperty("osArch", osArch);
|
||||
data.addProperty("osVersion", osVersion);
|
||||
data.addProperty("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
final JsonObject data = getServerData();
|
||||
|
||||
final JsonArray pluginData = new JsonArray();
|
||||
for (final Object metrics : knownMetricsInstances) {
|
||||
try {
|
||||
final Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics);
|
||||
if (plugin instanceof JsonObject) {
|
||||
pluginData.add((JsonObject) plugin);
|
||||
}
|
||||
} catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
data.add("plugins", pluginData);
|
||||
|
||||
try {
|
||||
sendData(plugin, data);
|
||||
} catch (final Exception e) {
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadConfig() throws IOException {
|
||||
final File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
bStatsFolder.mkdirs();
|
||||
final File configFile = new File(bStatsFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
writeFile(configFile,
|
||||
"#bStats collects some data for plugin authors like how many servers are using their plugins.",
|
||||
"#To honor their work, you should not disable it.",
|
||||
"#This has nearly no effect on the server performance!",
|
||||
"#Check out https://bStats.org/ to learn more :)",
|
||||
"enabled: true",
|
||||
"serverUuid: \"" + UUID.randomUUID() + "\"",
|
||||
"logFailedRequests: false",
|
||||
"logSentData: false",
|
||||
"logResponseStatusText: false");
|
||||
}
|
||||
|
||||
final Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
|
||||
|
||||
enabled = configuration.getBoolean("enabled", true);
|
||||
serverUUID = configuration.getString("serverUuid");
|
||||
logFailedRequests = configuration.getBoolean("logFailedRequests", false);
|
||||
logSentData = configuration.getBoolean("logSentData", false);
|
||||
logResponseStatusText = configuration.getBoolean("logResponseStatusText", false);
|
||||
}
|
||||
|
||||
private Class<?> getFirstBStatsClass() {
|
||||
final Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats");
|
||||
configPath.toFile().mkdirs();
|
||||
final File tempFile = new File(configPath.toFile(), "temp.txt");
|
||||
|
||||
try {
|
||||
final String className = readFile(tempFile);
|
||||
if (className != null) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
writeFile(tempFile, getClass().getName());
|
||||
return getClass();
|
||||
} catch (final IOException e) {
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to get first bStats class!", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String readFile(final File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
try (final BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
|
||||
return bufferedReader.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFile(final File file, final String... lines) throws IOException {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
try (final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) {
|
||||
for (final String line : lines) {
|
||||
bufferedWriter.write(line);
|
||||
bufferedWriter.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendData(final Plugin plugin, final JsonObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data);
|
||||
}
|
||||
|
||||
final HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
final byte[] compressedData = compress(data.toString());
|
||||
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
connection.setDoOutput(true);
|
||||
try (final DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
|
||||
if (logResponseStatusText) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
|
||||
} else {
|
||||
connection.getInputStream().close();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (final GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -21,7 +21,7 @@ import eu.kennytv.maintenance.api.MaintenanceProvider;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.bungee.MaintenanceBungeePlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProxySenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.Connection;
|
||||
@@ -37,12 +37,12 @@ public final class BungeeSenderInfo implements ProxySenderInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUuid() {
|
||||
return sender instanceof ProxiedPlayer ? ((ProxiedPlayer) sender).getUniqueId() : null;
|
||||
public UUID uuid() {
|
||||
return sender instanceof ProxiedPlayer proxiedPlayer ? proxiedPlayer.getUniqueId() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
public String name() {
|
||||
return sender.getName();
|
||||
}
|
||||
|
||||
@@ -51,12 +51,6 @@ public final class BungeeSenderInfo implements ProxySenderInfo {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void sendMessage(final String message) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(final Component component) {
|
||||
((MaintenanceBungeePlugin) MaintenanceProvider.get()).audiences().sender(sender).sendMessage(component);
|
||||
@@ -73,13 +67,13 @@ public final class BungeeSenderInfo implements ProxySenderInfo {
|
||||
|
||||
@Override
|
||||
public boolean canAccess(final Server server) {
|
||||
return ((BungeeServer) server).getServer().canAccess(sender);
|
||||
return ((BungeeServer) server).server().canAccess(sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(final Component component) {
|
||||
if (sender instanceof Connection) {
|
||||
((Connection) sender).disconnect(ComponentUtil.toBadComponent(component));
|
||||
if (sender instanceof Connection connection) {
|
||||
connection.disconnect(ComponentUtil.toBadComponent(component));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -18,17 +18,12 @@
|
||||
package eu.kennytv.maintenance.bungee.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public final class BungeeServer implements Server {
|
||||
private final ServerInfo server;
|
||||
|
||||
public BungeeServer(final ServerInfo server) {
|
||||
this.server = server;
|
||||
}
|
||||
public record BungeeServer(ServerInfo server) implements Server {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -52,8 +47,4 @@ public final class BungeeServer implements Server {
|
||||
public boolean isRegisteredServer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ServerInfo getServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.util;
|
||||
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
@@ -4,4 +4,4 @@ main: eu.kennytv.maintenance.bungee.MaintenanceBungeeBase
|
||||
author: kennytv
|
||||
description: ${description}
|
||||
softDepends: [ServerListPlus, LuckPerms]
|
||||
website: https://www.spigotmc.org/resources/maintenance.40699/
|
||||
website: https://hangar.papermc.io/kennytv/Maintenance
|
||||
@@ -1,7 +1,13 @@
|
||||
dependencies {
|
||||
api(projects.maintenanceApiProxy)
|
||||
api(projects.maintenanceCore)
|
||||
implementation(libs.hikariCP)
|
||||
|
||||
// Try to reasonably minimize it...
|
||||
implementation(libs.jedis) {
|
||||
exclude("com.google.code.gson", "gson")
|
||||
exclude("org.slf4j", "slf4j-api")
|
||||
}
|
||||
|
||||
compileOnly(libs.luckperms)
|
||||
compileOnly(libs.guava)
|
||||
compileOnly(libs.gson)
|
||||
|
||||
+152
-32
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -28,16 +28,18 @@ import eu.kennytv.maintenance.core.proxy.runnable.SingleMaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.proxy.runnable.SingleMaintenanceScheduleRunnable;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProfileLookup;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.RateLimitedException;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -45,14 +47,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.annotations.Blocking;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author kennytv
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implements MaintenanceProxy {
|
||||
private final Map<String, Task> serverTasks = new HashMap<>();
|
||||
private static final Pattern USERNAME_PATTERN = Pattern.compile("^[A-Za-z0-9_.]{1,16}$");
|
||||
private final Map<String, MaintenanceRunnableBase> serverTasks = new HashMap<>();
|
||||
protected SettingsProxy settingsProxy;
|
||||
|
||||
protected MaintenanceProxyPlugin(final String version, final ServerType serverType) {
|
||||
@@ -62,37 +68,54 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
if (settingsProxy.getMySQL() != null) {
|
||||
settingsProxy.getMySQL().close();
|
||||
if (settingsProxy.redisHandler() != null) {
|
||||
settingsProxy.redisHandler().close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaintenance(final boolean maintenance) {
|
||||
if (settingsProxy.hasMySQL()) {
|
||||
settingsProxy.setMaintenanceToSQL(maintenance);
|
||||
}
|
||||
super.setMaintenance(maintenance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenance(final Server server) {
|
||||
return settingsProxy.isMaintenance(server.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMaintenanceToServer(final Server server, final boolean maintenance) {
|
||||
public boolean setMaintenanceToServer(final Server server, final boolean maintenance, @Nullable final String mode) {
|
||||
if (maintenance) {
|
||||
if (!settingsProxy.addMaintenanceServer(server.getName())) return false;
|
||||
} else {
|
||||
if (!settingsProxy.removeMaintenanceServer(server.getName())) return false;
|
||||
if (settingsProxy.addMaintenanceServer(server.getName(), mode)) {
|
||||
serverActions(server, true);
|
||||
return true;
|
||||
}
|
||||
return mode != null && settingsProxy.setMaintenanceServerMode(server.getName(), mode);
|
||||
}
|
||||
serverActions(server, maintenance);
|
||||
|
||||
if (!settingsProxy.removeMaintenanceServer(server.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
serverActions(server, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMaintenanceToServer(final Server server, final boolean maintenance) {
|
||||
return setMaintenanceToServer(server, maintenance, null);
|
||||
}
|
||||
|
||||
public boolean setMaintenanceModeToServer(final Server server, @Nullable final String mode) {
|
||||
if (!settingsProxy.isMaintenance(server.getName())) {
|
||||
return false;
|
||||
}
|
||||
return settingsProxy.setMaintenanceServerMode(server.getName(), mode);
|
||||
}
|
||||
|
||||
public @Nullable String activeMode(final Server server) {
|
||||
return settingsProxy.activeMode(server.getName());
|
||||
}
|
||||
|
||||
public void serverActions(final Server server, final boolean maintenance) {
|
||||
if (server == null) return;
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip to the even fire for dummy servers
|
||||
if (server.isRegisteredServer()) {
|
||||
@@ -112,6 +135,19 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
}
|
||||
|
||||
eventManager.callEvent(new ServerMaintenanceChangedEvent(server, maintenance));
|
||||
if (maintenance) {
|
||||
sendWebhookMessage("webhookSingleMaintenanceActivated", DiscordWebhook.EventType.MAINTENANCE_ENABLED, "%SERVER%", server.getName());
|
||||
} else {
|
||||
sendWebhookMessage("webhookSingleMaintenanceDeactivated", DiscordWebhook.EventType.MAINTENANCE_DISABLED, "%SERVER%", server.getName());
|
||||
}
|
||||
|
||||
for (final String command : (maintenance ? settingsProxy.getCommandsOnMaintenanceEnable(server) : settingsProxy.getCommandsOnMaintenanceDisable(server))) {
|
||||
try {
|
||||
executeConsoleCommand(command.replace("%SERVER%", server.getName()));
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,28 +155,45 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
return serverTasks.containsKey(server.getName());
|
||||
}
|
||||
|
||||
public boolean hasServerTasks() {
|
||||
return !serverTasks.isEmpty();
|
||||
}
|
||||
|
||||
public Map<String, MaintenanceRunnableBase> getServerTasks() {
|
||||
return serverTasks;
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase getServerTask(final String server) {
|
||||
return serverTasks.get(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getMaintenanceServers() {
|
||||
return Collections.unmodifiableSet(settingsProxy.getMaintenanceServers());
|
||||
}
|
||||
|
||||
public void cancelSingleTask(final Server server) {
|
||||
final Task task = serverTasks.remove(server.getName());
|
||||
final MaintenanceRunnableBase task = serverTasks.remove(server.getName());
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
task.getTask().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase startSingleMaintenanceRunnable(final Server server, final long duration, final TimeUnit unit, final boolean enable) {
|
||||
final MaintenanceRunnableBase runnable = new SingleMaintenanceRunnable(this, settingsProxy, (int) unit.toSeconds(duration), enable, server);
|
||||
serverTasks.put(server.getName(), runnable.getTask());
|
||||
public MaintenanceRunnableBase startSingleMaintenanceRunnable(final Server server, final Duration duration, final boolean enable, @Nullable final String mode) {
|
||||
final MaintenanceRunnableBase runnable = new SingleMaintenanceRunnable(this, settingsProxy, (int) duration.getSeconds(), enable, server, mode);
|
||||
serverTasks.put(server.getName(), runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase scheduleSingleMaintenanceRunnable(final Server server, final long duration, final long maintenanceDuration, final TimeUnit unit) {
|
||||
public MaintenanceRunnableBase startSingleMaintenanceRunnable(final Server server, final Duration duration, final boolean enable) {
|
||||
return startSingleMaintenanceRunnable(server, duration, enable, null);
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase scheduleSingleMaintenanceRunnable(final Server server, final Duration enableIn,
|
||||
final Duration maintenanceDuration, @Nullable final String mode) {
|
||||
final MaintenanceRunnableBase runnable = new SingleMaintenanceScheduleRunnable(this, settingsProxy,
|
||||
(int) unit.toSeconds(duration), (int) unit.toSeconds(maintenanceDuration), server);
|
||||
serverTasks.put(server.getName(), runnable.getTask());
|
||||
(int) enableIn.getSeconds(), (int) maintenanceDuration.getSeconds(), server, mode);
|
||||
serverTasks.put(server.getName(), runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
@@ -175,9 +228,68 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
kickPlayersFromProxy();
|
||||
}
|
||||
|
||||
@Blocking
|
||||
@Nullable
|
||||
protected ProfileLookup doUUIDLookup(final String name) throws IOException {
|
||||
final URL url = new URL("https://api.ashcon.app/mojang/v2/user/" + name);
|
||||
ProfileLookup profileLookup = null;
|
||||
if (USERNAME_PATTERN.matcher(name).matches()) {
|
||||
try {
|
||||
profileLookup = doUUIDLookupMojangAPI(name);
|
||||
} catch (RateLimitedException e) {
|
||||
// Use fallback API if rate limit is reached
|
||||
profileLookup = doUUIDLookupAshconAPI(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (profileLookup == null && settingsProxy.isFallbackToOfflineUUID()) {
|
||||
// Use offline uuid
|
||||
return new ProfileLookup(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)), name);
|
||||
}
|
||||
|
||||
return profileLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Official Mojang API
|
||||
*/
|
||||
@Nullable
|
||||
private ProfileLookup doUUIDLookupMojangAPI(final String name) throws IOException {
|
||||
final URL url = URI.create("https://api.mojang.com/users/profiles/minecraft/" + name).toURL();
|
||||
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
int status = connection.getResponseCode();
|
||||
if (status == 429) {
|
||||
throw new RateLimitedException();
|
||||
}
|
||||
if (status == 404) {
|
||||
// Return null if profile not found
|
||||
return null;
|
||||
}
|
||||
|
||||
try (final InputStream in = connection.getInputStream()) {
|
||||
final String output = CharStreams.toString(new InputStreamReader(in));
|
||||
final JsonObject json = GSON.fromJson(output, JsonObject.class);
|
||||
|
||||
final UUID uuid = fromStringUUIDWithoutDashes(json.getAsJsonPrimitive("id").getAsString());
|
||||
final String username = json.getAsJsonPrimitive("name").getAsString();
|
||||
return new ProfileLookup(uuid, username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback API (Ashcon API)
|
||||
*/
|
||||
@Nullable
|
||||
private ProfileLookup doUUIDLookupAshconAPI(final String name) throws IOException {
|
||||
final URL url = URI.create("https://api.ashcon.app/mojang/v2/user/" + name).toURL();
|
||||
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
if (connection.getResponseCode() == 403) {
|
||||
// Return null if profile not found
|
||||
return null;
|
||||
}
|
||||
|
||||
try (final InputStream in = connection.getInputStream()) {
|
||||
final String output = CharStreams.toString(new InputStreamReader(in));
|
||||
final JsonObject json = GSON.fromJson(output, JsonObject.class);
|
||||
@@ -188,6 +300,14 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
}
|
||||
}
|
||||
|
||||
private UUID fromStringUUIDWithoutDashes(String undashedUUID) {
|
||||
return UUID.fromString(
|
||||
undashedUUID.substring(0, 8) + "-" + undashedUUID.substring(8, 12) + "-" +
|
||||
undashedUUID.substring(12, 16) + "-" + undashedUUID.substring(16, 20) + "-" +
|
||||
undashedUUID.substring(20, 32)
|
||||
);
|
||||
}
|
||||
|
||||
public SettingsProxy getSettingsProxy() {
|
||||
return settingsProxy;
|
||||
}
|
||||
@@ -200,4 +320,4 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
protected abstract void kickPlayersTo(Server server);
|
||||
|
||||
protected abstract void kickPlayersFromProxy();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,239 +17,259 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.proxy.mysql.MySQL;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import eu.kennytv.maintenance.core.proxy.redis.RedisHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SettingsProxy extends Settings {
|
||||
private final MaintenanceProxyPlugin proxyPlugin;
|
||||
private Set<String> maintenanceServers;
|
||||
|
||||
private Map<String, String> maintenanceServers;
|
||||
private List<String> fallbackServers;
|
||||
private String waitingServer;
|
||||
private boolean fallbackToOfflineUUID;
|
||||
|
||||
private String mySQLTable;
|
||||
private String serverTable;
|
||||
private String maintenanceQuery;
|
||||
private String serverQuery;
|
||||
private MySQL mySQL;
|
||||
private Map<String, List<String>> commandsOnMaintenanceEnable;
|
||||
private Map<String, List<String>> commandsOnMaintenanceDisable;
|
||||
|
||||
private long millisecondsToCheck;
|
||||
private long lastMySQLCheck;
|
||||
private long lastServerCheck;
|
||||
private RedisHandler redisHandler;
|
||||
|
||||
public SettingsProxy(final MaintenanceProxyPlugin plugin) {
|
||||
super(plugin);
|
||||
this.proxyPlugin = plugin;
|
||||
}
|
||||
|
||||
private void setupMySQL() {
|
||||
plugin.getLogger().info("Trying to open database connection... (also, you can simply ignore the SLF4J soft-warning if it shows up)");
|
||||
final ConfigSection section = config.getSection("mysql");
|
||||
if (section == null) {
|
||||
plugin.getLogger().warning("Section missing: mysql");
|
||||
return;
|
||||
}
|
||||
|
||||
mySQL = new MySQL(plugin.getLogger(),
|
||||
section.getString("host"),
|
||||
section.getInt("port"),
|
||||
section.getString("username"),
|
||||
section.getString("password"),
|
||||
section.getString("database"),
|
||||
section.getBoolean("use-ssl", true));
|
||||
|
||||
// Varchar as the value regarding the possibility of saving stuff like the motd as well in future updates
|
||||
mySQLTable = section.getString("table", "maintenance_settings");
|
||||
serverTable = section.getString("servertable", "maintenance_servers");
|
||||
mySQL.executeUpdate("CREATE TABLE IF NOT EXISTS " + mySQLTable + " (setting VARCHAR(16) PRIMARY KEY, value VARCHAR(255))");
|
||||
mySQL.executeUpdate("CREATE TABLE IF NOT EXISTS " + serverTable + " (server VARCHAR(64) PRIMARY KEY)");
|
||||
maintenanceQuery = "SELECT * FROM " + mySQLTable + " WHERE setting = ?";
|
||||
serverQuery = "SELECT * FROM " + serverTable;
|
||||
plugin.getLogger().info("Done!");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadExtraSettings() {
|
||||
// Open database connection if enabled and not already done
|
||||
if (!hasMySQL() && config.getBoolean("mysql.use-mysql")) {
|
||||
try {
|
||||
setupMySQL();
|
||||
} catch (final Exception e) {
|
||||
mySQL = null;
|
||||
plugin.getLogger().warning("Error while trying do open database connection!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
// Open Redis connection if enabled and not already done
|
||||
final ConfigSection redisSection = config.getSection("redis");
|
||||
if (!hasRedis() && redisSection.getBoolean("enabled")) {
|
||||
connectToRedis(redisSection.getString("uri"));
|
||||
}
|
||||
if (hasRedis()) {
|
||||
maintenance = redisHandler.loadMaintenanceStatus();
|
||||
activeMode = normalizeMode(redisHandler.loadGlobalMode());
|
||||
maintenanceServers = redisHandler.loadMaintenanceServers();
|
||||
}
|
||||
|
||||
final Object fallback = config.getObject("fallback");
|
||||
fallbackServers = fallback instanceof String ? Collections.singletonList((String) fallback) : config.getStringList("fallback");
|
||||
fallbackServers = fallback instanceof String s ? Collections.singletonList(s) : config.getStringList("fallback", Collections.emptyList());
|
||||
|
||||
waitingServer = config.getString("waiting-server", "");
|
||||
if (waitingServer.isEmpty() || waitingServer.equalsIgnoreCase("none")) {
|
||||
waitingServer = null;
|
||||
}
|
||||
fallbackToOfflineUUID = config.getBoolean("fallback-to-offline-uuid", false);
|
||||
|
||||
if (hasMySQL()) {
|
||||
maintenance = loadMaintenance();
|
||||
maintenanceServers = loadMaintenanceServersFromSQL();
|
||||
commandsOnMaintenanceEnable = new HashMap<>();
|
||||
final ConfigSection enableCommandsSection = config.getSection("commands-on-single-maintenance-enable");
|
||||
for (final String key : enableCommandsSection.getKeys()) {
|
||||
commandsOnMaintenanceEnable.put(key.toLowerCase(Locale.ROOT), enableCommandsSection.getStringList(key));
|
||||
}
|
||||
|
||||
final long configValue = config.getInt("mysql.update-interval");
|
||||
// Even if set to 0, only check every 500 millis
|
||||
millisecondsToCheck = configValue > 0 ? configValue * 1000 : 500;
|
||||
lastMySQLCheck = System.currentTimeMillis();
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
commandsOnMaintenanceDisable = new HashMap<>();
|
||||
final ConfigSection disableCommandsSection = config.getSection("commands-on-single-maintenance-disable");
|
||||
for (final String key : disableCommandsSection.getKeys()) {
|
||||
commandsOnMaintenanceDisable.put(key.toLowerCase(Locale.ROOT), disableCommandsSection.getStringList(key));
|
||||
}
|
||||
|
||||
if (!hasRedis()) {
|
||||
final ConfigSection maintenanceServersSection = config.getSection("proxied-maintenance-servers");
|
||||
maintenanceServers = new LinkedHashMap<>();
|
||||
for (final Map.Entry<String, ?> entry : maintenanceServersSection.getValues().entrySet()) {
|
||||
if (entry.getKey() != null) {
|
||||
maintenanceServers.put(entry.getKey(), normalizeServerMode(entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void connectToRedis(final String redisUri) {
|
||||
redisHandler = new RedisHandler((MaintenanceProxyPlugin) plugin, this);
|
||||
try {
|
||||
redisHandler.setup(redisUri);
|
||||
} catch (final Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error while trying to open Redis connection!", e);
|
||||
e.printStackTrace();
|
||||
redisHandler.close();
|
||||
redisHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMaintenance(final String serverName) {
|
||||
return maintenanceServers.containsKey(serverName);
|
||||
}
|
||||
|
||||
public @Nullable String activeMode(final String serverName) {
|
||||
final String mode = maintenanceServers.get(serverName);
|
||||
return mode == null || mode.equals("default") ? null : mode;
|
||||
}
|
||||
|
||||
public Component getServerKickMessage(final String server) {
|
||||
String message = getLanguageStringOrNull("singleMaintenanceKicks." + server, "%SERVER%", server);
|
||||
if (message == null) {
|
||||
message = getLanguageString("singleMaintenanceKick", "%SERVER%", server);
|
||||
}
|
||||
return parse(plugin.replacePingVariables(message, activeMode(server)));
|
||||
}
|
||||
|
||||
public Component getFullServerKickMessage(final String server) {
|
||||
String message = getLanguageStringOrNull("singleMaintenanceKicksComplete." + server, "%SERVER%", server);
|
||||
if (message == null) {
|
||||
message = getLanguageString("singleMaintenanceKickComplete", "%SERVER%", server);
|
||||
}
|
||||
return parse(plugin.replacePingVariables(message, activeMode(server)));
|
||||
}
|
||||
|
||||
public boolean hasRedis() {
|
||||
return redisHandler != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadWhitelistedPlayers() {
|
||||
if (hasRedis()) {
|
||||
redisHandler.loadPlayers(getWhitelistedPlayers());
|
||||
} else {
|
||||
final List<String> list = config.getStringList("proxied-maintenance-servers");
|
||||
maintenanceServers = list == null ? new HashSet<>() : new HashSet<>(list);
|
||||
super.loadWhitelistedPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenance() {
|
||||
if (hasMySQL() && System.currentTimeMillis() - lastMySQLCheck > millisecondsToCheck) {
|
||||
final boolean databaseValue = loadMaintenance();
|
||||
if (databaseValue != maintenance) {
|
||||
maintenance = databaseValue;
|
||||
plugin.serverActions(maintenance);
|
||||
public void setMaintenance(final boolean maintenance, @Nullable final String mode) {
|
||||
super.setMaintenance(maintenance, mode);
|
||||
if (hasRedis()) {
|
||||
redisHandler.set(maintenance, mode != null ? mode : "default");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveMode(@Nullable final String activeMode) {
|
||||
super.setActiveMode(activeMode);
|
||||
if (hasRedis() && isMaintenance()) {
|
||||
redisHandler.set(true, activeMode != null ? activeMode : "default");
|
||||
}
|
||||
}
|
||||
|
||||
public void setActiveModeDirect(@Nullable final String activeMode) {
|
||||
super.setActiveMode(activeMode);
|
||||
}
|
||||
|
||||
public void setMaintenanceDirect(final boolean maintenance, @Nullable final String mode) {
|
||||
super.setMaintenance(maintenance, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addWhitelistedPlayer(final UUID uuid, final String name) {
|
||||
if (super.addWhitelistedPlayer(uuid, name)) {
|
||||
if (hasRedis()) {
|
||||
redisHandler.addPlayer(uuid, name);
|
||||
}
|
||||
lastMySQLCheck = System.currentTimeMillis();
|
||||
return true;
|
||||
}
|
||||
return maintenance;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isMaintenance(final String serverName) {
|
||||
if (hasMySQL() && System.currentTimeMillis() - lastServerCheck > millisecondsToCheck) {
|
||||
final Set<String> databaseValue = loadMaintenanceServersFromSQL();
|
||||
if (!maintenanceServers.equals(databaseValue)) {
|
||||
// Enable maintenance on yet unlisted servers
|
||||
for (final String s : databaseValue) {
|
||||
if (!maintenanceServers.contains(s)) {
|
||||
proxyPlugin.serverActions(proxyPlugin.getServer(s), true);
|
||||
}
|
||||
}
|
||||
// Disable maintenance on now unlisted servers
|
||||
for (final String s : maintenanceServers) {
|
||||
if (!databaseValue.contains(s)) {
|
||||
proxyPlugin.serverActions(proxyPlugin.getServer(s), false);
|
||||
}
|
||||
}
|
||||
maintenanceServers = databaseValue;
|
||||
public boolean addWhitelistedPlayerDirect(final UUID uuid, final String name) {
|
||||
return super.addWhitelistedPlayer(uuid, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeWhitelistedPlayer(final UUID uuid) {
|
||||
if (super.removeWhitelistedPlayer(uuid)) {
|
||||
if (hasRedis()) {
|
||||
redisHandler.removePlayer(uuid);
|
||||
}
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
return true;
|
||||
}
|
||||
return maintenanceServers.contains(serverName);
|
||||
return false;
|
||||
}
|
||||
|
||||
public Component getServerKickMessage(final String server) {
|
||||
Component message = getMessageOrNull("singleMaintenanceKicks." + server, "%SERVER%", server);
|
||||
if (message == null) {
|
||||
message = getMessage("singleMaintenanceKick", "%SERVER%", server);
|
||||
public boolean removeWhitelistedPlayerDirect(final UUID uuid) {
|
||||
return super.removeWhitelistedPlayer(uuid);
|
||||
}
|
||||
|
||||
boolean addMaintenanceServer(final String server, @Nullable final String mode) {
|
||||
final String normalizedMode = normalizeServerMode(mode);
|
||||
if (hasRedis()) {
|
||||
if (redisHandler.addServer(server, normalizedMode)) {
|
||||
maintenanceServers.put(server, normalizedMode);
|
||||
return true;
|
||||
}
|
||||
} else if (maintenanceServers.putIfAbsent(server, normalizedMode) == null) {
|
||||
saveServersToConfig();
|
||||
return true;
|
||||
}
|
||||
return plugin.replacePingVariables(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Full = being kicked from the proxy, not just a proxied server
|
||||
public Component getFullServerKickMessage(final String server) {
|
||||
Component message = getMessageOrNull("singleMaintenanceKicksComplete." + server, "%SERVER%", server);
|
||||
if (message == null) {
|
||||
message = getMessage("singleMaintenanceKickComplete", "%SERVER%", server);
|
||||
boolean setMaintenanceServerMode(final String server, @Nullable final String mode) {
|
||||
final String normalizedMode = normalizeServerMode(mode);
|
||||
final String oldMode = maintenanceServers.get(server);
|
||||
if (oldMode == null || oldMode.equals(normalizedMode)) {
|
||||
return false;
|
||||
}
|
||||
return plugin.replacePingVariables(message);
|
||||
}
|
||||
|
||||
public boolean hasMySQL() {
|
||||
return mySQL != null;
|
||||
}
|
||||
|
||||
void setMaintenanceToSQL(final boolean maintenance) {
|
||||
plugin.async(() -> {
|
||||
final String s = String.valueOf(maintenance);
|
||||
mySQL.executeUpdate("INSERT INTO " + mySQLTable + " (setting, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = ?", "maintenance", s, s);
|
||||
lastMySQLCheck = System.currentTimeMillis();
|
||||
});
|
||||
}
|
||||
|
||||
boolean addMaintenanceServer(final String server) {
|
||||
if (hasMySQL()) {
|
||||
maintenanceServers = loadMaintenanceServersFromSQL();
|
||||
if (!maintenanceServers.add(server)) return false;
|
||||
plugin.async(() -> mySQL.executeUpdate("INSERT INTO " + serverTable + " (server) VALUES (?)", server));
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
maintenanceServers.put(server, normalizedMode);
|
||||
if (hasRedis()) {
|
||||
redisHandler.setServerMode(server, normalizedMode);
|
||||
} else {
|
||||
if (!maintenanceServers.add(server)) return false;
|
||||
saveServersToConfig();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean removeMaintenanceServer(final String server) {
|
||||
if (hasMySQL()) {
|
||||
maintenanceServers = loadMaintenanceServersFromSQL();
|
||||
if (!maintenanceServers.remove(server)) return false;
|
||||
|
||||
plugin.async(() -> mySQL.executeUpdate("DELETE FROM " + serverTable + " WHERE server = ?", server));
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
} else {
|
||||
if (!maintenanceServers.remove(server)) return false;
|
||||
|
||||
if (hasRedis()) {
|
||||
if (redisHandler.removeServer(server)) {
|
||||
maintenanceServers.remove(server);
|
||||
return true;
|
||||
}
|
||||
} else if (maintenanceServers.remove(server) != null) {
|
||||
saveServersToConfig();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> loadMaintenanceServersFromSQL() {
|
||||
final Set<String> maintenanceServers = new HashSet<>();
|
||||
mySQL.executeQuery(serverQuery, rs -> {
|
||||
try {
|
||||
while (rs.next()) {
|
||||
maintenanceServers.add(rs.getString("server"));
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
plugin.getLogger().warning("An error occured while trying to get the list of single servers with maintenance!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return maintenanceServers;
|
||||
public boolean addMaintenanceServerDirect(final String server, @Nullable final String mode) {
|
||||
return maintenanceServers.put(server, normalizeServerMode(mode)) == null;
|
||||
}
|
||||
|
||||
private boolean loadMaintenance() {
|
||||
final boolean[] databaseValue = {false};
|
||||
mySQL.executeQuery(maintenanceQuery, rs -> {
|
||||
try {
|
||||
if (rs.next()) {
|
||||
databaseValue[0] = Boolean.parseBoolean(rs.getString("value"));
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
plugin.getLogger().warning("An error occured while trying to get the maintenance value from the database!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, "maintenance");
|
||||
return databaseValue[0];
|
||||
public boolean removeMaintenanceServerDirect(final String server) {
|
||||
return maintenanceServers.remove(server) != null;
|
||||
}
|
||||
|
||||
public void setMaintenanceServerModeDirect(final String server, @Nullable final String mode) {
|
||||
if (maintenanceServers.containsKey(server)) {
|
||||
maintenanceServers.put(server, normalizeServerMode(mode));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveServersToConfig() {
|
||||
config.set("proxied-maintenance-servers", new ArrayList<>(maintenanceServers));
|
||||
config.set("proxied-maintenance-servers", new LinkedHashMap<>(maintenanceServers));
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
public Set<String> getMaintenanceServers() {
|
||||
return maintenanceServers;
|
||||
return maintenanceServers.keySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Server getFallbackServer() {
|
||||
for (final String fallbackServer : fallbackServers) {
|
||||
final Server server = proxyPlugin.getServer(fallbackServer);
|
||||
final Server server = ((MaintenanceProxy) plugin).getServer(fallbackServer);
|
||||
if (server != null && !isMaintenance(server.getName())) {
|
||||
return server;
|
||||
}
|
||||
@@ -262,8 +282,37 @@ public final class SettingsProxy extends Settings {
|
||||
return waitingServer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MySQL getMySQL() {
|
||||
return mySQL;
|
||||
public boolean isFallbackToOfflineUUID() {
|
||||
return fallbackToOfflineUUID;
|
||||
}
|
||||
|
||||
public List<String> getCommandsOnMaintenanceEnable(final Server server) {
|
||||
final List<String> enableCommands = new ArrayList<>(commandsOnMaintenanceEnable.getOrDefault("all", Collections.emptyList()));
|
||||
final List<String> serverEnableCommands = commandsOnMaintenanceEnable.get(server.getName().toLowerCase(Locale.ROOT));
|
||||
if (serverEnableCommands != null) {
|
||||
enableCommands.addAll(serverEnableCommands);
|
||||
}
|
||||
return enableCommands;
|
||||
}
|
||||
|
||||
public List<String> getCommandsOnMaintenanceDisable(final Server server) {
|
||||
final List<String> disableCommands = new ArrayList<>(commandsOnMaintenanceDisable.getOrDefault("all", Collections.emptyList()));
|
||||
final List<String> serverDisableCommands = commandsOnMaintenanceDisable.get(server.getName().toLowerCase(Locale.ROOT));
|
||||
if (serverDisableCommands != null) {
|
||||
disableCommands.addAll(serverDisableCommands);
|
||||
}
|
||||
return disableCommands;
|
||||
}
|
||||
|
||||
public @Nullable RedisHandler redisHandler() {
|
||||
return redisHandler;
|
||||
}
|
||||
|
||||
private static String normalizeServerMode(@Nullable final Object mode) {
|
||||
if (mode == null) {
|
||||
return "default";
|
||||
}
|
||||
final String value = String.valueOf(mode).toLowerCase(Locale.ROOT);
|
||||
return value.isBlank() ? "default" : value;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -26,6 +26,7 @@ import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class MaintenanceProxyCommand extends MaintenanceCommand {
|
||||
private final MaintenanceProxyPlugin plugin;
|
||||
@@ -40,7 +41,7 @@ public abstract class MaintenanceProxyCommand extends MaintenanceCommand {
|
||||
@Override
|
||||
protected void addToggleAndTimerCommands() {
|
||||
add(new SingleToggleCommand(plugin), "on", "off");
|
||||
add(new StatusCommand(plugin), "status");
|
||||
add(new ProxyStatusCommand(plugin), "status");
|
||||
|
||||
add(new SingleStarttimerCommand(plugin), "starttimer", "start");
|
||||
add(new SingleEndtimerCommand(plugin), "endtimer", "end");
|
||||
@@ -52,7 +53,7 @@ public abstract class MaintenanceProxyCommand extends MaintenanceCommand {
|
||||
public List<String> getMaintenanceServersCompletion(final String s) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final String server : settingsBungee.getMaintenanceServers()) {
|
||||
if (server.toLowerCase().startsWith(s)) {
|
||||
if (server.toLowerCase(Locale.ROOT).startsWith(s)) {
|
||||
list.add(server);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,6 +20,8 @@ package eu.kennytv.maintenance.core.proxy.command;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ProxyCommandInfo extends CommandInfo {
|
||||
protected final MaintenanceProxyPlugin plugin;
|
||||
@@ -33,4 +35,10 @@ public abstract class ProxyCommandInfo extends CommandInfo {
|
||||
protected SettingsProxy getSettings() {
|
||||
return plugin.getSettingsProxy();
|
||||
}
|
||||
|
||||
protected List<String> getModesCompletion() {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getPingMessages().getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -22,8 +22,10 @@ import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class AbortSingleTimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -60,13 +62,24 @@ public final class AbortSingleTimerCommand extends ProxyCommandInfo {
|
||||
}
|
||||
|
||||
plugin.cancelSingleTask(server);
|
||||
sender.send(getMessage("singleTimerCancelled"));
|
||||
sender.send(getMessage("singleTimerCancelled", "%SERVER%", server.getName()));
|
||||
} else
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getServersCompletion(args[1]) : Collections.emptyList();
|
||||
if (args.length != 2 || !sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final String prefix = args[1].toLowerCase(Locale.ROOT);
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final String server : plugin.getServerTasks().keySet()) {
|
||||
if (server.toLowerCase(Locale.ROOT).startsWith(prefix)) {
|
||||
list.add(server);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.Map;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class ProxyStatusCommand extends ProxyCommandInfo {
|
||||
|
||||
public ProxyStatusCommand(final MaintenanceProxyPlugin plugin) {
|
||||
super(plugin, "singleserver.status");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
final MaintenanceRunnable task = plugin.getCurrentTask();
|
||||
if (task != null) {
|
||||
final String key = task.shouldEnable() ? "maintenanceStatusOffWithStartTimer" : "maintenanceStatusOnWithEndTimer";
|
||||
sender.send(getMessage(key, "%TIME%", task.getTime()));
|
||||
} else {
|
||||
sender.send(getMessage(plugin.isMaintenance() ? "maintenanceStatusOn" : "maintenanceStatusOff"));
|
||||
}
|
||||
|
||||
if (getSettings().getMaintenanceServers().isEmpty() && !plugin.hasServerTasks()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEmpty"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage("singleServerMaintenanceList"));
|
||||
// Enabled servers first
|
||||
for (final String server : getSettings().getMaintenanceServers()) {
|
||||
final MaintenanceRunnableBase serverTask = plugin.getServerTask(server);
|
||||
if (serverTask != null) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEntryWithEndTimer", "%SERVER%", server, "%TIME%", serverTask.getTime()));
|
||||
continue;
|
||||
}
|
||||
|
||||
sender.send(getMessage("singleServerMaintenanceListEntry", "%SERVER%", server));
|
||||
}
|
||||
|
||||
// Then enabling tasks
|
||||
for (final Map.Entry<String, MaintenanceRunnableBase> entry : plugin.getServerTasks().entrySet()) {
|
||||
final MaintenanceRunnableBase serverTask = entry.getValue();
|
||||
if (serverTask.shouldEnable()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEntryWithStartTimer", "%SERVER%", entry.getKey(), "%TIME%", serverTask.getTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getHelpMessage() {
|
||||
return getMessage("helpStatus");
|
||||
}
|
||||
}
|
||||
+74
-30
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -21,11 +21,14 @@ import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Locale;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class SingleEndtimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -41,41 +44,82 @@ public final class SingleEndtimerCommand extends ProxyCommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 2) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[1])) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (!plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(Integer.parseInt(args[1]), TimeUnit.MINUTES, false);
|
||||
sender.send(getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
endGlobal(sender, args[1]);
|
||||
} else if (args.length == 3) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[2], false)) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
endGlobal(sender, args[2]);
|
||||
} else {
|
||||
endSingle(sender, args[1], args[2]);
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, args[1]);
|
||||
if (server == null) return;
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, Integer.parseInt(args[2]), TimeUnit.MINUTES, false);
|
||||
sender.send(getMessage("endtimerStarted", "%TIME%", runnable.getTime()));
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void endGlobal(final SenderInfo sender, final String durationArg) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (!plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, false);
|
||||
|
||||
final Component message = getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookEndtimerStarted", DiscordWebhook.EventType.ENDTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
private void endSingle(final SenderInfo sender, final String serverArg, final String durationArg) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, serverArg);
|
||||
if (server == null) return;
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, duration, false);
|
||||
final Component message = getMessage(
|
||||
"singleEndtimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName()
|
||||
);
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookSingleEndtimerStarted", DiscordWebhook.EventType.ENDTIMER_STARTED,
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase()) : Collections.emptyList();
|
||||
if (args.length != 2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("timer") && plugin.isMaintenance()) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
suggestions.addAll(plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
+82
-33
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -22,10 +22,12 @@ import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SingleScheduleTimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -41,43 +43,90 @@ public final class SingleScheduleTimerCommand extends ProxyCommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 3) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[1])
|
||||
|| plugin.getCommandManager().checkTimerArgs(sender, args[2], false)) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
scheduleGlobal(sender, null, args[1], args[2]);
|
||||
} else if (args.length == 4 || args.length == 5) {
|
||||
final String mode = args.length == 5 ? args[2] : null;
|
||||
final String enableInArg = args.length == 5 ? args[3] : args[2];
|
||||
final String durationArg = args.length == 5 ? args[4] : args[3];
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
scheduleGlobal(sender, mode, enableInArg, durationArg);
|
||||
} else {
|
||||
scheduleSingle(sender, args[1], mode, enableInArg, durationArg);
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.scheduleMaintenanceRunnable(Integer.parseInt(args[1]), Integer.parseInt(args[2]), TimeUnit.MINUTES);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
} else if (args.length == 4) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[2], false)
|
||||
|| plugin.getCommandManager().checkTimerArgs(sender, args[3], false)) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, args[1]);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.scheduleSingleMaintenanceRunnable(server, Integer.parseInt(args[2]), Integer.parseInt(args[3]), TimeUnit.MINUTES);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", runnable.getTime()));
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleGlobal(final SenderInfo sender, @Nullable final String mode, final String enableInArg, final String durationArg) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, enableInArg);
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (enableIn == null || duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.scheduleMaintenanceRunnable(enableIn, duration, mode);
|
||||
sender.send(getMessage(
|
||||
"scheduletimerStarted",
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) duration.getSeconds())
|
||||
));
|
||||
}
|
||||
|
||||
private void scheduleSingle(final SenderInfo sender, final String serverArg, @Nullable final String mode,
|
||||
final String enableInArg, final String durationArg) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, enableInArg, false);
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (enableIn == null || duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, serverArg);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.scheduleSingleMaintenanceRunnable(server, enableIn, duration, mode);
|
||||
sender.send(getMessage(
|
||||
"singleScheduletimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) duration.getSeconds()),
|
||||
"%SERVER%", server.getName()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getServersCompletion(args[1].toLowerCase()) : Collections.emptyList();
|
||||
if (args.length == 2) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("timer")) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
suggestions.addAll(plugin.getCommandManager().getServersCompletion(args[1].toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
if (args.length == 3) {
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
return sender.hasMaintenancePermission("timer") ? getModesCompletion() : Collections.emptyList();
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer") && plugin.getServer(args[1]) != null) {
|
||||
return getModesCompletion();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+84
-31
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -21,11 +21,15 @@ import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Locale;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SingleStarttimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -41,41 +45,90 @@ public final class SingleStarttimerCommand extends ProxyCommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 2) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[1])) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
startGlobal(sender, null, args[1]);
|
||||
} else if (args.length == 3 || args.length == 4) {
|
||||
final String mode = args.length == 4 ? args[2] : null;
|
||||
final String durationArg = args.length == 4 ? args[3] : args[2];
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
startGlobal(sender, mode, durationArg);
|
||||
} else {
|
||||
startSingle(sender, args[1], mode, durationArg);
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(Integer.parseInt(args[1]), TimeUnit.MINUTES, true);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
} else if (args.length == 3) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[2], false)) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, args[1]);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, Integer.parseInt(args[2]), TimeUnit.MINUTES, true);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", runnable.getTime()));
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void startGlobal(final SenderInfo sender, @Nullable final String mode, final String durationArg) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, true, mode);
|
||||
final Component message = getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookStarttimerStarted", DiscordWebhook.EventType.STARTTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
private void startSingle(final SenderInfo sender, final String serverArg, @Nullable final String mode, final String durationArg) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, serverArg);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, duration, true, mode);
|
||||
final Component message = getMessage(
|
||||
"singleStarttimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName()
|
||||
);
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookSingleStarttimerStarted", DiscordWebhook.EventType.STARTTIMER_STARTED,
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getServersCompletion(args[1].toLowerCase()) : Collections.emptyList();
|
||||
if (args.length == 2) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("timer")) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
suggestions.addAll(plugin.getCommandManager().getServersCompletion(args[1].toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
if (args.length == 3) {
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
return sender.hasMaintenancePermission("timer") ? getModesCompletion() : Collections.emptyList();
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer") && plugin.getServer(args[1]) != null) {
|
||||
return getModesCompletion();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+107
-33
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,14 +17,15 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.DummyServer;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.server.DummyServer;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class SingleToggleCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -39,48 +40,121 @@ public final class SingleToggleCommand extends ProxyCommandInfo {
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 0 || args.length > 3) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
if (args.length == 1) {
|
||||
if (checkPermission(sender, "toggle")) return;
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
if (maintenance == plugin.isMaintenance()) {
|
||||
sender.send(getMessage(maintenance ? "alreadyEnabled" : "alreadyDisabled"));
|
||||
toggleGlobal(sender, maintenance, null);
|
||||
return;
|
||||
}
|
||||
|
||||
final String server = args[1];
|
||||
final String mode = args.length == 3 ? args[2] : null;
|
||||
if (server.equalsIgnoreCase("global")) {
|
||||
if (!maintenance && args.length == 3) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.setMaintenance(maintenance);
|
||||
} else if (args.length == 2) {
|
||||
if (checkPermission(sender, "singleserver.toggle")) return;
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
Server server = plugin.getServer(args[1]);
|
||||
if (server == null) {
|
||||
if (maintenance) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
// Let servers be removed from the maintenance list, even if they don't exist on the proxy
|
||||
server = new DummyServer(args[1]);
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
toggleGlobal(sender, maintenance, mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!maintenance && args.length != 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
toggleServer(sender, maintenance, server, mode);
|
||||
}
|
||||
|
||||
private void toggleGlobal(final SenderInfo sender, final boolean maintenance, final String mode) {
|
||||
if (checkPermission(sender, "toggle")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance && plugin.isMaintenance()) {
|
||||
if (mode != null) {
|
||||
plugin.getSettings().setActiveMode(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.setMaintenanceToServer(server, maintenance)) {
|
||||
if (!sender.isPlayer() || !server.getName().equals(plugin.getServerNameOf(sender))) {
|
||||
sender.send(getMessage(maintenance ? "singleMaintenanceActivated" : "singleMaintenanceDeactivated", "%SERVER%", server.getName()));
|
||||
}
|
||||
} else {
|
||||
sender.send(getMessage(maintenance ? "singleServerAlreadyEnabled" : "singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance == plugin.isMaintenance()) {
|
||||
sender.send(getMessage(maintenance ? "alreadyEnabled" : "alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.setMaintenance(maintenance, mode);
|
||||
}
|
||||
|
||||
private void toggleServer(final SenderInfo sender, final boolean maintenance, final String serverArg, final String mode) {
|
||||
if (checkPermission(sender, "singleserver.toggle")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = plugin.getServer(serverArg);
|
||||
if (server == null) {
|
||||
if (maintenance) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
// Let servers be removed from the maintenance list, even if they don't exist on the proxy
|
||||
server = new DummyServer(serverArg);
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (maintenance && plugin.isMaintenance(server) && mode != null) {
|
||||
if (plugin.setMaintenanceModeToServer(server, mode)) {
|
||||
return;
|
||||
}
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.setMaintenanceToServer(server, maintenance, mode)) {
|
||||
if (!sender.isPlayer() || !server.getName().equals(plugin.getServerNameOf(sender))) {
|
||||
sender.send(getMessage(maintenance ? "singleMaintenanceActivated" : "singleMaintenanceDeactivated", "%SERVER%", server.getName()));
|
||||
}
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
sender.send(getMessage(maintenance ? "singleServerAlreadyEnabled" : "singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2 || !sender.hasMaintenancePermission("singleserver.toggle")) return Collections.emptyList();
|
||||
return args[0].equalsIgnoreCase("off") ? plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase())
|
||||
: plugin.getCommandManager().getServersCompletion(args[1].toLowerCase());
|
||||
if (args.length == 2) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("toggle") && args[0].equalsIgnoreCase("on")) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
|
||||
if (!sender.hasMaintenancePermission("singleserver.toggle")) {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
final List<String> serverSuggestions = args[0].equalsIgnoreCase("off")
|
||||
? plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase(Locale.ROOT))
|
||||
: plugin.getCommandManager().getServersCompletion(args[1].toLowerCase(Locale.ROOT));
|
||||
suggestions.addAll(serverSuggestions);
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
if (args.length == 3 && args[0].equalsIgnoreCase("on") && (sender.hasMaintenancePermission("toggle") || sender.hasMaintenancePermission("singleserver.toggle"))) {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getPingMessages().getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
public final class StatusCommand extends ProxyCommandInfo {
|
||||
|
||||
public StatusCommand(final MaintenanceProxyPlugin plugin) {
|
||||
super(plugin, "singleserver.status");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (getSettings().getMaintenanceServers().isEmpty()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEmpty"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage("singleServerMaintenanceList"));
|
||||
for (final String server : getSettings().getMaintenanceServers()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEntry", "%SERVER%", server));
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -31,7 +31,7 @@ import java.util.function.Function;
|
||||
public final class LuckPermsProxyHook {
|
||||
|
||||
public static <T> void register(final MaintenanceProxyPlugin plugin, final Function<T, String> function) {
|
||||
LuckPermsProvider.get().getContextManager().registerCalculator(new MaintenanceProxyCalculator<T>(plugin, function));
|
||||
LuckPermsProvider.get().getContextManager().registerCalculator(new MaintenanceProxyCalculator<>(plugin, function));
|
||||
}
|
||||
|
||||
public static final class MaintenanceProxyCalculator<T> implements ContextCalculator<T> {
|
||||
|
||||
+6
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -24,6 +24,7 @@ import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProxySenderInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ServerConnectResult;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ProxyJoinListenerBase extends JoinListenerBase {
|
||||
@@ -48,7 +49,7 @@ public abstract class ProxyJoinListenerBase extends JoinListenerBase {
|
||||
protected ServerConnectResult serverConnect(final ProxySenderInfo sender, final Server target, final boolean normalServerConnect) {
|
||||
// Check waiting server for global maintenance
|
||||
if (settings.isMaintenance()) {
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.getUuid())) return ALLOWED;
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.uuid())) return ALLOWED;
|
||||
|
||||
final Server waitingServer = shouldConnectToWaitingServer(sender);
|
||||
// Should never be null, but just in case
|
||||
@@ -70,13 +71,13 @@ public abstract class ProxyJoinListenerBase extends JoinListenerBase {
|
||||
|
||||
// Single server maintenance
|
||||
if (!settings.isMaintenance(target.getName())) return ALLOWED;
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.getUuid())
|
||||
|| sender.hasMaintenancePermission("singleserver.bypass." + target.getName().toLowerCase())) {
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.uuid())
|
||||
|| sender.hasMaintenancePermission("singleserver.bypass." + target.getName().toLowerCase(Locale.ROOT))) {
|
||||
return ALLOWED;
|
||||
}
|
||||
|
||||
if (settings.isJoinNotifications()) {
|
||||
broadcastJoinNotification(sender.getName(), target);
|
||||
broadcastJoinNotification(sender.name(), target);
|
||||
}
|
||||
|
||||
if (normalServerConnect) {
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.mysql;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public final class MySQL {
|
||||
private final Logger logger;
|
||||
private final HikariDataSource hikariDataSource;
|
||||
|
||||
public MySQL(final Logger logger, final String hostname, final int port, final String username, final String password, final String database, final boolean useSSL) {
|
||||
this.logger = logger;
|
||||
|
||||
final HikariConfig hikariConfig = new HikariConfig();
|
||||
hikariConfig.setMaximumPoolSize(10);
|
||||
hikariConfig.addDataSourceProperty("serverName", hostname);
|
||||
hikariConfig.addDataSourceProperty("user", username);
|
||||
hikariConfig.addDataSourceProperty("password", password);
|
||||
|
||||
String urlProperty = "jdbc:mysql://" + hostname + ":" + port + "/" + database;
|
||||
if (!useSSL) {
|
||||
urlProperty += "?useSSL=false";
|
||||
}
|
||||
hikariConfig.addDataSourceProperty("url", urlProperty);
|
||||
|
||||
hikariConfig.setJdbcUrl(urlProperty);
|
||||
hikariConfig.setDataSourceClassName(findDriver("com.mysql.jdbc.jdbc2.optional.MysqlDataSource", "com.mysql.cj.jdbc.MysqlDataSource", "org.mariadb.jdbc.MariaDbDataSource"));
|
||||
hikariDataSource = new HikariDataSource(hikariConfig);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String findDriver(final String... classNames) {
|
||||
for (final String name : classNames) {
|
||||
try {
|
||||
Class.forName(name);
|
||||
return name;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No sql driver class found");
|
||||
}
|
||||
|
||||
public void executeUpdate(final String query, final Consumer<Integer> callback, final Object... objects) {
|
||||
try (final Connection connection = hikariDataSource.getConnection()) {
|
||||
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
int current = 1;
|
||||
for (final Object object : objects) {
|
||||
preparedStatement.setObject(current, object);
|
||||
current++;
|
||||
}
|
||||
|
||||
callback.accept(preparedStatement.executeUpdate());
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error while executing update method: " + query);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void executeQuery(final String query, final Consumer<ResultSet> callback, final Object... objects) {
|
||||
try (final Connection connection = hikariDataSource.getConnection()) {
|
||||
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
int current = 1;
|
||||
for (final Object object : objects) {
|
||||
preparedStatement.setObject(current, object);
|
||||
current++;
|
||||
}
|
||||
|
||||
try (final ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
callback.accept(resultSet);
|
||||
}
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error while executing query method: " + query);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void executeUpdate(final String query, final Object... objects) {
|
||||
executeUpdate(query, res -> {
|
||||
}, objects);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
hikariDataSource.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package eu.kennytv.maintenance.core.proxy.redis;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import redis.clients.jedis.ConnectionPoolConfig;
|
||||
import redis.clients.jedis.RedisClient;
|
||||
import redis.clients.jedis.StreamEntryID;
|
||||
import redis.clients.jedis.params.XReadParams;
|
||||
import redis.clients.jedis.resps.StreamEntry;
|
||||
|
||||
public final class RedisHandler {
|
||||
|
||||
private static final String REDIS_STREAM_KEY = "maintenance:stream";
|
||||
private static final String REDIS_MAINTENANCE_KEY = "maintenance:status";
|
||||
private static final String REDIS_MODE_KEY = "maintenance:mode";
|
||||
private static final String REDIS_SERVERS_KEY = "maintenance:servers";
|
||||
private static final String REDIS_SERVER_MODES_KEY = "maintenance:server_modes";
|
||||
private static final String REDIS_WHITELIST_KEY = "maintenance:whitelist";
|
||||
|
||||
private static final String MSG_TYPE_MAINTENANCE_UPDATE = "status";
|
||||
private static final String MSG_TYPE_SERVER_ADD = "server_add";
|
||||
private static final String MSG_TYPE_SERVER_REMOVE = "server_remove";
|
||||
private static final String MSG_TYPE_PLAYER_ADD = "player_add";
|
||||
private static final String MSG_TYPE_PLAYER_REMOVE = "player_remove";
|
||||
private static final String MSG_TYPE = "type";
|
||||
private static final String MSG_VALUE = "val";
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private final MaintenanceProxyPlugin plugin;
|
||||
private final SettingsProxy settings;
|
||||
private RedisClient client;
|
||||
private StreamEntryID lastStreamId = new StreamEntryID();
|
||||
|
||||
public RedisHandler(final MaintenanceProxyPlugin plugin, final SettingsProxy settings) {
|
||||
this.plugin = plugin;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public void setup(final String redisUri) {
|
||||
plugin.getLogger().info("Trying to open Redis connection...");
|
||||
|
||||
final ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
|
||||
poolConfig.setMaxWait(Duration.ofSeconds(5));
|
||||
poolConfig.setMaxTotal(2);
|
||||
client = RedisClient.builder().poolConfig(poolConfig).fromURI(URI.create(redisUri)).build();
|
||||
|
||||
// Get the latest stream ID to only receive new messages
|
||||
final List<StreamEntry> messages = client.xrevrange(REDIS_STREAM_KEY, (StreamEntryID) null, null, 1);
|
||||
if (!messages.isEmpty()) {
|
||||
lastStreamId = messages.getFirst().getID();
|
||||
}
|
||||
|
||||
running.set(true);
|
||||
plugin.async(this::listenToStream);
|
||||
|
||||
plugin.getLogger().info("Connected to Redis!");
|
||||
}
|
||||
|
||||
public boolean loadMaintenanceStatus() {
|
||||
return Boolean.parseBoolean(client.get(REDIS_MAINTENANCE_KEY));
|
||||
}
|
||||
|
||||
public @Nullable String loadGlobalMode() {
|
||||
return client.get(REDIS_MODE_KEY);
|
||||
}
|
||||
|
||||
public Map<String, String> loadMaintenanceServers() {
|
||||
final Set<String> servers = client.smembers(REDIS_SERVERS_KEY);
|
||||
final Map<String, String> serverModes = new HashMap<>();
|
||||
if (servers == null) {
|
||||
return serverModes;
|
||||
}
|
||||
|
||||
final Map<String, String> persistedModes = client.hgetAll(REDIS_SERVER_MODES_KEY);
|
||||
for (final String server : servers) {
|
||||
serverModes.put(server, normalizeMode(persistedModes.get(server)));
|
||||
}
|
||||
return serverModes;
|
||||
}
|
||||
|
||||
public void loadPlayers(final Map<UUID, String> players) {
|
||||
for (final Map.Entry<String, String> entry : client.hgetAll(REDIS_WHITELIST_KEY).entrySet()) {
|
||||
players.put(UUID.fromString(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void set(final boolean maintenance, final String mode) {
|
||||
client.set(REDIS_MAINTENANCE_KEY, Boolean.toString(maintenance));
|
||||
client.set(REDIS_MODE_KEY, mode);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_MAINTENANCE_UPDATE,
|
||||
MSG_VALUE, Boolean.toString(maintenance),
|
||||
"mode", mode
|
||||
));
|
||||
}
|
||||
|
||||
public boolean addServer(final String server, final String mode) {
|
||||
if (client.sadd(REDIS_SERVERS_KEY, server) < 1) {
|
||||
return false;
|
||||
}
|
||||
client.hset(REDIS_SERVER_MODES_KEY, server, mode);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_SERVER_ADD,
|
||||
MSG_VALUE, server,
|
||||
"mode", mode
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setServerMode(final String server, final String mode) {
|
||||
client.hset(REDIS_SERVER_MODES_KEY, server, mode);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_SERVER_ADD,
|
||||
MSG_VALUE, server,
|
||||
"mode", mode
|
||||
));
|
||||
}
|
||||
|
||||
public boolean removeServer(final String server) {
|
||||
if (client.srem(REDIS_SERVERS_KEY, server) < 1) {
|
||||
return false;
|
||||
}
|
||||
client.hdel(REDIS_SERVER_MODES_KEY, server);
|
||||
send(MSG_TYPE_SERVER_REMOVE, server);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addPlayer(final UUID uuid, final String name) {
|
||||
final String value = uuid.toString();
|
||||
client.hset(REDIS_WHITELIST_KEY, value, name);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_PLAYER_ADD,
|
||||
MSG_VALUE, value,
|
||||
"name", name
|
||||
));
|
||||
}
|
||||
|
||||
public void removePlayer(final UUID uuid) {
|
||||
final String value = uuid.toString();
|
||||
client.hdel(REDIS_WHITELIST_KEY, value);
|
||||
send(MSG_TYPE_PLAYER_REMOVE, value);
|
||||
}
|
||||
|
||||
private void send(final String type, final String value) {
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(MSG_TYPE, type, MSG_VALUE, value));
|
||||
}
|
||||
|
||||
private void listenToStream() {
|
||||
while (running.get()) {
|
||||
try {
|
||||
final List<Map.Entry<String, List<StreamEntry>>> result = client.xread(
|
||||
XReadParams.xReadParams().block(5000),
|
||||
Map.of(REDIS_STREAM_KEY, lastStreamId)
|
||||
);
|
||||
if (result != null) {
|
||||
for (final Map.Entry<String, List<StreamEntry>> streamEntries : result) {
|
||||
for (final StreamEntry entry : streamEntries.getValue()) {
|
||||
plugin.sync(() -> processStreamMessage(entry));
|
||||
lastStreamId = entry.getID();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
if (running.get()) {
|
||||
plugin.getLogger().log(Level.WARNING, "Error reading from Redis stream", e);
|
||||
try {
|
||||
Thread.sleep(1000); // retry
|
||||
} catch (final InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processStreamMessage(final StreamEntry message) {
|
||||
final Map<String, String> body = message.getFields();
|
||||
final String type = body.get(MSG_TYPE);
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String value = body.get(MSG_VALUE);
|
||||
switch (type) {
|
||||
case MSG_TYPE_MAINTENANCE_UPDATE -> handleStatus(value, body.get("mode"));
|
||||
case MSG_TYPE_SERVER_ADD -> handleServerStatus(value, body.get("mode"), true, "Server added to maintenance via Redis: ");
|
||||
case MSG_TYPE_SERVER_REMOVE -> handleServerStatus(value, null, false, "Server removed from maintenance via Redis: ");
|
||||
case MSG_TYPE_PLAYER_ADD -> handlePlayerAdd(value, body.get("name"));
|
||||
case MSG_TYPE_PLAYER_REMOVE -> handlePlayerRemove(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStatus(final String value, @Nullable final String mode) {
|
||||
final boolean maintenance = Boolean.parseBoolean(value);
|
||||
if (settings.isMaintenance() != maintenance) {
|
||||
plugin.getLogger().info("Maintenance status updated via Redis: " + maintenance);
|
||||
settings.setMaintenanceDirect(maintenance, mode);
|
||||
plugin.serverActions(maintenance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance && !normalizeMode(mode).equals(normalizeMode(settings.activeMode()))) {
|
||||
settings.setActiveModeDirect(mode);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleServerStatus(final String value, @Nullable final String mode, final boolean maintenance, final String log) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean success;
|
||||
if (maintenance) {
|
||||
success = settings.addMaintenanceServerDirect(value, mode);
|
||||
settings.setMaintenanceServerModeDirect(value, mode);
|
||||
} else {
|
||||
success = settings.removeMaintenanceServerDirect(value);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
plugin.getLogger().info(log + value);
|
||||
final Server server = plugin.getServerOrDummy(value);
|
||||
plugin.serverActions(server, maintenance);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePlayerAdd(final String uuid, final String name) {
|
||||
settings.addWhitelistedPlayerDirect(UUID.fromString(uuid), name);
|
||||
}
|
||||
|
||||
private void handlePlayerRemove(final String value) {
|
||||
settings.removeWhitelistedPlayerDirect(UUID.fromString(value));
|
||||
}
|
||||
|
||||
public void close() {
|
||||
running.set(false);
|
||||
if (client != null) {
|
||||
client.close();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeMode(@Nullable final String mode) {
|
||||
return mode == null || mode.isBlank() ? "default" : mode.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
+14
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,19 +17,23 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.runnable;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SingleMaintenanceRunnable extends MaintenanceRunnableBase {
|
||||
protected final Server server;
|
||||
protected final String mode;
|
||||
|
||||
public SingleMaintenanceRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds, final boolean enable, final Server server) {
|
||||
public SingleMaintenanceRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds,
|
||||
final boolean enable, final Server server, @Nullable final String mode) {
|
||||
super(plugin, settings, seconds, enable);
|
||||
this.server = server;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,7 +43,12 @@ public class SingleMaintenanceRunnable extends MaintenanceRunnableBase {
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
((MaintenanceProxy) plugin).setMaintenanceToServer(server, enable);
|
||||
final MaintenanceProxyPlugin proxyPlugin = (MaintenanceProxyPlugin) plugin;
|
||||
proxyPlugin.setMaintenanceToServer(server, enable, mode);
|
||||
// Make sure no-ops have the platform task cancelled as well
|
||||
if (proxyPlugin.getServerTasks().remove(server.getName(), this)) {
|
||||
getTask().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -21,28 +21,29 @@ import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SingleMaintenanceScheduleRunnable extends SingleMaintenanceRunnable {
|
||||
private final int maintenanceDuration;
|
||||
private final int maintenanceDurationSeconds;
|
||||
|
||||
public SingleMaintenanceScheduleRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds, final int maintenanceDuration, final Server server) {
|
||||
super(plugin, settings, seconds, true, server);
|
||||
this.maintenanceDuration = maintenanceDuration;
|
||||
public SingleMaintenanceScheduleRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds,
|
||||
final int maintenanceDuration, final Server server, @Nullable final String mode) {
|
||||
super(plugin, settings, seconds, true, server, mode);
|
||||
this.maintenanceDurationSeconds = maintenanceDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
super.finish();
|
||||
((MaintenanceProxyPlugin) plugin).startSingleMaintenanceRunnable(server, maintenanceDuration, TimeUnit.SECONDS, false);
|
||||
((MaintenanceProxyPlugin) plugin).startSingleMaintenanceRunnable(server, Duration.ofSeconds(maintenanceDurationSeconds), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component getStartMessage() {
|
||||
return settings.getMessage("singleScheduletimerBroadcast",
|
||||
"%SERVER%", server.getName(),
|
||||
"%TIME%", getTime(), "%DURATION%", plugin.getFormattedTime(maintenanceDuration));
|
||||
"%TIME%", getTime(), "%DURATION%", plugin.getFormattedTime(maintenanceDurationSeconds));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,20 +19,5 @@ package eu.kennytv.maintenance.core.proxy.util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ProfileLookup {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
|
||||
public ProfileLookup(final UUID uuid, final String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public record ProfileLookup(UUID uuid, String name) {
|
||||
}
|
||||
+3
-25
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -18,18 +18,11 @@
|
||||
package eu.kennytv.maintenance.core.proxy.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class ProxyOfflineSenderInfo implements ProxySenderInfo {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
|
||||
public ProxyOfflineSenderInfo(final UUID uuid, final String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
public record ProxyOfflineSenderInfo(UUID uuid, String name) implements ProxySenderInfo {
|
||||
|
||||
@Override
|
||||
public boolean canAccess(final Server server) {
|
||||
@@ -40,26 +33,11 @@ public final class ProxyOfflineSenderInfo implements ProxySenderInfo {
|
||||
public void disconnect(final Component component) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final String permission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void sendMessage(final String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(final Component component) {
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,7 +19,7 @@ package eu.kennytv.maintenance.core.proxy.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public interface ProxySenderInfo extends SenderInfo {
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
+14
-6
@@ -1,17 +1,25 @@
|
||||
plugins {
|
||||
id("net.kyori.blossom")
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
}
|
||||
|
||||
blossom {
|
||||
replaceToken("\$VERSION", project.version)
|
||||
replaceToken("\$IMPL_VERSION", "git-Maintenance-${project.version}:${rootProject.latestCommitHash()}")
|
||||
sourceSets {
|
||||
main {
|
||||
blossom {
|
||||
javaSources {
|
||||
property("version", project.version.toString())
|
||||
property("impl_version", "git-Maintenance-${project.version}:${rootProject.latestCommitHash()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.maintenanceApi)
|
||||
api(projects.adventure.adventureApi) {
|
||||
targetConfiguration = "shadow"
|
||||
}
|
||||
compileOnlyApi(libs.adventureApi)
|
||||
compileOnlyApi(libs.adventureTextMinimessage)
|
||||
compileOnlyApi(libs.adventureTextSerializerPlain)
|
||||
compileOnlyApi(libs.adventureTextSerializerLegacy)
|
||||
compileOnly(libs.luckperms)
|
||||
compileOnly(libs.serverlistplus)
|
||||
compileOnly(libs.guava)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2023 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.util;
|
||||
|
||||
public final class MaintenanceVersion {
|
||||
|
||||
public static final String VERSION = "{{ version }}";
|
||||
private static final String IMPLEMENTATION_VERSION = "{{ impl_version }}";
|
||||
|
||||
public static String getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
public static String getImplementationVersion() {
|
||||
return IMPLEMENTATION_VERSION;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.Gson;
|
||||
@@ -33,44 +32,50 @@ import eu.kennytv.maintenance.core.dump.PluginDump;
|
||||
import eu.kennytv.maintenance.core.hook.ServerListPlusHook;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceScheduleRunnable;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import eu.kennytv.maintenance.core.util.Version;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class MaintenancePlugin implements Maintenance {
|
||||
public static final Gson GSON = new GsonBuilder().create();
|
||||
protected final eu.kennytv.maintenance.core.event.EventManager eventManager;
|
||||
public static final String HANGAR_URL = "https://hangar.papermc.io/kennytv/Maintenance";
|
||||
private static final Pattern INT_PATTERN = Pattern.compile("[0-9]+");
|
||||
protected final EventManager eventManager;
|
||||
protected final Version version;
|
||||
protected Settings settings;
|
||||
protected ServerListPlusHook serverListPlusHook;
|
||||
protected MaintenanceRunnable runnable;
|
||||
protected MaintenanceCommand commandManager;
|
||||
private final String prefix;
|
||||
private final Component prefix;
|
||||
private final ServerType serverType;
|
||||
private Version newestVersion;
|
||||
private boolean debug;
|
||||
@@ -78,7 +83,12 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
protected MaintenancePlugin(final String version, final ServerType serverType) {
|
||||
this.version = new Version(version);
|
||||
this.serverType = serverType;
|
||||
this.prefix = "§8[§eMaintenance" + serverType + "§8] ";
|
||||
this.prefix = Component.text()
|
||||
.append(Component.text().content("[").color(NamedTextColor.DARK_GRAY))
|
||||
.append(Component.text().content("Maintenance").color(NamedTextColor.YELLOW))
|
||||
.append(Component.text().content("]").color(NamedTextColor.DARK_GRAY))
|
||||
.append(Component.text(" "))
|
||||
.build();
|
||||
this.eventManager = new eu.kennytv.maintenance.core.event.EventManager();
|
||||
MaintenanceProvider.setMaintenance(this);
|
||||
}
|
||||
@@ -87,20 +97,9 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaintenance(final boolean maintenance) {
|
||||
settings.setMaintenance(maintenance);
|
||||
settings.getConfig().set("maintenance-enabled", maintenance);
|
||||
settings.saveConfig();
|
||||
public void setMaintenance(final boolean maintenance, final String mode) {
|
||||
settings.setMaintenance(maintenance, mode);
|
||||
serverActions(maintenance);
|
||||
|
||||
for (final String command : (maintenance ? settings.getCommandsOnMaintenanceEnable() : settings.getCommandsOnMaintenanceDisable())) {
|
||||
try {
|
||||
executeConsoleCommand(command);
|
||||
} catch (final Exception e) {
|
||||
getLogger().severe("Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void serverActions(final boolean maintenance) {
|
||||
@@ -111,33 +110,70 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
serverListPlusHook.setEnabled(!maintenance);
|
||||
}
|
||||
|
||||
if (maintenance) {
|
||||
broadcast(settings.getMessage("maintenanceActivated"));
|
||||
if (settings.isKickOnlinePlayers()) {
|
||||
kickPlayers();
|
||||
}
|
||||
} else {
|
||||
broadcast(settings.getMessage("maintenanceDeactivated"));
|
||||
broadcast(maintenance ? settings.getMessage("maintenanceActivated") : settings.getMessage("maintenanceDeactivated"));
|
||||
if (maintenance && settings.isKickOnlinePlayers()) {
|
||||
kickPlayers();
|
||||
}
|
||||
|
||||
eventManager.callEvent(new MaintenanceChangedEvent(maintenance));
|
||||
if (maintenance) {
|
||||
sendWebhookMessage("webhookMaintenanceActivated", DiscordWebhook.EventType.MAINTENANCE_ENABLED);
|
||||
} else {
|
||||
sendWebhookMessage("webhookMaintenanceDeactivated", DiscordWebhook.EventType.MAINTENANCE_DISABLED);
|
||||
}
|
||||
|
||||
for (final String command : (maintenance ? settings.getCommandsOnMaintenanceEnable() : settings.getCommandsOnMaintenanceDisable())) {
|
||||
try {
|
||||
executeConsoleCommand(command);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Component replacePingVariables(final Component component) {
|
||||
return component.replaceText(builder -> builder.matchLiteral("%TIMER%").replacement(c -> getTimerMessage()))
|
||||
.replaceText(builder -> builder.matchLiteral("%ONLINE%").replacement(c -> Component.text(getOnlinePlayers())))
|
||||
.replaceText(builder -> builder.matchLiteral("%MAX%").replacement(c -> Component.text(getMaxPlayers())));
|
||||
public void sendWebhookMessage(final String messageKey, final DiscordWebhook.EventType eventType, final String... replacements) {
|
||||
if (!settings.isWebhookEnabled() || !settings.isWebhookEventEnabled(eventType.configKey())) {
|
||||
return;
|
||||
}
|
||||
|
||||
async(() -> {
|
||||
try {
|
||||
DiscordWebhook.sendMessage(settings.getWebhookMessage(messageKey, replacements), eventType, settings);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.WARNING, "Could not send Discord webhook message", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Component getTimerMessage() {
|
||||
public String getTargetTimestamp(final Duration duration) {
|
||||
return Long.toString((System.currentTimeMillis() + duration.toMillis()) / TimeUnit.SECONDS.toMillis(1));
|
||||
}
|
||||
|
||||
public String replacePingVariables(final String component) {
|
||||
return replacePingVariables(component, settings.activeMode());
|
||||
}
|
||||
|
||||
public String replacePingVariables(String component, @Nullable final String mode) {
|
||||
if (component.contains("%TIMER%")) {
|
||||
component = component.replace("%TIMER%", getTimerMessage());
|
||||
}
|
||||
final String reason = settings.activeReason() == null ? "" : settings.activeReason();
|
||||
component = component.replace("%REASON%", reason);
|
||||
component = component.replace("%MODE%", mode == null ? "default" : mode);
|
||||
component = component.replace("%ONLINE%", String.valueOf(getOnlinePlayers()));
|
||||
component = component.replace("%MAX%", String.valueOf(getMaxPlayers()));
|
||||
return component;
|
||||
}
|
||||
|
||||
public String getTimerMessage() {
|
||||
if (!isTaskRunning()) {
|
||||
return settings.getMessage("motdTimerNotRunning");
|
||||
return settings.getLanguageString("motdTimerNotRunning");
|
||||
}
|
||||
|
||||
final int preHours = runnable.getSecondsLeft() / 60;
|
||||
final int minutes = preHours % 60;
|
||||
final int seconds = runnable.getSecondsLeft() % 60;
|
||||
return settings.getMessage("motdTimer",
|
||||
return settings.getLanguageString("motdTimer",
|
||||
"%HOURS%", String.format("%02d", preHours / 60),
|
||||
"%MINUTES%", String.format("%02d", minutes),
|
||||
"%SECONDS%", String.format("%02d", seconds));
|
||||
@@ -157,28 +193,36 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
|
||||
private void append(final StringBuilder builder, final String timeUnit, final int time) {
|
||||
if (time == 0) return;
|
||||
if (builder.length() != 0) {
|
||||
if (!builder.isEmpty()) {
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append(time).append(' ').append(settings.language.getString(time == 1 ? timeUnit : timeUnit + "s"));
|
||||
}
|
||||
|
||||
public void startMaintenanceRunnable(final long duration, final TimeUnit unit, final boolean enable) {
|
||||
runnable = new MaintenanceRunnable(this, settings, (int) unit.toSeconds(duration), enable);
|
||||
public void startMaintenanceRunnable(final Duration duration, final boolean enable) {
|
||||
startMaintenanceRunnable(duration, enable, null);
|
||||
}
|
||||
|
||||
public void startMaintenanceRunnable(final Duration duration, final boolean enable, @Nullable final String mode) {
|
||||
runnable = new MaintenanceRunnable(this, settings, (int) duration.getSeconds(), enable, mode);
|
||||
// Save the endtimer to be able to continue it after a server stop
|
||||
if (settings.isSaveEndtimerOnStop() && !runnable.shouldEnable()) {
|
||||
settings.setSavedEndtimer(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(runnable.getSecondsLeft()));
|
||||
}
|
||||
}
|
||||
|
||||
public void scheduleMaintenanceRunnable(final long duration, final int maintenanceDuration, final TimeUnit unit) {
|
||||
runnable = new MaintenanceScheduleRunnable(this, settings, (int) unit.toSeconds(duration), (int) unit.toSeconds(maintenanceDuration));
|
||||
public void scheduleMaintenanceRunnable(final Duration enableIn, final Duration maintenanceDuration) {
|
||||
scheduleMaintenanceRunnable(enableIn, maintenanceDuration, null);
|
||||
}
|
||||
|
||||
public void scheduleMaintenanceRunnable(final Duration enableIn, final Duration maintenanceDuration, @Nullable final String mode) {
|
||||
runnable = new MaintenanceScheduleRunnable(this, settings, (int) enableIn.getSeconds(), (int) maintenanceDuration.getSeconds(), mode);
|
||||
}
|
||||
|
||||
public boolean updateAvailable() {
|
||||
try {
|
||||
checkNewestVersion();
|
||||
return version.compareTo(newestVersion) == -1;
|
||||
return version.compareTo(newestVersion) < 0;
|
||||
} catch (final Exception e) {
|
||||
return false;
|
||||
}
|
||||
@@ -198,7 +242,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
setMaintenance(false);
|
||||
settings.setSavedEndtimer(0);
|
||||
} else {
|
||||
startMaintenanceRunnable(settings.getSavedEndtimer() - current, TimeUnit.MILLISECONDS, false);
|
||||
startMaintenanceRunnable(Duration.ofMillis(settings.getSavedEndtimer() - current), false);
|
||||
getLogger().info("The timer has been continued - maintenance will be disabled in: " + getTimerMessage());
|
||||
}
|
||||
}
|
||||
@@ -209,45 +253,46 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
try {
|
||||
checkNewestVersion();
|
||||
} catch (final Exception e) {
|
||||
getLogger().warning("An error occured during update checking!");
|
||||
return;
|
||||
}
|
||||
|
||||
final int compare = version.compareTo(newestVersion);
|
||||
if (compare == -1) {
|
||||
//TODO mmmmmmmm legacy chat
|
||||
getLogger().info("§cNewest version available: §aVersion " + newestVersion + "§c, you're on §a" + version);
|
||||
} else if (compare == 1) {
|
||||
if (version.getTag().equalsIgnoreCase("snapshot")) {
|
||||
getLogger().info("§cYou're running a development version, please report bugs on the Discord server (https://discord.gg/vGCUzHq) or the GitHub issue tracker (https://github.com/kennytv/Maintenance/issues)");
|
||||
if (compare < 0) {
|
||||
getLogger().warning("Newest version available: Version " + newestVersion + ", you're on " + version);
|
||||
} else if (compare > 0) {
|
||||
if (version.getTag() != null && !version.getTag().isBlank()) {
|
||||
getLogger().info("You're running a development version, please report bugs on the Discord server (https://discord.gg/vGCUzHq) or the GitHub issue tracker (https://github.com/kennytv/Maintenance/issues)");
|
||||
} else {
|
||||
getLogger().info("§cYou're running a version, that doesn't exist! §cN§ai§dc§ee§5!");
|
||||
getLogger().info("You're running a version, that doesn't exist!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean installUpdate() {
|
||||
// Ore sad :(
|
||||
Preconditions.checkArgument(serverType != ServerType.SPONGE);
|
||||
try {
|
||||
final String fileSuffix = serverType == ServerType.VELOCITY ? "Velocity" : "";
|
||||
final URLConnection conn = new URL("https://github.com/kennytv/Maintenance/releases/download/" + newestVersion + "/Maintenance" + fileSuffix + ".jar").openConnection();
|
||||
writeFile(new BufferedInputStream(conn.getInputStream()), new BufferedOutputStream(new FileOutputStream(getPluginFolder() + "Maintenance.tmp")));
|
||||
final File file = new File(getPluginFolder() + "Maintenance.tmp");
|
||||
final long newlength = file.length();
|
||||
if (newlength < 10000) {
|
||||
file.delete();
|
||||
return false;
|
||||
}
|
||||
public boolean installUpdate() throws Exception {
|
||||
final String fileName = "Maintenance-" + serverType.name() + "-" + newestVersion + ".jar";
|
||||
final Path tempFilePath = Paths.get(getPluginFolder() + "Maintenance.tmp");
|
||||
final URLConnection connection = URI.create("https://github.com/kennytv/Maintenance/releases/download/" + newestVersion + "/" + fileName).toURL().openConnection();
|
||||
try (final BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
|
||||
final BufferedOutputStream os = new BufferedOutputStream(Files.newOutputStream(tempFilePath))) {
|
||||
writeFile(is, os);
|
||||
}
|
||||
|
||||
writeFile(new FileInputStream(file), new BufferedOutputStream(new FileOutputStream(getPluginFile())));
|
||||
final File file = tempFilePath.toFile();
|
||||
final long newlength = file.length();
|
||||
if (newlength < 10_000) {
|
||||
// Sanity check the file length
|
||||
file.delete();
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
try (final InputStream is = Files.newInputStream(file.toPath());
|
||||
final BufferedOutputStream os = new BufferedOutputStream(Files.newOutputStream(getPluginFile().toPath()))) {
|
||||
writeFile(is, os);
|
||||
}
|
||||
|
||||
file.delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeFile(final InputStream is, final OutputStream os) throws IOException {
|
||||
@@ -256,13 +301,14 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
while ((chunkSize = is.read(chunk)) != -1) {
|
||||
os.write(chunk, 0, chunkSize);
|
||||
}
|
||||
is.close();
|
||||
os.close();
|
||||
}
|
||||
|
||||
private void checkNewestVersion() throws Exception {
|
||||
final HttpURLConnection c = (HttpURLConnection) new URL("https://api.spigotmc.org/legacy/update.php?resource=40699").openConnection();
|
||||
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(c.getInputStream()))) {
|
||||
final URLConnection connection = URI.create("https://hangar.papermc.io/api/v1/projects/Maintenance/latestrelease").toURL().openConnection();
|
||||
connection.setRequestProperty("User-Agent", "Maintenance/" + getVersion());
|
||||
connection.setConnectTimeout(5_000);
|
||||
connection.setReadTimeout(10_000);
|
||||
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
final String newVersionString = reader.readLine();
|
||||
final Version newVersion = new Version(newVersionString);
|
||||
if (!newVersion.equals(version)) {
|
||||
@@ -274,7 +320,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
public String pasteDump() {
|
||||
final MaintenanceDump dump = new MaintenanceDump(this, settings);
|
||||
try {
|
||||
final HttpURLConnection connection = (HttpURLConnection) new URL("https://hastebin.com/documents").openConnection();
|
||||
final HttpURLConnection connection = (HttpURLConnection) URI.create("https://api.pastes.dev/post").toURL().openConnection();
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("User-Agent", "Maintenance/" + getVersion());
|
||||
@@ -285,7 +331,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() == 503) {
|
||||
getLogger().warning("Could not paste dump, hastebin.com down?");
|
||||
getLogger().warning("Could not paste dump, pastes.dev down?");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -300,8 +346,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
return jsonOutput.get("key").getAsString();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
getLogger().log(Level.WARNING, "Could not paste dump :(");
|
||||
e.printStackTrace();
|
||||
getLogger().log(Level.WARNING, "Could not paste dump :(", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -316,8 +361,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
try {
|
||||
loadIcon(file);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.WARNING, "Could not load the 'maintenance-icon.png' file!");
|
||||
e.printStackTrace();
|
||||
getLogger().log(Level.WARNING, "Could not load the 'maintenance-icon.png' file!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +393,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
public boolean isNumeric(final String string) {
|
||||
return string.matches("[0-9]+");
|
||||
return INT_PATTERN.matcher(string).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -362,6 +406,10 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
return runnable != null;
|
||||
}
|
||||
|
||||
public @Nullable MaintenanceRunnable getCurrentTask() {
|
||||
return runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getSettings() {
|
||||
return settings;
|
||||
@@ -400,7 +448,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
return newestVersion;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
public Component prefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@@ -424,14 +472,16 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
return "plugins/";
|
||||
}
|
||||
|
||||
public void sync(final Runnable runnable) {
|
||||
async(runnable);
|
||||
}
|
||||
|
||||
public abstract void async(Runnable runnable);
|
||||
|
||||
protected abstract void executeConsoleCommand(String command);
|
||||
|
||||
public abstract void broadcast(Component component);
|
||||
|
||||
public abstract void sendUpdateNotification(SenderInfo sender);
|
||||
|
||||
public abstract Task startMaintenanceRunnable(Runnable runnable);
|
||||
|
||||
/**
|
||||
@@ -440,14 +490,14 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
*
|
||||
* @param name name of the player
|
||||
*/
|
||||
public abstract void getOfflinePlayer(String name, Consumer<@Nullable SenderInfo> consumer);
|
||||
public abstract CompletableFuture<@Nullable SenderInfo> getOfflinePlayer(String name);
|
||||
|
||||
public abstract void getOfflinePlayer(UUID uuid, Consumer<@Nullable SenderInfo> consumer);
|
||||
public abstract CompletableFuture<@Nullable SenderInfo> getOfflinePlayer(UUID uuid);
|
||||
|
||||
public abstract File getDataFolder();
|
||||
|
||||
@Nullable
|
||||
public InputStream getResource(String name) {
|
||||
public InputStream getResource(final String name) {
|
||||
return this.getClass().getClassLoader().getResourceAsStream(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,14 +20,8 @@ package eu.kennytv.maintenance.core;
|
||||
import eu.kennytv.maintenance.api.event.MaintenanceReloadedEvent;
|
||||
import eu.kennytv.maintenance.core.config.Config;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.config.DisplayedMessages;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.TextComponent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.TextReplacementConfig;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -35,32 +29,47 @@ import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
public static final String NEW_LINE_REPLACEMENT = "<br>";
|
||||
private static final int CONFIG_VERSION = 6;
|
||||
private static final int LANGUAGE_VERSION = 1;
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int CONFIG_VERSION = 12;
|
||||
private static final int LANGUAGE_VERSION = 5;
|
||||
protected final MaintenancePlugin plugin;
|
||||
private final Map<UUID, String> whitelistedPlayers = new HashMap<>();
|
||||
private final String[] unsupportedFields;
|
||||
protected boolean maintenance;
|
||||
protected String activeMode;
|
||||
protected String activeReason;
|
||||
private Set<Integer> broadcastIntervals;
|
||||
private List<Component> pingMessages;
|
||||
private List<Component> timerSpecificPingMessages;
|
||||
private DisplayedMessages pingMessages;
|
||||
private DisplayedMessages timerSpecificPingMessages;
|
||||
private List<String> commandsOnMaintenanceEnable;
|
||||
private List<String> commandsOnMaintenanceDisable;
|
||||
private Component playerCountMessage;
|
||||
private List<Component> playerCountHoverLines;
|
||||
private Component prefix;
|
||||
private DisplayedMessages legacyParsedPlayerCountMessages;
|
||||
private DisplayedMessages legacyParsedTimerPlayerCountMessages;
|
||||
private DisplayedMessages legacyParsedPlayerCountHoverLines;
|
||||
private DisplayedMessages legacyParsedTimerPlayerCountHoverLines;
|
||||
private String prefixString;
|
||||
private String plainTextPrefix;
|
||||
private String languageName;
|
||||
private boolean enablePingMessages;
|
||||
private boolean customPlayerCountMessage;
|
||||
private boolean customPlayerCountHoverMessage;
|
||||
private boolean customMaintenanceIcon;
|
||||
private boolean joinNotifications;
|
||||
private boolean updateChecks;
|
||||
@@ -69,6 +78,13 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
private boolean debug;
|
||||
private long savedEndtimer;
|
||||
|
||||
// Webhook settings
|
||||
private boolean webhookEnabled;
|
||||
private Set<String> webhookEnabledEvents;
|
||||
private String webhookUrl;
|
||||
private String webhookUsername;
|
||||
private String webhookAvatarUrl;
|
||||
|
||||
protected Config config;
|
||||
protected Config language;
|
||||
protected Config whitelist;
|
||||
@@ -112,11 +128,11 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
try {
|
||||
updateLanguageFile();
|
||||
} catch (final IOException e) {
|
||||
plugin.getLogger().severe("Couldn't update language file");
|
||||
e.printStackTrace();
|
||||
plugin.getLogger().log(Level.SEVERE, "Couldn't update language file", e);
|
||||
}
|
||||
|
||||
prefix = MiniMessage.miniMessage().deserialize(language.getString("prefix"));
|
||||
prefixString = language.getString("prefix");
|
||||
plainTextPrefix = PlainTextComponentSerializer.plainText().serialize(parse(prefixString));
|
||||
|
||||
plugin.getEventManager().callEvent(new MaintenanceReloadedEvent());
|
||||
}
|
||||
@@ -125,7 +141,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
try {
|
||||
config.save();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
plugin.getLogger().log(Level.SEVERE, "Couldn't save the config file!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +165,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
try {
|
||||
whitelist.save();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
plugin.getLogger().log(Level.SEVERE, "Couldn't save the whitelisted players file!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +175,9 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
if (file.exists()) return;
|
||||
|
||||
try (final InputStream in = plugin.getResource(fileName)) {
|
||||
if (in == null) {
|
||||
throw new IOException("No bundled language file " + fileName);
|
||||
}
|
||||
Files.copy(in, file.toPath());
|
||||
} catch (final IOException e) {
|
||||
plugin.getLogger().warning("Unable to provide language " + languageName);
|
||||
@@ -174,33 +193,58 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
private void loadSettings() {
|
||||
updateConfig();
|
||||
|
||||
enablePingMessages = config.getBoolean("enable-pingmessages", true);
|
||||
pingMessages = loadPingMessages("pingmessages");
|
||||
if (config.getBoolean("enable-timerspecific-messages")) {
|
||||
timerSpecificPingMessages = loadPingMessages("timerspecific-pingmessages");
|
||||
final ConfigSection pingMessageSection = config.getSection("ping-message");
|
||||
enablePingMessages = pingMessageSection.getBoolean("enabled", true);
|
||||
// Can't store this pre-parsed as components because gradients will break replacements
|
||||
pingMessages = loadPingMessages(pingMessageSection.getSection("messages"));
|
||||
if (pingMessageSection.getBoolean("enable-timer-specific-messages")) {
|
||||
timerSpecificPingMessages = loadPingMessages(pingMessageSection.getSection("timer-messages"));
|
||||
} else {
|
||||
timerSpecificPingMessages = null;
|
||||
}
|
||||
|
||||
maintenance = config.getBoolean("maintenance-enabled");
|
||||
activeMode = normalizeMode(config.getString("active-mode"));
|
||||
activeReason = normalizeReason(config.getString("active-reason"));
|
||||
commandsOnMaintenanceEnable = config.getStringList("commands-on-maintenance-enable");
|
||||
commandsOnMaintenanceDisable = config.getStringList("commands-on-maintenance-disable");
|
||||
customPlayerCountMessage = config.getBoolean("enable-playercountmessage");
|
||||
customMaintenanceIcon = config.getBoolean("custom-maintenance-icon");
|
||||
joinNotifications = config.getBoolean("send-join-notification");
|
||||
broadcastIntervals = new HashSet<>(config.getIntList("timer-broadcast-for-seconds"));
|
||||
|
||||
// Player count and player list hover messages are only accepted and rendered as legacy chat
|
||||
if (plugin.getServerType() != ServerType.SPONGE) {
|
||||
playerCountMessage = getConfigMessage("playercountmessage");
|
||||
final ConfigSection playerCountMessageSection = config.getSection("player-count-message");
|
||||
customPlayerCountMessage = playerCountMessageSection.getBoolean("enabled");
|
||||
legacyParsedPlayerCountMessages = loadPlayerCountMessages(playerCountMessageSection.getSection("messages"));
|
||||
if (playerCountMessageSection.getBoolean("enable-timer-specific-message")) {
|
||||
legacyParsedTimerPlayerCountMessages = loadPlayerCountMessages(playerCountMessageSection.getSection("timer-messages"));
|
||||
} else {
|
||||
legacyParsedTimerPlayerCountMessages = null;
|
||||
}
|
||||
}
|
||||
|
||||
playerCountHoverLines = new ArrayList<>();
|
||||
final String playerHoverMessage = config.getString("playercounthovermessage");
|
||||
for (final String line : playerHoverMessage.split("<br>")) {
|
||||
playerCountHoverLines.add(MiniMessage.miniMessage().deserialize(line));
|
||||
final ConfigSection listHoverMessageSection = config.getSection("player-list-hover-message");
|
||||
customPlayerCountHoverMessage = listHoverMessageSection.getBoolean("enabled");
|
||||
legacyParsedPlayerCountHoverLines = loadPlayerCountHoverMessages(listHoverMessageSection.getSection("messages"));
|
||||
if (listHoverMessageSection.getBoolean("enable-timer-specific-message")) {
|
||||
legacyParsedTimerPlayerCountHoverLines = loadPlayerCountHoverMessages(listHoverMessageSection.getSection("timer-messages"));
|
||||
} else {
|
||||
legacyParsedTimerPlayerCountHoverLines = null;
|
||||
}
|
||||
|
||||
languageName = config.getString("language").toLowerCase();
|
||||
languageName = config.getString("language").toLowerCase(Locale.ROOT);
|
||||
kickOnlinePlayers = config.getBoolean("kick-online-players", true);
|
||||
updateChecks = config.getBoolean("update-checks", true);
|
||||
debug = config.getBoolean("debug");
|
||||
|
||||
final ConfigSection webhookSection = config.getSection("webhook");
|
||||
webhookEnabled = webhookSection.getBoolean("enabled");
|
||||
webhookEnabledEvents = new HashSet<>(webhookSection.getStringList("enabled-events"));
|
||||
webhookUrl = webhookSection.getString("url", "");
|
||||
webhookUsername = webhookSection.getString("username", "Maintenance");
|
||||
webhookAvatarUrl = webhookSection.getString("avatar-url", "");
|
||||
|
||||
final ConfigSection section = config.getSection("continue-endtimer-after-restart");
|
||||
saveEndtimerOnStop = section.getBoolean("enabled");
|
||||
savedEndtimer = section.getLong("end");
|
||||
@@ -209,23 +253,29 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
plugin.loadMaintenanceIcon();
|
||||
}
|
||||
|
||||
loadWhitelistedPlayers();
|
||||
|
||||
loadExtraSettings();
|
||||
}
|
||||
|
||||
protected void loadWhitelistedPlayers() {
|
||||
whitelistedPlayers.clear();
|
||||
for (final Map.Entry<String, Object> entry : whitelist.getValues().entrySet()) {
|
||||
try {
|
||||
whitelistedPlayers.put(UUID.fromString(entry.getKey()), (String) entry.getValue());
|
||||
} catch (final Exception e) {
|
||||
plugin.getLogger().warning("invalid WhitelistedPlayers entry: " + entry.getKey());
|
||||
whitelistedPlayers.put(UUID.fromString(entry.getKey()), String.valueOf(entry.getValue()));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
plugin.getLogger().warning("Invalid WhitelistedPlayers entry: " + entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
loadExtraSettings();
|
||||
}
|
||||
|
||||
private void updatePluginDirectory() {
|
||||
// All plugin identifiers were changed to 'Maintenance' ('maintenance' for Sponge and Velocity) in 3.0.5
|
||||
String oldDirName = "Maintenance" + plugin.getServerType();
|
||||
if (plugin.getServerType() == ServerType.SPONGE || plugin.getServerType() == ServerType.VELOCITY) {
|
||||
oldDirName = oldDirName.toLowerCase();
|
||||
oldDirName = oldDirName.toLowerCase(Locale.ROOT);
|
||||
} else if (plugin.getServerType() == ServerType.PAPER) {
|
||||
oldDirName = "MaintenanceSpigot";
|
||||
}
|
||||
|
||||
final File oldDir = new File(plugin.getDataFolder().getParentFile(), oldDirName);
|
||||
@@ -237,8 +287,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
Files.move(oldDir.toPath(), plugin.getDataFolder().toPath());
|
||||
plugin.getLogger().info("Moved old " + oldDirName + " to new " + plugin.getDataFolder().getName() + " directory!");
|
||||
} catch (final IOException e) {
|
||||
plugin.getLogger().severe("Error while copying " + oldDirName + " to new " + plugin.getDataFolder().getName() + " directory!");
|
||||
e.printStackTrace();
|
||||
plugin.getLogger().log(Level.SEVERE, "Error while copying " + oldDirName + " to new " + plugin.getDataFolder().getName() + " directory!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,11 +297,52 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
plugin.getLogger().info("Updating config to the latest version...");
|
||||
if (version < 6) {
|
||||
// Legacy text to MiniMessage
|
||||
legacyToMinimessage(config.getStringList("pingmessages"));
|
||||
legacyToMinimessage(config.getStringList("timerspecific-pingmessages"));
|
||||
config.getStringList("pingmessages").replaceAll(this::legacyToMinimessage);
|
||||
config.getStringList("timerspecific-pingmessages").replaceAll(this::legacyToMinimessage);
|
||||
config.set("playercounthovermessage", legacyToMinimessage(config.getString("playercounthovermessage")));
|
||||
config.set("playercountmessage", legacyToMinimessage(config.getString("playercountmessage")));
|
||||
}
|
||||
if (version < 9) {
|
||||
config.move("enable-pingmessages", "ping-message.enabled");
|
||||
config.move("pingmessages", "ping-message.messages");
|
||||
config.move("enable-timerspecific-messages", "ping-message.enable-timer-specific-messages");
|
||||
config.move("timerspecific-pingmessages", "ping-message.timer-messages");
|
||||
|
||||
config.move("enable-playercountmessage", "player-count-message.enabled");
|
||||
config.move("playercountmessage", "player-count-message.message");
|
||||
|
||||
config.move("enable-playercounthovermessage", "player-list-hover-message.enabled");
|
||||
config.move("playercounthovermessage", "player-list-hover-message.message");
|
||||
}
|
||||
if (version < 11) {
|
||||
config.move("player-count-message.message", "player-count-message.messages.default");
|
||||
config.move("player-count-message.timer-message", "player-count-message.timer-messages.default");
|
||||
|
||||
config.move("player-list-hover-message.message", "player-list-hover-message.messages.default");
|
||||
config.move("player-list-hover-message.timer-message", "player-list-hover-message.timer-messages.default");
|
||||
|
||||
migrateModeSection("ping-message.messages");
|
||||
migrateModeSection("ping-message.timer-messages");
|
||||
migrateModeSection("player-count-message.messages");
|
||||
migrateModeSection("player-count-message.timer-messages");
|
||||
migrateModeSection("player-list-hover-message.messages");
|
||||
migrateModeSection("player-list-hover-message.timer-messages");
|
||||
|
||||
final Object proxiedMaintenanceServers = config.getObject("proxied-maintenance-servers");
|
||||
if (proxiedMaintenanceServers instanceof List<?> list) {
|
||||
final Map<String, String> migratedServers = new LinkedHashMap<>();
|
||||
for (final Object entry : list) {
|
||||
if (entry != null) {
|
||||
migratedServers.put(String.valueOf(entry), "default");
|
||||
}
|
||||
}
|
||||
config.set("proxied-maintenance-servers", migratedServers);
|
||||
}
|
||||
}
|
||||
if (version < 12) {
|
||||
// Fix typo in the shipped config key :>
|
||||
config.move("redis.enables", "redis.enabled");
|
||||
}
|
||||
|
||||
createFile("config-new.yml", "config.yml");
|
||||
final File file = new File(plugin.getDataFolder(), "config-new.yml");
|
||||
@@ -263,7 +353,8 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
config.addMissingFields(tempConfig.getValues(), tempConfig.getComments());
|
||||
config.addMissingFields(tempConfig);
|
||||
config.replaceComments(tempConfig);
|
||||
config.set("config-version", CONFIG_VERSION);
|
||||
|
||||
file.delete();
|
||||
@@ -283,11 +374,10 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
plugin.getLogger().info("Updating language file to the latest version...");
|
||||
if (version < 1) {
|
||||
for (final Map.Entry<String, Object> entry : language.getValues().entrySet()) {
|
||||
if (!(entry.getValue() instanceof String)) {
|
||||
if (!(entry.getValue() instanceof String value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = (String) entry.getValue();
|
||||
value = value.replace("&8[&eMaintenance&8] ", "<prefix>");
|
||||
value = legacyToMinimessage(value);
|
||||
value = value.replace("%NEWLINE%", NEW_LINE_REPLACEMENT);
|
||||
@@ -306,7 +396,8 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
language.addMissingFields(tempConfig.getValues(), tempConfig.getComments());
|
||||
language.addMissingFields(tempConfig);
|
||||
language.replaceComments(tempConfig);
|
||||
tempConfig.clear();
|
||||
|
||||
language.set("language-version", LANGUAGE_VERSION);
|
||||
@@ -322,26 +413,35 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return serialized.replaceAll("</[a-z_]+>", "");
|
||||
}
|
||||
|
||||
private void legacyToMinimessage(final List<String> list) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.set(i, legacyToMinimessage(list.get(i)));
|
||||
private void migrateModeSection(final String path) {
|
||||
final Object section = config.getObject(path);
|
||||
if (section == null || section instanceof Map<?, ?>) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("default", section);
|
||||
config.set(path, map);
|
||||
}
|
||||
|
||||
public Component getConfigMessage(final String path) {
|
||||
final String s = config.getString(path);
|
||||
private String getConfigMessage(final ConfigSection section, final String path) {
|
||||
final String s = section.getString(path);
|
||||
if (s == null) {
|
||||
plugin.getLogger().warning("The config file is missing the following string: " + path);
|
||||
return Component.text("null");
|
||||
return path;
|
||||
}
|
||||
return MiniMessage.miniMessage().deserialize(replaceNewlineVar(s));
|
||||
return replaceNewlineVar(s);
|
||||
}
|
||||
|
||||
public Component getMessage(final String path, final String... replacements) {
|
||||
public String getConfigMessage(final String path) {
|
||||
return getConfigMessage(config, path);
|
||||
}
|
||||
|
||||
public String getLanguageString(final String path, final String... replacements) {
|
||||
String s = language.getString(path);
|
||||
if (s == null) {
|
||||
plugin.getLogger().warning("The language file is missing the following string: " + path);
|
||||
return Component.text("null");
|
||||
return path;
|
||||
}
|
||||
if (replacements.length != 0) {
|
||||
if (replacements.length % 2 != 0) {
|
||||
@@ -353,35 +453,85 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
s = s.replace(key, replacements[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return MiniMessage.miniMessage().deserialize(replaceNewlineVar(s))
|
||||
.replaceText(TextReplacementConfig.builder().matchLiteral("<prefix>").replacement(prefix).build());
|
||||
return replaceNewlineVar(s).replace("<prefix>", prefixString);
|
||||
}
|
||||
|
||||
public @Nullable Component getMessageOrNull(final String path, final String... replacements) {
|
||||
return language.contains(path) ? getMessage(path, replacements) : null;
|
||||
public String getWebhookMessage(final String path, final String... replacements) {
|
||||
String s = language.getString(path);
|
||||
if (s == null) {
|
||||
plugin.getLogger().warning("The language file is missing the following webhook message: " + path);
|
||||
return path;
|
||||
}
|
||||
if (replacements.length != 0) {
|
||||
if (replacements.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Invalid replacement count: " + replacements.length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < replacements.length; i += 2) {
|
||||
final String key = replacements[i];
|
||||
s = s.replace(key, replacements[i + 1]);
|
||||
}
|
||||
}
|
||||
return replaceNewlineVar(s);
|
||||
}
|
||||
|
||||
public @Nullable String getLanguageStringOrNull(final String path, final String... replacements) {
|
||||
return language.contains(path) ? getLanguageString(path, replacements) : null;
|
||||
}
|
||||
|
||||
public Component getMessage(final String path, final String... replacements) {
|
||||
return parse(getLanguageString(path, replacements));
|
||||
}
|
||||
|
||||
// TODO Cache platform components if there are no replacements
|
||||
public Component getRandomPingMessage() {
|
||||
if (plugin.isTaskRunning() && !plugin.getRunnable().shouldEnable()
|
||||
&& hasTimerSpecificPingMessages() && !timerSpecificPingMessages.isEmpty()) {
|
||||
return getPingMessage(timerSpecificPingMessages);
|
||||
}
|
||||
return pingMessages.isEmpty() ? Component.empty() : getPingMessage(pingMessages);
|
||||
return getRandomPingMessage(activeMode);
|
||||
}
|
||||
|
||||
private Component getPingMessage(final List<Component> list) {
|
||||
final Component component = list.size() == 1 ? list.get(0) : list.get(RANDOM.nextInt(list.size()));
|
||||
return plugin.replacePingVariables(component);
|
||||
public Component getRandomPingMessage(@Nullable final String mode) {
|
||||
if (plugin.isTaskRunning() && !plugin.getRunnable().shouldEnable() && hasTimerSpecificPingMessages()) {
|
||||
return getPingMessage(timerSpecificPingMessages, mode);
|
||||
}
|
||||
return pingMessages.isEmpty() ? Component.empty() : getPingMessage(pingMessages, mode);
|
||||
}
|
||||
|
||||
private List<Component> loadPingMessages(final String path) {
|
||||
final List<String> list = config.getStringList(path);
|
||||
final List<Component> components = new ArrayList<>(list.size());
|
||||
for (final String s : list) {
|
||||
components.add(MiniMessage.miniMessage().deserialize(replaceNewlineVar(s)));
|
||||
private Component getPingMessage(final DisplayedMessages list, @Nullable final String mode) {
|
||||
final String component = list.getRandomEntry(mode);
|
||||
return parse(plugin.replacePingVariables(component, mode));
|
||||
}
|
||||
|
||||
private DisplayedMessages loadPingMessages(final ConfigSection section) {
|
||||
return loadDisplayedMessages(section, (list, s) -> list.add(replaceNewlineVar(s)));
|
||||
}
|
||||
|
||||
private DisplayedMessages loadPlayerCountHoverMessages(final ConfigSection section) {
|
||||
return loadDisplayedMessages(section, (list, s) -> {
|
||||
// Split into separate messages (one per player entry)
|
||||
for (final String line : s.split("<br>")) {
|
||||
list.add(toLegacy(parse(line)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DisplayedMessages loadPlayerCountMessages(final ConfigSection section) {
|
||||
return loadDisplayedMessages(section, (list, s) -> list.add(toLegacy(parse(s))));
|
||||
}
|
||||
|
||||
private DisplayedMessages loadDisplayedMessages(final ConfigSection section, final BiConsumer<List<String>, String> messageFunction) {
|
||||
final Map<String, List<String>> modes = new HashMap<>(section.getKeys().size());
|
||||
for (final Map.Entry<String, Object> entry : section.getValues().entrySet()) {
|
||||
final List<String> components = new ArrayList<>();
|
||||
if (entry.getValue() instanceof String s) {
|
||||
messageFunction.accept(components, s);
|
||||
} else if (entry.getValue() instanceof List<?> list) {
|
||||
//noinspection unchecked
|
||||
for (final String s : ((Iterable<String>) list)) {
|
||||
messageFunction.accept(components, s);
|
||||
}
|
||||
}
|
||||
modes.put(entry.getKey(), components);
|
||||
}
|
||||
return components;
|
||||
return new DisplayedMessages(modes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -395,7 +545,6 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean removeWhitelistedPlayer(final String name) {
|
||||
UUID uuid = null;
|
||||
@@ -410,18 +559,19 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return false;
|
||||
}
|
||||
|
||||
whitelistedPlayers.remove(uuid);
|
||||
whitelist.remove(uuid.toString());
|
||||
saveWhitelistedPlayers();
|
||||
return true;
|
||||
return removeWhitelistedPlayer(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addWhitelistedPlayer(final UUID uuid, final String name) {
|
||||
final boolean added = whitelistedPlayers.put(uuid, name) == null;
|
||||
final boolean added = !name.equals(whitelistedPlayers.put(uuid, name));
|
||||
if (!added) {
|
||||
return false;
|
||||
}
|
||||
|
||||
whitelist.set(uuid.toString(), name);
|
||||
saveWhitelistedPlayers();
|
||||
return added;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -439,8 +589,47 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return maintenance;
|
||||
}
|
||||
|
||||
public void setMaintenance(final boolean maintenance) {
|
||||
this.maintenance = maintenance;
|
||||
@Override
|
||||
public @Nullable String activeMode() {
|
||||
return activeMode;
|
||||
}
|
||||
|
||||
public void setActiveMode(@Nullable final String activeMode) {
|
||||
final String normalized = normalizeMode(activeMode);
|
||||
if (Objects.equals(this.activeMode, normalized)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeMode = normalized;
|
||||
config.set("active-mode", normalized != null ? normalized : "default");
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String activeReason() {
|
||||
return activeReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveReason(@Nullable final String reason) {
|
||||
final String normalized = normalizeReason(reason);
|
||||
if (Objects.equals(this.activeReason, normalized)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeReason = normalized;
|
||||
config.set("active-reason", normalized);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
public void setMaintenance(final boolean maintenance, @Nullable final String mode) {
|
||||
if (this.maintenance != maintenance) {
|
||||
this.maintenance = maintenance;
|
||||
config.set("maintenance-enabled", maintenance);
|
||||
this.activeMode = normalizeMode(mode);
|
||||
config.set("active-mode", this.activeMode != null ? this.activeMode : "default");
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -484,7 +673,10 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
}
|
||||
|
||||
public void setSavedEndtimer(final long millis) {
|
||||
if (savedEndtimer == millis) return;
|
||||
if (savedEndtimer == millis) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savedEndtimer = millis;
|
||||
config.getSection("continue-endtimer-after-restart").set("end", millis);
|
||||
saveConfig();
|
||||
@@ -494,7 +686,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return config;
|
||||
}
|
||||
|
||||
public List<Component> getPingMessages() {
|
||||
public DisplayedMessages getPingMessages() {
|
||||
return pingMessages;
|
||||
}
|
||||
|
||||
@@ -506,8 +698,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return commandsOnMaintenanceDisable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Component> getTimerSpecificPingMessages() {
|
||||
public @Nullable DisplayedMessages getTimerSpecificPingMessages() {
|
||||
return timerSpecificPingMessages;
|
||||
}
|
||||
|
||||
@@ -515,22 +706,60 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return broadcastIntervals;
|
||||
}
|
||||
|
||||
// Yikeseroo
|
||||
public String getPlayerCountMessage() {
|
||||
return LegacyComponentSerializer.legacySection().serialize(plugin.replacePingVariables(playerCountMessage));
|
||||
public boolean isWebhookEnabled() {
|
||||
return webhookEnabled;
|
||||
}
|
||||
|
||||
public String[] getPlayerCountHoverLines() {
|
||||
final String[] lines = new String[playerCountHoverLines.size()];
|
||||
for (int i = 0; i < playerCountHoverLines.size(); i++) {
|
||||
final Component component = plugin.replacePingVariables(playerCountHoverLines.get(i));
|
||||
lines[i] = LegacyComponentSerializer.legacySection().serialize(component);
|
||||
public boolean isWebhookEventEnabled(final String event) {
|
||||
return webhookEnabledEvents.contains(event);
|
||||
}
|
||||
|
||||
public String getWebhookUrl() {
|
||||
return webhookUrl;
|
||||
}
|
||||
|
||||
public String getWebhookUsername() {
|
||||
return webhookUsername;
|
||||
}
|
||||
|
||||
public String getWebhookAvatarUrl() {
|
||||
return webhookAvatarUrl;
|
||||
}
|
||||
|
||||
public String getPlainTextPrefix() {
|
||||
return plainTextPrefix;
|
||||
}
|
||||
|
||||
public String getLegacyParsedPlayerCountMessage() {
|
||||
return getLegacyParsedPlayerCountMessage(activeMode);
|
||||
}
|
||||
|
||||
public String getLegacyParsedPlayerCountMessage(@Nullable final String mode) {
|
||||
final DisplayedMessages messages = timerDependent(legacyParsedPlayerCountMessages, legacyParsedTimerPlayerCountMessages);
|
||||
final String message = messages.getRandomEntry(mode);
|
||||
return plugin.replacePingVariables(message, mode);
|
||||
}
|
||||
|
||||
public String[] getLegacyParsedPlayerCountHoverLines() {
|
||||
return getLegacyParsedPlayerCountHoverLines(activeMode);
|
||||
}
|
||||
|
||||
public String[] getLegacyParsedPlayerCountHoverLines(@Nullable final String mode) {
|
||||
final DisplayedMessages messages = timerDependent(legacyParsedPlayerCountHoverLines, legacyParsedTimerPlayerCountHoverLines);
|
||||
final List<String> lines = messages.getMessages(mode);
|
||||
final String[] parsedLines = new String[lines.size()];
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
parsedLines[i] = plugin.replacePingVariables(lines.get(i), mode);
|
||||
}
|
||||
return lines;
|
||||
return parsedLines;
|
||||
}
|
||||
|
||||
private <T> T timerDependent(final T normal, final @Nullable T timerSpecific) {
|
||||
return timerSpecific != null && plugin.isTaskRunning() && !plugin.getRunnable().shouldEnable() ? timerSpecific : normal;
|
||||
}
|
||||
|
||||
public Component getKickMessage() {
|
||||
return plugin.replacePingVariables(getMessage("kickmessage"));
|
||||
return parse(plugin.replacePingVariables(getLanguageString("kickmessage")));
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
@@ -541,8 +770,20 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return customPlayerCountMessage;
|
||||
}
|
||||
|
||||
public boolean hasCustomPlayerCountHoverMessage() {
|
||||
return customPlayerCountHoverMessage;
|
||||
}
|
||||
|
||||
protected Component parse(final String s) {
|
||||
return MiniMessage.miniMessage().deserialize(s);
|
||||
}
|
||||
|
||||
protected String toLegacy(final Component component) {
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note on why this even exists: Yaml will force save all strings containing line breaks '\n' in this rather chunky format:
|
||||
* Note on why this even exists: YAML will force save all strings containing line breaks '\n' in this rather chunky format:
|
||||
* key: |-
|
||||
* First line text
|
||||
* Second line text.
|
||||
@@ -555,6 +796,17 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return s.replace(NEW_LINE_REPLACEMENT, "\n");
|
||||
}
|
||||
|
||||
protected @Nullable String normalizeMode(@Nullable final String mode) {
|
||||
if (mode == null || mode.isBlank() || mode.equalsIgnoreCase("default")) {
|
||||
return null;
|
||||
}
|
||||
return mode.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private String normalizeReason(@Nullable final String reason) {
|
||||
return reason == null || reason.isBlank() ? "" : reason;
|
||||
}
|
||||
|
||||
protected void loadExtraSettings() {
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,7 +20,7 @@ package eu.kennytv.maintenance.core.command;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -28,23 +28,29 @@ import eu.kennytv.maintenance.core.command.subcommand.MotdCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.ReloadCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.RemoveMotdCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.ScheduleTimerCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.SetReasonCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.SetMotdCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.StarttimerCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.StatusCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.ToggleCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.UpdateCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.WhitelistAddCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.WhitelistCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.WhitelistRemoveCommand;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class MaintenanceCommand {
|
||||
private static final long MAX_TASK_DURATION_SECONDS = TimeUnit.DAYS.toSeconds(28);
|
||||
protected final MaintenancePlugin plugin;
|
||||
protected final Settings settings;
|
||||
private final Map<String, CommandInfo> commandExecutors = new LinkedHashMap<>();
|
||||
@@ -70,6 +76,7 @@ public abstract class MaintenanceCommand {
|
||||
add(new SetMotdCommand(plugin), "setmotd");
|
||||
add(new RemoveMotdCommand(plugin), "removemotd");
|
||||
add(new MotdCommand(plugin), "motd");
|
||||
add(new SetReasonCommand(plugin), "setreason");
|
||||
|
||||
add(new UpdateCommand(plugin), "update", "forceupdate");
|
||||
add(new DumpCommand(plugin), "dump");
|
||||
@@ -93,7 +100,7 @@ public abstract class MaintenanceCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandInfo command = commandExecutors.get(args[0].toLowerCase());
|
||||
final CommandInfo command = commandExecutors.get(args[0].toLowerCase(Locale.ROOT));
|
||||
if (command == null) {
|
||||
help.sendUsage(sender);
|
||||
return;
|
||||
@@ -108,7 +115,7 @@ public abstract class MaintenanceCommand {
|
||||
|
||||
public List<String> getSuggestions(final SenderInfo sender, final String[] args) {
|
||||
if (!sender.hasMaintenancePermission("command") || args.length == 0) return Collections.emptyList();
|
||||
final String s = args[0].toLowerCase();
|
||||
final String s = args[0].toLowerCase(Locale.ROOT);
|
||||
if (args.length == 1) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final Map.Entry<String, CommandInfo> entry : commandExecutors.entrySet()) {
|
||||
@@ -125,33 +132,63 @@ public abstract class MaintenanceCommand {
|
||||
return info != null && info.hasPermission(sender) ? info.getTabCompletion(sender, args) : Collections.emptyList();
|
||||
}
|
||||
|
||||
public boolean checkTimerArgs(final SenderInfo sender, final String time, final boolean taskCheck) {
|
||||
if (!plugin.isNumeric(time)) return true;
|
||||
if (taskCheck) {
|
||||
if (plugin.isTaskRunning()) {
|
||||
sender.send(settings.getMessage("timerAlreadyRunning"));
|
||||
return true;
|
||||
/**
|
||||
* Converts a user given time like {@code 2d}, {@code 2d12h}, {@code 1h5m}, or {@code 30s} to an ISO-8601 duration string.
|
||||
* Days are supported in addition to hours, minutes, and seconds.
|
||||
*
|
||||
* @return ISO duration string
|
||||
*/
|
||||
private static String toIsoDuration(final String time) {
|
||||
final String upper = time.toUpperCase(Locale.ROOT);
|
||||
final int dayIndex = upper.indexOf('D');
|
||||
if (dayIndex == -1) {
|
||||
return "PT" + upper;
|
||||
}
|
||||
|
||||
final String days = upper.substring(0, dayIndex + 1);
|
||||
final String rest = upper.substring(dayIndex + 1);
|
||||
return rest.isEmpty() ? "P" + days : "P" + days + "T" + rest;
|
||||
}
|
||||
|
||||
public @Nullable Duration parseDurationAndCheckTask(final SenderInfo sender, final String time, final boolean taskCheck) {
|
||||
final Duration duration;
|
||||
if (plugin.isNumeric(time)) {
|
||||
// Assume minutes by default as per old command behavior
|
||||
duration = Duration.ofMinutes(Integer.parseInt(time));
|
||||
} else {
|
||||
try {
|
||||
duration = Duration.parse(toIsoDuration(time));
|
||||
} catch (final DateTimeParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final int minutes = Integer.parseInt(time);
|
||||
if (minutes > 40320) {
|
||||
if (taskCheck) {
|
||||
if (plugin.isTaskRunning()) {
|
||||
sender.send(settings.getMessage("timerAlreadyRunning"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final long seconds = duration.getSeconds();
|
||||
if (seconds > MAX_TASK_DURATION_SECONDS) {
|
||||
sender.send(settings.getMessage("timerTooLong"));
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
if (minutes < 1) {
|
||||
sender.sendMessage("§8§o[kennytv whispers to you] §7§oThink about running a timer for a negative amount of minutes. Doesn't work §lthat §7§owell.");
|
||||
return true;
|
||||
if (seconds < 1) {
|
||||
sender.sendRich("<i><dark_gray>[kennytv whispers to you] <gray>Think about running a timer for a negative amount of minutes. Doesn't work <b>that</b> <gray>well.");
|
||||
return null;
|
||||
}
|
||||
return false;
|
||||
return duration;
|
||||
}
|
||||
|
||||
public boolean checkTimerArgs(final SenderInfo sender, final String time) {
|
||||
return checkTimerArgs(sender, time, true);
|
||||
public @Nullable Duration parseDurationAndCheckTask(final SenderInfo sender, final String time) {
|
||||
return parseDurationAndCheckTask(sender, time, true);
|
||||
}
|
||||
|
||||
protected void addToggleAndTimerCommands() {
|
||||
add(new ToggleCommand(plugin), "on", "off");
|
||||
add(new StatusCommand(plugin), "status");
|
||||
|
||||
add(new StarttimerCommand(plugin), "starttimer", "start");
|
||||
add(new EndtimerCommand(plugin), "endtimer", "end");
|
||||
@@ -159,24 +196,6 @@ public abstract class MaintenanceCommand {
|
||||
add(new AbortTimerCommand(plugin), "aborttimer", "abort");
|
||||
}
|
||||
|
||||
public void checkForUpdate(final SenderInfo sender) {
|
||||
if (plugin.updateAvailable()) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§cNewest version available: §aVersion " + plugin.getNewestVersion() + "§c, you're on §a" + plugin.getVersion());
|
||||
// Ore sad :(
|
||||
if (plugin.getServerType() != ServerType.SPONGE) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§c§lWARNING: §cYou will have to restart the server to prevent further issues and to complete the update! If you can't do that, don't update!");
|
||||
sendUpdateMessage(sender);
|
||||
} else {
|
||||
sender.sendMessage(plugin.getPrefix() + "§aDownload the latest version on the Ore plugin page: §bhttps://ore.spongepowered.org/KennyTV/Maintenance");
|
||||
}
|
||||
} else
|
||||
sender.sendMessage(plugin.getPrefix() + "§aYou already have the latest version of the plugin!");
|
||||
}
|
||||
|
||||
protected void sendUpdateMessage(final SenderInfo sender) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§eUse §c§l/maintenance forceupdate §eto update!");
|
||||
}
|
||||
|
||||
public List<String> getServersCompletion(final String s) {
|
||||
return null;
|
||||
}
|
||||
@@ -189,8 +208,6 @@ public abstract class MaintenanceCommand {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void sendDumpMessage(final SenderInfo sender, final String url);
|
||||
|
||||
public List<CommandInfo> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -32,6 +32,6 @@ public final class DebugCommand extends CommandInfo {
|
||||
if (checkArgs(sender, args, 1)) return;
|
||||
|
||||
plugin.setDebug(!plugin.isDebug());
|
||||
sender.sendMessage("Debug mode is now " + (plugin.isDebug() ? "enabled" : "disabled"));
|
||||
sender.sendPrefixedRich("<gray>Debug mode is now " + (plugin.isDebug() ? "<green>enabled" : "<red>disabled"));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,8 +20,14 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import static eu.kennytv.maintenance.core.util.ComponentUtil.clickEvent;
|
||||
|
||||
public final class DumpCommand extends CommandInfo {
|
||||
private long lastDump;
|
||||
@@ -34,25 +40,29 @@ public final class DumpCommand extends CommandInfo {
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 1)) return;
|
||||
if (System.currentTimeMillis() - lastDump < TimeUnit.MINUTES.toMillis(5)) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§cYou can only create a dump every 5 minutes!");
|
||||
sender.sendPrefixedRich("<red>You can only create a dump every 5 minutes!");
|
||||
return;
|
||||
}
|
||||
|
||||
lastDump = System.currentTimeMillis();
|
||||
sender.sendMessage(plugin.getPrefix() + "§7The dump is being created, this might take a moment.");
|
||||
sender.sendPrefixedRich("<gray>The dump is being created, this might take a moment.");
|
||||
plugin.async(() -> {
|
||||
final String key = plugin.pasteDump();
|
||||
if (key == null) {
|
||||
if (sender.isPlayer()) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§cCould not paste dump (see the console for details)");
|
||||
sender.sendPrefixedRich("<red>Could not paste dump (see the console for details)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final String url = "https://hastebin.com/" + key;
|
||||
sender.sendMessage(plugin.getPrefix() + "§c" + url);
|
||||
final String url = "https://pastes.dev/" + key;
|
||||
sender.sendPrefixedRich("<red><click:open_url:'" + url + "'>" + url + "</click>");
|
||||
if (sender.isPlayer()) {
|
||||
plugin.getCommandManager().sendDumpMessage(sender, url);
|
||||
final TextComponent text = Component.text().content("Click here to copy the link").color(NamedTextColor.GRAY)
|
||||
.clickEvent(clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, url))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Click here to copy the link to your clipboard").color(NamedTextColor.GREEN)))
|
||||
.build();
|
||||
sender.send(plugin.prefix().append(text));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+14
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,9 +19,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class EndtimerCommand extends CommandInfo {
|
||||
|
||||
@@ -32,7 +33,9 @@ public final class EndtimerCommand extends CommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 2)) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[1])) {
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[1]);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
@@ -41,7 +44,12 @@ public final class EndtimerCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(Integer.parseInt(args[1]), TimeUnit.MINUTES, false);
|
||||
sender.send(getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
plugin.startMaintenanceRunnable(duration, false);
|
||||
|
||||
final Component message = getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookEndtimerStarted", DiscordWebhook.EventType.ENDTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -21,10 +21,9 @@ import com.google.common.base.Preconditions;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class HelpCommand extends CommandInfo {
|
||||
private static final int COMMANDS_PER_PAGE = 8;
|
||||
@@ -86,7 +85,7 @@ public final class HelpCommand extends CommandInfo {
|
||||
if (page * COMMANDS_PER_PAGE < commands.size()) {
|
||||
sender.send(getMessage("helpNextPage", "%PAGE%", Integer.toString(page + 1)));
|
||||
} else {
|
||||
sender.sendMessage("§8× §eVersion " + plugin.getVersion() + " §7by §bkennytv");
|
||||
sender.sendRich("<dark_gray>× <yellow>Version: " + plugin.getVersion() + " <gray>by <aqua>kennytv");
|
||||
}
|
||||
|
||||
sender.send(header);
|
||||
|
||||
+54
-15
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,12 +19,14 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class MotdCommand extends CommandInfo {
|
||||
|
||||
@@ -34,21 +36,29 @@ public final class MotdCommand extends CommandInfo {
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
sendList(sender, getSettings().getPingMessages());
|
||||
} else if (args.length == 2 && args[1].equalsIgnoreCase("timer")) {
|
||||
int indexOffset = 1;
|
||||
boolean timer = false;
|
||||
if (args.length > indexOffset && args[indexOffset].equalsIgnoreCase("timer")) {
|
||||
if (!getSettings().hasTimerSpecificPingMessages()) {
|
||||
sender.send(getMessage("timerMotdDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
sendList(sender, getSettings().getTimerSpecificPingMessages());
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
timer = true;
|
||||
indexOffset++;
|
||||
}
|
||||
|
||||
if (args.length > indexOffset + 1) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final String mode = args.length == indexOffset + 1 ? args[indexOffset].toLowerCase(Locale.ROOT) : "default";
|
||||
final ConfigSection section = getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages");
|
||||
sendList(sender, getModeMessages(section, mode));
|
||||
}
|
||||
|
||||
private void sendList(final SenderInfo sender, final List<Component> list) {
|
||||
private void sendList(final SenderInfo sender, @Nullable final List<String> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
sender.send(getMessage("motdListEmpty"));
|
||||
return;
|
||||
@@ -56,15 +66,44 @@ public final class MotdCommand extends CommandInfo {
|
||||
|
||||
sender.send(getMessage("motdList"));
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
sender.sendMessage("§b" + (i + 1) + "§8§m---------");
|
||||
sender.send(list.get(i));
|
||||
sender.sendRich("<aqua>" + (i + 1) + "<dark_gray><st>---------");
|
||||
sender.sendRich(list.get(i).replace("<br>", "\n"));
|
||||
}
|
||||
sender.sendMessage("§8§m----------");
|
||||
sender.sendRich("<dark_gray><st>----------");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2 || !getSettings().hasTimerSpecificPingMessages()) return Collections.emptyList();
|
||||
return Collections.singletonList("timer");
|
||||
if (args.length < 2 || args.length > 3) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
if (getSettings().hasTimerSpecificPingMessages()) {
|
||||
list.add("timer");
|
||||
}
|
||||
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection("messages").getKeys());
|
||||
modes.remove("default");
|
||||
list.addAll(modes);
|
||||
return list;
|
||||
}
|
||||
|
||||
if (!args[1].equalsIgnoreCase("timer") || !getSettings().hasTimerSpecificPingMessages()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection("timer-messages").getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
private @Nullable List<String> getModeMessages(final ConfigSection section, final String mode) {
|
||||
final Object value = section.getObject(mode);
|
||||
if (value instanceof String s) {
|
||||
return Collections.singletonList(s);
|
||||
}
|
||||
return section.getStringList(mode);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
+71
-17
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,12 +20,12 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class RemoveMotdCommand extends CommandInfo {
|
||||
|
||||
@@ -34,31 +34,49 @@ public final class RemoveMotdCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, String[] args) {
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
int indexOffset = 1;
|
||||
boolean timerPingMessages = false;
|
||||
if (args.length == 3 && args[1].equalsIgnoreCase("timer")) {
|
||||
if (args.length > indexOffset && args[indexOffset].equalsIgnoreCase("timer")) {
|
||||
if (!getSettings().hasTimerSpecificPingMessages()) {
|
||||
sender.send(getMessage("timerMotdDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
args = plugin.removeArrayIndex(args, 1);
|
||||
indexOffset++;
|
||||
timerPingMessages = true;
|
||||
}
|
||||
if (checkArgs(sender, args, 2)) return;
|
||||
if (!plugin.isNumeric(args[1])) {
|
||||
|
||||
if (args.length <= indexOffset) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
String mode = "default";
|
||||
if (!plugin.isNumeric(args[indexOffset])) {
|
||||
mode = args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
indexOffset++;
|
||||
}
|
||||
|
||||
if (args.length != indexOffset + 1 || !plugin.isNumeric(args[indexOffset])) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Settings settings = getSettings();
|
||||
final List<String> pingMessages = settings.getConfig().getStringList(timerPingMessages ? "timerspecific-pingmessages" : "pingmessages");
|
||||
if (pingMessages.size() < 2) {
|
||||
final ConfigSection section = settings.getConfig().getSection("ping-message");
|
||||
final ConfigSection messageSection = section.getSection(timerPingMessages ? "timer-messages" : "messages");
|
||||
final List<String> pingMessages = getModeMessages(messageSection, mode);
|
||||
if (pingMessages.isEmpty()) {
|
||||
sender.send(getMessage("motdListEmpty"));
|
||||
return;
|
||||
}
|
||||
if (mode.equals("default") && pingMessages.size() < 2) {
|
||||
sender.send(getMessage("removeMotdError"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int index = Integer.parseInt(args[1]);
|
||||
final int index = Integer.parseInt(args[indexOffset]);
|
||||
if (index == 0 || index > pingMessages.size()) {
|
||||
sender.send(getMessage("setMotdIndexError",
|
||||
"%MOTDS%", Integer.toString(pingMessages.size()),
|
||||
@@ -66,24 +84,60 @@ public final class RemoveMotdCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Component> pingComponents = timerPingMessages ? settings.getTimerSpecificPingMessages() : settings.getPingMessages();
|
||||
pingComponents.remove(index - 1);
|
||||
pingMessages.remove(index - 1);
|
||||
settings.getConfig().set(timerPingMessages ? "timerspecific-pingmessages" : "pingmessages", pingMessages);
|
||||
if (pingMessages.isEmpty()) {
|
||||
messageSection.remove(mode);
|
||||
} else {
|
||||
messageSection.set(mode, pingMessages);
|
||||
}
|
||||
settings.saveConfig();
|
||||
sender.send(getMessage("removedMotd", "%INDEX%", args[1]));
|
||||
settings.reloadConfigs();
|
||||
sender.send(getMessage("removedMotd", "%INDEX%", Integer.toString(index)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 2 || (args.length == 3 && args[1].equalsIgnoreCase("timer"))) {
|
||||
final int size = (args.length == 3 ? plugin.getSettings().getTimerSpecificPingMessages() : plugin.getSettings().getPingMessages()).size();
|
||||
final int indexOffset;
|
||||
final boolean timer;
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("timer")) {
|
||||
timer = true;
|
||||
indexOffset = 2;
|
||||
} else {
|
||||
timer = false;
|
||||
indexOffset = 1;
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1 && !plugin.isNumeric(args[indexOffset])) {
|
||||
return getModeCompletions(timer);
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1 || (args.length == indexOffset + 2 && !plugin.isNumeric(args[indexOffset]))) {
|
||||
final String mode = plugin.isNumeric(args[indexOffset]) ? "default" : args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
final int size = getModeMessages(getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages"), mode).size();
|
||||
final List<String> list = new ArrayList<>(size);
|
||||
for (int i = 1; i <= size; i++) {
|
||||
list.add(Integer.toString(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<String> getModeCompletions(final boolean timer) {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages").getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
private List<String> getModeMessages(final ConfigSection section, final String mode) {
|
||||
final Object value = section.getObject(mode);
|
||||
if (value == null) {
|
||||
return List.of();
|
||||
}
|
||||
if (value instanceof String s) {
|
||||
return new ArrayList<>(Collections.singletonList(s));
|
||||
}
|
||||
return new ArrayList<>(section.getStringList(mode));
|
||||
}
|
||||
}
|
||||
+12
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,8 +20,7 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.Duration;
|
||||
|
||||
public final class ScheduleTimerCommand extends CommandInfo {
|
||||
|
||||
@@ -32,8 +31,10 @@ public final class ScheduleTimerCommand extends CommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 3)) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[1])
|
||||
|| plugin.getCommandManager().checkTimerArgs(sender, args[2], false)) {
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[1]);
|
||||
final Duration maintenanceDuration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[2], false);
|
||||
if (enableIn == null || maintenanceDuration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
@@ -42,7 +43,11 @@ public final class ScheduleTimerCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.scheduleMaintenanceRunnable(Integer.parseInt(args[1]), Integer.parseInt(args[2]), TimeUnit.MINUTES);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
plugin.scheduleMaintenanceRunnable(enableIn, maintenanceDuration);
|
||||
sender.send(getMessage(
|
||||
"scheduletimerStarted",
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) maintenanceDuration.getSeconds())
|
||||
));
|
||||
}
|
||||
}
|
||||
+82
-33
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,14 +20,13 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static eu.kennytv.maintenance.core.Settings.NEW_LINE_REPLACEMENT;
|
||||
|
||||
@@ -38,27 +37,41 @@ public final class SetMotdCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, String[] args) {
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
int indexOffset = 1;
|
||||
boolean timerPingMessages = false;
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("timer")) {
|
||||
if (args.length > indexOffset && args[indexOffset].equalsIgnoreCase("timer")) {
|
||||
if (!getSettings().hasTimerSpecificPingMessages()) {
|
||||
sender.send(getMessage("timerMotdDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the "timer" off the args to keep the rest the code cleaner
|
||||
args = plugin.removeArrayIndex(args, 1);
|
||||
indexOffset++;
|
||||
timerPingMessages = true;
|
||||
}
|
||||
if (args.length < 4 || !plugin.isNumeric(args[1])) {
|
||||
|
||||
if (args.length <= indexOffset + 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
String mode = "default";
|
||||
if (!plugin.isNumeric(args[indexOffset])) {
|
||||
mode = args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
indexOffset++;
|
||||
}
|
||||
|
||||
if (args.length <= indexOffset + 2 || !plugin.isNumeric(args[indexOffset])) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Settings settings = getSettings();
|
||||
final List<Component> pingComponents = timerPingMessages ? settings.getTimerSpecificPingMessages() : settings.getPingMessages();
|
||||
final List<String> pingMessages = settings.getConfig().getStringList(timerPingMessages ? "timerspecific-pingmessages" : "pingmessages");
|
||||
final int index = Integer.parseInt(args[1]);
|
||||
final ConfigSection section = settings.getConfig().getSection("ping-message");
|
||||
final ConfigSection messageSection = section.getSection(timerPingMessages ? "timer-messages" : "messages");
|
||||
final List<String> pingMessages = getOrCreateModeMessages(messageSection, mode);
|
||||
|
||||
final int index = Integer.parseInt(args[indexOffset]);
|
||||
if (index == 0 || index > pingMessages.size() + 1) {
|
||||
sender.send(getMessage("setMotdIndexError",
|
||||
"%MOTDS%", Integer.toString(pingMessages.size()),
|
||||
@@ -66,60 +79,96 @@ public final class SetMotdCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.isNumeric(args[2])) {
|
||||
if (!plugin.isNumeric(args[indexOffset + 1])) {
|
||||
sender.send(getMessage("setMotdLineError"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int line = Integer.parseInt(args[2]);
|
||||
final int line = Integer.parseInt(args[indexOffset + 1]);
|
||||
if (line != 1 && line != 2) {
|
||||
sender.send(getMessage("setMotdLineError"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String message = String.join(" ", Arrays.copyOfRange(args, 3, args.length));
|
||||
final String message = String.join(" ", Arrays.copyOfRange(args, indexOffset + 2, args.length));
|
||||
final String oldMessage = index > pingMessages.size() ? "" : pingMessages.get(index - 1);
|
||||
final String newMessage;
|
||||
if (line == 1) {
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT) ?
|
||||
message + NEW_LINE_REPLACEMENT + oldMessage.split(NEW_LINE_REPLACEMENT, 2)[1] : message;
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT)
|
||||
? message + NEW_LINE_REPLACEMENT + oldMessage.split(NEW_LINE_REPLACEMENT, 2)[1]
|
||||
: message;
|
||||
} else {
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT) ?
|
||||
oldMessage.split(NEW_LINE_REPLACEMENT, 2)[0] + NEW_LINE_REPLACEMENT + message : oldMessage + NEW_LINE_REPLACEMENT + message;
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT)
|
||||
? oldMessage.split(NEW_LINE_REPLACEMENT, 2)[0] + NEW_LINE_REPLACEMENT + message
|
||||
: oldMessage + NEW_LINE_REPLACEMENT + message;
|
||||
}
|
||||
|
||||
// Replace component in config and the cached list
|
||||
final Component component = MiniMessage.miniMessage().deserialize(newMessage.replace(NEW_LINE_REPLACEMENT, "\n"));
|
||||
if (index > pingMessages.size()) {
|
||||
pingMessages.add(newMessage);
|
||||
pingComponents.add(component);
|
||||
} else {
|
||||
pingMessages.set(index - 1, newMessage);
|
||||
pingComponents.set(index - 1, component);
|
||||
}
|
||||
|
||||
settings.getConfig().set(timerPingMessages ? "timerspecific-pingmessages" : "pingmessages", pingMessages);
|
||||
messageSection.set(mode, pingMessages);
|
||||
settings.saveConfig();
|
||||
sender.send(settings.getMessage("setMotd",
|
||||
"%LINE%", args[2],
|
||||
"%INDEX%", args[1]).replaceText(builder -> builder.matchLiteral("%MOTD%").replacement(component)));
|
||||
settings.reloadConfigs();
|
||||
|
||||
sender.send(settings.getMessage(
|
||||
"setMotd",
|
||||
"%LINE%", Integer.toString(line),
|
||||
"%INDEX%", Integer.toString(index),
|
||||
"%MOTD%", newMessage.replace(NEW_LINE_REPLACEMENT, "\n")
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, String[] args) {
|
||||
boolean timer = false;
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
final int indexOffset;
|
||||
final boolean timer;
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("timer")) {
|
||||
args = plugin.removeArrayIndex(args, 1);
|
||||
timer = true;
|
||||
indexOffset = 2;
|
||||
} else {
|
||||
timer = false;
|
||||
indexOffset = 1;
|
||||
}
|
||||
if (args.length == 3) return Arrays.asList("1", "2");
|
||||
if (args.length == 2) {
|
||||
|
||||
if (args.length == indexOffset + 2) {
|
||||
return Arrays.asList("1", "2");
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1 && !plugin.isNumeric(args[indexOffset])) {
|
||||
return getModeCompletions(timer);
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1) {
|
||||
final String mode = plugin.isNumeric(args[indexOffset]) ? "default" : args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
final ConfigSection section = getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages");
|
||||
final int size = getOrCreateModeMessages(section, mode).size();
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (int i = 1; i <= (timer ? getSettings().getTimerSpecificPingMessages() : getSettings().getPingMessages()).size() + 1; i++) {
|
||||
for (int i = 1; i <= size + 1; i++) {
|
||||
list.add(String.valueOf(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<String> getModeCompletions(final boolean timer) {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages").getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
private List<String> getOrCreateModeMessages(final ConfigSection section, final String mode) {
|
||||
final Object value = section.getObject(mode);
|
||||
if (value == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (value instanceof String s) {
|
||||
return new ArrayList<>(Collections.singletonList(s));
|
||||
}
|
||||
return new ArrayList<>(section.getStringList(mode));
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class SetReasonCommand extends CommandInfo {
|
||||
|
||||
public SetReasonCommand(final MaintenancePlugin plugin) {
|
||||
super(plugin, "setreason");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final String value = String.join(" ", java.util.Arrays.copyOfRange(args, 1, args.length));
|
||||
final String reason;
|
||||
if (value.equalsIgnoreCase("none") || value.equalsIgnoreCase("clear") || value.equals("-")) {
|
||||
reason = null;
|
||||
} else {
|
||||
reason = value;
|
||||
}
|
||||
|
||||
getSettings().setActiveReason(reason);
|
||||
sender.send(getMessage("setReason", "%REASON%", reason == null ? "-" : reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList("none");
|
||||
}
|
||||
}
|
||||
+14
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,9 +19,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class StarttimerCommand extends CommandInfo {
|
||||
|
||||
@@ -32,7 +33,9 @@ public final class StarttimerCommand extends CommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 2)) return;
|
||||
if (plugin.getCommandManager().checkTimerArgs(sender, args[1])) {
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[1]);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
@@ -41,7 +44,12 @@ public final class StarttimerCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(Integer.parseInt(args[1]), TimeUnit.MINUTES, true);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
plugin.startMaintenanceRunnable(duration, true);
|
||||
|
||||
final Component message = getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookStarttimerStarted", DiscordWebhook.EventType.STARTTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class StatusCommand extends CommandInfo {
|
||||
|
||||
public StatusCommand(final MaintenancePlugin plugin) {
|
||||
super(plugin, "status");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
final MaintenanceRunnable task = plugin.getCurrentTask();
|
||||
if (task != null) {
|
||||
final String key = task.shouldEnable() ? "maintenanceStatusOffWithStartTimer" : "maintenanceStatusOnWithEndTimer";
|
||||
sender.send(getMessage(key, "%TIME%", task.getTime()));
|
||||
} else {
|
||||
sender.send(getMessage(plugin.isMaintenance() ? "maintenanceStatusOn" : "maintenanceStatusOff"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getHelpMessage() {
|
||||
return getMessage("helpGlobalStatus");
|
||||
}
|
||||
}
|
||||
+38
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,6 +20,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class ToggleCommand extends CommandInfo {
|
||||
|
||||
@@ -29,14 +33,45 @@ public final class ToggleCommand extends CommandInfo {
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 1)) return;
|
||||
if (args.length == 0 || args.length > 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
final String mode;
|
||||
if (maintenance && args.length == 2) {
|
||||
mode = args[1].toLowerCase(Locale.ROOT);
|
||||
} else if (!maintenance && args.length == 1) {
|
||||
mode = null;
|
||||
} else if (maintenance) {
|
||||
mode = null;
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance == plugin.isMaintenance()) {
|
||||
if (mode != null) {
|
||||
plugin.getSettings().setActiveMode(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage(maintenance ? "alreadyEnabled" : "alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.setMaintenance(maintenance);
|
||||
plugin.setMaintenance(maintenance, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2 || !args[0].equalsIgnoreCase("on")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> modes = new ArrayList<>(getSettings().getPingMessages().getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
}
|
||||
+35
-14
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,7 +20,11 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
public final class UpdateCommand extends CommandInfo {
|
||||
|
||||
@@ -32,24 +36,41 @@ public final class UpdateCommand extends CommandInfo {
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 1)) return;
|
||||
if (args[0].equalsIgnoreCase("update")) {
|
||||
plugin.async(() -> plugin.getCommandManager().checkForUpdate(sender));
|
||||
} else {
|
||||
if (!plugin.updateAvailable()) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§aYou already have the latest version of the plugin!");
|
||||
return;
|
||||
}
|
||||
// Ore very sad :(
|
||||
if (plugin.getServerType() == ServerType.SPONGE) {
|
||||
sender.sendMessage(plugin.getPrefix() + "§cSorry, automated downloading of the plugin is not supported on Sponge! Please download the latest version manually! :(");
|
||||
return;
|
||||
}
|
||||
plugin.async(() -> checkForUpdate(sender));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage("updateDownloading"));
|
||||
if (!plugin.updateAvailable()) {
|
||||
sender.sendPrefixedRich("<green>You already have the latest version of the plugin!");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage("updateDownloading"));
|
||||
try {
|
||||
if (plugin.installUpdate()) {
|
||||
sender.send(getMessage("updateFinished"));
|
||||
} else {
|
||||
sender.send(getMessage("updateFailed"));
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
sender.send(getMessage("updateFailed"));
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForUpdate(final SenderInfo sender) {
|
||||
if (!plugin.updateAvailable()) {
|
||||
sender.sendPrefixedRich("<green>You already have the latest version of the plugin!");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendPrefixedRich("<red>Newest version available: <green>Version " + plugin.getNewestVersion() + "<red>, you're on <green>" + plugin.getVersion());
|
||||
sender.sendPrefixedRich("<b><red>WARNING: <red>You will have to restart the server to prevent further issues and to complete the update! If you can't do that, don't update!");
|
||||
|
||||
sender.send(Component.text().append(MiniMessage.miniMessage().deserialize("<gold>× <dark_gray>[<green>Update<dark_gray>]"))
|
||||
.clickEvent(ClickEvent.runCommand("/maintenance forceupdate"))
|
||||
.hoverEvent(HoverEvent.showText(Component.text().content("Click here to update the plugin").color(NamedTextColor.GREEN)))
|
||||
.append(Component.text().content(", or manually run ").color(NamedTextColor.GRAY))
|
||||
.append(Component.text().content("/maintenance forceupdate").color(NamedTextColor.RED)).build());
|
||||
}
|
||||
}
|
||||
+19
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -25,6 +25,7 @@ import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class WhitelistAddCommand extends CommandInfo {
|
||||
|
||||
@@ -64,7 +65,13 @@ public final class WhitelistAddCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
private void addPlayerToWhitelist(final SenderInfo sender, final String name) {
|
||||
plugin.getOfflinePlayer(name, selected -> {
|
||||
plugin.getOfflinePlayer(name).whenComplete((selected, ex) -> {
|
||||
if (ex != null) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error while fetching offline player", ex);
|
||||
sender.send(getMessage("offlinePlayerFetchError"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == null) {
|
||||
sender.send(getMessage("playerNotOnline"));
|
||||
return;
|
||||
@@ -75,7 +82,13 @@ public final class WhitelistAddCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
private void addPlayerToWhitelist(final SenderInfo sender, final UUID uuid) {
|
||||
plugin.getOfflinePlayer(uuid, selected -> {
|
||||
plugin.getOfflinePlayer(uuid).whenComplete((selected, ex) -> {
|
||||
if (ex != null) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error while fetching offline player", ex);
|
||||
sender.send(getMessage("offlinePlayerFetchError"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == null) {
|
||||
sender.send(getMessage("playerNotFoundUuid"));
|
||||
return;
|
||||
@@ -86,10 +99,10 @@ public final class WhitelistAddCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
private void addPlayerToWhitelist(final SenderInfo sender, final SenderInfo selected) {
|
||||
if (getSettings().addWhitelistedPlayer(selected.getUuid(), selected.getName())) {
|
||||
sender.send(getMessage("whitelistAdded", "%PLAYER%", selected.getName()));
|
||||
if (getSettings().addWhitelistedPlayer(selected.uuid(), selected.name())) {
|
||||
sender.send(getMessage("whitelistAdded", "%PLAYER%", selected.name()));
|
||||
} else {
|
||||
sender.send(getMessage("whitelistAlreadyAdded", "%PLAYER%", selected.getName()));
|
||||
sender.send(getMessage("whitelistAlreadyAdded", "%PLAYER%", selected.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -20,10 +20,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class WhitelistCommand extends CommandInfo {
|
||||
|
||||
|
||||
+9
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -24,6 +24,7 @@ import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
@@ -50,8 +51,9 @@ public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
if (args.length != 2) return Collections.emptyList();
|
||||
|
||||
final List<String> list = new ArrayList<>();
|
||||
String matchNameLowerCase = args[1].toLowerCase(Locale.ROOT);
|
||||
for (final String name : getSettings().getWhitelistedPlayers().values()) {
|
||||
if (name.toLowerCase().startsWith(args[1])) {
|
||||
if (name.toLowerCase(Locale.ROOT).startsWith(matchNameLowerCase)) {
|
||||
list.add(name);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +61,7 @@ public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
private void removePlayerFromWhitelist(final SenderInfo sender, final String name) {
|
||||
plugin.getOfflinePlayer(name, selected -> {
|
||||
plugin.getOfflinePlayer(name).whenComplete((selected, ex) -> {
|
||||
if (selected == null) {
|
||||
if (getSettings().removeWhitelistedPlayer(name)) {
|
||||
sender.send(getMessage("whitelistRemoved", "%PLAYER%", name));
|
||||
@@ -69,22 +71,22 @@ public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
removePlayerFromWhitelist(sender, selected.getUuid(), selected.getName());
|
||||
removePlayerFromWhitelist(sender, selected.uuid(), selected.name());
|
||||
});
|
||||
}
|
||||
|
||||
private void removePlayerFromWhitelist(final SenderInfo sender, final UUID uuid) {
|
||||
plugin.getOfflinePlayer(uuid, selected -> {
|
||||
plugin.getOfflinePlayer(uuid).whenComplete((selected, ex) -> {
|
||||
if (selected == null) {
|
||||
removePlayerFromWhitelist(sender, uuid, uuid.toString());
|
||||
} else {
|
||||
removePlayerFromWhitelist(sender, selected.getUuid(), selected.getName());
|
||||
removePlayerFromWhitelist(sender, selected.uuid(), selected.name());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removePlayerFromWhitelist(final SenderInfo sender, final UUID uuid, final String toReplace) {
|
||||
if (getSettings().removeWhitelistedPlayer(uuid)) {
|
||||
if (plugin.getSettings().removeWhitelistedPlayer(uuid)) {
|
||||
sender.send(getMessage("whitelistRemoved", "%PLAYER%", toReplace));
|
||||
} else {
|
||||
sender.send(getMessage("whitelistNotFound"));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,23 +17,20 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.config;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Further modified version of the SimpleConfig project of PSandro (https://github.com/PSandro/SimpleConfig).
|
||||
* Further modified version of the <a href="https://github.com/PSandro/SimpleConfig">SimpleConfig</a>SimpleConfig project of PSandro.
|
||||
*
|
||||
* @author PSandro on 26.01.19
|
||||
* @author kennytv
|
||||
@@ -41,17 +38,20 @@ import java.util.Set;
|
||||
public final class Config extends ConfigSection {
|
||||
|
||||
private static final String AWESOME_HEADER =
|
||||
"###################################################################################################################\n" +
|
||||
"# __ __ _ _ _ _ _ #\n" +
|
||||
"# | \\/ | __ _(_)_ __ | |_ ___ _ __ __ _ _ __ ___ ___ | |__ _ _ | | _____ _ __ _ __ _ _| |___ __ #\n" +
|
||||
"# | |\\/| |/ _` | | '_ \\| __/ _ \\ '_ \\ / _` | '_ \\ / __/ _ \\ | '_ \\| | | | | |/ / _ \\ '_ \\| '_ \\| | | | __\\ \\ / / #\n" +
|
||||
"# | | | | (_| | | | | | || __/ | | | (_| | | | | (_| __/ | |_) | |_| | | < __/ | | | | | | |_| | |_ \\ V / #\n" +
|
||||
"# |_| |_|\\__,_|_|_| |_|\\__\\___|_| |_|\\__,_|_| |_|\\___\\___| |_.__/ \\__, | |_|\\_\\___|_| |_|_| |_|\\__, |\\__| \\_/ #\n" +
|
||||
"# |___/ |___/ #\n" +
|
||||
"###################################################################################################################\n" +
|
||||
"# You can report bugs here: https://github.com/kennytv/Maintenance/issues\n" +
|
||||
"# If you need any other help/support, you can also join my Discord server: https://discord.gg/vGCUzHq\n" +
|
||||
"# The config and language files use MiniMessage, NOT legacy text for input. Use https://webui.adventure.kyori.net/ to edit and preview the formatted text.";
|
||||
"""
|
||||
###################################################################################################################
|
||||
# __ __ _ _ _ _ _ #
|
||||
# | \\/ | __ _(_)_ __ | |_ ___ _ __ __ _ _ __ ___ ___ | |__ _ _ | | _____ _ __ _ __ _ _| |___ __ #
|
||||
# | |\\/| |/ _` | | '_ \\| __/ _ \\ '_ \\ / _` | '_ \\ / __/ _ \\ | '_ \\| | | | | |/ / _ \\ '_ \\| '_ \\| | | | __\\ \\ / / #
|
||||
# | | | | (_| | | | | | || __/ | | | (_| | | | | (_| __/ | |_) | |_| | | < __/ | | | | | | |_| | |_ \\ V / #
|
||||
# |_| |_|\\__,_|_|_| |_|\\__\\___|_| |_|\\__,_|_| |_|\\___\\___| |_.__/ \\__, | |_|\\_\\___|_| |_|_| |_|\\__, |\\__| \\_/ #
|
||||
# |___/ |___/ #
|
||||
###################################################################################################################
|
||||
# You can report bugs here: https://github.com/kennytv/Maintenance/issues
|
||||
# If you need any other help/support, you can also join my Discord server: https://discord.gg/vGCUzHq
|
||||
# The config and language files use MiniMessage, NOT legacy text for input. Use https://webui.advntr.dev/ to edit and preview the formatted text.
|
||||
# For a full list of formats and fancy examples of MiniMessage, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
""";
|
||||
private final Yaml yaml = createYaml();
|
||||
private final File file;
|
||||
private final Set<String> unsupportedFields;
|
||||
@@ -61,11 +61,11 @@ public final class Config extends ConfigSection {
|
||||
public Config(final File file, final String... unsupportedFields) {
|
||||
super(null, "");
|
||||
this.file = file;
|
||||
this.unsupportedFields = unsupportedFields.length == 0 ? Collections.emptySet() : Sets.newHashSet(unsupportedFields);
|
||||
this.unsupportedFields = unsupportedFields.length == 0 ? Collections.emptySet() : Set.of(unsupportedFields);
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
final String data = new String(Files.readAllBytes(this.file.toPath()), StandardCharsets.UTF_8);
|
||||
final String data = Files.readString(this.file.toPath());
|
||||
final Map<String, Object> map = yaml.load(data);
|
||||
this.values = map != null ? map : new LinkedHashMap<>();
|
||||
this.comments = ConfigSerializer.deserializeComments(data);
|
||||
@@ -101,38 +101,15 @@ public final class Config extends ConfigSection {
|
||||
}
|
||||
|
||||
public void saveTo(final File file) throws IOException {
|
||||
final byte[] bytes = toString().getBytes(StandardCharsets.UTF_8);
|
||||
if (file.getParentFile() != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
Files.write(file.toPath(), bytes);
|
||||
Files.writeString(file.toPath(), toString());
|
||||
}
|
||||
|
||||
public boolean addMissingFields(final Map<String, Object> fields, final Map<String, String[]> comments) {
|
||||
// Note: Only scans for the first two levels
|
||||
boolean changed = false;
|
||||
for (final Map.Entry<String, Object> entry : fields.entrySet()) {
|
||||
final Object o = values.get(entry.getKey());
|
||||
if (o != null) {
|
||||
final Object o2 = entry.getValue();
|
||||
if (!(o instanceof Map) || !(o2 instanceof Map)) continue;
|
||||
|
||||
final Map<String, Object> deepMap = (Map<String, Object>) o2;
|
||||
for (final Map.Entry<String, Object> deepEntry : ((Map<String, Object>) o2).entrySet()) {
|
||||
if (deepMap.containsKey(deepEntry.getKey())) continue;
|
||||
deepMap.put(deepEntry.getKey(), deepEntry.getValue());
|
||||
changed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
this.comments = new HashMap<>(comments);
|
||||
return changed;
|
||||
public void replaceComments(final Config fromConfig) {
|
||||
this.comments = new HashMap<>(fromConfig.comments);
|
||||
}
|
||||
|
||||
private static Yaml createYaml() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -17,15 +17,14 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.config;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Further modified version of the SimpleConfig project of PSandro (https://github.com/PSandro/SimpleConfig).
|
||||
* Further modified version of the <a href="https://github.com/PSandro/SimpleConfig">SimpleConfig</a>SimpleConfig project of PSandro.
|
||||
*
|
||||
* @author kennytv
|
||||
*/
|
||||
@@ -36,7 +35,7 @@ public class ConfigSection {
|
||||
protected Map<String, Object> values;
|
||||
|
||||
ConfigSection(final Config root, final String currentPath) {
|
||||
this(root, currentPath, new HashMap<>());
|
||||
this(root, currentPath, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
ConfigSection(final Config root, final String currentPath, final Map<String, Object> values) {
|
||||
@@ -63,20 +62,18 @@ public class ConfigSection {
|
||||
|
||||
@Nullable
|
||||
public Object getObject(final String key, final Object def) {
|
||||
int i1 = -1;
|
||||
int i2;
|
||||
final String[] parts = key.split("\\.");
|
||||
ConfigSection section = this;
|
||||
while ((i1 = key.indexOf('.', i2 = i1 + 1)) != -1) {
|
||||
section = section.getSection(key.substring(i2, i1));
|
||||
if (section == null) return def;
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
section = section.getSection(parts[i]);
|
||||
if (section == null) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
final String subKey = key.substring(i2);
|
||||
if (section == this) {
|
||||
final Object result = values.get(subKey);
|
||||
return result != null ? result : def;
|
||||
}
|
||||
return section.getObject(subKey, def);
|
||||
final String valueKey = parts[parts.length - 1];
|
||||
final Object o = section.values.get(valueKey);
|
||||
return o != null ? o : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -97,17 +94,41 @@ public class ConfigSection {
|
||||
return new ConfigSection(getRoot(), getFullKeyInPath(key), (Map<String, Object>) o);
|
||||
}
|
||||
|
||||
public ConfigSection getOrCreateSection(final String key) {
|
||||
final Object o = getObject(key);
|
||||
if (!(o instanceof Map)) {
|
||||
set(key, new LinkedHashMap<>());
|
||||
return getSection(key);
|
||||
}
|
||||
return new ConfigSection(getRoot(), getFullKeyInPath(key), (Map<String, Object>) o);
|
||||
}
|
||||
|
||||
public boolean contains(final String key) {
|
||||
return getObject(key) != null;
|
||||
}
|
||||
|
||||
public void set(final String key, @Nullable final Object value) {
|
||||
//TODO go deep if necessary
|
||||
int nextSeparatorIndex = -1;
|
||||
int sectionStartIndex;
|
||||
ConfigSection section = this;
|
||||
while ((nextSeparatorIndex = key.indexOf('.', sectionStartIndex = nextSeparatorIndex + 1)) != -1) {
|
||||
section = section.getOrCreateSection(key.substring(sectionStartIndex, nextSeparatorIndex));
|
||||
}
|
||||
|
||||
final String sectionKey = key.substring(sectionStartIndex);
|
||||
if (value == null) {
|
||||
values.remove(key);
|
||||
section.values.remove(sectionKey);
|
||||
getRoot().getComments().remove(getFullKeyInPath(key));
|
||||
} else {
|
||||
values.put(key, value);
|
||||
section.values.put(sectionKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void move(final String key, final String toKey) {
|
||||
final Object o = getObject(key);
|
||||
if (o != null) {
|
||||
remove(key);
|
||||
set(toKey, o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +161,7 @@ public class ConfigSection {
|
||||
@Nullable
|
||||
public String getString(final String key, @Nullable final String def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof String ? (String) o : def;
|
||||
return o instanceof String s ? s : def;
|
||||
}
|
||||
|
||||
public int getInt(final String key) {
|
||||
@@ -149,7 +170,7 @@ public class ConfigSection {
|
||||
|
||||
public int getInt(final String key, final int def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof Number ? ((Number) o).intValue() : def;
|
||||
return o instanceof Number number ? number.intValue() : def;
|
||||
}
|
||||
|
||||
public double getDouble(final String key) {
|
||||
@@ -158,7 +179,7 @@ public class ConfigSection {
|
||||
|
||||
public double getDouble(final String key, final double def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof Number ? ((Number) o).doubleValue() : def;
|
||||
return o instanceof Number number ? number.doubleValue() : def;
|
||||
}
|
||||
|
||||
public long getLong(final String key) {
|
||||
@@ -167,7 +188,7 @@ public class ConfigSection {
|
||||
|
||||
public long getLong(final String key, final long def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof Number ? ((Number) o).longValue() : def;
|
||||
return o instanceof Number number ? number.longValue() : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -192,6 +213,28 @@ public class ConfigSection {
|
||||
return list != null ? list : def;
|
||||
}
|
||||
|
||||
public boolean addMissingFields(final ConfigSection fromSection) {
|
||||
boolean changed = false;
|
||||
for (final Map.Entry<String, Object> entry : fromSection.values.entrySet()) {
|
||||
final String key = entry.getKey();
|
||||
final Object value = this.values.get(key);
|
||||
if (value != null) {
|
||||
// Go deeper for sections
|
||||
final Object newValue = entry.getValue();
|
||||
if (!(value instanceof Map) || !(newValue instanceof Map)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changed |= this.getSection(key).addMissingFields(fromSection.getSection(key));
|
||||
} else {
|
||||
// Value is missing
|
||||
this.values.put(key, entry.getValue());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return root config
|
||||
*/
|
||||
@@ -207,6 +250,6 @@ public class ConfigSection {
|
||||
}
|
||||
|
||||
protected String getFullKeyInPath(final String key) {
|
||||
return currentPath.isEmpty() ? key : currentPath + "." + key;
|
||||
return currentPath.isEmpty() ? key : currentPath + '.' + key;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -26,7 +26,7 @@ import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Further modified version of the SimpleConfig project of PSandro (https://github.com/PSandro/SimpleConfig).
|
||||
* Further modified version of the <a href="https://github.com/PSandro/SimpleConfig">SimpleConfig</a>SimpleConfig project of PSandro.
|
||||
*
|
||||
* @author PSandro on 26.01.19
|
||||
* @author kennytv
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.config;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class DisplayedMessages {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private final Map<String, List<String>> messages;
|
||||
private final List<String> defaultMessages;
|
||||
|
||||
public DisplayedMessages(final Map<String, List<String>> messages) {
|
||||
this.messages = messages;
|
||||
this.defaultMessages = messages.get("default");
|
||||
}
|
||||
|
||||
public List<String> getMessages(@Nullable final String key) {
|
||||
Preconditions.checkNotNull(defaultMessages, "no default message configured in messages list");
|
||||
if (key == null || messages.size() == 1) {
|
||||
return defaultMessages;
|
||||
}
|
||||
return messages.getOrDefault(key, defaultMessages);
|
||||
}
|
||||
|
||||
public String getRandomEntry(@Nullable final String key) {
|
||||
final List<String> messages = getMessages(key);
|
||||
return messages.get(RANDOM.nextInt(messages.size()));
|
||||
}
|
||||
|
||||
public List<String> getDefaultMessages() {
|
||||
return defaultMessages;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return messages.isEmpty();
|
||||
}
|
||||
|
||||
public Set<String> getKeys() {
|
||||
return messages.keySet();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -24,7 +24,9 @@ import eu.kennytv.maintenance.core.Settings;
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public final class MaintenanceDump {
|
||||
private final ServerDump general;
|
||||
private final Map<String, Object> configuration;
|
||||
@@ -34,12 +36,8 @@ public final class MaintenanceDump {
|
||||
general = new ServerDump(plugin.getVersion(), plugin.getServerType().toString(), plugin.getServerVersion(), plugin.getMaintenanceServersDump());
|
||||
|
||||
configuration = new LinkedHashMap<>(settings.getConfig().getValues());
|
||||
final Object o = configuration.get("mysql");
|
||||
if (o instanceof Map) {
|
||||
final Map<String, Object> map = new LinkedHashMap<>(((Map<String, Object>) o));
|
||||
map.keySet().removeIf(key -> !key.equals("use-mysql") && !key.equals("update-interval"));
|
||||
configuration.put("mysql", map);
|
||||
}
|
||||
addDBSettings("redis", "enabled");
|
||||
redact("webhook", "url");
|
||||
configuration.put("whitelisted-players", settings.getWhitelistedPlayers());
|
||||
configuration.put("icon-exists", new File(plugin.getDataFolder(), "maintenance-icon.png").exists());
|
||||
|
||||
@@ -47,4 +45,28 @@ public final class MaintenanceDump {
|
||||
jsonObject.add("plugins", MaintenancePlugin.GSON.toJsonTree(plugin.getPlugins()));
|
||||
plugins = jsonObject;
|
||||
}
|
||||
|
||||
private void redact(final String key, final String... subKeys) {
|
||||
final Object o = configuration.get(key);
|
||||
if (o instanceof Map) {
|
||||
final Map<String, Object> map = new LinkedHashMap<>(((Map<String, Object>) o));
|
||||
for (final String subKey : subKeys) {
|
||||
final Object value = map.get(subKey);
|
||||
if (value != null) {
|
||||
map.put(subKey, value.toString().isEmpty() ? "" : "<redacted>");
|
||||
}
|
||||
}
|
||||
configuration.put(key, map);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDBSettings(final String key, final String... allowedSubKeys) {
|
||||
final Set<String> allowedSubKeysSet = Set.of(allowedSubKeys);
|
||||
final Object o = configuration.get(key);
|
||||
if (o instanceof Map) {
|
||||
final Map<String, Object> map = new LinkedHashMap<>(((Map<String, Object>) o));
|
||||
map.keySet().removeIf(subKey -> !allowedSubKeysSet.contains(subKey));
|
||||
configuration.put(key, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,11 +19,7 @@ package eu.kennytv.maintenance.core.dump;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class PluginDump {
|
||||
private final String name;
|
||||
private final String version;
|
||||
private final List<String> authors;
|
||||
|
||||
public record PluginDump(String name, String version, List<String> authors) {
|
||||
public PluginDump(final String name, final String version, final List<String> authors) {
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,16 +19,5 @@ package eu.kennytv.maintenance.core.dump;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class ServerDump {
|
||||
private final String pluginVersion;
|
||||
private final String platform;
|
||||
private final String serverVersion;
|
||||
private final List<String> maintenance;
|
||||
|
||||
public ServerDump(final String pluginVersion, final String platform, final String serverVersion, final List<String> maintenance) {
|
||||
this.pluginVersion = pluginVersion;
|
||||
this.platform = platform;
|
||||
this.serverVersion = serverVersion;
|
||||
this.maintenance = maintenance;
|
||||
}
|
||||
public record ServerDump(String pluginVersion, String platform, String serverVersion, List<String> maintenance) {
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -24,10 +24,10 @@ public final class ServerListPlusHook {
|
||||
private final ServerListPlusCore serverListPlusCore;
|
||||
|
||||
public ServerListPlusHook(final Object serverListPlus) {
|
||||
if (!(serverListPlus instanceof ServerListPlusPlugin)) {
|
||||
if (!(serverListPlus instanceof ServerListPlusPlugin serverListPlusPlugin)) {
|
||||
throw new IllegalArgumentException("Couldn't parse ServerListPlus instance!");
|
||||
}
|
||||
this.serverListPlusCore = ((ServerListPlusPlugin) serverListPlus).getCore();
|
||||
this.serverListPlusCore = serverListPlusPlugin.getCore();
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enable) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2022 kennytv (https://github.com/kennytv)
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* 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
|
||||
@@ -19,11 +19,19 @@ package eu.kennytv.maintenance.core.listener;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import static eu.kennytv.maintenance.core.util.ComponentUtil.clickEvent;
|
||||
|
||||
public abstract class JoinListenerBase {
|
||||
protected final MaintenancePlugin plugin;
|
||||
@@ -42,13 +50,16 @@ public abstract class JoinListenerBase {
|
||||
* @return true if the sender should be kicked
|
||||
*/
|
||||
protected boolean shouldKick(final SenderInfo sender, final boolean updateCheck) {
|
||||
if (!settings.isMaintenance() || sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.getUuid())) {
|
||||
if (!settings.isMaintenance() || sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.uuid())) {
|
||||
if (updateCheck) {
|
||||
updateCheck(sender);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.isWebhookEnabled()) {
|
||||
plugin.sendWebhookMessage("webhookJoinNotification", DiscordWebhook.EventType.BLOCKED_JOIN, "%PLAYER%", sender.name());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,13 +69,22 @@ public abstract class JoinListenerBase {
|
||||
|
||||
protected void updateCheck(final SenderInfo sender) {
|
||||
if (!settings.hasUpdateChecks()) return;
|
||||
if (!sender.hasPermission("maintenance.admin") || notifiedPlayers.contains(sender.getUuid())) return;
|
||||
if (!sender.hasPermission("maintenance.admin") || notifiedPlayers.contains(sender.uuid())) return;
|
||||
plugin.async(() -> {
|
||||
if (!plugin.updateAvailable()) return;
|
||||
if (!plugin.updateAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifiedPlayers.add(sender.getUuid());
|
||||
sender.sendMessage(plugin.getPrefix() + "§cThere is a newer version available: §aVersion " + plugin.getNewestVersion() + "§c, you're on §a" + plugin.getVersion());
|
||||
plugin.sendUpdateNotification(sender);
|
||||
notifiedPlayers.add(sender.uuid());
|
||||
sender.sendPrefixedRich("<red>There is a newer version available: <green>Version " + plugin.getNewestVersion() + "<red>, you're on <green>" + plugin.getVersion());
|
||||
|
||||
final TextComponent text = Component.text().content("Download it at: ").color(NamedTextColor.RED)
|
||||
.append(Component.text().content(MaintenancePlugin.HANGAR_URL).color(NamedTextColor.GOLD))
|
||||
.append(Component.text().content(" (CLICK ME)").color(NamedTextColor.GRAY).decorate(TextDecoration.BOLD))
|
||||
.clickEvent(clickEvent(ClickEvent.Action.OPEN_URL, MaintenancePlugin.HANGAR_URL))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Download the latest version").color(NamedTextColor.GREEN)))
|
||||
.build();
|
||||
sender.send(plugin.prefix().append(text));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 182 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user