Compare commits

...

121 Commits

Author SHA1 Message Date
44dae51e1c fixed playtimer 2025-06-24 21:23:05 +02:00
035864631d changed behavior to spawn in survival mode 2025-06-24 20:09:36 +02:00
f3b884058e code cleanup shrinkingborder 2025-06-23 20:34:48 +02:00
03d4f4e6d8 fixed bug in ShrinkingBorder 2025-06-23 20:28:13 +02:00
7422a89d98 fixed bug in fight detector 2025-06-23 19:52:30 +02:00
3590a5d278 finalized strikesystem 2025-06-22 14:20:45 +02:00
15ac47b314 auto playtime increment 2025-06-22 11:59:46 +02:00
af644a71ee ticketing enable and disable 2025-06-22 11:57:46 +02:00
0ce69f207f fixed bugs in strike handling 2025-06-22 10:59:38 +02:00
76297bb3af WIP: basic strike handling 2025-06-22 10:34:27 +02:00
1aad8f07c4 various bugfixes 2025-06-21 23:16:30 +02:00
f26f4ed56a cleanup 2025-06-21 21:35:31 +02:00
831eacaf47 added verbose logging for api requests
added autostrike for early leave
2025-06-21 21:22:49 +02:00
c71a2567bd fixed adminmarker handling api data wrong 2025-06-21 20:18:32 +02:00
72e88ce491 added spawnpoint for varo 2025-06-21 18:51:37 +02:00
66d84f4677 projectstart for varo 2025-06-21 18:15:25 +02:00
427aed9a7e fixed bug in teamtasks 2025-06-21 17:55:52 +02:00
0d1e6070ce updated playtimer and teamtasks 2025-06-21 17:18:47 +02:00
220fb9e229 moved existing spawning behavior to craftattack 2025-06-21 11:41:13 +02:00
9acac488f2 added api for querying admin-players 2025-06-21 11:38:09 +02:00
d71c0d768e configured shrinkingBorder for production use 2025-06-21 11:31:16 +02:00
9ef4c2e96b added playtimer ticket api 2025-06-20 17:07:53 +02:00
5d33d2aff7 updated adminmarker 2025-06-20 14:29:46 +02:00
3f1065fd3a added teamlist command 2025-06-19 23:49:48 +02:00
aa868deeca added team task management 2025-06-19 21:41:43 +02:00
b6c298cec3 unlimited admin access 2025-06-19 01:18:14 +02:00
8f5a96dc31 changed report text 2025-06-19 00:54:40 +02:00
2824c1053b WIP: report implementation for varo 2025-06-19 00:40:49 +02:00
ccf383cdb5 fixed configuration file not saving correctly 2025-06-15 18:55:17 +02:00
fce9449b7e implemented PlayTimer 2025-06-15 18:42:49 +02:00
69e971f618 Teams corrections
full implementation of FightDetector
2025-06-11 21:36:22 +02:00
b1f188dece generic tweaks
started implementation of FightDetector
2025-06-09 13:52:39 +02:00
a4289d5ac9 periodic team fetch 2025-05-30 22:00:42 +02:00
1fef363c50 Merge remote-tracking branch 'origin/master' 2025-05-30 18:35:14 +02:00
558e6f84f1 api header support, team api integration 2025-05-30 18:35:11 +02:00
bdbb8b5824 api header support, team api integration 2025-05-30 18:34:49 +02:00
8093a4a644 various changes for team management 2025-05-30 12:44:48 +02:00
50147a06e2 added removal of forbidden items in containers 2025-04-13 20:52:03 +02:00
a52476650e registered missing listener for DisplayName 2025-04-12 20:42:32 +02:00
0e5e841527 Merge branch 'master-shrinkingBorder' 2025-04-11 20:44:00 +02:00
ea5279dd82 Merge branch 'master-netherPrevent' 2025-04-11 20:43:08 +02:00
32cbbe6c51 made displayName independent of other appliances 2025-04-11 19:16:13 +02:00
9544c953a2 fixed shrinking border warning 2025-04-09 23:05:46 +02:00
34df173940 changed integer setting constructor to correctly use maximum 2025-04-09 22:32:36 +02:00
ca99e6cfef added integer setting 2025-04-09 16:50:29 +02:00
b0414ae6ab added warning with corresponding setting 2025-04-09 00:08:01 +02:00
c28d34ab88 added shrinking border to config 2025-04-08 19:03:22 +02:00
9bae26044a added shrinking border appliance 2025-04-08 19:00:49 +02:00
d1b5d81fa7 moved tablist to common and made project title configurable 2025-04-08 15:10:23 +02:00
e37e410542 moved report appliance to common 2025-04-08 15:04:50 +02:00
956d2717d8 Merge remote-tracking branch 'origin/master' 2025-04-08 11:49:31 +02:00
ef7232e687 fixed missing countdown for JoinProtection 2025-04-08 11:49:25 +02:00
ff31215295 added option for end prevent 2025-04-07 23:48:06 +02:00
a4a254ebbe removed unnecessary listeners 2025-04-07 22:33:01 +02:00
71d9faa9f4 added nether prevent 2025-04-07 22:17:54 +02:00
859733e3dd finalized JoinProtection 2025-04-07 19:15:35 +02:00
d94bbb7417 Merge branch 'master' into master-joinProtection 2025-04-07 17:55:47 +02:00
153a968776 added entity and potion listeners 2025-04-07 17:52:48 +02:00
639d06b01d Merge branch 'master' into master-joinProtection 2025-04-07 16:20:14 +02:00
fdbb525870 added forbiddenItems appliance with material detection 2025-04-07 16:19:44 +02:00
fcc2abdc49 added option to disable internal plugin http server 2025-04-07 14:53:18 +02:00
a3729734cb fixed error when opening settings and not all setting categories were used 2025-04-07 14:46:14 +02:00
90b623ea07 added option for local build tasks 2025-04-07 12:42:08 +02:00
9f49f44075 added join protection 2025-04-07 00:39:28 +02:00
e9a8e83019 cleanup build.gradle files 2025-04-05 14:39:45 +02:00
7c81286feb updated plugin.yml 2025-04-04 23:03:57 +02:00
e7cf3caae8 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	core/src/main/java/eu/mhsl/craftattack/core/appliance/Appliance.java
2025-04-04 23:03:47 +02:00
8742f5f631 updated command registration for usage without static plugin.yml file 2025-04-04 23:02:50 +02:00
2c0e264ece updated command registration for usage without static plugin.yml file 2025-04-04 22:51:01 +02:00
4592d53d22 splittet project to craftattack and varo flavors 2025-04-04 21:35:07 +02:00
6d0913fa0c splittet project to core and common functionalities 2025-04-04 20:08:53 +02:00
71d5d8303d added lightning fire control 2025-03-15 23:37:24 +01:00
49eeb646ea using common interface instead of individual methods in displayname appliance 2025-03-15 21:54:18 +01:00
ceca038b27 code reformat 2025-03-15 21:41:07 +01:00
76ceb9ef79 removed public directive where possible to reduce number of global accessible classes 2025-03-15 21:38:55 +01:00
219879974c categorized appliances in groups 2025-03-15 21:20:57 +01:00
bd630ebb7a fixed tabcomplete on mute command 2025-02-01 23:01:48 +01:00
c56f318f1c fixed wrong formatting on playtime 2025-02-01 23:01:28 +01:00
4d98d7aa75 removed restart kick message to trigger limbo when used 2024-12-27 11:01:18 +01:00
619190d0ae changed acInform teleport action to grim spectate 2024-12-27 11:00:55 +01:00
06641c5d84 added chatmute 2024-12-27 10:59:56 +01:00
2a52177043 added support for sentences in acinform reports 2024-12-26 20:50:01 +01:00
fc067a2ae0 moved late integrity check to login flow 2024-12-26 20:49:24 +01:00
116a9c11a2 updated ac inform design 2024-12-26 01:56:40 +01:00
3f29ceb08f fixed not respawning at spawnpoint on player death 2024-12-25 23:34:23 +01:00
a33ee357e8 fixed acinform not working with floating point numbers 2024-12-25 23:06:15 +01:00
e36256d5be async whitelist check 2024-12-25 21:09:42 +01:00
0e3a54a1b9 made repository calls async 2024-12-25 16:12:07 +01:00
2e67b41b44 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/main/java/eu/mhsl/craftattack/spawn/appliances/spawnpoint/Spawnpoint.java
2024-12-25 00:37:31 +01:00
e4ac8f7a63 renamed spawnpoint key name 2024-12-25 00:37:12 +01:00
e89e9d2181 renamed spawnpoint key name 2024-12-25 00:35:09 +01:00
8faf0efd60 added spawnpoint 2024-12-25 00:32:54 +01:00
2f1aeb71ee disabled infobars join restore 2024-12-25 00:13:54 +01:00
6475a7b825 updated texts and coordinates 2024-12-24 11:23:19 +01:00
193d8d778f Merge remote-tracking branch 'origin/master' 2024-12-20 21:52:25 +01:00
38da5b1d34 updated coordinates 2024-12-20 21:52:22 +01:00
04e3ddb09f changed custom advancements datapack name 2024-12-20 21:49:04 +01:00
47db27a86e removed bedrock block from WorldMuseum 2024-12-20 19:55:07 +01:00
f13534da3f more robust error handling on Whitelist 2024-12-20 19:54:38 +01:00
e45698c88a fix swapped command feedback 2024-12-20 18:46:20 +01:00
9197840873 removed unwanted HotbarRefill message 2024-12-20 13:34:32 +01:00
63d8335b3a reformatted code 2024-12-17 13:58:41 +01:00
184617e9c3 added EventRepository 2024-12-16 00:30:38 +01:00
696c4bc260 added bedrock displayname prefix 2024-12-09 22:04:30 +01:00
9004609c1b better synchronous call warning 2024-12-08 22:36:11 +01:00
ddedcea8ea added FeedbackRepository 2024-12-08 22:27:54 +01:00
318a30fe54 Merge branch 'refs/heads/master' into develop-apiUtil
# Conflicts:
#	src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
2024-12-08 13:10:27 +01:00
4808d22f6a Merge remote-tracking branch 'origin/develop-apiUtil' into develop-apiUtil
# Conflicts:
#	src/main/java/eu/mhsl/craftattack/spawn/api/client/repositories/ReportRepository.java
#	src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
2024-12-05 23:10:05 +01:00
6b2a323a9c implemented report repository 2024-12-05 23:02:15 +01:00
694ca0efba implemented reports request in Report.java 2024-12-05 22:16:15 +01:00
0276763a8d started implementing report repository 2024-12-05 18:58:55 +01:00
8811328571 implemented working WhitelistRepository 2024-12-05 12:46:51 +01:00
b3c43f1763 WIP: repositoryLoader and infrastructure 2024-12-04 23:34:12 +01:00
86677c942f implemented repository design pattern 2024-12-04 22:11:28 +01:00
31581fc643 added api util 2024-12-04 09:23:20 +01:00
a412f5c24c Merge remote-tracking branch 'refs/remotes/origin/master' into develop-feedback 2024-12-03 20:38:06 +01:00
eae979ee65 added feedback 2024-12-03 20:37:14 +01:00
28b9b84e07 changed afk timings map and time limit 2024-12-01 21:40:52 +01:00
a5cdb93f1b added /reports command 2024-12-01 18:03:33 +01:00
1572096020 Merge remote-tracking branch 'refs/remotes/origin/master' into develop-feedback 2024-12-01 17:41:12 +01:00
0ff9512a7f added wait message for ComponentUtil 2024-12-01 17:39:51 +01:00
280 changed files with 5752 additions and 2006 deletions

2
.gitignore vendored
View File

@ -118,3 +118,5 @@ run/
!gradle-wrapper.jar
/gradlew
/gradlew.bat
local.gradle

View File

@ -1,47 +1,36 @@
plugins {
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'java'
id 'com.gradleup.shadow' version "8.3.5"
}
group = 'eu.mhsl.craftattack'
version = '1.0'
allprojects {
group = 'de.mhsl.craftattack'
version = '1.0.0'
repositories {
mavenCentral()
maven {
name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/groups/public/"
}
maven {
url = uri("https://repo.opencollab.dev/main/")
repositories {
mavenCentral()
maven {
name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/groups/public/"
}
maven {
url = uri("https://repo.opencollab.dev/main/")
}
}
}
dependencies {
compileOnly 'io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT'
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'com.sparkjava:spark-core:2.9.4'
implementation 'org.reflections:reflections:0.10.2'
}
subprojects {
apply plugin: 'java'
apply plugin: 'com.gradleup.shadow'
def targetJavaVersion = 21
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
}
tasks.withType(JavaCompile).configureEach {
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
options.release = targetJavaVersion
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
}
@ -51,18 +40,14 @@ configurations {
shadowJar {
configurations = [project.configurations.shadowImplementation]
archiveClassifier.set('')
relocate 'org.apache.httpcomponents', 'eu.mhsl.lib.shadow.httpclient'
relocate 'com.sparkjava', 'eu.mhsl.lib.shadow.spark-core'
mergeServiceFiles()
}
tasks.register('copyJarToServer', Exec) {
dependsOn shadowJar
mustRunAfter shadowJar
commandLine 'scp', 'build/libs/spawn-1.0-all.jar', 'root@10.20.6.1:/home/minecraft/server/plugins'
}
tasks.register('copyJarToTestServer', Exec) {
dependsOn shadowJar
mustRunAfter shadowJar
commandLine 'cp', 'build/libs/spawn-1.0-all.jar', '/home/elias/Dokumente/mcTestServer/plugins/spawn-1.0-all.jar'
if (file("local.gradle").exists()) {
apply from: "local.gradle"
}

8
common/build.gradle Normal file
View File

@ -0,0 +1,8 @@
dependencies {
implementation project(':core')
compileOnly 'io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT'
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'com.sparkjava:spark-core:2.9.4'
}

View File

@ -0,0 +1,28 @@
package eu.mhsl.craftattack.spawn.common.api;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.apache.http.client.utils.URIBuilder;
import org.bukkit.configuration.ConfigurationSection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
public class CraftAttackApi {
private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("api"));
public final static String basePath = apiConfig.getString("baseurl");
public final static String apiSecret = apiConfig.getString("secret");
public static URI getBaseUri() {
Objects.requireNonNull(basePath);
try {
return new URI(basePath);
} catch(URISyntaxException e) {
throw new RuntimeException(e);
}
}
public static void withAuthorizationSecret(URIBuilder builder) {
builder.addParameter("secret", apiSecret);
}
}

View File

@ -0,0 +1,28 @@
package eu.mhsl.craftattack.spawn.common.api;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.util.Objects;
public class VaroApi {
private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("varoApi"));
public final static String basePath = apiConfig.getString("endpoint");
public final static String apiSecret = apiConfig.getString("auth");
public static URI getBaseUri() {
Objects.requireNonNull(basePath);
try {
return new URI(basePath);
} catch(URISyntaxException e) {
throw new RuntimeException(e);
}
}
public static void authorizationHeader(HttpRequest.Builder builder) {
builder.header("Authorization", apiSecret);
}
}

View File

@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.common.api.repositories;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi;
import java.util.UUID;
public class CraftAttackReportRepository extends ReportRepository {
public CraftAttackReportRepository() {
super(CraftAttackApi.getBaseUri(), new RequestModifier(CraftAttackApi::withAuthorizationSecret, null));
}
public ReqResp<PlayerReports> queryReports(UUID player) {
return this.get(
"report",
(parameters) -> parameters.addParameter("uuid", player.toString()),
PlayerReports.class
);
}
public ReqResp<ReportUrl> createReport(ReportCreationInfo data) {
return this.post(
"report",
data,
ReportUrl.class
);
}
}

View File

@ -0,0 +1,42 @@
package eu.mhsl.craftattack.spawn.common.api.repositories;
import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.core.api.client.RepositoryLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.URI;
import java.util.List;
import java.util.UUID;
@RepositoryLoader.Abstraction
public abstract class ReportRepository extends HttpRepository {
public ReportRepository(URI basePath, RequestModifier... baseRequestModifier) {
super(basePath, baseRequestModifier);
}
public record ReportCreationInfo(@NotNull UUID reporter, @Nullable UUID reported, String reason) {
}
public record ReportUrl(@NotNull String url) {
}
public record PlayerReports(
List<Report> from_self,
Object to_self
) {
public record Report(
@Nullable Reporter reported,
@NotNull String subject,
boolean draft,
@NotNull String status,
@NotNull String url
) {
public record Reporter(
@NotNull String username,
@NotNull String uuid
) {
}
}
}
}

View File

@ -0,0 +1,45 @@
package eu.mhsl.craftattack.spawn.common.api.repositories;
import eu.mhsl.craftattack.spawn.common.api.VaroApi;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import org.apache.commons.lang3.NotImplementedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class VaroReportRepository extends ReportRepository {
public VaroReportRepository() {
super(VaroApi.getBaseUri(), new RequestModifier(null, VaroApi::authorizationHeader));
}
public ReqResp<PlayerReports> queryReports(UUID player) {
throw new NotImplementedException("Report querying is not supported in Varo!");
}
public ReqResp<ReportUrl> createReport(ReportCreationInfo data) {
return this.post(
"report",
data,
ReportUrl.class
);
}
public record StrikeCreationInfo(
@Nullable UUID reporter, // null for automatic creations
@NotNull UUID reported,
@NotNull String reason,
@Nullable String body,
@Nullable String notice,
@Nullable String statement,
int strike_reason_id // internal strike mapping
) {
}
public ReqResp<Void> createStrike(StrikeCreationInfo data) {
return this.put(
"report",
data,
Void.class
);
}
}

View File

@ -0,0 +1,20 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.debug;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.AppliancesCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.UserInfoCommand;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class Debug extends Appliance {
@Override
@NotNull
protected List<ApplianceCommand<?>> commands() {
return List.of(
new UserInfoCommand(),
new AppliancesCommand()
);
}
}

View File

@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.debug.command;
package eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.debug.Debug;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.Debug;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.debug.command;
package eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.debug.Debug;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.Debug;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
@ -50,13 +50,13 @@ public class UserInfoCommand extends ApplianceCommand<Debug> {
.appendNewline()
.append(
Component
.text("Erster Besuch: " + formatUnixTimestamp(player.getFirstPlayed()), NamedTextColor.GRAY)
.text("Erster Besuch: " + this.formatUnixTimestamp(player.getFirstPlayed()), NamedTextColor.GRAY)
.clickEvent(ClickEvent.copyToClipboard(String.valueOf(player.getFirstPlayed())))
)
.appendNewline()
.append(
Component
.text("Letzter Besuch: " + formatUnixTimestamp(player.getLastSeen()), NamedTextColor.GRAY)
.text("Letzter Besuch: " + this.formatUnixTimestamp(player.getLastSeen()), NamedTextColor.GRAY)
.clickEvent(ClickEvent.copyToClipboard(String.valueOf(player.getLastSeen())))
)
.appendNewline()

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.titleClear;
package eu.mhsl.craftattack.spawn.common.appliances.internal.titleClear;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;

View File

@ -0,0 +1,12 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.titleClear;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class TitleClearListener extends ApplianceListener<TitleClear> {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
this.getAppliance().clearTitle(event.getPlayer());
}
}

View File

@ -0,0 +1,34 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.adminMarker;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName.DisplayName;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class AdminMarker extends Appliance implements DisplayName.Colored {
public final static String adminPermission = "admin";
@Override
public @Nullable TextColor getNameColor(Player player) {
if(player.hasPermission(adminPermission))
return TextColor.color(Color.AQUA.asRGB()); // TODO read permission from config
return TextColor.color(Color.WHITE.asRGB());
}
@Override
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
apiBuilder.get("isAdmin", request -> {
OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(request.queryParams("player")));
Main.logger().info(String.format("Adminstatus requested for %s, response: %s", player.getUniqueId(), player.isOp()));
return player.isOp();
});
}
}

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.chatMention;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.sound.Sound;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@ -56,7 +56,7 @@ public class ChatMention extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(ChatMentionSetting.class);
refreshPlayers();
this.refreshPlayers();
}
@Override

View File

@ -1,10 +1,10 @@
package eu.mhsl.craftattack.spawn.appliances.chatMention;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -15,7 +15,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
import java.util.ArrayList;
import java.util.List;
public class ChatMentionListener extends ApplianceListener<ChatMention> {
class ChatMentionListener extends ApplianceListener<ChatMention> {
@SuppressWarnings("UnstableApiUsage")
@EventHandler
public void coloringEvent(AsyncChatDecorateEvent event) {
@ -30,11 +30,11 @@ public class ChatMentionListener extends ApplianceListener<ChatMention> {
Component result = words.stream()
.map(word -> {
String wordWithoutAnnotation = word.replace("@", "");
boolean isPlayer = getAppliance().getPlayerNames().contains(wordWithoutAnnotation);
boolean isPlayer = this.getAppliance().getPlayerNames().contains(wordWithoutAnnotation);
if(isPlayer && config.applyMentions()) {
mentioned.add(wordWithoutAnnotation);
Component mention = Component.text(
getAppliance().formatPlayer(wordWithoutAnnotation),
this.getAppliance().formatPlayer(wordWithoutAnnotation),
NamedTextColor.GOLD
);
return chatMessages.addReportActions(mention, wordWithoutAnnotation);
@ -45,12 +45,12 @@ public class ChatMentionListener extends ApplianceListener<ChatMention> {
.reduce(ComponentUtil::appendWithSpace)
.orElseThrow();
getAppliance().notifyPlayers(mentioned);
this.getAppliance().notifyPlayers(mentioned);
event.result(result);
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
getAppliance().refreshPlayers();
this.getAppliance().refreshPlayers();
}
}

View File

@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.chatMention;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.MultiBoolSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.MultiBoolSetting;
import org.bukkit.Material;
public class ChatMentionSetting extends MultiBoolSetting<ChatMentionSetting.ChatMentionConfig> implements CategorizedSetting {
@ -15,7 +15,8 @@ public class ChatMentionSetting extends MultiBoolSetting<ChatMentionSetting.Chat
public record ChatMentionConfig(
@DisplayName("Spielernamen hervorheben") boolean applyMentions,
@DisplayName("Benachrichtigungston") boolean notifyOnMention
) {}
) {
}
public ChatMentionSetting() {
super(Settings.Key.ChatMentions);

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.chatMessages;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
@ -19,7 +19,7 @@ public class ChatMessages extends Appliance {
}
public Component getReportablePlayerName(Player player) {
return addReportActions(player.displayName(), player.getName());
return this.addReportActions(player.displayName(), player.getName());
}
public Component addReportActions(Component message, String username) {

View File

@ -1,54 +1,59 @@
package eu.mhsl.craftattack.spawn.appliances.chatMessages;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Color;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Optional;
public class ChatMessagesListener extends ApplianceListener<ChatMessages> {
class ChatMessagesListener extends ApplianceListener<ChatMessages> {
@EventHandler
public void onPlayerChatEvent(AsyncChatEvent event) {
event.renderer(
(source, sourceDisplayName, message, viewer) ->
Component.text("")
.append(getAppliance().getReportablePlayerName(source))
.append(this.getAppliance().getReportablePlayerName(source))
.append(Component.text(" > ").color(TextColor.color(Color.GRAY.asRGB())))
.append(message).color(TextColor.color(Color.SILVER.asRGB()))
);
}
@EventHandler
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerJoin(PlayerJoinEvent event) {
boolean wasHidden = event.joinMessage() == null;
event.joinMessage(null);
if(wasHidden) return;
IteratorUtil.onlinePlayers(player -> {
if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return;
player.sendMessage(
Component
.text(">>> ").color(NamedTextColor.GREEN)
.append(getAppliance().getReportablePlayerName(event.getPlayer()))
.append(this.getAppliance().getReportablePlayerName(event.getPlayer()))
);
});
}
@EventHandler
public void onPlayerLeave(PlayerQuitEvent event) {
boolean wasHidden = event.quitMessage() == null;
event.quitMessage(null);
if(wasHidden) return;
IteratorUtil.onlinePlayers(player -> {
if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return;
player.sendMessage(
Component
.text("<<< ").color(NamedTextColor.RED)
.append(getAppliance().getReportablePlayerName(event.getPlayer()))
.append(this.getAppliance().getReportablePlayerName(event.getPlayer()))
);
});
}

View File

@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.chatMessages;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting;
import org.bukkit.Material;
public class ShowJoinAndLeaveMessagesSetting extends BoolSetting implements CategorizedSetting {

View File

@ -0,0 +1,91 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.util.server.Floodgate;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
public class DisplayName extends Appliance {
public interface Prefixed {
@Nullable
Component getNamePrefix(Player player);
}
public interface Colored {
@Nullable
TextColor getNameColor(Player player);
}
public void update(Player player) {
List<Colored> coloring = Main.instance().getAppliances().stream()
.filter(appliance -> appliance instanceof Colored)
.map(appliance -> (Colored) appliance)
.toList();
if(coloring.size() > 1) throw new IllegalStateException(
"There are two or more appliances which provide coloring for player names. This is currently not supported!"
);
TextColor playerColor = coloring.isEmpty()
? NamedTextColor.WHITE
: coloring.getFirst().getNameColor(player);
List<Prefixed> prefixes = Main.instance().getAppliances().stream()
.filter(appliance -> appliance instanceof Prefixed)
.map(appliance -> (Prefixed) appliance)
.toList();
ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text();
prefixes.stream()
.map(prefixed -> prefixed.getNamePrefix(player))
.filter(Objects::nonNull)
.forEach(prefix -> playerName
.append(prefix)
.append(ComponentUtil.clearedSpace())
);
if(Floodgate.isBedrock(player)) {
playerName
.append(
Component.text("\uD83C\uDFAE", NamedTextColor.GRAY)
.hoverEvent(HoverEvent.showText(Component.text(
String.format("%s spielt die Minecraft: Bedrock Edition", player.getName())
)))
)
.append(ComponentUtil.clearedSpace());
}
playerName.append(Component.text(player.getName(), playerColor));
this.setGlobal(player, playerName.build());
}
private void setGlobal(Player player, Component component) {
try {
player.customName(component);
player.displayName(component);
player.playerListName(component);
} catch(Exception e) {
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
}
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new DisplayNameUpdateListener());
}
}

View File

@ -0,0 +1,13 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerJoinEvent;
class DisplayNameUpdateListener extends ApplianceListener<DisplayName> {
@EventHandler(priority = EventPriority.LOW)
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().update(event.getPlayer());
}
}

View File

@ -0,0 +1,28 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.DiscordCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.HelpCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.SpawnCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.TeamspeakCommand;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class Help extends Appliance {
public Help() {
super("help");
}
@Override
@NotNull
protected List<ApplianceCommand<?>> commands() {
return List.of(
new HelpCommand(),
new SpawnCommand(),
new TeamspeakCommand(),
new DiscordCommand()
);
}
}

View File

@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class DiscordCommand extends ApplianceCommand<Help> {
public DiscordCommand() {
super("discord");
}
private final static String discordLink = "https://discord.gg/TXxspGVanq";
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
sender.sendMessage(
Component.text("Offizieller Discord Server: ", NamedTextColor.GOLD)
.append(Component.text(discordLink, NamedTextColor.AQUA))
.clickEvent(ClickEvent.openUrl(discordLink))
);
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.help.command;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
@ -18,7 +18,8 @@ public class HelpCommand extends ApplianceCommand<Help> {
sender.sendMessage(
Component.text("Willkommen auf Craftattack!", NamedTextColor.GOLD)
.appendNewline()
.append(Component.text("Hier ist ein Hilfetext!", NamedTextColor.GRAY))
.append(Component.text("Wenn du hilfe benötigst kannst du dich jederzeit an einen Admin wenden." +
" Weitere Informationen zu Funktionen und Befehlen erhältst du zudem im Turm am Spawn.", NamedTextColor.GRAY))
);
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.help.command;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
@ -19,8 +19,8 @@ public class SpawnCommand extends ApplianceCommand<Help> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(!getAppliance().localConfig().isString(spawnKey))
if(!this.getAppliance().localConfig().isString(spawnKey))
throw new ApplianceCommand.Error("Es wurde kein Spawnbereich hinterlegt!");
sender.sendMessage(Component.text(Objects.requireNonNull(getAppliance().localConfig().getString(spawnKey)), NamedTextColor.GOLD));
sender.sendMessage(Component.text(Objects.requireNonNull(this.getAppliance().localConfig().getString(spawnKey)), NamedTextColor.GOLD));
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.help.command;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
@ -17,12 +17,12 @@ public class TeamspeakCommand extends ApplianceCommand<Help> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(!getAppliance().localConfig().isString(teamspeakKey))
if(!this.getAppliance().localConfig().isString(teamspeakKey))
throw new ApplianceCommand.Error("Es wurde kein Teamspeak hinterlegt!");
sender.sendMessage(
Component.text()
.append(Component.text("Joine unserem Teamspeak: ", NamedTextColor.GOLD))
.append(getTeamspeakIp(getAppliance().localConfig().getString(teamspeakKey)))
.append(this.getTeamspeakIp(this.getAppliance().localConfig().getString(teamspeakKey)))
);
}

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.infoBars;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.core.Main;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.Ticks;
@ -56,7 +56,9 @@ public abstract class Bar {
return Math.clamp(this.progress(), 0, 1);
}
protected void beforeRefresh() {}
protected void beforeRefresh() {
}
protected abstract Duration refresh();
protected abstract String name();

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.infoBars;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
public class InfoBarCommand extends ApplianceCommand.PlayerChecked<InfoBars> {
class InfoBarCommand extends ApplianceCommand.PlayerChecked<InfoBars> {
public InfoBarCommand() {
super("infobar");
}
@ -17,9 +17,9 @@ public class InfoBarCommand extends ApplianceCommand.PlayerChecked<InfoBars> {
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
if(args.length == 0) throw new Error("<show|hide|hideall> [bar name]");
switch(args[0]) {
case "hideAll" -> getAppliance().hideAll(getPlayer());
case "show" -> getAppliance().show(getPlayer(), args[1]);
case "hide" -> getAppliance().hide(getPlayer(), args[1]);
case "hideAll" -> this.getAppliance().hideAll(this.getPlayer());
case "show" -> this.getAppliance().show(this.getPlayer(), args[1]);
case "hide" -> this.getAppliance().hide(this.getPlayer(), args[1]);
default -> throw new Error("Erlaubte Optionen sind 'show', 'hide', 'hideAll'!");
}
}
@ -27,6 +27,6 @@ public class InfoBarCommand extends ApplianceCommand.PlayerChecked<InfoBars> {
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(args.length == 1) return List.of("show", "hide", "hideAll");
return getAppliance().getInfoBars().stream().map(Bar::name).toList();
return this.getAppliance().getInfoBars().stream().map(Bar::name).toList();
}
}

View File

@ -1,11 +1,11 @@
package eu.mhsl.craftattack.spawn.appliances.infoBars;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.infoBars.bars.MsptBar;
import eu.mhsl.craftattack.spawn.appliances.infoBars.bars.PlayerCounterBar;
import eu.mhsl.craftattack.spawn.appliances.infoBars.bars.TpsBar;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.MsptBar;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.PlayerCounterBar;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.TpsBar;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@ -51,32 +51,33 @@ public class InfoBars extends Appliance {
private List<String> getStoredBars(Player player) {
PersistentDataContainer container = player.getPersistentDataContainer();
if(!container.has(infoBarKey)) return List.of();
return container.get(infoBarKey, PersistentDataType.LIST.strings());
if(!container.has(this.infoBarKey)) return List.of();
return container.get(this.infoBarKey, PersistentDataType.LIST.strings());
}
private void setStoredBars(Player player, List<String> bars) {
player.getPersistentDataContainer().set(infoBarKey, PersistentDataType.LIST.strings(), bars);
player.getPersistentDataContainer().set(this.infoBarKey, PersistentDataType.LIST.strings(), bars);
}
private Bar getBarByName(String name) {
return infoBars.stream()
return this.infoBars.stream()
.filter(bar -> bar.name().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
private void validateBarName(String name) {
if(getBarByName(name) == null) throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name));
if(this.getBarByName(name) == null)
throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name));
}
public List<Bar> getInfoBars() {
return infoBars;
return this.infoBars;
}
@Override
public void onDisable() {
infoBars.forEach(Bar::stopUpdate);
this.infoBars.forEach(Bar::stopUpdate);
}
@Override

View File

@ -0,0 +1,12 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class ShowPreviousBarsListener extends ApplianceListener<InfoBars> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
// this.getAppliance().showAll(event.getPlayer());
}
}

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.infoBars.bars;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.appliances.infoBars.Bar;
import eu.mhsl.craftattack.spawn.util.statistics.ServerMonitor;
import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.core.util.statistics.ServerMonitor;
import eu.mhsl.craftattack.spawn.core.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.infoBars.bars;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliances.infoBars.Bar;
import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit;
import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit.PlayerLimit;
import eu.mhsl.craftattack.spawn.core.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.infoBars.bars;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.appliances.infoBars.Bar;
import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.core.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@ -1,11 +1,11 @@
package eu.mhsl.craftattack.spawn.appliances.privateMessage;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.appliances.privateMessage.commands.PrivateMessageCommand;
import eu.mhsl.craftattack.spawn.appliances.privateMessage.commands.PrivateReplyCommand;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateMessageCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateReplyCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
@ -23,7 +23,9 @@ public class PrivateMessage extends Appliance {
public final int targetChangeTimeoutSeconds = 30;
public final int conversationTimeoutMinutes = 30;
private record Conversation(UUID target, Long lastSet) {}
private record Conversation(UUID target, Long lastSet) {
}
private final Map<Player, List<Conversation>> replyMapping = new WeakHashMap<>();
public void reply(Player sender, String message) {
@ -32,7 +34,7 @@ public class PrivateMessage extends Appliance {
List<Conversation> replyList = this.replyMapping.get(sender);
List<Conversation> tooOldConversations = replyList.stream()
.filter(conversation -> conversation.lastSet < System.currentTimeMillis() - (conversationTimeoutMinutes*60*1000))
.filter(conversation -> conversation.lastSet < System.currentTimeMillis() - (this.conversationTimeoutMinutes * 60 * 1000))
.toList();
replyList.removeAll(tooOldConversations);
@ -60,12 +62,13 @@ public class PrivateMessage extends Appliance {
}
List<Conversation> oldConversations = replyList.stream()
.filter(conversation -> conversation.lastSet < System.currentTimeMillis() - (targetChangeTimeoutSeconds*1000))
.filter(conversation -> conversation.lastSet < System.currentTimeMillis() - (this.targetChangeTimeoutSeconds * 1000))
.toList();
if(oldConversations.contains(youngestEntry) || replyList.size() == 1) {
Player target = Bukkit.getPlayer(youngestEntry.target());
if(target == null) throw new ApplianceCommand.Error("Der Spieler " + Bukkit.getOfflinePlayer(youngestEntry.target()).getName() + " ist nicht mehr verfügbar.");
if(target == null)
throw new ApplianceCommand.Error("Der Spieler " + Bukkit.getOfflinePlayer(youngestEntry.target()).getName() + " ist nicht mehr verfügbar.");
replyList.clear();
this.sendWhisper(sender, new ResolvedPmUserArguments(target, message));
@ -98,11 +101,11 @@ public class PrivateMessage extends Appliance {
.toList();
playerNames.forEach(playerName -> component.append(
Component.text("[")
.append(Component.text(playerName, NamedTextColor.GOLD))
.append(Component.text("]"))
.clickEvent(ClickEvent.runCommand(String.format("/msg %s %s", playerName, message)))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um diesem Spieler zu schreiben.").color(NamedTextColor.GOLD))))
Component.text("[")
.append(Component.text(playerName, NamedTextColor.GOLD))
.append(Component.text("]"))
.clickEvent(ClickEvent.runCommand(String.format("/msg %s %s", playerName, message)))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um diesem Spieler zu schreiben.").color(NamedTextColor.GOLD))))
.append(Component.text(" "))
);
component.appendNewline();
@ -167,12 +170,16 @@ public class PrivateMessage extends Appliance {
);
}
public record ResolvedPmUserArguments(Player receiver, String message) {}
public record ResolvedPmUserArguments(Player receiver, String message) {
}
public ResolvedPmUserArguments resolveImplicit(String[] args) {
if(args.length < 2) throw new ApplianceCommand.Error("Es muss ein Spieler sowie eine Nachricht angegeben werden.");
if(args.length < 2)
throw new ApplianceCommand.Error("Es muss ein Spieler sowie eine Nachricht angegeben werden.");
List<String> arguments = List.of(args);
Player targetPlayer = Bukkit.getPlayer(arguments.getFirst());
if(targetPlayer == null) throw new ApplianceCommand.Error(String.format("Der Spieler %s konnte nicht gefunden werden.", arguments.getFirst()));
if(targetPlayer == null)
throw new ApplianceCommand.Error(String.format("Der Spieler %s konnte nicht gefunden werden.", arguments.getFirst()));
String message = arguments.stream().skip(1).collect(Collectors.joining(" "));
return new ResolvedPmUserArguments(targetPlayer, message);
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.privateMessage.commands;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.privateMessage.PrivateMessage;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -13,6 +13,6 @@ public class PrivateMessageCommand extends ApplianceCommand.PlayerChecked<Privat
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().sendWhisper(getPlayer(), getAppliance().resolveImplicit(args));
this.getAppliance().sendWhisper(this.getPlayer(), this.getAppliance().resolveImplicit(args));
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.privateMessage.commands;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.privateMessage.PrivateMessage;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -13,6 +13,6 @@ public class PrivateReplyCommand extends ApplianceCommand.PlayerChecked<PrivateM
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().reply(getPlayer(), String.join(" ", args));
this.getAppliance().reply(this.getPlayer(), String.join(" ", args));
}
}

View File

@ -0,0 +1,180 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report;
import eu.mhsl.craftattack.spawn.common.api.repositories.ReportRepository;
import eu.mhsl.craftattack.spawn.common.api.repositories.VaroReportRepository;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.common.api.repositories.CraftAttackReportRepository;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
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 org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
public class Report extends Appliance {
public static Component helpText() {
return Component.text()
.appendNewline()
.append(Component.text(" Um einen Spieler zu melden, verwende ", NamedTextColor.GRAY)).appendNewline()
.append(Component.text("/report", NamedTextColor.GOLD)).appendNewline()
.append(Component.text("oder", NamedTextColor.GRAY)).appendNewline()
.append(Component.text("/report <spieler> [grund]", NamedTextColor.GOLD)).appendNewline()
.build();
}
public Report() {
super("report");
}
public void reportToUnknown(@NotNull Player issuer) {
CraftAttackReportRepository.ReportCreationInfo request = new CraftAttackReportRepository.ReportCreationInfo(issuer.getUniqueId(), null, "");
Bukkit.getScheduler().runTaskAsynchronously(
Main.instance(),
() -> this.createReport(issuer, request)
);
}
public void reportToKnown(@NotNull Player issuer, @NotNull String targetUsername, @Nullable String reason) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(targetUsername);
if(issuer.getUniqueId().equals(offlinePlayer.getUniqueId())) {
issuer.sendMessage(Component.text("Du kannst dich nicht selbst reporten.", NamedTextColor.RED));
return;
}
ReportRepository.ReportCreationInfo request = new ReportRepository.ReportCreationInfo(
issuer.getUniqueId(),
offlinePlayer.getUniqueId(),
Optional.ofNullable(reason).orElse("")
);
Bukkit.getScheduler().runTaskAsynchronously(
Main.instance(),
() -> this.createReport(issuer, request)
);
}
private void createReport(Player issuer, ReportRepository.ReportCreationInfo reportRequest) {
ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(VaroReportRepository.class)
.createReport(reportRequest);
switch(createdReport.status()) {
case 200: // varo-endpoint specific
case 201:
issuer.sendMessage(
Component.text()
.append(Component.text("\\/".repeat(20), NamedTextColor.DARK_GRAY))
.appendNewline()
.append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden:", NamedTextColor.GOLD))
.appendNewline()
.appendNewline()
.append(
Component
.text(createdReport.data().url(), NamedTextColor.GRAY) // URL mit Weltkugel-Emoji
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url()))
)
.appendNewline()
.appendNewline()
.append(Component.text("Ohne das Fertigstellen des Reports wird dieser nicht bearbeitet!", NamedTextColor.DARK_RED))
.appendNewline()
.append(Component.text("/\\".repeat(20), NamedTextColor.DARK_GRAY))
);
break;
case 400:
issuer.sendMessage(
Component.text()
.append(Component.text("Der angegebene Nutzer ist in unserem System nicht bekannt.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Bist du sicher, dass du den Namen richtig geschrieben hast?", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Du kannst dich alternativ jederzeit bei einem Admin melden.", NamedTextColor.GRAY))
);
break;
case 401:
default:
Main.logger().warning("Failed to request Report: " + createdReport.status());
issuer.sendMessage(
Component.text()
.append(Component.text("Interner Serverfehler beim anlegen des Reports.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Bitte melde dich bei einem Admin!", NamedTextColor.RED))
);
break;
}
}
public void queryReports(Player issuer) {
ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(VaroReportRepository.class)
.queryReports(issuer.getUniqueId());
if(userReports.status() != 200) {
Main.logger().warning("Failed to request Reports: " + userReports.status());
issuer.sendMessage(
Component.text()
.append(Component.text("Interner Serverfehler beim abfragen der Reports.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Bitte melde dich bei einem Admin!", NamedTextColor.RED))
);
return;
}
List<ReportRepository.PlayerReports.Report> reports = userReports
.data()
.from_self()
.stream()
.filter(report -> !report.draft())
.toList()
.reversed();
if(reports.isEmpty()) {
issuer.sendMessage(
Component.text()
.append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY))
);
return;
}
ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text()
.append(Component.newline())
.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD))
.appendNewline();
reports.forEach(report -> {
component
.append(Component.text(" - ", NamedTextColor.WHITE))
.append(
report.reported() != null
? Component.text(report.reported().username(), NamedTextColor.WHITE)
: Component.text("Unbekannt", NamedTextColor.YELLOW)
)
.append(Component.text(String.format(": %s", report.subject()), NamedTextColor.GRAY))
.clickEvent(ClickEvent.openUrl(report.url()))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD)));
component.appendNewline();
});
issuer.sendMessage(component.build());
}
@Override
@NotNull
protected List<ApplianceCommand<?>> commands() {
return List.of(
new ReportCommand(),
new ReportsCommand()
);
}
}

View File

@ -1,8 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.report;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
@ -17,31 +16,25 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ReportCommand extends ApplianceCommand.PlayerChecked<Report> {
class ReportCommand extends ApplianceCommand.PlayerChecked<Report> {
public ReportCommand() {
super("report");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendMessage(
Component.newline()
.append(Component.text("Reportanfrage wird erstellt...", NamedTextColor.GREEN))
.appendNewline()
.append(Component.text("Bitte warte einen Augenblick", NamedTextColor.GRAY))
.appendNewline()
);
sender.sendMessage(ComponentUtil.pleaseWait());
if(args.length == 0) {
getAppliance().reportToUnknown(getPlayer());
this.getAppliance().reportToUnknown(this.getPlayer());
}
if(args.length == 1) {
getAppliance().reportToKnown(getPlayer(), args[0], null);
this.getAppliance().reportToKnown(this.getPlayer(), args[0], null);
}
if(args.length > 1) {
getAppliance().reportToKnown(getPlayer(), args[0], Arrays.stream(args).skip(1).collect(Collectors.joining(" ")));
this.getAppliance().reportToKnown(this.getPlayer(), args[0], Arrays.stream(args).skip(1).collect(Collectors.joining(" ")));
}
}

View File

@ -0,0 +1,24 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
class ReportsCommand extends ApplianceCommand.PlayerChecked<Report> {
public ReportsCommand() {
super("reports");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendMessage(ComponentUtil.pleaseWait());
Bukkit.getScheduler().runTaskAsynchronously(
Main.instance(),
() -> this.getAppliance().queryReports(this.getPlayer())
);
}
}

View File

@ -0,0 +1,5 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
public interface CategorizedSetting {
SettingCategory category();
}

View File

@ -0,0 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
public enum SettingCategory {
Gameplay,
Visuals,
Misc,
}

View File

@ -1,10 +1,11 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.Setting;
import eu.mhsl.craftattack.spawn.appliances.settings.listeners.OpenSettingsShortcutListener;
import eu.mhsl.craftattack.spawn.appliances.settings.listeners.SettingsInventoryListener;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.Setting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.OpenSettingsShortcutListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.SettingsInventoryListener;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -32,11 +33,12 @@ public class Settings extends Appliance {
ChatMentions,
DoubleDoors,
KnockDoors,
BorderWarning
}
public static Settings instance() {
if(settingsInstance != null) return settingsInstance;
Settings.settingsInstance = queryAppliance(Settings.class);
Settings.settingsInstance = Main.instance().getAppliance(Settings.class);
return settingsInstance;
}
@ -57,20 +59,20 @@ public class Settings extends Appliance {
private final WeakHashMap<Player, List<Setting<?>>> settingsCache = new WeakHashMap<>();
private List<Setting<?>> getSettings(Player player) {
if(settingsCache.containsKey(player)) return settingsCache.get(player);
if(this.settingsCache.containsKey(player)) return this.settingsCache.get(player);
List<Setting<?>> settings = this.declaredSettings.stream()
.map(clazz -> {
try {
return clazz.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
} catch(NoSuchMethodException e) {
throw new RuntimeException(String.format("Setting '%s' does not have an accessible constructor", clazz.getName()), e);
}
})
.map(constructor -> {
try {
return constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
} catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(String.format("Failed to create instance of setting '%s'", constructor.getDeclaringClass().getName()), e);
}
})
@ -82,7 +84,7 @@ public class Settings extends Appliance {
}
public <T> T getSetting(Player player, Key key, Class<T> clazz) {
Setting<?> setting = getSettings(player).stream()
Setting<?> setting = this.getSettings(player).stream()
.filter(s -> Objects.equals(s.getKey(), key))
.findFirst()
.orElseThrow();
@ -95,8 +97,8 @@ public class Settings extends Appliance {
}
public void openSettings(Player player) {
List<Setting<?>> settings = getSettings(player);
Inventory inventory = Bukkit.createInventory(null, calculateInvSize(settings), Component.text("Einstellungen"));
List<Setting<?>> settings = this.getSettings(player);
Inventory inventory = Bukkit.createInventory(null, this.calculateInvSize(settings), Component.text("Einstellungen"));
AtomicInteger row = new AtomicInteger(0);
Arrays.stream(SettingCategory.values())
@ -106,11 +108,14 @@ public class Settings extends Appliance {
.filter(setting -> ((CategorizedSetting) setting).category().equals(category))
.toList();
for (int i = 0; i < categorizedSettings.size(); i++) {
//skip empty category rows
if(categorizedSettings.isEmpty()) return;
for(int i = 0; i < categorizedSettings.size(); i++) {
int slot = row.get() * 9 + i % 9;
inventory.setItem(slot, categorizedSettings.get(i).buildItem());
if (i % 9 == 8) {
if(i % 9 == 8) {
row.incrementAndGet();
}
}
@ -121,11 +126,11 @@ public class Settings extends Appliance {
.filter(setting -> !(setting instanceof CategorizedSetting))
.toList();
for (int i = 0; i < uncategorizedSettings.size(); i++) {
for(int i = 0; i < uncategorizedSettings.size(); i++) {
int slot = row.get() * 9 + i % 9;
inventory.setItem(slot, uncategorizedSettings.get(i).buildItem());
if (i % 9 == 8) {
if(i % 9 == 8) {
row.incrementAndGet();
}
}
@ -138,22 +143,28 @@ public class Settings extends Appliance {
int countOfUncategorized = (int) settings.stream()
.filter(setting -> !(setting instanceof CategorizedSetting))
.count();
int invSizeForUncategorized = (int) Math.ceil((double) countOfUncategorized / 9) * 9;
return Arrays.stream(SettingCategory.values())
int invSizeForCategorized = Arrays.stream(SettingCategory.values())
.map(settingCategory -> settings.stream()
.filter(setting -> setting instanceof CategorizedSetting)
.map(setting -> (CategorizedSetting) setting)
.filter(categorizedSetting -> categorizedSetting.category().equals(settingCategory))
.count())
.map(itemCount -> itemCount + countOfUncategorized)
.map(itemCount -> (int) Math.ceil((double) itemCount / 9))
.reduce(Integer::sum)
.orElse(1) * 9;
int invSize = invSizeForUncategorized + invSizeForCategorized;
if(invSize % 9 != 0) throw new IllegalStateException(
String.format("Failed to calculate settings inventory size. %d is not an multiple of 9", invSize)
);
return invSize;
}
public void onSettingsClose(Player player) {
if(!openSettingsInventories.containsKey(player)) return;
openSettingsInventories.remove(player);
if(!this.openSettingsInventories.containsKey(player)) return;
this.openSettingsInventories.remove(player);
player.updateInventory();
}
@ -162,7 +173,8 @@ public class Settings extends Appliance {
}
public OpenSettingsInventory getOpenInventory(Player player) {
if(hasSettingsNotOpen(player)) throw new RuntimeException("Cannot retrieve data from closed Settings inventory!");
if(this.hasSettingsNotOpen(player))
throw new RuntimeException("Cannot retrieve data from closed Settings inventory!");
return this.openSettingsInventories.get(player);
}

View File

@ -1,17 +1,17 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class SettingsCommand extends ApplianceCommand.PlayerChecked<Settings> {
class SettingsCommand extends ApplianceCommand.PlayerChecked<Settings> {
public SettingsCommand() {
super("settings");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().openSettings(getPlayer());
this.getAppliance().openSettings(this.getPlayer());
}
}

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting;
import org.bukkit.Material;
public class SettingsShortcutSetting extends BoolSetting implements CategorizedSetting {

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -17,14 +17,14 @@ public abstract class ActionSetting extends Setting<Void> {
@Override
public ItemMeta buildMeta(ItemMeta meta) {
meta.displayName(Component.text(title(), NamedTextColor.WHITE));
meta.lore(buildDescription(description()));
meta.displayName(Component.text(this.title(), NamedTextColor.WHITE));
meta.lore(this.buildDescription(this.description()));
return meta;
}
@Override
protected void change(Player player, ClickType clickType) {
onAction(player, clickType);
this.onAction(player, clickType);
}
@Override
@ -33,10 +33,12 @@ public abstract class ActionSetting extends Setting<Void> {
}
@Override
protected void fromStorage(PersistentDataContainer container) {}
protected void fromStorage(PersistentDataContainer container) {
}
@Override
protected void toStorage(PersistentDataContainer container, Void value) {}
protected void toStorage(PersistentDataContainer container, Void value) {
}
@Override
public Class<?> dataType() {

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
@ -20,19 +20,19 @@ public abstract class BoolSetting extends Setting<Boolean> {
@Override
public void fromStorage(PersistentDataContainer container) {
this.state = container.has(getNamespacedKey())
? Objects.requireNonNull(container.get(getNamespacedKey(), PersistentDataType.BOOLEAN))
: defaultValue();
this.state = container.has(this.getNamespacedKey())
? Objects.requireNonNull(container.get(this.getNamespacedKey(), PersistentDataType.BOOLEAN))
: this.defaultValue();
}
@Override
protected void toStorage(PersistentDataContainer container, Boolean value) {
container.set(getNamespacedKey(), PersistentDataType.BOOLEAN, value);
container.set(this.getNamespacedKey(), PersistentDataType.BOOLEAN, value);
}
@Override
public ItemMeta buildMeta(ItemMeta meta) {
meta.displayName(Component.text(title(), NamedTextColor.WHITE));
meta.displayName(Component.text(this.title(), NamedTextColor.WHITE));
List<Component> lore = new ArrayList<>(List.of(
Component.empty()
.append(Component.text("Status: ", NamedTextColor.DARK_GRAY))
@ -42,7 +42,7 @@ public abstract class BoolSetting extends Setting<Boolean> {
),
Component.empty()
));
lore.addAll(buildDescription(description()));
lore.addAll(this.buildDescription(this.description()));
meta.lore(lore);
return meta;
}
@ -59,6 +59,6 @@ public abstract class BoolSetting extends Setting<Boolean> {
@Override
public Boolean state() {
return state;
return this.state;
}
}

View File

@ -0,0 +1,103 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public abstract class IntegerSetting extends Setting<Integer> {
private final List<Integer> options;
public IntegerSetting(Settings.Key key, int minimum, int maximum) {
this(key, IntStream.range(minimum, maximum+1).boxed().toList());
}
public IntegerSetting(Settings.Key key, List<Integer> options) {
super(key);
this.options = options;
}
@Override
public ItemMeta buildMeta(ItemMeta meta) {
Component componentBefore = Component.text(" " + this.fillWithSpaces(this.options.getLast()));
Component componentAfter = Component.text(" " + this.fillWithSpaces(this.options.getFirst()));
int listIndex = this.options.indexOf(this.state);
if(listIndex > 0) componentBefore = Component.text(" " + this.fillWithSpaces(this.options.get(listIndex-1)));
if(listIndex < this.options.size()-1) componentAfter = Component.text(" " + this.fillWithSpaces(this.options.get(listIndex+1)));
meta.displayName(Component.text(this.title(), NamedTextColor.WHITE));
List<Component> lore = new ArrayList<>(Stream.of(
Component.empty()
.append(Component.text("Wert: ", NamedTextColor.DARK_GRAY)),
Component.empty()
.append(componentBefore.color(NamedTextColor.DARK_GRAY))
.append(Component.text(" " + this.fillWithSpaces(this.state), NamedTextColor.GREEN))
.append(componentAfter.color(NamedTextColor.DARK_GRAY)),
Component.empty()
).toList());
lore.addAll(this.buildDescription(this.description()));
meta.lore(lore);
return meta;
}
private String fillWithSpaces(Integer option) {
String optionString = option.toString();
int optionLength = optionString.length();
int maxInteger = this.options.stream().mapToInt(value -> value).max().orElse(0);
int maxLength = String.valueOf(maxInteger).length();
int padding = maxLength - optionLength;
int padEnd = padding / 2;
int padStart = padding - padEnd;
optionString = " ".repeat(padStart) + optionString + " ".repeat(padEnd);
return optionString;
}
@Override
protected void change(Player player, ClickType clickType) {
int elementBefore = this.options.getLast();
int elementAfter = this.options.getFirst();
int listIndex = this.options.indexOf(this.state);
if(listIndex > 0) elementBefore = this.options.get(listIndex-1);
if(listIndex < this.options.size()-1) elementAfter = this.options.get(listIndex+1);
if(clickType.equals(ClickType.LEFT)) this.state = elementBefore;
if(clickType.equals(ClickType.RIGHT)) this.state = elementAfter;
}
@Override
protected void fromStorage(PersistentDataContainer container) {
this.state = container.has(this.getNamespacedKey())
? Integer.valueOf(Objects.requireNonNull(container.get(this.getNamespacedKey(), PersistentDataType.STRING)))
: this.defaultValue();
if(!this.options.contains(this.state)) this.state = this.defaultValue();
}
@Override
protected void toStorage(PersistentDataContainer container, Integer value) {
container.set(this.getNamespacedKey(), PersistentDataType.STRING, new Gson().toJson(value));
}
@Override
public Class<?> dataType() {
return Integer.class;
}
@Override
public Integer state() {
return this.state;
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
@ -35,9 +35,10 @@ public abstract class MultiBoolSetting<T> extends Setting<T> {
@Override
public ItemMeta buildMeta(ItemMeta meta) {
record SettingField(String name, String displayName, Boolean value) {}
record SettingField(String name, String displayName, Boolean value) {
}
meta.displayName(Component.text(title(), NamedTextColor.WHITE));
meta.displayName(Component.text(this.title(), NamedTextColor.WHITE));
List<Component> lore = new ArrayList<>();
lore.add(Component.text("Status: ", NamedTextColor.DARK_GRAY));
@ -54,12 +55,12 @@ public abstract class MultiBoolSetting<T> extends Setting<T> {
: component.getName();
return new SettingField(component.getName(), displayName, value);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
} catch(NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
})
.map(field -> {
if (cursorPosition == null) cursorPosition = field.name;
if(this.cursorPosition == null) this.cursorPosition = field.name;
boolean isSelected = field.name.equals(this.cursorPosition);
return Component.text()
.append(Component.text(
@ -79,7 +80,7 @@ public abstract class MultiBoolSetting<T> extends Setting<T> {
.toList()
);
lore.add(Component.empty());
lore.addAll(buildDescription(description()));
lore.addAll(this.buildDescription(this.description()));
lore.add(Component.empty());
lore.add(Component.text("Linksklick", NamedTextColor.AQUA).append(Component.text(" zum Wählen der Option", NamedTextColor.GRAY)));
lore.add(Component.text("Rechtsklick", NamedTextColor.AQUA).append(Component.text(" zum Ändern des Wertes", NamedTextColor.GRAY)));
@ -96,16 +97,16 @@ public abstract class MultiBoolSetting<T> extends Setting<T> {
.findFirst()
.orElse(-1);
if (clickType.equals(ClickType.LEFT)) {
if(clickType.equals(ClickType.LEFT)) {
currentIndex = (currentIndex + 1) % recordComponents.length;
this.cursorPosition = recordComponents[currentIndex].getName();
} else if (clickType.equals(ClickType.RIGHT)) {
} else if(clickType.equals(ClickType.RIGHT)) {
try {
Object[] values = Arrays.stream(recordComponents)
.map(rc -> {
try {
return this.state.getClass().getDeclaredMethod(rc.getName()).invoke(this.state);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
} catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
})
@ -119,7 +120,8 @@ public abstract class MultiBoolSetting<T> extends Setting<T> {
this.state = (T) this.state.getClass().getConstructor(
Arrays.stream(recordComponents).map(RecordComponent::getType).toArray(Class[]::new)
).newInstance(values);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
} catch(NoSuchMethodException | InvocationTargetException | IllegalAccessException |
InstantiationException e) {
throw new RuntimeException(e);
}
}
@ -127,21 +129,21 @@ public abstract class MultiBoolSetting<T> extends Setting<T> {
@Override
protected void fromStorage(PersistentDataContainer container) {
String data = container.has(getNamespacedKey())
? Objects.requireNonNull(container.get(getNamespacedKey(), PersistentDataType.STRING))
: new Gson().toJson(defaultValue());
String data = container.has(this.getNamespacedKey())
? Objects.requireNonNull(container.get(this.getNamespacedKey(), PersistentDataType.STRING))
: new Gson().toJson(this.defaultValue());
try {
//noinspection unchecked
this.state = (T) new Gson().fromJson(data, dataType());
this.state = (T) new Gson().fromJson(data, this.dataType());
} catch(Exception e) {
this.state = defaultValue();
this.state = this.defaultValue();
}
}
@Override
protected void toStorage(PersistentDataContainer container, T value) {
container.set(getNamespacedKey(), PersistentDataType.STRING, new Gson().toJson(value));
container.set(this.getNamespacedKey(), PersistentDataType.STRING, new Gson().toJson(value));
}
@Override

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
@ -37,7 +37,7 @@ public abstract class SelectSetting extends Setting<SelectSetting.Options.Option
@Override
public ItemMeta buildMeta(ItemMeta meta) {
meta.displayName(Component.text(title(), NamedTextColor.WHITE));
meta.displayName(Component.text(this.title(), NamedTextColor.WHITE));
List<Component> lore = new ArrayList<>();
lore.add(Component.text("Status: ", NamedTextColor.DARK_GRAY));
lore.addAll(
@ -52,7 +52,7 @@ public abstract class SelectSetting extends Setting<SelectSetting.Options.Option
.toList()
);
lore.add(Component.empty());
lore.addAll(buildDescription(description()));
lore.addAll(this.buildDescription(this.description()));
meta.lore(lore);
return meta;
}
@ -65,9 +65,9 @@ public abstract class SelectSetting extends Setting<SelectSetting.Options.Option
.filter(i -> options.get(i).equals(this.state))
.mapToObj(i -> {
int nextIndex = i + optionModifier;
if (nextIndex >= options.size()) {
if(nextIndex >= options.size()) {
return options.getFirst();
} else if (nextIndex < 0) {
} else if(nextIndex < 0) {
return options.getLast();
} else {
return options.get(nextIndex);
@ -79,19 +79,19 @@ public abstract class SelectSetting extends Setting<SelectSetting.Options.Option
@Override
protected void fromStorage(PersistentDataContainer container) {
String data = container.has(getNamespacedKey())
? Objects.requireNonNull(container.get(getNamespacedKey(), PersistentDataType.STRING))
: defaultValue().key.asString();
String data = container.has(this.getNamespacedKey())
? Objects.requireNonNull(container.get(this.getNamespacedKey(), PersistentDataType.STRING))
: this.defaultValue().key.asString();
this.state = this.options.options.stream()
.filter(option -> option.key.asString().equals(data))
.findFirst()
.orElse(defaultValue());
.orElse(this.defaultValue());
}
@Override
protected void toStorage(PersistentDataContainer container, Options.Option value) {
container.set(getNamespacedKey(), PersistentDataType.STRING, value.key.asString());
container.set(this.getNamespacedKey(), PersistentDataType.STRING, value.key.asString());
}
@Override

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@ -17,7 +17,7 @@ import org.bukkit.persistence.PersistentDataContainer;
import java.util.List;
public abstract class Setting<TDataType> {
TDataType state;
protected TDataType state;
private final Settings.Key key;
public Setting(Settings.Key key) {
@ -25,25 +25,25 @@ public abstract class Setting<TDataType> {
}
public NamespacedKey getNamespacedKey() {
return new NamespacedKey(Main.instance(), key.name());
return new NamespacedKey(Main.instance(), this.key.name());
}
public Settings.Key getKey() {
return key;
return this.key;
}
public void initializeFromPlayer(Player p) {
fromStorage(p.getPersistentDataContainer());
this.fromStorage(p.getPersistentDataContainer());
}
public void triggerChange(Player p, ClickType clickType) {
this.change(p, clickType);
toStorage(p.getPersistentDataContainer(), this.state());
this.toStorage(p.getPersistentDataContainer(), this.state());
}
public ItemStack buildItem() {
ItemStack stack = new ItemStack(icon(), 1);
stack.setItemMeta(buildMeta(stack.getItemMeta()));
ItemStack stack = new ItemStack(this.icon(), 1);
stack.setItemMeta(this.buildMeta(stack.getItemMeta()));
return stack;
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.settings.listeners;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
@ -9,8 +9,9 @@ public class OpenSettingsShortcutListener extends ApplianceListener<Settings> {
@EventHandler
public void onItemSwitch(PlayerSwapHandItemsEvent event) {
if(!event.getPlayer().isSneaking()) return;
if(!Settings.instance().getSetting(event.getPlayer(), Settings.Key.EnableSettingsShortcut, Boolean.class)) return;
if(!Settings.instance().getSetting(event.getPlayer(), Settings.Key.EnableSettingsShortcut, Boolean.class))
return;
event.setCancelled(true);
getAppliance().openSettings(event.getPlayer());
this.getAppliance().openSettings(event.getPlayer());
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.settings.listeners;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.InventoryClickEvent;
@ -11,10 +11,10 @@ public class SettingsInventoryListener extends ApplianceListener<Settings> {
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
Player player = (Player) event.getWhoClicked();
if(getAppliance().hasSettingsNotOpen(player)) return;
if(this.getAppliance().hasSettingsNotOpen(player)) return;
event.setCancelled(true);
Settings.OpenSettingsInventory openInventory = getAppliance().getOpenInventory(player);
Settings.OpenSettingsInventory openInventory = this.getAppliance().getOpenInventory(player);
openInventory.settings().stream()
.filter(setting -> setting.buildItem().equals(event.getCurrentItem()))
.findFirst()
@ -26,6 +26,6 @@ public class SettingsInventoryListener extends ApplianceListener<Settings> {
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
getAppliance().onSettingsClose((Player) event.getPlayer());
this.getAppliance().onSettingsClose((Player) event.getPlayer());
}
}

View File

@ -1,13 +1,13 @@
package eu.mhsl.craftattack.spawn.appliances.tablist;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.tablist;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.report.Report;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.util.statistics.NetworkMonitor;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.util.text.RainbowComponent;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report.Report;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.core.util.statistics.NetworkMonitor;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.util.text.RainbowComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.util.Ticks;
@ -23,7 +23,8 @@ import java.util.List;
public class Tablist extends Appliance {
private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 7 ", 7, 3);
private final String projectTitle = this.localConfig().getString("projectTitle", "Title not configured");
private final RainbowComponent serverName = new RainbowComponent(String.format(" %s ", this.projectTitle), 7, 3);
private NetworkMonitor networkMonitor;
private OperatingSystemMXBean systemMonitor;
@ -36,7 +37,7 @@ public class Tablist extends Appliance {
Settings.instance().declareSetting(TechnicalTablistSetting.class);
int tabRefreshRate = 3;
this.networkMonitor = new NetworkMonitor(localConfig().getString("interface"), Duration.ofSeconds(1));
this.networkMonitor = new NetworkMonitor(this.localConfig().getString("interface"), Duration.ofSeconds(1));
this.systemMonitor = ManagementFactory.getOperatingSystemMXBean();
Bukkit.getScheduler().runTaskTimerAsynchronously(
@ -53,14 +54,14 @@ public class Tablist extends Appliance {
}
public void fullUpdate(Player player) {
updateHeader(player);
updateFooter(player);
this.updateHeader(player);
this.updateFooter(player);
}
private void updateHeader(Player player) {
boolean detailedInfo = queryAppliance(Settings.class).getSetting(player, Settings.Key.TechnicalTab, Boolean.class);
boolean detailedInfo = this.queryAppliance(Settings.class).getSetting(player, Settings.Key.TechnicalTab, Boolean.class);
Component header = Component.newline()
.append(serverName.getRainbowState()).appendNewline()
.append(this.serverName.getRainbowState()).appendNewline()
.append(Component.text("mhsl.eu", NamedTextColor.GOLD)).appendNewline().appendNewline()
.append(ComponentUtil.getFormattedTickTimes(detailedInfo)).appendNewline();

View File

@ -0,0 +1,12 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.tablist;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class TablistListener extends ApplianceListener<Tablist> {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
this.getAppliance().fullUpdate(event.getPlayer());
}
}

View File

@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.tablist;
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.tablist;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting;
import org.bukkit.Material;
public class TechnicalTablistSetting extends BoolSetting implements CategorizedSetting {

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.acInform;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
@ -11,62 +11,60 @@ import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.List;
public class AcInform extends Appliance {
public void processCommand(@NotNull String[] args) {
String anticheatName = null;
String playerName = null;
String checkName = null;
Integer violationCount = null;
Float violationCount = null;
for(int i = 0; i < args.length; i++) {
if(!args[i].startsWith("--")) continue;
if(i == args.length-1) continue;
String nextArgument = args[i+1];
if(nextArgument.startsWith("--")) continue;
StringBuilder valueBuilder = new StringBuilder();
for(int j = i + 1; j < args.length; j++) {
if(args[j].startsWith("--")) break;
if(!valueBuilder.isEmpty()) valueBuilder.append(" ");
valueBuilder.append(args[j]);
}
String value = valueBuilder.toString();
switch(args[i]) {
case "--anticheatName" -> anticheatName = nextArgument;
case "--playerName" -> playerName = nextArgument;
case "--check" -> checkName = nextArgument;
case "--violationCount" -> violationCount = Integer.valueOf(nextArgument);
case "--anticheatName" -> anticheatName = value;
case "--playerName" -> playerName = value;
case "--check" -> checkName = value;
case "--violationCount" -> violationCount = value.isEmpty() ? null : Float.valueOf(value);
}
}
this.notifyAdmins(anticheatName, playerName, checkName, violationCount);
}
public void notifyAdmins(@Nullable String anticheatName, @Nullable String playerName, @Nullable String checkName, @Nullable Integer violationCount) {
public void notifyAdmins(@Nullable String anticheatName, @Nullable String playerName, @Nullable String checkName, @Nullable Float violationCount) {
ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text();
Component prefix = Component.text("# ", NamedTextColor.DARK_RED);
NamedTextColor textColor = NamedTextColor.GRAY;
if(playerName == null || playerName.isBlank()) throw new ApplianceCommand.Error("acinform command needs a player (--playerName)");
if(playerName == null || playerName.isBlank())
throw new ApplianceCommand.Error("acinform command needs a player (--playerName)");
if(anticheatName != null && !anticheatName.isBlank()) {
component.append(
Component.newline()
.append(prefix)
.append(Component.text("[", textColor))
.append(Component.text("Anticheat", NamedTextColor.RED))
.append(Component.text("] ", textColor))
.append(Component.text(anticheatName, NamedTextColor.WHITE))
.append(Component.text(":", textColor))
);
component
.append(Component.text(" ", NamedTextColor.GRAY))
.append(Component.text("[", textColor))
.append(Component.text(anticheatName, NamedTextColor.RED))
.append(Component.text("]: ", textColor));
}
component.append(
Component.newline()
.append(prefix)
.append(Component.text("Player ", textColor))
.append(Component.text(playerName, NamedTextColor.WHITE))
.append(Component.text(" "))
);
component
.append(Component.text("Player ", textColor))
.append(Component.text(playerName, NamedTextColor.WHITE))
.append(Component.text(" "));
if(checkName == null || checkName.isBlank()) {
component.append(Component.text("got detected by Anticheat", textColor));
} else if(violationCount != null){
} else if(violationCount != null) {
component.append(
Component.text("failed ", textColor)
.append(Component.text(String.format("%sx ", violationCount), NamedTextColor.WHITE))
@ -85,8 +83,7 @@ public class AcInform extends Appliance {
component.append(
Component.newline()
.append(prefix)
.append(Component.text("", NamedTextColor.GRAY))
.append(Component.text("[", NamedTextColor.GRAY))
.append(Component.text("Report", NamedTextColor.GOLD))
.append(Component.text("]", NamedTextColor.GRAY))
@ -109,12 +106,11 @@ public class AcInform extends Appliance {
component.append(
Component.text(" [", NamedTextColor.GRAY)
.append(Component.text("Teleport", NamedTextColor.GOLD))
.append(Component.text("Spectate/Teleport", NamedTextColor.GOLD))
.append(Component.text("]", NamedTextColor.GRAY))
.clickEvent(ClickEvent.suggestCommand(String.format("/tp %s", playerName)))
.clickEvent(ClickEvent.suggestCommand(String.format("/grim spectate %s", playerName)))
);
component.appendNewline();
TextComponent finalMessage = component.build();
Bukkit.getOnlinePlayers().stream()

View File

@ -1,12 +1,12 @@
package eu.mhsl.craftattack.spawn.appliances.acInform;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class AcInformCommand extends ApplianceCommand<AcInform> {
class AcInformCommand extends ApplianceCommand<AcInform> {
public AcInformCommand() {
super("acInform");
}
@ -14,6 +14,6 @@ public class AcInformCommand extends ApplianceCommand<AcInform> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
if(sender instanceof Player) throw new ApplianceCommand.Error("Dieser Command ist nicht für Spieler!");
getAppliance().processCommand(args);
this.getAppliance().processCommand(args);
}
}

View File

@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.adminChat;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@ -1,11 +1,11 @@
package eu.mhsl.craftattack.spawn.appliances.adminChat;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class AdminChatCommand extends ApplianceCommand.PlayerChecked<AdminChat> {
class AdminChatCommand extends ApplianceCommand.PlayerChecked<AdminChat> {
public static final String commandName = "adminchat";
public AdminChatCommand() {
@ -17,6 +17,6 @@ public class AdminChatCommand extends ApplianceCommand.PlayerChecked<AdminChat>
if(!sender.hasPermission("admin")) return;
String message = String.join(" ", args);
getAppliance().sendMessage(getPlayer(), message);
this.getAppliance().sendMessage(this.getPlayer(), message);
}
}

View File

@ -0,0 +1,44 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
public class ChatMute extends Appliance {
private static final String namespace = ChatMute.class.getSimpleName().toLowerCase(Locale.ROOT);
public static NamespacedKey mutedUntilKey = new NamespacedKey(namespace, "mutedUntilMillis".toLowerCase());
public void mutePlayer(Player player, int durationHours) {
PersistentDataContainer container = player.getPersistentDataContainer();
long mutedUntil = System.currentTimeMillis() + (long) durationHours * 60 * 60 * 1000;
container.set(ChatMute.mutedUntilKey, PersistentDataType.LONG, mutedUntil);
}
public @Nullable Long muteStatus(Player player) {
PersistentDataContainer container = player.getPersistentDataContainer();
if(!container.has(mutedUntilKey)) return null;
long mutedUntil = Objects.requireNonNull(container.get(mutedUntilKey, PersistentDataType.LONG));
if(mutedUntil < System.currentTimeMillis()) return null;
return mutedUntil;
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(new MuteCommand());
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new ChatMuteListener());
}
}

View File

@ -0,0 +1,25 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.util.text.DataSizeConverter;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.event.EventHandler;
import org.jetbrains.annotations.Nullable;
class ChatMuteListener extends ApplianceListener<ChatMute> {
@EventHandler
public void onChat(AsyncChatEvent event) {
@Nullable Long muteDuration = this.getAppliance().muteStatus(event.getPlayer());
if(muteDuration == null) return;
event.setCancelled(true);
event.getPlayer().sendMessage(Component.text(
String.format(
"Du bist für %s gestummt!",
DataSizeConverter.formatSecondsToHumanReadable((int) ((muteDuration - System.currentTimeMillis()) / 1000))
),
NamedTextColor.RED
));
}
}

View File

@ -0,0 +1,35 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
class MuteCommand extends ApplianceCommand<ChatMute> {
public MuteCommand() {
super("mute");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
if(args.length < 2) throw new Error("Syntax: mute <player> <duration in hours>");
Player player = Objects.requireNonNull(Bukkit.getPlayer(args[0]));
int durationInHours = Integer.parseInt(args[1]);
this.getAppliance().mutePlayer(player, durationInHours);
sender.sendMessage(String.format("%s wurde für %d Stunden gestummt!", player.getName(), durationInHours));
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(args.length == 2) {
return List.of("1", "2", "4", "8", "24", "48");
}
return null;
}
}

View File

@ -0,0 +1,63 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.endPrevent;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class EndPrevent extends Appliance {
private final String endPreventKey = "endPrevent";
private State endPreventState;
private final World endWorld = Bukkit.getWorlds().stream().filter(world -> world.getEnvironment().equals(World.Environment.THE_END)).findFirst().orElseThrow();
public enum State {
OPEN,
CLOSED,
NO_OUTER
}
public EndPrevent() {
super("endPrevent");
this.endPreventState = State.valueOf(this.localConfig().getString(this.endPreventKey, State.OPEN.name()));
this.updateEndBorder();
}
private void updateEndBorder() {
if(this.endPreventState == State.NO_OUTER) this.endWorld.getWorldBorder().setSize(500);
if(this.endPreventState == State.OPEN) this.endWorld.getWorldBorder().setSize(this.endWorld.getWorldBorder().getMaxSize());
}
public void setEndState(State state) {
this.localConfig().set(this.endPreventKey, state.name());
Configuration.saveChanges();
this.endPreventState = state;
this.updateEndBorder();
}
public boolean isEndClosed() {
return this.endPreventState.equals(State.CLOSED);
}
public boolean isOnlyInner() {
return this.endPreventState.equals(State.NO_OUTER);
}
public State getEndPreventState() {
return this.endPreventState;
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new EndPreventListener());
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(new EndPreventCommand());
}
}

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.endPrevent;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.endPrevent;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
@ -11,8 +11,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
public class EndPreventCommand extends ApplianceCommand<EndPrevent> {
private final Map<String, Boolean> arguments = Map.of("preventEnd", true, "allowEnd", false);
class EndPreventCommand extends ApplianceCommand<EndPrevent> {
private final Map<String, EndPrevent.State> arguments = Map.of(
"preventEnd", EndPrevent.State.CLOSED,
"allowEnd", EndPrevent.State.OPEN,
"onlyInnerEnd", EndPrevent.State.NO_OUTER
);
public EndPreventCommand() {
super("endPrevent");
@ -20,18 +24,18 @@ public class EndPreventCommand extends ApplianceCommand<EndPrevent> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
if(args.length == 1 && arguments.containsKey(args[0])) {
getAppliance().setEndDisabled(arguments.get(args[0]));
if(args.length == 1 && this.arguments.containsKey(args[0])) {
this.getAppliance().setEndState(this.arguments.get(args[0]));
sender.sendMessage(Component.text("Setting updated!", NamedTextColor.GREEN));
}
sender.sendMessage(Component.text(
String.format("The End is %s!", getAppliance().isEndDisabled() ? "open" : "closed"),
String.format("The End is now on '%s'!", this.getAppliance().getEndPreventState().name()),
NamedTextColor.GOLD
));
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return arguments.keySet().stream().toList();
return this.arguments.keySet().stream().toList();
}
}

View File

@ -0,0 +1,49 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.endPrevent;
import com.destroystokyo.paper.event.player.PlayerTeleportEndGatewayEvent;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerPortalEvent;
class EndPreventListener extends ApplianceListener<EndPrevent> {
@EventHandler
public void onEnderEyeInteraction(PlayerInteractEvent event) {
if(event.getClickedBlock() == null) return;
if(!event.getClickedBlock().getType().equals(Material.END_PORTAL_FRAME)) return;
if(event.getItem() == null) return;
if(!event.getItem().getType().equals(Material.ENDER_EYE)) return;
this.cancelIfClosed(event, event.getPlayer());
}
@EventHandler
public void onPlayerPortal(PlayerPortalEvent event) {
if(!event.getTo().getWorld().getEnvironment().equals(World.Environment.THE_END)) return;
this.cancelIfClosed(event, event.getPlayer());
}
@EventHandler
public void onPlayerEndGateway(PlayerTeleportEndGatewayEvent event) {
this.cancelIfOnlyInner(event, event.getPlayer());
}
private void cancelIfClosed(Cancellable event, Player p) {
if(!this.getAppliance().isEndClosed()) return;
event.setCancelled(true);
p.sendActionBar(Component.text("Das End ist nicht freigeschaltet!", NamedTextColor.RED));
}
private void cancelIfOnlyInner(Cancellable event, Player p) {
if(!this.getAppliance().isOnlyInner()) return;
event.setCancelled(true);
p.sendActionBar(Component.text("Das Outer End ist nicht freigeschaltet!", NamedTextColor.RED));
}
}

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.kick;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.kick;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.kick;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.kick;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -12,14 +12,14 @@ import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class KickCommand extends ApplianceCommand<Kick> {
class KickCommand extends ApplianceCommand<Kick> {
public KickCommand() {
super("kick");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().kick(
this.getAppliance().kick(
args[0],
Arrays.stream(args).skip(1).collect(Collectors.joining(" "))
);

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.maintenance;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.config.Configuration;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
@ -18,17 +18,17 @@ public class Maintenance extends Appliance {
@Override
public void onEnable() {
this.isInMaintenance = localConfig().getBoolean(configKey, false);
this.isInMaintenance = this.localConfig().getBoolean(this.configKey, false);
}
public void setState(boolean enabled) {
this.isInMaintenance = enabled;
localConfig().set(configKey, enabled);
this.localConfig().set(this.configKey, enabled);
Configuration.saveChanges();
}
public boolean isInMaintenance() {
return isInMaintenance;
return this.isInMaintenance;
}
@Override

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.maintenance;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
public class MaintenanceCommand extends ApplianceCommand<Maintenance> {
class MaintenanceCommand extends ApplianceCommand<Maintenance> {
private final Map<String, Boolean> arguments = Map.of("enable", true, "disable", false);
public MaintenanceCommand() {
@ -20,18 +20,18 @@ public class MaintenanceCommand extends ApplianceCommand<Maintenance> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
if(args.length == 1 && arguments.containsKey(args[0])) {
getAppliance().setState(arguments.get(args[0]));
if(args.length == 1 && this.arguments.containsKey(args[0])) {
this.getAppliance().setState(this.arguments.get(args[0]));
sender.sendMessage(Component.text("Maintanance mode updated!", NamedTextColor.GREEN));
}
sender.sendMessage(Component.text(
String.format("Maintanance mode is %b", getAppliance().isInMaintenance()),
String.format("Maintanance mode is %b", this.getAppliance().isInMaintenance()),
NamedTextColor.GOLD
));
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return arguments.keySet().stream().toList();
return this.arguments.keySet().stream().toList();
}
}

View File

@ -1,14 +1,14 @@
package eu.mhsl.craftattack.spawn.appliances.maintenance;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerLoginEvent;
public class PreventMaintenanceJoinListener extends ApplianceListener<Maintenance> {
class PreventMaintenanceJoinListener extends ApplianceListener<Maintenance> {
@EventHandler
public void onJoin(PlayerLoginEvent event) {
if(!getAppliance().isInMaintenance()) return;
if(!this.getAppliance().isInMaintenance()) return;
if(event.getPlayer().hasPermission("bypassMaintainance")) return;
DisconnectInfo disconnectInfo = new DisconnectInfo(

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.panicBan;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@ -22,15 +22,15 @@ public class PanicBan extends Appliance {
if(player == null)
throw new ApplianceCommand.Error("Player not found");
panicBans.put(player.getUniqueId(), System.currentTimeMillis());
this.panicBans.put(player.getUniqueId(), System.currentTimeMillis());
this.getDisconnectInfo(player.getUniqueId()).applyKick(player);
}
public boolean isBanned(UUID player) {
if(panicBans.containsKey(player) && panicBans.get(player) < System.currentTimeMillis() - 15 * 60 * 1000)
panicBans.remove(player);
if(this.panicBans.containsKey(player) && this.panicBans.get(player) < System.currentTimeMillis() - 15 * 60 * 1000)
this.panicBans.remove(player);
return panicBans.containsKey(player);
return this.panicBans.containsKey(player);
}
public DisconnectInfo getDisconnectInfo(UUID playerUuid) {

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.panicBan;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
@ -13,14 +13,14 @@ import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class PanicBanCommand extends ApplianceCommand<PanicBan> {
class PanicBanCommand extends ApplianceCommand<PanicBan> {
public PanicBanCommand() {
super("panicBan");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().panicBan(args[0]);
this.getAppliance().panicBan(args[0]);
}
@Override

View File

@ -0,0 +1,15 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
class PanicBanJoinListener extends ApplianceListener<PanicBan> {
@EventHandler
public void onLogin(AsyncPlayerPreLoginEvent event) {
if(this.getAppliance().isBanned(event.getUniqueId())) {
event.kickMessage(this.getAppliance().getDisconnectInfo(event.getUniqueId()).getComponent());
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_BANNED);
}
}
}

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.playerlimit;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.config.Configuration;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
@ -15,18 +15,18 @@ public class PlayerLimit extends Appliance {
public PlayerLimit() {
super("playerLimit");
this.limit = localConfig().getInt(playerLimitKey);
this.limit = this.localConfig().getInt(playerLimitKey);
Bukkit.setMaxPlayers(Integer.MAX_VALUE);
}
public void setPlayerLimit(int limit) {
this.limit = limit;
localConfig().set(playerLimitKey, limit);
this.localConfig().set(playerLimitKey, limit);
Configuration.saveChanges();
}
public int getLimit() {
return limit;
return this.limit;
}
@Override

View File

@ -1,12 +1,12 @@
package eu.mhsl.craftattack.spawn.appliances.playerlimit;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
public class PlayerLimiterListener extends ApplianceListener<PlayerLimit> {
class PlayerLimiterListener extends ApplianceListener<PlayerLimit> {
@EventHandler
public void onLogin(AsyncPlayerPreLoginEvent playerPreLoginEvent) {
playerPreLoginEvent.kickMessage(
@ -18,7 +18,7 @@ public class PlayerLimiterListener extends ApplianceListener<PlayerLimit> {
).getComponent()
);
if(Bukkit.getOnlinePlayers().size() >= getAppliance().getLimit())
if(Bukkit.getOnlinePlayers().size() >= this.getAppliance().getLimit())
playerPreLoginEvent.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_FULL);
}
}

View File

@ -1,13 +1,13 @@
package eu.mhsl.craftattack.spawn.appliances.playerlimit;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class SetPlayerLimitCommand extends ApplianceCommand<PlayerLimit> {
class SetPlayerLimitCommand extends ApplianceCommand<PlayerLimit> {
public SetPlayerLimitCommand() {
super("setPlayerLimit");
}
@ -18,11 +18,11 @@ public class SetPlayerLimitCommand extends ApplianceCommand<PlayerLimit> {
sender.sendMessage(
Component.text()
.append(Component.text("Das aktuelle Spielerlimit beträgt: ", NamedTextColor.GRAY))
.append(Component.text(getAppliance().getLimit(), NamedTextColor.GOLD))
.append(Component.text(this.getAppliance().getLimit(), NamedTextColor.GOLD))
);
return;
}
getAppliance().setPlayerLimit(Integer.parseInt(args[0]));
this.getAppliance().setPlayerLimit(Integer.parseInt(args[0]));
}
}

View File

@ -1,18 +1,17 @@
package eu.mhsl.craftattack.spawn.appliances.restart.command;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.restart.Restart;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class CancelRestartCommand extends ApplianceCommand<Restart> {
class CancelRestartCommand extends ApplianceCommand<Restart> {
public CancelRestartCommand() {
super("cancelRestart");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().cancelRestart();
this.getAppliance().cancelRestart();
}
}

View File

@ -1,12 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.restart;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.restart.command.CancelRestartCommand;
import eu.mhsl.craftattack.spawn.appliances.restart.command.ScheduleRestartCommand;
import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.util.text.Countdown;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.core.util.text.Countdown;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
@ -51,13 +48,6 @@ public class Restart extends Appliance {
}
private void onDone() {
IteratorUtil.onlinePlayers(
player -> new DisconnectInfo(
"Serverneustart",
"Wir sind gleich wieder online!",
"Verbinde Dich dann erneut.",
player.getUniqueId()
).applyKick(player));
Bukkit.shutdown();
}

View File

@ -1,18 +1,17 @@
package eu.mhsl.craftattack.spawn.appliances.restart.command;
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.restart.Restart;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ScheduleRestartCommand extends ApplianceCommand<Restart> {
class ScheduleRestartCommand extends ApplianceCommand<Restart> {
public ScheduleRestartCommand() {
super("scheduleRestart");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().scheduleRestart();
this.getAppliance().scheduleRestart();
}
}

7
core/build.gradle Normal file
View File

@ -0,0 +1,7 @@
dependencies {
compileOnly 'io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT'
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'com.sparkjava:spark-core:2.9.4'
implementation 'org.reflections:reflections:0.10.2'
}

View File

@ -1,8 +1,9 @@
package eu.mhsl.craftattack.spawn;
package eu.mhsl.craftattack.spawn.core;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.config.Configuration;
import eu.mhsl.craftattack.spawn.core.api.client.RepositoryLoader;
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin;
@ -15,19 +16,22 @@ import java.util.logging.Level;
import java.util.logging.Logger;
public final class Main extends JavaPlugin {
public static final String projectPackage = "eu.mhsl.craftattack.spawn";
private static Main instance;
private static Logger logger;
private List<Appliance> appliances;
private RepositoryLoader repositoryLoader;
private Reflections reflections;
@Override
public void onEnable() {
instance = this;
logger = instance().getLogger();
saveDefaultConfig();
this.saveDefaultConfig();
try {
this.wrappedEnable();
} catch (Exception e) {
} catch(Exception e) {
Main.logger().log(Level.SEVERE, "Error while initializing Spawn plugin, shutting down!", e);
Bukkit.shutdown();
}
@ -37,40 +41,46 @@ public final class Main extends JavaPlugin {
Configuration.readConfig();
List<String> disabledAppliances = Configuration.pluginConfig.getStringList("disabledAppliances");
Main.logger.info("Loading appliances...");
Reflections reflections = new Reflections(this.getClass().getPackageName());
Set<Class<? extends Appliance>> applianceClasses = reflections.getSubTypesOf(Appliance.class);
Main.logger().info("Initializing reflections...");
this.reflections = new Reflections(projectPackage);
this.appliances = applianceClasses.stream()
Main.logger().info("Loading Repositories...");
this.repositoryLoader = new RepositoryLoader();
Main.logger().info(String.format("Loaded %d repositories!", this.repositoryLoader.getRepositories().size()));
Main.logger().info("Loading appliances...");
this.appliances = this.findSubtypesOf(Appliance.class).stream()
.filter(applianceClass -> !disabledAppliances.contains(applianceClass.getSimpleName()))
.map(applianceClass -> {
try {
return (Appliance) applianceClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
} catch(Exception e) {
throw new RuntimeException(String.format("Failed to create instance of '%s'", applianceClass.getName()), e);
}
})
.toList();
Main.logger().info(String.format("Loaded %d appliances!", appliances.size()));
Main.logger().info(String.format("Loaded %d appliances!", this.appliances.size()));
Main.logger().info("Initializing appliances...");
this.appliances.forEach(appliance -> {
appliance.onEnable();
appliance.initialize(this);
});
Main.logger().info(String.format("Initialized %d appliances!", appliances.size()));
Main.logger().info(String.format("Initialized %d appliances!", this.appliances.size()));
Main.logger().info("Starting HTTP API...");
new HttpServer();
if(Configuration.pluginConfig.getBoolean("httpServerEnabled", true)) {
Main.logger().info("Starting HTTP API...");
new HttpServer();
}
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
Main.logger().info("Startup complete!");
}
@Override
public void onDisable() {
Main.logger().info("Disabling appliances...");
appliances.forEach(appliance -> {
this.appliances.forEach(appliance -> {
Main.logger().info("Disabling " + appliance.getClass().getSimpleName());
appliance.onDisable();
appliance.destruct(this);
@ -78,7 +88,7 @@ public final class Main extends JavaPlugin {
HandlerList.unregisterAll(this);
Bukkit.getScheduler().cancelTasks(this);
Main.logger().info("Disabled " + appliances.size() + " appliances!");
Main.logger().info("Disabled " + this.appliances.size() + " appliances!");
}
public <T extends Appliance> T getAppliance(Class<T> clazz) {
@ -89,13 +99,21 @@ public final class Main extends JavaPlugin {
.orElseThrow(() -> new RuntimeException(String.format("Appliance %s not loaded or instantiated!", clazz)));
}
public <T> Set<Class<? extends T>> findSubtypesOf(Class<T> type) {
return this.reflections.getSubTypesOf(type);
}
@SuppressWarnings("unchecked")
public static <T> Class<T> getApplianceType(Class<?> clazz) {
return (Class<T>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
public List<Appliance> getAppliances() {
return appliances;
return this.appliances;
}
public RepositoryLoader getRepositoryLoader() {
return this.repositoryLoader;
}
public static Main instance() {

View File

@ -0,0 +1,14 @@
package eu.mhsl.craftattack.spawn.core.api;
public class HttpStatus {
public static final int OK = 200;
public static final int CREATED = 201;
public static final int ACCEPTED = 202;
public static final int NO_CONTENT = 204;
public static final int BAD_REQUEST = 400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int INTERNAL_SERVER_ERROR = 500;
public static final int SERVICE_UNAVAILABLE = 503;
}

View File

@ -0,0 +1,127 @@
package eu.mhsl.craftattack.spawn.core.api.client;
import eu.mhsl.craftattack.spawn.core.Main;
import org.apache.http.client.utils.URIBuilder;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@RepositoryLoader.Abstraction
public abstract class HttpRepository extends Repository {
public record RequestModifier(
@Nullable Consumer<URIBuilder> uri,
@Nullable Consumer<HttpRequest.Builder> header
) {}
private final List<RequestModifier> baseRequestModifier;
public HttpRepository(URI basePath) {
this(basePath, new RequestModifier(null, null));
}
public HttpRepository(URI basePath, RequestModifier... baseRequestModifier) {
super(basePath);
this.baseRequestModifier = baseRequestModifier == null
? List.of()
: List.of(baseRequestModifier);
}
protected <TInput, TOutput> ReqResp<TOutput> post(String command, TInput data, Class<TOutput> outputType) {
return this.post(command, parameters -> {
}, data, outputType);
}
protected <TInput, TOutput> ReqResp<TOutput> post(String command, Consumer<URIBuilder> parameters, TInput data, Class<TOutput> outputType) {
HttpRequest request = this.getRequestBuilder(this.getUri(command, parameters))
.POST(HttpRequest.BodyPublishers.ofString(this.gson.toJson(data)))
.build();
return this.execute(request, outputType, data);
}
protected <TInput, TOutput> ReqResp<TOutput> put(String command, TInput data, Class<TOutput> outputType) {
return this.put(command, parameters -> {
}, data, outputType);
}
protected <TInput, TOutput> ReqResp<TOutput> put(String command, Consumer<URIBuilder> parameters, TInput data, Class<TOutput> outputType) {
HttpRequest request = this.getRequestBuilder(this.getUri(command, parameters))
.PUT(HttpRequest.BodyPublishers.ofString(this.gson.toJson(data)))
.build();
return this.execute(request, outputType, data);
}
protected <TOutput> ReqResp<TOutput> get(String command, Class<TOutput> outputType) {
return this.get(command, parameters -> {
}, outputType);
}
protected <TOutput> ReqResp<TOutput> get(String command, Consumer<URIBuilder> parameters, Class<TOutput> outputType) {
HttpRequest request = this.getRequestBuilder(this.getUri(command, parameters))
.GET()
.build();
return this.execute(request, outputType, null);
}
private URI getUri(String command, Consumer<URIBuilder> parameters) {
try {
URIBuilder builder = new URIBuilder(this.basePath + "/" + command);
this.baseRequestModifier.stream()
.map(requestModifier -> requestModifier.uri)
.filter(Objects::nonNull)
.forEach(modifier -> modifier.accept(builder));
parameters.accept(builder);
return builder.build();
} catch(URISyntaxException e) {
throw new RuntimeException(e);
}
}
private HttpRequest.Builder getRequestBuilder(URI endpoint) {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(endpoint)
.header("User-Agent", Main.instance().getServer().getBukkitVersion())
.header("Content-Type", "application/json");
this.baseRequestModifier.stream()
.map(requestModifier -> requestModifier.header)
.filter(Objects::nonNull)
.forEach(modifier -> modifier.accept(builder));
return builder;
}
private <TResponse> ReqResp<TResponse> execute(HttpRequest request, Class<TResponse> clazz, Object original) {
ReqResp<String> rawResponse = this.sendHttp(request);
Main.logger().info(String.format(
"Request: %s\nRequest-Data: %s\nResponse: %s",
request,
this.gson.toJson(original),
rawResponse
));
return new ReqResp<>(rawResponse.status(), this.gson.fromJson(rawResponse.data(), clazz));
}
private ReqResp<String> sendHttp(HttpRequest request) {
try(HttpClient client = HttpClient.newHttpClient()) {
this.validateThread(request.uri().getPath());
HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
return new ReqResp<>(httpResponse.statusCode(), httpResponse.body());
} catch(IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,30 @@
package eu.mhsl.craftattack.spawn.core.api.client;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.mhsl.craftattack.spawn.core.Main;
import org.bukkit.Bukkit;
import java.net.URI;
public abstract class Repository {
protected URI basePath;
protected Gson gson;
public Repository(URI basePath) {
this.basePath = basePath;
this.gson = new GsonBuilder()
.serializeNulls()
.create();
}
protected void validateThread(String commandName) {
if(!Bukkit.isPrimaryThread()) return;
Main.logger().warning(String.format(
"Repository '%s' was called synchronously with command '%s'!",
this.getClass().getSimpleName(),
commandName
));
}
}

View File

@ -0,0 +1,52 @@
package eu.mhsl.craftattack.spawn.core.api.client;
import eu.mhsl.craftattack.spawn.core.Main;
import org.apache.commons.lang3.NotImplementedException;
import org.reflections.Reflections;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Set;
public class RepositoryLoader {
private final List<Repository> repositories;
/**
* Defines a repository as an abstraction and will not be loaded by this RepositoryLoader
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Abstraction {
}
public RepositoryLoader() {
Reflections reflections = new Reflections(Main.projectPackage);
Set<Class<? extends Repository>> repositories = reflections.getSubTypesOf(Repository.class);
this.repositories = repositories.stream()
.filter(repository -> !repository.isAnnotationPresent(Abstraction.class))
.map(repository -> {
try {
return (Repository) repository.getDeclaredConstructor().newInstance();
} catch(InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
})
.toList();
}
public <T> T getRepository(Class<T> clazz) {
//noinspection unchecked
return this.repositories.stream()
.filter(clazz::isInstance)
.map(repository -> (T) repository)
.findFirst()
.orElseThrow(() -> new NotImplementedException(String.format("Repository '%s' not found!", clazz.getSimpleName())));
}
public List<Repository> getRepositories() {
return this.repositories;
}
}

View File

@ -0,0 +1,16 @@
package eu.mhsl.craftattack.spawn.core.api.client;
import com.google.gson.Gson;
import java.lang.reflect.Type;
public record ReqResp<TData>(int status, TData data) {
public ReqResp<?> convertToTypeToken(Type type) {
var gson = new Gson();
return new ReqResp<>(this.status, gson.fromJson(gson.toJson(this.data), type));
}
@SuppressWarnings("unchecked")
public <T> T cast() {
return (T) this;
}
}

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.api;
package eu.mhsl.craftattack.spawn.core.api.server;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import org.bukkit.configuration.ConfigurationSection;
import spark.Request;
import spark.Spark;

View File

@ -1,16 +1,19 @@
package eu.mhsl.craftattack.spawn.appliance;
package eu.mhsl.craftattack.spawn.core.appliance;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.config.Configuration;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.client.Repository;
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -72,8 +75,8 @@ public abstract class Appliance {
*/
@NotNull
public ConfigurationSection localConfig() {
return Optional.ofNullable(Configuration.cfg.getConfigurationSection(localConfigPath))
.orElseGet(() -> Configuration.cfg.createSection(localConfigPath));
return Optional.ofNullable(Configuration.cfg.getConfigurationSection(this.localConfigPath))
.orElseGet(() -> Configuration.cfg.createSection(this.localConfigPath));
}
public void onEnable() {
@ -83,37 +86,47 @@ public abstract class Appliance {
}
public void initialize(@NotNull JavaPlugin plugin) {
this.listeners = listeners();
this.commands = commands();
this.listeners = this.listeners();
this.commands = this.commands();
listeners.forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, plugin));
commands.forEach(command -> setCommandExecutor(plugin, command.commandName, command));
this.listeners.forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, plugin));
this.commands.forEach(command -> this.setCommandExecutor(plugin, command.commandName, command));
}
public void destruct(@NotNull JavaPlugin plugin) {
listeners.forEach(HandlerList::unregisterAll);
this.listeners.forEach(HandlerList::unregisterAll);
}
protected static <T extends Appliance> T queryAppliance(Class<T> clazz) {
protected <T extends Appliance> T queryAppliance(Class<T> clazz) {
return Main.instance().getAppliance(clazz);
}
protected <T extends Repository> T queryRepository(Class<T> clazz) {
return Main.instance().getRepositoryLoader().getRepository(clazz);
}
private void setCommandExecutor(JavaPlugin plugin, String name, ApplianceCommand<?> executor) {
PluginCommand command = plugin.getCommand(name);
if(command != null && executor != null) {
try {
PluginCommand command = this.createPluginCommand(name, plugin);
command.setExecutor(executor);
command.setTabCompleter(executor);
} else {
Main.logger().warning("Command " + name + " is not specified in plugin.yml!");
throw new RuntimeException("All commands must be registered in plugin.yml. Missing command: " + name);
plugin.getServer().getCommandMap().register(plugin.getName(), command);
} catch(Exception e) {
throw new RuntimeException(String.format("Failed to register command '%s'", name), e);
}
}
private PluginCommand createPluginCommand(String name, JavaPlugin plugin) throws Exception {
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
constructor.setAccessible(true);
return constructor.newInstance(name, plugin);
}
public List<Listener> getListeners() {
return listeners;
return this.listeners;
}
public List<ApplianceCommand<?>> getCommands() {
return commands;
return this.commands;
}
}

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.appliance;
package eu.mhsl.craftattack.spawn.core.appliance;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.core.Main;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
@ -33,12 +33,12 @@ public abstract class ApplianceCommand<T extends Appliance> extends CachedApplia
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
try {
execute(sender, command, label, args);
this.execute(sender, command, label, args);
} catch(Error e) {
sender.sendMessage(errorMessage.append(Component.text(e.getMessage())));
sender.sendMessage(this.errorMessage.append(Component.text(e.getMessage())));
} catch(Exception e) {
sender.sendMessage(errorMessage.append(Component.text("Interner Fehler")));
Main.logger().warning("Error executing appliance command " + commandName + ": " + e.getMessage());
sender.sendMessage(this.errorMessage.append(Component.text("Interner Fehler")));
Main.logger().warning("Error executing appliance command " + this.commandName + ": " + e.getMessage());
e.printStackTrace(System.err);
return false;
}
@ -80,7 +80,7 @@ public abstract class ApplianceCommand<T extends Appliance> extends CachedApplia
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(!(sender instanceof Player)) {
sender.sendMessage(notPlayerMessage);
sender.sendMessage(this.notPlayerMessage);
return false;
}
this.player = (Player) sender;

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliance;
package eu.mhsl.craftattack.spawn.core.appliance;
import org.bukkit.event.Listener;

View File

@ -1,12 +1,12 @@
package eu.mhsl.craftattack.spawn.appliance;
package eu.mhsl.craftattack.spawn.core.appliance;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.core.Main;
public class CachedApplianceSupplier<T extends Appliance> implements IApplianceSupplier<T> {
private final T appliance;
public CachedApplianceSupplier() {
this.appliance = Main.instance().getAppliance(Main.getApplianceType(getClass()));
this.appliance = Main.instance().getAppliance(Main.getApplianceType(this.getClass()));
}
@Override

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliance;
package eu.mhsl.craftattack.spawn.core.appliance;
public interface IApplianceSupplier<T extends Appliance> {
T getAppliance();

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.config;
package eu.mhsl.craftattack.spawn.core.config;
import org.bukkit.Bukkit;
import org.bukkit.Location;

View File

@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.config;
package eu.mhsl.craftattack.spawn.core.config;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.core.Main;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.util;
package eu.mhsl.craftattack.spawn.core.util;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.util;
package eu.mhsl.craftattack.spawn.core.util;
public class NumberUtil {
public static double map(double oldValue, double oldMin, double oldMax, double newMin, double newMax) {

View File

@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.util.entity;
package eu.mhsl.craftattack.spawn.core.util.entity;
import eu.mhsl.craftattack.spawn.config.ConfigUtil;
import eu.mhsl.craftattack.spawn.config.Configuration;
import eu.mhsl.craftattack.spawn.util.world.ChunkUtils;
import eu.mhsl.craftattack.spawn.core.config.ConfigUtil;
import eu.mhsl.craftattack.spawn.core.config.Configuration;
import eu.mhsl.craftattack.spawn.core.util.world.ChunkUtils;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.EntityType;
@ -24,7 +24,7 @@ public class DisplayVillager {
this.villager = (Villager) this.location.getWorld().getEntity(uuid);
Objects.requireNonNull(this.villager);
} catch(NullPointerException | IllegalArgumentException e) {
this.villager = getBaseVillager();
this.villager = this.getBaseVillager();
villagerCreator.accept(this.villager);
}
@ -32,7 +32,7 @@ public class DisplayVillager {
}
public Villager getVillager() {
return villager;
return this.villager;
}
private Villager getBaseVillager() {
@ -79,11 +79,11 @@ public class DisplayVillager {
}
public Villager getVillager() {
return villager.getVillager();
return this.villager.getVillager();
}
public UUID getUniqueId() {
return getVillager().getUniqueId();
return this.getVillager().getUniqueId();
}
}
}

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.util.entity;
package eu.mhsl.craftattack.spawn.core.util.entity;
import org.bukkit.Material;
import org.bukkit.Statistic;

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.util.inventory;
package eu.mhsl.craftattack.spawn.core.util.inventory;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.destroystokyo.paper.profile.ProfileProperty;

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