Skip to content

Commit bdf0d86

Browse files
authored
Feature/gh 3/title support (#12)
* WIP: New config format, add title sender * Cleanup * Doc updates
1 parent 1c92aec commit bdf0d86

19 files changed

+579
-299
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@
22

33
## [Unreleased]
44
### Added
5+
- GH-3: Title type announcement sender (@tajobe)
56
- MIT license
67

78
### Changed
89
- MC 1.21, Java 21, Kotlin 2.1
10+
- **BREAKING**: New config format
911

1012
### Fixed
1113
- Config Auto-reload is now a repeating task as intended
1214
- Config actually created on first run
1315
- Copy updated default header when config is updated
1416

17+
### Removed
18+
- Offline variant is now the default jar, no longer producing an "online" version
19+
- debugMode option: log levels are controlled by the server's log4j configuration
20+
1521
## [1.0.0] - 2020-02-10
1622
### Added
1723
- CHANGELOG

README.md

Lines changed: 84 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ Messages can be sent server-wide or controlled by permissions after a delay and
66

77
## Features
88

9-
- Timed server-wide announcements/news messages
9+
- Timed server-wide announcements/news messages sent as either a chat message, boss bar, or title/subtitle
1010
- Easily configurable
11-
- Configure one-time or repeating messages
11+
- Configure one-time (per reload) or repeating/rotating messages
1212
- Extensive Permissions support - configure messages to only send to users including or excluding certain permission nodes
1313
- Optional debug mode to track down who is/isn't receiving messages and why
1414
- Automatic config reloading to get new messages
@@ -17,66 +17,91 @@ Messages can be sent server-wide or controlled by permissions after a delay and
1717

1818
## Configuration
1919

20-
Format:
21-
```yaml
22-
config-version(int): **DO NOT EDIT**, used to migrate configurations as the format changes
23-
auto-reloadconfig(int): <Time in minutes to check/reload config for message updates(0 for off)>
24-
NOTE: When config is reloaded, will reset delays for messages and cause one-time messages to resend
25-
debug-mode(boolean): <Should we pring debug to server.log(true/false)?>
26-
NOTE: Look for fine and finer level log messages in server.log
27-
announcements: Add announcements below to this section
28-
<message label>(String, must be unique):
29-
message(String, required) OR messages(String list, see sample config): <Message to send>
30-
random-order(boolean, optional): <if list of messages should be sent in random order>
31-
sender(String, optional): <Message Sender(chat or boss, default: chat)>
32-
bar(section, optional):
33-
hold(int, optional): <Time in sections for bar to be displayed on announce>
34-
color(String, optional): <bar color(https://hub.spigotmc.org/javadocs/spigot/org/bukkit/boss/BarColor.html)>
35-
style(String, optional): <bar style(https://hub.spigotmc.org/javadocs/spigot/org/bukkit/boss/BarStyle.html)>
36-
animate(section, optional):
37-
enable(boolean, optional): <if bar should animate hold time>
38-
reverse(boolean, optional): <if animation should be reversed>
39-
delay(int, optional - default 0): <Delay to send message on in seconds>
40-
repeat(int, optional): <time between repeat sendings of the message in seconds>
41-
includesperms(String list, optional):
42-
- <only send to those with this perm>
43-
- <and this one>
44-
excludesperms(String list, optional):
45-
- <don't send to those with this perm>
46-
- <and this one>
47-
```
48-
**Note**: For permissions, includes are applied first, then excludes are taken out
20+
- `autoReload`(duration*, optional): Time between automatically reloading plugin configuration, unset or `0m` for off
21+
- `announcements`(Announcement list): list of announcement configurations, see below for detail
22+
- General announcement configuration:
23+
- `type`(Announcement type): Type of announcement: `Chat`, `Title`, or `Boss`
24+
- `messages` OR `message`(Message list): Message(s) to send, depends on type (see sample and description below)
25+
- `random`(boolean, optional): if list of messages should be sent in random order
26+
- `delay`(duration*, optional - default 0): Delay after loading to send message
27+
- `repeat`(duration*, optional): time between sending/repeating each message
28+
- `includesPermissions`**(String list, optional): Only send announcement to players with these permissions
29+
- `excludesPermissions`**(String list, optional): Exclude players with these permissions from receiving the announcement
30+
- `<additional options depending on announcement type>`
31+
- `Chat` type announcement:
32+
- `messages`(String list): Message(s) to send
33+
- `Boss` type announcement:
34+
- `hold`(duration*): Time for boss bar to be on screen
35+
- `color`(BarColor): Color of bar, one of PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE
36+
- `style`(BarStyle): Style of bar, one of SOLID, SEGMENTED_6, SEGMENTED_10, SEGMENTED_12, SEGMENTED_20
37+
- `animate`(boolean): if bar should animate over hold time
38+
- `reverseAnimation`(boolean): if animation should be reversed
39+
- `messages`(BossBarMessage list):
40+
- `message`(string): message string
41+
- ...boss bar config overrides per message eg hold, color, style, animate, etc...
42+
- `Title` type announcement:
43+
- `fadeIn`(duration*): Time it takes for title to fade in
44+
- `stay`(duration*): Time for title to stay on screen
45+
- `fadeOut`(duration*): Time it takes for title to fade out
46+
- `messages`(TitleMessage list):
47+
- `title`(string): title string
48+
- `subtitle`(string): subtitle string, appears below title slightly smaller
49+
- ...title config overrides eg fadeIn, stay, fadeOut...
50+
- `config-version`: **Internal use for configuration migrations, do not edit**
51+
52+
<sub>
53+
*Durations are in the form '10s', '5m', '1h', or ISO-8601 duration. See [parse](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.time/-duration/-companion/parse.html).
54+
55+
**For permissions, includes are applied first, then excludes are taken out
56+
</sub>
57+
4958

5059
### Example
5160

5261
```yaml
53-
config-version: 1
54-
auto-reloadconfig: 20
55-
debug-mode: false
62+
autoReload: 10m
5663
announcements:
57-
repeating-example:
58-
messages:
59-
- This is an automatically generated repeating message!
60-
delay: 15
61-
repeat: 60
62-
permissions-example:
63-
messages:
64-
- This is another automatically generated repeating message for people with build permission!
65-
delay: 30
66-
repeat: 60
67-
includesperms:
68-
- permissions.build
69-
one-time-example:
70-
messages:
71-
- This is an automatically generated one-time message!
72-
delay: 45
73-
message-group-example:
74-
messages:
75-
- This is an automatically generated sequential message group (1 of 3)!
76-
- This is an automatically generated sequential message group (2 of 3)!
77-
- This is an automatically generated sequential message group (3 of 3)!
78-
delay: 30
79-
repeat: 30
80-
sender: bossbar
81-
random-order: false
64+
- type: Chat
65+
delay: 30s
66+
repeat: 2m
67+
includesPermissions:
68+
- permissions.build
69+
- another.permission
70+
excludesPermissions:
71+
- permissions.admin
72+
messages:
73+
- hello
74+
- world
75+
- type: Chat
76+
repeat: 1m 40s
77+
messages:
78+
- abc
79+
- xyz
80+
- type: Title
81+
repeat: 30s
82+
messages:
83+
- title: Title!
84+
subtitle: Subtitle!
85+
- title: Title only custom durations
86+
fadeIn: 100ms
87+
stay: 10s
88+
fadeOut: 1s
89+
fadeIn: 500ms
90+
stay: 5s
91+
fadeOut: 500ms
92+
- type: Boss
93+
random: true
94+
repeat: 15s
95+
messages:
96+
- message: eyy
97+
- message: custom bar config
98+
hold: 10s
99+
color: GREEN
100+
style: SEGMENTED_20
101+
reverseAnimation: true
102+
hold: 5s
103+
color: PURPLE
104+
style: SOLID
105+
animate: true
106+
config-version: 1
82107
```

build.gradle.kts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,17 @@ kotlin {
5959
jvmToolchain(21)
6060
}
6161

62+
val shadowJarOnly: Boolean = project.property("shadowJarOnly")?.toString()?.toBoolean() ?: false
63+
val runtimeClasspath by configurations.runtimeClasspath
64+
6265
dependencies {
6366
compileOnly(libs.spigot)
6467
implementation(libs.kotlinLogger)
68+
implementation(libs.jacksonKotlin)
69+
implementation(libs.jacksonDataformatYaml)
70+
implementation(kotlin("stdlib"))
71+
72+
testImplementation(libs.spigot)
6573
}
6674

6775
tasks {
@@ -70,8 +78,10 @@ tasks {
7078
}
7179

7280
processResources {
73-
// inject runtime libraries into online plugin variant
74-
val libraries = configurations.runtimeClasspath.get().resolvedConfiguration.resolvedArtifacts
81+
inputs.property("shadowJarOnly", shadowJarOnly)
82+
83+
// inject "online" libraries into online plugin variant
84+
val libraries = runtimeClasspath.resolvedConfiguration.resolvedArtifacts
7585
.joinToString("\n - ", prefix = "\n - ") { artifact ->
7686
val id = artifact.moduleVersion.id
7787
"${id.group}:${id.name}:${id.version}"
@@ -102,19 +112,29 @@ tasks {
102112
}
103113
}
104114

105-
jar {
106-
exclude("offline-plugin.yml")
107-
}
108-
109115
// offline jar should be ready to go with all dependencies
110116
shadowJar {
111-
minimize()
112-
archiveClassifier.set("offline")
117+
mergeServiceFiles()
118+
minimize {
119+
// if present, kotlin-reflect must be excluded from minimization
120+
exclude(dependency("org.jetbrains.kotlin:kotlin-reflect"))
121+
}
122+
archiveClassifier.set(if (shadowJarOnly) "" else "offline")
113123
exclude("plugin.yml")
114124
rename("offline-plugin.yml", "plugin.yml")
115125

116126
// avoid classpath conflicts/pollution via relocation
117127
isEnableRelocation = true
118128
relocationPrefix = "${project.group}.${project.name.lowercase()}.libraries"
129+
130+
// if using reflection, don't relocate kotlin:
131+
// shadow relocation doesn't relocate certain metadata breaking some synthetic classes in the case of reflection (used by jackson, for example)
132+
// see also: https://github.com/JetBrains/Exposed/issues/1353
133+
if (runtimeClasspath.resolvedConfiguration.resolvedArtifacts.any { it.name == "kotlin-reflect" }) {
134+
logger.warn("Detected kotlin-reflect in runtime classpath, not relocating kotlin! Proceed with caution.")
135+
relocate("kotlin", "kotlin")
136+
}
119137
}
138+
139+
build { dependsOn(shadowJar) }
120140
}

gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
kotlin.code.style=official
2+
# only produce an "offline" jar due to classpath challenges with jackson and reflection
3+
shadowJarOnly=true

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[versions]
22
axionRelease = "1.18.18"
3+
jackson = "2.19.0"
34
kotlin = "2.1.21"
45
kotlinLogger = "7.0.7"
56
ktlintGradle = "12.2.0"
@@ -14,5 +15,7 @@ ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle"}
1415
shadow = { id = "com.gradleup.shadow", version.ref = "shadow"}
1516

1617
[libraries]
18+
jacksonKotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version.ref = "jackson" }
19+
jacksonDataformatYaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" }
1720
kotlinLogger = { group = "io.github.oshai", name = "kotlin-logging", version.ref = "kotlinLogger" }
1821
spigot = { group = "org.spigotmc", name = "spigot-api", version.ref = "spigot" }

src/main/kotlin/org/simplemc/simpleannounce/Extensions.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.simplemc.simpleannounce
33
import org.bukkit.configuration.ConfigurationSection
44
import org.bukkit.configuration.file.FileConfigurationOptions
55
import org.bukkit.configuration.file.YamlConfiguration
6+
import kotlin.time.Duration
67

78
fun String.oneOf(ignoreCase: Boolean, vararg values: String): Boolean =
89
values.any { it.equals(this, ignoreCase = ignoreCase) }
@@ -11,6 +12,8 @@ fun String.oneOf(vararg values: String): Boolean = oneOf(false, *values)
1112

1213
fun String.oneOfIgnoreCase(vararg values: String): Boolean = oneOf(true, *values)
1314

15+
val Duration.inTicks: Long get() = this.inWholeMilliseconds / 50
16+
1417
/**
1518
* Get a string list if and only if it exists in the config (ignoring defaults).
1619
* If the path does not exist, returns empty list

0 commit comments

Comments
 (0)