Compare commits

...

117 Commits

Author SHA1 Message Date
092d33beb3 prototype for grief detection 2025-03-29 22:03:12 +01: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
f0ffb3ad21 Merge remote-tracking branch 'refs/remotes/origin/master' into develop-feedback 2024-11-28 22:16:13 +01:00
3c82939120 removed unwanted logging 2024-11-28 21:24:10 +01:00
c9935637b9 removed inconsistent spacing in acinform 2024-11-28 21:22:28 +01:00
b35c5b9240 updated afk and sleep tag icons 2024-11-25 20:40:43 +01:00
39814dae05 added sleepTag 2024-11-24 22:44:48 +01:00
7e3b043c98 added afkTag 2024-11-24 21:13:25 +01:00
5324749a64 Merge remote-tracking branch 'origin/master' 2024-11-24 20:06:22 +01:00
b9f88bc4b2 extended infobars, added coloring 2024-11-24 20:06:18 +01:00
8470115a74 added endPrevent 2024-11-24 02:33:36 +01:00
0f976d2316 added infobars 2024-11-10 17:28:02 +01:00
c52207298e Merge remote-tracking branch 'origin/master' 2024-11-10 12:23:52 +01:00
fd902f4ec6 updated maintenance kick message 2024-11-10 12:23:44 +01:00
0a7052b6f5 added error message for missing player name AcInform 2024-11-06 10:29:20 +01:00
0ea2d4958b Merge remote-tracking branch 'origin/master' 2024-11-05 22:28:33 +01:00
e8534b42ac prevented players from using ac inform command 2024-11-05 22:28:11 +01:00
3abf5a95e8 updated maintenance command feedback 2024-11-05 21:24:06 +01:00
fc5b76290e Merge remote-tracking branch 'origin/master' 2024-11-05 21:23:48 +01:00
01d1926104 removed webserver shutdown 2024-11-05 21:23:43 +01:00
e7075140e3 added anticheat info command 2024-11-05 20:48:42 +01:00
125b604393 small refactoring and added missing async task 2024-10-20 14:24:12 +02:00
c468696537 started with feedback applience 2024-10-19 19:53:51 +02:00
bc84b06f0d added AdminChat 2024-10-19 18:11:19 +02:00
b1427ac90e added playertime command 2024-10-19 15:46:34 +02:00
918ee5ed00 updated interfering listener 2024-10-09 14:36:08 +02:00
77fbc12873 Merge pull request 'develop-chatReply' (#5) from develop-chatReply into master
Reviewed-on: #5
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2024-10-06 13:52:58 +00:00
14a33cae39 Merge branch 'master' into develop-chatReply 2024-10-06 13:52:18 +00:00
8462e43e89 replaced replyMapping.get(sender) with variable replyList 2024-10-06 15:50:17 +02:00
985b36ddc8 solved pr comments except making chatMessages an object variable 2024-10-06 15:23:37 +02:00
d69089a0eb cleaned up the code 2024-10-06 12:32:20 +02:00
346847d2b2 Merge pull request 'master-customAdvancements' (#4) from master-customAdvancements into master
Reviewed-on: #4
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2024-10-05 16:40:51 +00:00
e3b07aa62f solved pr comments 2024-10-05 18:37:04 +02:00
497c5ad749 too old conversations get deleted, added prefix, changed some logic 2024-10-02 23:37:45 +02:00
471cd8e610 finished logic for reply command 2024-10-02 22:27:16 +02:00
f96356b620 added choose options 2024-09-30 16:23:32 +02:00
d8259b79ae Merge remote-tracking branch 'origin/develop-chatReply' into develop-chatReply 2024-09-29 22:44:10 +02:00
26b9fd5e0f changed basic reply logic 2024-09-29 22:43:55 +02:00
9390d8c67b Merge remote-tracking branch 'refs/remotes/origin/master' into develop-chatReply
# Conflicts:
#	src/main/java/eu/mhsl/craftattack/spawn/Main.java
#	src/main/java/eu/mhsl/craftattack/spawn/util/statistics/NetworkMonitor.java
#	src/main/resources/config.yml
#	src/main/resources/plugin.yml
2024-09-29 12:45:36 +02:00
da33e6e592 added more custom advancements, added pending advancements 2024-09-28 23:48:52 +02:00
1aebae6cd5 added fleischerchest and pixelblock advancements 2024-09-28 15:43:14 +02:00
bf94e152c8 changed snowball knockback 2024-09-28 12:59:42 +02:00
3c1dea3451 Merge remote-tracking branch 'origin/master' 2024-09-27 23:43:12 +02:00
36fc89e915 added snowball knockback 2024-09-27 23:42:56 +02:00
7a15984f19 added snowball knockback 2024-09-27 23:35:01 +02:00
87870c96f7 added snowball knockback 2024-09-27 23:32:28 +02:00
70a1740644 fixed rainbowComponent not folding back on end of color spectrum, ensuring no sudden color switch 2024-09-27 18:16:42 +02:00
24fbd50c62 added appendWithSpace component util 2024-09-27 18:02:24 +02:00
05e88845be network monitor now disables itself when not being able to read system stats properly 2024-09-27 17:09:45 +02:00
e5ff3d36fa added reflection for appliance loading, extended logging and error handling 2024-09-27 11:07:33 +02:00
d66996bc73 added year rank 2024-09-26 20:16:25 +02:00
247dae0155 added OptionLinks which are shown to the client as 'Server Links' 2024-09-25 19:04:12 +02:00
d7bc440620 added tab completion for maintenance command 2024-09-25 17:52:41 +02:00
b2021d5815 added maintenance mode 2024-09-25 14:49:30 +02:00
f4da7e7674 small cleanup in settings 2024-09-24 21:49:06 +02:00
da8e421532 moved setting declaration to each owning appliance 2024-09-24 19:20:59 +02:00
7a97b1595e added option to disable specific appliances 2024-09-24 18:31:48 +02:00
aaad9fe7d8 show more info in technical tab view 2024-09-23 19:18:19 +02:00
f89a935c05 added texture pack selector 2024-09-17 22:11:59 +02:00
f49cca7f33 added glowing berries 2024-09-14 20:36:04 +02:00
e11b3fd7bc renamed eventHandlers to listeners 2024-09-14 20:35:14 +02:00
d6a3fe358d added system monitor to tablist 2024-08-31 16:31:02 +02:00
0959eb4aa5 moved appliance queries to base method 2024-08-31 15:06:46 +02:00
50e4192e32 fixed double doors opening when sneaking 2024-08-31 12:14:48 +02:00
d21f009f7d door knocking no longer triggers when destroying doors 2024-08-31 11:39:40 +02:00
825fed639c fixed portable crafting when placing crafting tables 2024-08-31 11:17:55 +02:00
077c40f29d added knocking doors 2024-08-31 00:43:53 +02:00
2d696dcdbd added double doors 2024-08-30 23:58:01 +02:00
203 changed files with 5089 additions and 1384 deletions

View File

@@ -26,6 +26,7 @@ dependencies {
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT' compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
implementation 'org.apache.httpcomponents:httpclient:4.5.14' implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'com.sparkjava:spark-core:2.9.4' implementation 'com.sparkjava:spark-core:2.9.4'
implementation 'org.reflections:reflections:0.10.2'
} }
def targetJavaVersion = 21 def targetJavaVersion = 21

View File

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

19
gradlew vendored
View File

@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,7 +83,8 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -201,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command; # Collect all arguments for the java command:
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# shell script including quotes and variable substitutions, so put them in # and any embedded shellness will be escaped.
# double quotes to make sure that they get re-expanded; and # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# * put everything else in single quotes, so that it's not re-expanded. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \

20
gradlew.bat vendored
View File

@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail

View File

@@ -1,40 +1,18 @@
package eu.mhsl.craftattack.spawn; package eu.mhsl.craftattack.spawn;
import eu.mhsl.craftattack.spawn.api.HttpServer; import eu.mhsl.craftattack.spawn.api.client.RepositoryLoader;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.antiSignEdit.AntiSignEdit;
import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker;
import eu.mhsl.craftattack.spawn.appliances.autoShulker.AutoShulker;
import eu.mhsl.craftattack.spawn.appliances.chatMention.ChatMention;
import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.appliances.customAdvancements.CustomAdvancements;
import eu.mhsl.craftattack.spawn.appliances.debug.Debug;
import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName;
import eu.mhsl.craftattack.spawn.appliances.event.Event;
import eu.mhsl.craftattack.spawn.appliances.fleischerchest.Fleischerchest;
import eu.mhsl.craftattack.spawn.appliances.help.Help;
import eu.mhsl.craftattack.spawn.appliances.hotbarRefill.HotbarRefill;
import eu.mhsl.craftattack.spawn.appliances.kick.Kick;
import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed;
import eu.mhsl.craftattack.spawn.appliances.panicBan.PanicBan;
import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit;
import eu.mhsl.craftattack.spawn.appliances.portableCrafting.PortableCrafting;
import eu.mhsl.craftattack.spawn.appliances.privateMessage.PrivateMessage;
import eu.mhsl.craftattack.spawn.appliances.projectStart.ProjectStart;
import eu.mhsl.craftattack.spawn.appliances.report.Report;
import eu.mhsl.craftattack.spawn.appliances.restart.Restart;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.tablist.Tablist;
import eu.mhsl.craftattack.spawn.appliances.titleClear.TitleClear;
import eu.mhsl.craftattack.spawn.appliances.whitelist.Whitelist;
import eu.mhsl.craftattack.spawn.appliances.worldmuseum.WorldMuseum;
import eu.mhsl.craftattack.spawn.config.Configuration; import eu.mhsl.craftattack.spawn.config.Configuration;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.reflections.Reflections;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public final class Main extends JavaPlugin { public final class Main extends JavaPlugin {
@@ -42,70 +20,71 @@ public final class Main extends JavaPlugin {
private static Logger logger; private static Logger logger;
private List<Appliance> appliances; private List<Appliance> appliances;
private HttpServer httpApi; private RepositoryLoader repositoryLoader;
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
logger = instance().getLogger(); logger = instance().getLogger();
saveDefaultConfig(); this.saveDefaultConfig();
try {
this.wrappedEnable();
} catch(Exception e) {
Main.logger().log(Level.SEVERE, "Error while initializing Spawn plugin, shutting down!", e);
Bukkit.shutdown();
}
}
private void wrappedEnable() {
Configuration.readConfig(); Configuration.readConfig();
List<String> disabledAppliances = Configuration.pluginConfig.getStringList("disabledAppliances");
appliances = List.of( Main.logger().info("Loading Repositories...");
new AdminMarker(), this.repositoryLoader = new RepositoryLoader();
new WorldMuseum(), Main.logger().info(String.format("Loaded %d repositories!", this.repositoryLoader.getRepositories().size()));
new TitleClear(),
new ProjectStart(),
new Tablist(),
new ChatMessages(),
new Report(),
new Event(),
new Help(),
new PlayerLimit(),
new Whitelist(),
new Restart(),
new Kick(),
new PanicBan(),
new Outlawed(),
new DisplayName(),
new Debug(),
new Fleischerchest(),
new CustomAdvancements(),
new Settings(),
new PortableCrafting(),
new AutoShulker(),
new AntiSignEdit(),
new HotbarRefill(),
new ChatMention(),
new PrivateMessage()
);
Main.logger.info("Loading appliances..."); Main.logger().info("Loading appliances...");
appliances.forEach(appliance -> { Reflections reflections = new Reflections(this.getClass().getPackageName());
Main.logger().info("Enabling " + appliance.getClass().getSimpleName()); Set<Class<? extends Appliance>> applianceClasses = reflections.getSubTypesOf(Appliance.class);
this.appliances = applianceClasses.stream()
.filter(applianceClass -> !disabledAppliances.contains(applianceClass.getSimpleName()))
.map(applianceClass -> {
try {
return (Appliance) applianceClass.getDeclaredConstructor().newInstance();
} 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!", this.appliances.size()));
Main.logger().info("Initializing appliances...");
this.appliances.forEach(appliance -> {
appliance.onEnable(); appliance.onEnable();
appliance.initialize(this); appliance.initialize(this);
}); });
Main.logger().info("Loaded " + appliances.size() + " appliances!"); Main.logger().info(String.format("Initialized %d appliances!", this.appliances.size()));
Main.logger().info("Starting HTTP API"); Main.logger().info("Starting HTTP API...");
this.httpApi = new HttpServer(); new HttpServer();
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
Main.logger().info("Startup complete!");
} }
@Override @Override
public void onDisable() { public void onDisable() {
Main.logger().info("Disabling appliances..."); Main.logger().info("Disabling appliances...");
appliances.forEach(appliance -> { this.appliances.forEach(appliance -> {
Main.logger().info("Disabling " + appliance.getClass().getSimpleName()); Main.logger().info("Disabling " + appliance.getClass().getSimpleName());
appliance.onDisable(); appliance.onDisable();
appliance.destruct(this); appliance.destruct(this);
}); });
this.httpApi.stop();
HandlerList.unregisterAll(this); HandlerList.unregisterAll(this);
Bukkit.getScheduler().cancelTasks(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) { public <T extends Appliance> T getAppliance(Class<T> clazz) {
@@ -122,7 +101,11 @@ public final class Main extends JavaPlugin {
} }
public List<Appliance> getAppliances() { public List<Appliance> getAppliances() {
return appliances; return this.appliances;
}
public RepositoryLoader getRepositoryLoader() {
return this.repositoryLoader;
} }
public static Main instance() { public static Main instance() {

View File

@@ -0,0 +1,91 @@
package eu.mhsl.craftattack.spawn.api.client;
import eu.mhsl.craftattack.spawn.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.function.Consumer;
@RepositoryLoader.IgnoreRepository
public abstract class HttpRepository extends Repository {
private final Consumer<URIBuilder> baseUriBuilder;
public HttpRepository(URI basePath) {
this(basePath, null);
}
public HttpRepository(URI basePath, @Nullable Consumer<URIBuilder> baseUriBuilder) {
super(basePath);
this.baseUriBuilder = baseUriBuilder == null
? uriBuilder -> {
}
: baseUriBuilder;
}
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);
}
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);
}
private URI getUri(String command, Consumer<URIBuilder> parameters) {
try {
URIBuilder builder = new URIBuilder(this.basePath + "/" + command);
this.baseUriBuilder.accept(builder);
parameters.accept(builder);
return builder.build();
} catch(URISyntaxException e) {
throw new RuntimeException(e);
}
}
private HttpRequest.Builder getRequestBuilder(URI endpoint) {
return HttpRequest.newBuilder()
.uri(endpoint)
.header("User-Agent", Main.instance().getServer().getBukkitVersion())
.header("Content-Type", "application/json");
}
private <TResponse> ReqResp<TResponse> execute(HttpRequest request, Class<TResponse> clazz) {
ReqResp<String> rawResponse = this.sendHttp(request);
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,27 @@
package eu.mhsl.craftattack.spawn.api.client;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.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 Gson();
}
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,48 @@
package eu.mhsl.craftattack.spawn.api.client;
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;
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreRepository {
}
public RepositoryLoader() {
Reflections reflections = new Reflections(this.getClass().getPackageName());
Set<Class<? extends Repository>> repositories = reflections.getSubTypesOf(Repository.class);
this.repositories = repositories.stream()
.filter(repository -> !repository.isAnnotationPresent(IgnoreRepository.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,4 @@
package eu.mhsl.craftattack.spawn.api.client;
public record ReqResp<TData>(int status, TData data) {
}

View File

@@ -0,0 +1,29 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.EventApiUtil;
import java.util.UUID;
public class EventRepository extends HttpRepository {
public EventRepository() {
super(EventApiUtil.getBaseUri());
}
public record CreatedRoom(UUID uuid) {
}
public record QueueRoom(UUID player, UUID room) {
public record Response(String error) {
}
}
public ReqResp<CreatedRoom> createSession() {
return this.post("room", null, CreatedRoom.class);
}
public ReqResp<QueueRoom.Response> queueRoom(QueueRoom request) {
return this.post("queueRoom", request, QueueRoom.Response.class);
}
}

View File

@@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import com.google.common.reflect.TypeToken;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.WebsiteApiUtil;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class FeedbackRepository extends HttpRepository {
public FeedbackRepository() {
super(WebsiteApiUtil.getBaseUri(), WebsiteApiUtil::withAuthorizationSecret);
}
public record Request(String event, List<UUID> users) {
}
public ReqResp<Map<UUID, String>> createFeedbackUrls(Request data) {
final Type responseType = new TypeToken<Map<UUID, String>>() {
}.getType();
ReqResp<Object> rawData = this.post("feedback", data, Object.class);
return new ReqResp<>(rawData.status(), this.gson.fromJson(this.gson.toJson(rawData.data()), responseType));
}
}

View File

@@ -0,0 +1,57 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.WebsiteApiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.UUID;
public class ReportRepository extends HttpRepository {
public ReportRepository() {
super(WebsiteApiUtil.getBaseUri(), WebsiteApiUtil::withAuthorizationSecret);
}
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
) {
}
}
}
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,31 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.WebsiteApiUtil;
import java.util.UUID;
public class WhitelistRepository extends HttpRepository {
public WhitelistRepository() {
super(WebsiteApiUtil.getBaseUri(), WebsiteApiUtil::withAuthorizationSecret);
}
public record UserData(
UUID uuid,
String username,
String firstname,
String lastname,
Long banned_until,
Long outlawed_until
) {
}
public ReqResp<UserData> getUserData(UUID userId) {
return this.get(
"user",
parameters -> parameters.addParameter("uuid", userId.toString()),
UserData.class
);
}
}

View File

@@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.api; package eu.mhsl.craftattack.spawn.api.server;
import com.google.gson.Gson; import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.Main;
@@ -24,10 +24,6 @@ public class HttpServer {
Main.instance().getAppliances().forEach(appliance -> appliance.httpApi(new ApiBuilder(appliance))); Main.instance().getAppliances().forEach(appliance -> appliance.httpApi(new ApiBuilder(appliance)));
} }
public void stop() {
Spark.stop();
}
public record Response(Status status, Object error, Object response) { public record Response(Status status, Object error, Object response) {
public enum Status { public enum Status {
FAILURE, FAILURE,

View File

@@ -1,7 +1,8 @@
package eu.mhsl.craftattack.spawn.appliance; package eu.mhsl.craftattack.spawn.appliance;
import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer; import eu.mhsl.craftattack.spawn.api.client.Repository;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.config.Configuration; import eu.mhsl.craftattack.spawn.config.Configuration;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
@@ -42,7 +43,7 @@ public abstract class Appliance {
* @return List of listeners * @return List of listeners
*/ */
@NotNull @NotNull
protected List<Listener> eventHandlers() { protected List<Listener> listeners() {
return new ArrayList<>(); return new ArrayList<>();
} }
@@ -72,7 +73,8 @@ public abstract class Appliance {
*/ */
@NotNull @NotNull
public ConfigurationSection localConfig() { public ConfigurationSection localConfig() {
return Optional.ofNullable(Configuration.cfg.getConfigurationSection(localConfigPath)).orElse(Configuration.cfg); return Optional.ofNullable(Configuration.cfg.getConfigurationSection(this.localConfigPath))
.orElseGet(() -> Configuration.cfg.createSection(this.localConfigPath));
} }
public void onEnable() { public void onEnable() {
@@ -82,21 +84,25 @@ public abstract class Appliance {
} }
public void initialize(@NotNull JavaPlugin plugin) { public void initialize(@NotNull JavaPlugin plugin) {
this.listeners = eventHandlers(); this.listeners = this.listeners();
this.commands = commands(); this.commands = this.commands();
listeners.forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, plugin)); this.listeners.forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, plugin));
commands.forEach(command -> setCommandExecutor(plugin, command.commandName, command)); this.commands.forEach(command -> this.setCommandExecutor(plugin, command.commandName, command));
} }
public void destruct(@NotNull JavaPlugin plugin) { public void destruct(@NotNull JavaPlugin plugin) {
listeners.forEach(HandlerList::unregisterAll); this.listeners.forEach(HandlerList::unregisterAll);
} }
public <T extends Appliance> T queryAppliance(Class<T> clazz) { protected <T extends Appliance> T queryAppliance(Class<T> clazz) {
return Main.instance().getAppliance(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) { private void setCommandExecutor(JavaPlugin plugin, String name, ApplianceCommand<?> executor) {
PluginCommand command = plugin.getCommand(name); PluginCommand command = plugin.getCommand(name);
if(command != null && executor != null) { if(command != null && executor != null) {
@@ -109,10 +115,10 @@ public abstract class Appliance {
} }
public List<Listener> getListeners() { public List<Listener> getListeners() {
return listeners; return this.listeners;
} }
public List<ApplianceCommand<?>> getCommands() { public List<ApplianceCommand<?>> getCommands() {
return commands; return this.commands;
} }
} }

View File

@@ -17,7 +17,7 @@ import java.util.Optional;
/** /**
* Utility class which enables command name definition over a constructor. * Utility class which enables command name definition over a constructor.
*/ */
public abstract class ApplianceCommand<T extends Appliance> extends ApplianceSupplier<T> implements TabCompleter, CommandExecutor { public abstract class ApplianceCommand<T extends Appliance> extends CachedApplianceSupplier<T> implements TabCompleter, CommandExecutor {
public String commandName; public String commandName;
protected Component errorMessage = Component.text("Fehler: ").color(NamedTextColor.RED); protected Component errorMessage = Component.text("Fehler: ").color(NamedTextColor.RED);
@@ -33,12 +33,12 @@ public abstract class ApplianceCommand<T extends Appliance> extends ApplianceSup
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
try { try {
execute(sender, command, label, args); this.execute(sender, command, label, args);
} catch(Error e) { } catch(Error e) {
sender.sendMessage(errorMessage.append(Component.text(e.getMessage()))); sender.sendMessage(this.errorMessage.append(Component.text(e.getMessage())));
} catch(Exception e) { } catch(Exception e) {
sender.sendMessage(errorMessage.append(Component.text("Interner Fehler"))); sender.sendMessage(this.errorMessage.append(Component.text("Interner Fehler")));
Main.logger().warning("Error executing appliance command " + commandName + ": " + e.getMessage()); Main.logger().warning("Error executing appliance command " + this.commandName + ": " + e.getMessage());
e.printStackTrace(System.err); e.printStackTrace(System.err);
return false; return false;
} }
@@ -80,7 +80,7 @@ public abstract class ApplianceCommand<T extends Appliance> extends ApplianceSup
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(!(sender instanceof Player)) { if(!(sender instanceof Player)) {
sender.sendMessage(notPlayerMessage); sender.sendMessage(this.notPlayerMessage);
return false; return false;
} }
this.player = (Player) sender; this.player = (Player) sender;

View File

@@ -8,6 +8,6 @@ import org.bukkit.event.Listener;
* *
* @param <T> the type of your appliance * @param <T> the type of your appliance
*/ */
public abstract class ApplianceListener<T extends Appliance> extends ApplianceSupplier<T> implements Listener { public abstract class ApplianceListener<T extends Appliance> extends CachedApplianceSupplier<T> implements Listener {
} }

View File

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

View File

@@ -1,14 +0,0 @@
package eu.mhsl.craftattack.spawn.appliances.adminMarker;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Color;
import org.bukkit.entity.Player;
public class AdminMarkerListener extends ApplianceListener<AdminMarker> {
private TextColor getPlayerColor(Player player) {
if(player.hasPermission("chatcolor"))
return TextColor.color(Color.AQUA.asRGB()); // TODO read permission from config
return TextColor.color(Color.WHITE.asRGB());
}
}

View File

@@ -1,23 +0,0 @@
package eu.mhsl.craftattack.spawn.appliances.customAdvancements;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects;
public class CustomAdvancements extends Appliance {
public void grantAdvancement(String advancementName, Player player) {
player.getAdvancementProgress(Objects.requireNonNull(Bukkit.getAdvancement(Objects.requireNonNull(NamespacedKey.fromString("custom_advancements:craftattack/" + advancementName))))).awardCriteria("criteria");
}
@Override
@NotNull
protected List<Listener> eventHandlers() {
return List.of(new CustomAdvancementsDamageEntityListener());
}
}

View File

@@ -1,20 +0,0 @@
package eu.mhsl.craftattack.spawn.appliances.customAdvancements;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
public class CustomAdvancementsDamageEntityListener extends ApplianceListener<CustomAdvancements> {
@EventHandler
public void onEntityDamageEntity(EntityDamageByEntityEvent event) {
Entity damaged = event.getEntity();
if(!(damaged instanceof Player)) return;
Entity damager = event.getDamager();
if(!(damager instanceof Player)) return;
if(damaged.hasPermission("admin")) {
getAppliance().grantAdvancement("search_trouble", (Player) damager);
}
}
}

View File

@@ -1,54 +0,0 @@
package eu.mhsl.craftattack.spawn.appliances.displayName;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker;
import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarkerListener;
import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Supplier;
public class DisplayName extends Appliance {
public void update(Player player) {
TextColor playerColor = queryAppliance(AdminMarker.class).getPlayerColor(player);
List<Supplier<Component>> prefixes = List.of(
() -> queryAppliance(Outlawed.class).getNamePrefix(player)
);
ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text();
prefixes.forEach(supplier -> {
Component prefix = supplier.get();
if(prefix == null) return;
playerName.append(prefix).append(Component.text(" "));
});
playerName.append(Component.text(player.getName(), playerColor));
setGlobal(player, playerName.build());
}
private void setGlobal(Player player, Component component) {
try {
player.customName(component);
player.displayName(component);
player.playerListName(component);
} catch(Exception e) {
//TODO this throws often exceptions, but still works, don't know why
//Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
}
}
@Override
@NotNull
protected List<Listener> eventHandlers() {
return List.of(new AdminMarkerListener());
}
}

View File

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

View File

@@ -1,13 +0,0 @@
package eu.mhsl.craftattack.spawn.appliances.event.listener;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
public class ApplyPendingRewardsListener extends ApplianceListener<Event> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
getAppliance().applyPendingRewards(event.getPlayer());
}
}

View File

@@ -1,9 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.antiSignEdit; package eu.mhsl.craftattack.spawn.appliances.gameplay.antiSignEdit;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.SelectSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.settings.SignEditSetting;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
@@ -15,6 +14,11 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class AntiSignEdit extends Appliance { public class AntiSignEdit extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(SignEditSetting.class);
}
public boolean preventSignEdit(Player p, SignSide sign) { public boolean preventSignEdit(Player p, SignSide sign) {
SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.SignEdit, SelectSetting.Options.Option.class); SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.SignEdit, SelectSetting.Options.Option.class);
if(setting.is(SignEditSetting.editable)) return false; if(setting.is(SignEditSetting.editable)) return false;
@@ -37,7 +41,7 @@ public class AntiSignEdit extends Appliance {
} }
@Override @Override
protected @NotNull List<Listener> eventHandlers() { protected @NotNull List<Listener> listeners() {
return List.of(new OnSignEditListener()); return List.of(new OnSignEditListener());
} }
} }

View File

@@ -1,15 +1,15 @@
package eu.mhsl.craftattack.spawn.appliances.antiSignEdit; package eu.mhsl.craftattack.spawn.appliances.gameplay.antiSignEdit;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import io.papermc.paper.event.player.PlayerOpenSignEvent; import io.papermc.paper.event.player.PlayerOpenSignEvent;
import org.bukkit.block.sign.SignSide; import org.bukkit.block.sign.SignSide;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
public class OnSignEditListener extends ApplianceListener<AntiSignEdit> { class OnSignEditListener extends ApplianceListener<AntiSignEdit> {
@EventHandler @EventHandler
public void onEdit(PlayerOpenSignEvent event) { public void onEdit(PlayerOpenSignEvent event) {
if(event.getCause().equals(PlayerOpenSignEvent.Cause.PLACE)) return; if(event.getCause().equals(PlayerOpenSignEvent.Cause.PLACE)) return;
SignSide signSide = event.getSign().getSide(event.getSide()); SignSide signSide = event.getSign().getSide(event.getSide());
event.setCancelled(getAppliance().preventSignEdit(event.getPlayer(), signSide)); event.setCancelled(this.getAppliance().preventSignEdit(event.getPlayer(), signSide));
} }
} }

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.settings.settings; package eu.mhsl.craftattack.spawn.appliances.gameplay.antiSignEdit;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.SelectSetting;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;

View File

@@ -1,6 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.autoShulker; package eu.mhsl.craftattack.spawn.appliances.gameplay.autoShulker;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material; import org.bukkit.Material;
@@ -16,6 +17,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
public class AutoShulker extends Appliance { public class AutoShulker extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(AutoShulkerSetting.class);
}
public boolean tryAutoShulker(Player p, Item item) { public boolean tryAutoShulker(Player p, Item item) {
ItemStack itemStack = item.getItemStack(); ItemStack itemStack = item.getItemStack();
ItemStack offhandStack = p.getInventory().getItemInOffHand(); ItemStack offhandStack = p.getInventory().getItemInOffHand();
@@ -25,7 +31,8 @@ public class AutoShulker extends Appliance {
ShulkerBox shulkerBox = (ShulkerBox) blockStateMeta.getBlockState(); ShulkerBox shulkerBox = (ShulkerBox) blockStateMeta.getBlockState();
HashMap<Integer, ItemStack> leftOver = shulkerBox.getInventory().addItem(itemStack); HashMap<Integer, ItemStack> leftOver = shulkerBox.getInventory().addItem(itemStack);
if(leftOver.size() > 1) throw new IllegalStateException("Multiple ItemStacks cannot be processed by AutoShulker!"); if(leftOver.size() > 1)
throw new IllegalStateException("Multiple ItemStacks cannot be processed by AutoShulker!");
if(itemStack.equals(leftOver.get(0))) { if(itemStack.equals(leftOver.get(0))) {
p.sendActionBar(Component.text("Die Shulkerbox ist voll!", NamedTextColor.RED)); p.sendActionBar(Component.text("Die Shulkerbox ist voll!", NamedTextColor.RED));
return false; return false;
@@ -40,7 +47,7 @@ public class AutoShulker extends Appliance {
} }
@Override @Override
protected @NotNull List<Listener> eventHandlers() { protected @NotNull List<Listener> listeners() {
return List.of(new ItemPickupListener()); return List.of(new ItemPickupListener());
} }
} }

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.settings.settings; package eu.mhsl.craftattack.spawn.appliances.gameplay.autoShulker;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.SelectSetting;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;

View File

@@ -1,21 +1,20 @@
package eu.mhsl.craftattack.spawn.appliances.autoShulker; package eu.mhsl.craftattack.spawn.appliances.gameplay.autoShulker;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.SelectSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.SelectSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.settings.AutoShulkerSetting;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityPickupItemEvent;
public class ItemPickupListener extends ApplianceListener<AutoShulker> { class ItemPickupListener extends ApplianceListener<AutoShulker> {
@EventHandler @EventHandler
public void onPickup(EntityPickupItemEvent event) { public void onPickup(EntityPickupItemEvent event) {
if(event.getEntity() instanceof Player p) { if(event.getEntity() instanceof Player p) {
SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.AutoShulker, SelectSetting.Options.Option.class); SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.AutoShulker, SelectSetting.Options.Option.class);
if(setting.is(AutoShulkerSetting.disabled)) return; if(setting.is(AutoShulkerSetting.disabled)) return;
if(setting.is(AutoShulkerSetting.notFromPlayers) && event.getItem().getThrower() != null) return; if(setting.is(AutoShulkerSetting.notFromPlayers) && event.getItem().getThrower() != null) return;
event.setCancelled(getAppliance().tryAutoShulker(p, event.getItem())); event.setCancelled(this.getAppliance().tryAutoShulker(p, event.getItem()));
} }
} }
} }

View File

@@ -0,0 +1,11 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.customAdvancements;
public class Advancements {
public static String searchTrouble = "search_trouble";
public static String fleischerchest = "fleischerchest";
public static String craftPixelblock = "craft_pixelblock";
public static String usePixelblock = "use_pixelblock";
public static String start = "start";
public static String winner = "winner";
public static String participateEvent = "participate_event";
}

View File

@@ -0,0 +1,12 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.customAdvancements;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class ApplyPendingAdvancementsListener extends ApplianceListener<CustomAdvancements> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().applyPendingAdvancements(event.getPlayer());
}
}

View File

@@ -0,0 +1,66 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.customAdvancements;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.advancement.Advancement;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public class CustomAdvancements extends Appliance {
record PendingAdvancement(UUID receiver, String advancement) {
}
private final List<PendingAdvancement> pendingAdvancements = new ArrayList<>();
public void grantAdvancement(String advancementName, UUID playerUUID) {
Player player = Bukkit.getPlayer(playerUUID);
if(player == null) {
this.addPendingAdvancement(playerUUID, advancementName);
return;
}
try {
NamespacedKey namespacedKey = Objects.requireNonNull(
NamespacedKey.fromString("craftattack_advancements:craftattack/" + advancementName),
String.format("NamespacedKey with '%s' is invalid!", advancementName)
);
Advancement advancement = Objects.requireNonNull(
Bukkit.getAdvancement(namespacedKey),
String.format("The advancement '%s' does not exist!", namespacedKey.asString())
);
player.getAdvancementProgress(advancement).awardCriteria("criteria");
} catch(Exception e) {
Main.logger().info("Advancement " + advancementName + " not found! (is Custom Advancements data pack loaded?)");
throw e;
}
}
public void applyPendingAdvancements(Player player) {
if(this.pendingAdvancements.isEmpty()) return;
List<PendingAdvancement> grantedAdvancements = this.pendingAdvancements.stream()
.filter(pendingAdvancement -> pendingAdvancement.receiver.equals(player.getUniqueId())).toList();
this.pendingAdvancements.removeAll(grantedAdvancements);
grantedAdvancements.forEach(pendingAdvancement -> this.grantAdvancement(pendingAdvancement.advancement(), player.getUniqueId()));
}
private void addPendingAdvancement(UUID receiver, String advancement) {
this.pendingAdvancements.add(new PendingAdvancement(receiver, advancement));
}
@Override
@NotNull
protected List<Listener> listeners() {
return List.of(
new CustomAdvancementsListener(),
new ApplyPendingAdvancementsListener()
);
}
}

View File

@@ -0,0 +1,46 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.customAdvancements;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.inventory.CraftItemEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.inventory.ItemStack;
class CustomAdvancementsListener extends ApplianceListener<CustomAdvancements> {
@EventHandler
public void onEntityDamageEntity(EntityDamageByEntityEvent event) {
if(!(event.getEntity() instanceof Player damaged)) return;
if(!(event.getDamager() instanceof Player damager)) return;
if(!damager.getInventory().getItemInMainHand().getType().equals(Material.AIR)) return;
if(!damaged.hasPermission("admin")) return;
this.getAppliance().grantAdvancement(Advancements.searchTrouble, damager.getUniqueId());
}
@EventHandler
public void onCraftItem(CraftItemEvent event) {
ItemStack result = event.getInventory().getResult();
if(result == null) return;
if(!(event.getView().getPlayer() instanceof Player player)) return;
if(result.getType() == Material.RED_SHULKER_BOX) {
this.getAppliance().grantAdvancement(Advancements.fleischerchest, player.getUniqueId());
return;
}
if(result.getItemMeta().itemName().equals(Component.text("98fdf0ae-c3ab-4ef7-ae25-efd518d600de"))) {
this.getAppliance().grantAdvancement(Advancements.craftPixelblock, player.getUniqueId());
}
}
@EventHandler
public void onChangeWorld(PlayerChangedWorldEvent event) {
if(!event.getPlayer().getWorld().getName().startsWith("plugins/PixelBlocks/worlds")) return;
this.getAppliance().grantAdvancement(Advancements.usePixelblock, event.getPlayer().getUniqueId());
}
}

View File

@@ -0,0 +1,51 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.doubleDoor;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Door;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class DoubleDoor extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(DoubleDoorSetting.class);
}
public void openNextDoor(Block block) {
BlockData clickedData = block.getBlockData();
if(!(clickedData instanceof Door clickedDoor)) return;
BlockFace neighborFace = this.getNeighborFace(clickedDoor.getFacing(), clickedDoor.getHinge());
Block neighbourBlock = block.getRelative(neighborFace);
BlockData neighbourData = neighbourBlock.getBlockData();
if(!(neighbourData instanceof Door neighbourDoor)) return;
if(!(neighbourDoor.getFacing() == clickedDoor.getFacing())) return;
if(neighbourDoor.getHinge() == clickedDoor.getHinge()) return;
neighbourDoor.setOpen(!clickedDoor.isOpen());
neighbourBlock.setBlockData(neighbourDoor);
}
private @NotNull BlockFace getNeighborFace(BlockFace face, Door.Hinge hinge) {
return switch(face) {
case EAST -> (hinge == Door.Hinge.RIGHT) ? BlockFace.NORTH : BlockFace.SOUTH;
case WEST -> (hinge == Door.Hinge.RIGHT) ? BlockFace.SOUTH : BlockFace.NORTH;
case SOUTH -> (hinge == Door.Hinge.RIGHT) ? BlockFace.EAST : BlockFace.WEST;
case NORTH -> (hinge == Door.Hinge.RIGHT) ? BlockFace.WEST : BlockFace.EAST;
default ->
throw new IllegalStateException(String.format("BlockFace '%s' of clicked door is not valid!", face));
};
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new OnDoorInteractListener());
}
}

View File

@@ -0,0 +1,38 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.doubleDoor;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.BoolSetting;
import org.bukkit.Material;
public class DoubleDoorSetting extends BoolSetting implements CategorizedSetting {
public DoubleDoorSetting() {
super(Settings.Key.DoubleDoors);
}
@Override
protected String title() {
return "Automatische Doppeltüren";
}
@Override
protected String description() {
return "Öffnet und schließt die zweite Hälfte einer Doppeltür automatisch";
}
@Override
protected Material icon() {
return Material.OAK_DOOR;
}
@Override
protected Boolean defaultValue() {
return false;
}
@Override
public SettingCategory category() {
return SettingCategory.Gameplay;
}
}

View File

@@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.doubleDoor;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import java.util.Objects;
class OnDoorInteractListener extends ApplianceListener<DoubleDoor> {
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if(!event.hasBlock()) return;
if(!Objects.equals(event.getHand(), EquipmentSlot.HAND)) return;
if(!event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) return;
if(event.getPlayer().isSneaking()) return;
Block clickedBlock = event.getClickedBlock();
if(clickedBlock == null) return;
if(clickedBlock.getType().equals(Material.IRON_DOOR)) return;
if(!Settings.instance().getSetting(event.getPlayer(), Settings.Key.DoubleDoors, Boolean.class)) return;
this.getAppliance().openNextDoor(clickedBlock);
}
}

View File

@@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliances.fleischerchest; package eu.mhsl.craftattack.spawn.appliances.gameplay.fleischerchest;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -19,7 +19,7 @@ public class Fleischerchest extends Appliance {
@Override @Override
@NotNull @NotNull
protected List<Listener> eventHandlers() { protected List<Listener> listeners() {
return List.of(new FleischerchestCraftItemListener()); return List.of(new FleischerchestCraftItemListener());
} }
} }

View File

@@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliances.fleischerchest; package eu.mhsl.craftattack.spawn.appliances.gameplay.fleischerchest;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.Material; import org.bukkit.Material;
@@ -6,13 +6,13 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.PrepareItemCraftEvent; import org.bukkit.event.inventory.PrepareItemCraftEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
public class FleischerchestCraftItemListener extends ApplianceListener<Fleischerchest> { class FleischerchestCraftItemListener extends ApplianceListener<Fleischerchest> {
@EventHandler @EventHandler
public void onPrepareItemCraft(PrepareItemCraftEvent event) { public void onPrepareItemCraft(PrepareItemCraftEvent event) {
ItemStack result = event.getInventory().getResult(); ItemStack result = event.getInventory().getResult();
if(result == null) return; if(result == null) return;
if(result.getType() == Material.RED_SHULKER_BOX) { if(result.getType() != Material.RED_SHULKER_BOX) return;
getAppliance().renameItem(event.getInventory().getResult());
} this.getAppliance().renameItem(event.getInventory().getResult());
} }
} }

View File

@@ -0,0 +1,35 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.glowingBerries;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.util.Ticks;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class GlowingBerries extends Appliance {
private static final PotionEffect glowEffect = new PotionEffect(
PotionEffectType.GLOWING,
Ticks.TICKS_PER_SECOND * 15,
1,
false,
true,
true
);
public void letPlayerGlow(Player player) {
player.addPotionEffect(glowEffect);
Sound sound = Sound.sound(org.bukkit.Sound.BLOCK_AMETHYST_BLOCK_CHIME.key(), Sound.Source.PLAYER, 1f, 1f);
player.stopSound(sound);
player.playSound(sound, Sound.Emitter.self());
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new OnBerryEaten());
}
}

View File

@@ -0,0 +1,14 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.glowingBerries;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerItemConsumeEvent;
class OnBerryEaten extends ApplianceListener<GlowingBerries> {
@EventHandler
public void onEat(PlayerItemConsumeEvent event) {
if(event.getItem().getType().equals(Material.GLOW_BERRIES))
this.getAppliance().letPlayerGlow(event.getPlayer());
}
}

View File

@@ -1,7 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.hotbarRefill; package eu.mhsl.craftattack.spawn.appliances.gameplay.hotbarRefill;
import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -16,6 +17,11 @@ import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
public class HotbarRefill extends Appliance { public class HotbarRefill extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(HotbarRefillSetting.class);
}
public void handleHotbarChange(Player player, ItemStack item) { public void handleHotbarChange(Player player, ItemStack item) {
if(player.getGameMode().equals(GameMode.CREATIVE)) return; if(player.getGameMode().equals(GameMode.CREATIVE)) return;
if(item.getAmount() != 1) return; if(item.getAmount() != 1) return;
@@ -41,12 +47,11 @@ public class HotbarRefill extends Appliance {
player.sendActionBar(Component.text("Die Hotbar wurde aufgefüllt", NamedTextColor.GREEN)); player.sendActionBar(Component.text("Die Hotbar wurde aufgefüllt", NamedTextColor.GREEN));
}, 1); }, 1);
} catch(NoSuchElementException ignored) { } catch(NoSuchElementException ignored) {
player.sendActionBar(Component.text("Keine weiteren Items dieser Art im Inventar vorhanden", NamedTextColor.RED));
} }
} }
@Override @Override
protected @NotNull List<Listener> eventHandlers() { protected @NotNull List<Listener> listeners() {
return List.of(new ItemRefillListener()); return List.of(new HotbarRefillListener());
} }
} }

View File

@@ -0,0 +1,49 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.hotbarRefill;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerItemBreakEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.ItemStack;
import java.util.List;
class HotbarRefillListener extends ApplianceListener<HotbarRefill> {
private HotbarRefillSetting.HotbarReplaceConfig getPlayerSetting(Player player) {
return Settings.instance().getSetting(
player,
Settings.Key.HotbarReplacer,
HotbarRefillSetting.HotbarReplaceConfig.class
);
}
@EventHandler
public void blockPlace(BlockPlaceEvent event) {
ItemStack stackInHand = event.getItemInHand();
if(stackInHand.getAmount() != 1) return;
if(stackInHand.getType().getMaxDurability() > 0) return;
if(stackInHand.getType().getMaxStackSize() > 0) return;
if(!this.getPlayerSetting(event.getPlayer()).onBlocks()) return;
this.getAppliance().handleHotbarChange(event.getPlayer(), stackInHand);
}
@EventHandler
public void onPlayerItemBreak(PlayerItemBreakEvent event) {
if(!this.getPlayerSetting(event.getPlayer()).onTools()) return;
this.getAppliance().handleHotbarChange(event.getPlayer(), event.getBrokenItem());
}
@EventHandler
public void onPlayerItemConsume(PlayerItemConsumeEvent event) {
if(List.of(Material.POTION, Material.HONEY_BOTTLE).contains(event.getItem().getType())) return;
if(!this.getPlayerSetting(event.getPlayer()).onConsumable()) return;
this.getAppliance().handleHotbarChange(event.getPlayer(), event.getItem());
}
}

View File

@@ -1,12 +1,12 @@
package eu.mhsl.craftattack.spawn.appliances.settings.settings; package eu.mhsl.craftattack.spawn.appliances.gameplay.hotbarRefill;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.MultiBoolSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.MultiBoolSetting;
import org.bukkit.Material; import org.bukkit.Material;
public class HotbarReplaceSetting extends MultiBoolSetting<HotbarReplaceSetting.HotbarReplaceConfig> implements CategorizedSetting { public class HotbarRefillSetting extends MultiBoolSetting<HotbarRefillSetting.HotbarReplaceConfig> implements CategorizedSetting {
@Override @Override
public SettingCategory category() { public SettingCategory category() {
return SettingCategory.Gameplay; return SettingCategory.Gameplay;
@@ -16,9 +16,10 @@ public class HotbarReplaceSetting extends MultiBoolSetting<HotbarReplaceSetting.
@DisplayName("Blöcke") boolean onBlocks, @DisplayName("Blöcke") boolean onBlocks,
@DisplayName("Werkzeuge") boolean onTools, @DisplayName("Werkzeuge") boolean onTools,
@DisplayName("Essen") boolean onConsumable @DisplayName("Essen") boolean onConsumable
) {} ) {
}
public HotbarReplaceSetting() { public HotbarRefillSetting() {
super(Settings.Key.HotbarReplacer); super(Settings.Key.HotbarReplacer);
} }

View File

@@ -0,0 +1,65 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.knockDoor;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.SelectSetting;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class KnockDoor extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(KnockDoorSetting.class);
}
public void knockAtDoor(Player knockingPlayer, Block knockedBlock) {
SelectSetting.Options.Option setting = Settings.instance().getSetting(knockingPlayer, Settings.Key.KnockDoors, SelectSetting.Options.Option.class);
if(setting.is(KnockDoorSetting.disabled)) return;
if(setting.is(KnockDoorSetting.knockSingleTime)) {
this.playSound(knockedBlock);
}
if(setting.is(KnockDoorSetting.knockThreeTimes)) {
String metadataKey = new NamespacedKey(Main.instance(), KnockDoor.class.getName()).getNamespace();
if(knockingPlayer.hasMetadata(metadataKey)) return;
knockingPlayer.setMetadata(metadataKey, new FixedMetadataValue(Main.instance(), 0));
new BukkitRunnable() {
@Override
public void run() {
int timesKnocked = knockingPlayer.getMetadata(metadataKey).getFirst().asInt();
if(timesKnocked >= 3) {
knockingPlayer.removeMetadata(metadataKey, Main.instance());
this.cancel();
return;
}
KnockDoor.this.playSound(knockedBlock);
knockingPlayer.setMetadata(metadataKey, new FixedMetadataValue(Main.instance(), timesKnocked + 1));
}
}.runTaskTimer(Main.instance(), 0, 8);
}
}
private void playSound(Block knockedBlock) {
Location knockLocation = knockedBlock.getLocation();
Sound sound = knockedBlock.getType() == Material.IRON_DOOR
? Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR
: Sound.ITEM_SHIELD_BLOCK;
knockLocation.getWorld().playSound(knockLocation, sound, SoundCategory.PLAYERS, 1f, 1f);
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new KnockDoorListener());
}
}

View File

@@ -0,0 +1,18 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.knockDoor;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.GameMode;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Door;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockDamageAbortEvent;
class KnockDoorListener extends ApplianceListener<KnockDoor> {
@EventHandler
public void onKnock(BlockDamageAbortEvent event) {
if(event.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
Block block = event.getBlock();
if(!(block.getBlockData() instanceof Door)) return;
this.getAppliance().knockAtDoor(event.getPlayer(), block);
}
}

View File

@@ -0,0 +1,50 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.knockDoor;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.SelectSetting;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import java.util.List;
import java.util.Locale;
public class KnockDoorSetting extends SelectSetting implements CategorizedSetting {
private static final String namespace = KnockDoorSetting.class.getSimpleName().toLowerCase(Locale.ROOT);
public static Options.Option disabled = new Options.Option("Deaktiviert", new NamespacedKey(namespace, "disabled"));
public static Options.Option knockSingleTime = new Options.Option("Einmal an der Tür anklopfen", new NamespacedKey(namespace, "single"));
public static Options.Option knockThreeTimes = new Options.Option("Dreimal an der Tür anklopfen", new NamespacedKey(namespace, "three"));
public KnockDoorSetting() {
super(
Settings.Key.KnockDoors,
new Options(List.of(disabled, knockSingleTime, knockThreeTimes))
);
}
@Override
public SettingCategory category() {
return SettingCategory.Gameplay;
}
@Override
protected String title() {
return "Klopfen an Türen";
}
@Override
protected String description() {
return "Klopft durch das schlagen an eine Tür an.";
}
@Override
protected Material icon() {
return Material.BELL;
}
@Override
protected Options.Option defaultValue() {
return disabled;
}
}

View File

@@ -0,0 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.outlawed;
class OutlawChangeNotPermitted extends Exception {
public OutlawChangeNotPermitted(String message) {
super(message);
}
}

View File

@@ -1,10 +1,10 @@
package eu.mhsl.craftattack.spawn.appliances.outlawed; package eu.mhsl.craftattack.spawn.appliances.gameplay.outlawed;
import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.displayName.DisplayName;
import eu.mhsl.craftattack.spawn.appliances.whitelist.Whitelist; import eu.mhsl.craftattack.spawn.appliances.tooling.whitelist.Whitelist;
import eu.mhsl.craftattack.spawn.config.Configuration; import eu.mhsl.craftattack.spawn.config.Configuration;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo; import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
public class Outlawed extends Appliance { public class Outlawed extends Appliance implements DisplayName.Prefixed {
public final int timeoutInMs = 1000 * 60 * 60 * 6; public final int timeoutInMs = 1000 * 60 * 60 * 6;
private final Map<UUID, Long> timeouts = new HashMap<>(); private final Map<UUID, Long> timeouts = new HashMap<>();
@@ -34,13 +34,13 @@ public class Outlawed extends Appliance {
super("outlawed"); super("outlawed");
Bukkit.getScheduler().runTaskTimerAsynchronously( Bukkit.getScheduler().runTaskTimerAsynchronously(
Main.instance(), Main.instance(),
() -> playerStatusMap.forEach((player, status) -> { () -> this.playerStatusMap.forEach((player, status) -> {
if(!player.isOnline()) return; if(!player.isOnline()) return;
if(status != Status.FORCED) return; if(status != Status.FORCED) return;
try { try {
Main.instance().getAppliance(Whitelist.class).integrityCheck(player); this.queryAppliance(Whitelist.class).fullIntegrityCheck(player);
} catch(DisconnectInfo.Throwable e) { } catch(DisconnectInfo.Throwable e) {
Bukkit.getScheduler().runTask(Main.instance(), () -> e.getDisconnectScreen().applyKick(player)); e.getDisconnectScreen().applyKick(player);
} }
}), }),
20 * 60, 20 * 60,
@@ -49,55 +49,55 @@ public class Outlawed extends Appliance {
} }
public void switchLawStatus(Player player) throws OutlawChangeNotPermitted { public void switchLawStatus(Player player) throws OutlawChangeNotPermitted {
if(getLawStatus(player).equals(Status.FORCED)) { if(this.getLawStatus(player).equals(Status.FORCED)) {
throw new OutlawChangeNotPermitted("Dein Vogelfreistatus wurde als Strafe auferlegt und kann daher nicht verändert werden."); throw new OutlawChangeNotPermitted("Dein Vogelfreistatus wurde als Strafe auferlegt und kann daher nicht verändert werden.");
} }
if(isTimeout(player)) { if(this.isTimeout(player)) {
throw new OutlawChangeNotPermitted("Du kannst deinen Vogelfreistatus nicht so schnell wechseln. Bitte warte einige Stunden bevor du umschaltest!"); throw new OutlawChangeNotPermitted("Du kannst deinen Vogelfreistatus nicht so schnell wechseln. Bitte warte einige Stunden bevor du umschaltest!");
} }
setLawStatus(player, isOutlawed(player) ? Status.DISABLED : Status.VOLUNTARILY); this.setLawStatus(player, this.isOutlawed(player) ? Status.DISABLED : Status.VOLUNTARILY);
setTimeout(player); this.setTimeout(player);
} }
public void updateForcedStatus(Player player, boolean forced) { public void updateForcedStatus(Player player, boolean forced) {
setLawStatus(player, forced ? Status.FORCED : getLawStatus(player) == Status.FORCED ? Status.DISABLED : getLawStatus(player)); this.setLawStatus(player, forced ? Status.FORCED : this.getLawStatus(player) == Status.FORCED ? Status.DISABLED : this.getLawStatus(player));
} }
public Status getLawStatus(Player player) { public Status getLawStatus(Player player) {
return playerStatusMap.computeIfAbsent(player, p -> { return this.playerStatusMap.computeIfAbsent(player, p -> {
if(localConfig().getStringList(voluntarilyEntry).contains(p.getUniqueId().toString())) if(this.localConfig().getStringList(this.voluntarilyEntry).contains(p.getUniqueId().toString()))
return Status.VOLUNTARILY; return Status.VOLUNTARILY;
return Status.DISABLED; return Status.DISABLED;
}); });
} }
private void setLawStatus(Player player, Status status) { private void setLawStatus(Player player, Status status) {
playerStatusMap.put(player, status); this.playerStatusMap.put(player, status);
Main.instance().getAppliance(DisplayName.class).update(player); this.queryAppliance(DisplayName.class).update(player);
List<String> newList = localConfig().getStringList(voluntarilyEntry); List<String> newList = this.localConfig().getStringList(this.voluntarilyEntry);
if(status.equals(Status.VOLUNTARILY)) { if(status.equals(Status.VOLUNTARILY)) {
newList.add(player.getUniqueId().toString()); newList.add(player.getUniqueId().toString());
} else { } else {
newList.remove(player.getUniqueId().toString()); newList.remove(player.getUniqueId().toString());
} }
localConfig().set(voluntarilyEntry, newList.stream().distinct().toList()); this.localConfig().set(this.voluntarilyEntry, newList.stream().distinct().toList());
Configuration.saveChanges(); Configuration.saveChanges();
} }
public boolean isOutlawed(Player player) { public boolean isOutlawed(Player player) {
return getLawStatus(player) != Status.DISABLED; return this.getLawStatus(player) != Status.DISABLED;
} }
private boolean isTimeout(Player player) { private boolean isTimeout(Player player) {
return timeouts.get(player.getUniqueId()) < System.currentTimeMillis() - timeoutInMs; return this.timeouts.getOrDefault(player.getUniqueId(), 0L) > System.currentTimeMillis() - this.timeoutInMs;
} }
private void setTimeout(Player player) { private void setTimeout(Player player) {
timeouts.put(player.getUniqueId(), System.currentTimeMillis()); this.timeouts.put(player.getUniqueId(), System.currentTimeMillis());
} }
public Component getStatusDescription(Status status) { public Component getStatusDescription(Status status) {
@@ -110,8 +110,9 @@ public class Outlawed extends Appliance {
}; };
} }
@Override
public Component getNamePrefix(Player player) { public Component getNamePrefix(Player player) {
if(isOutlawed(player)) { if(this.isOutlawed(player)) {
return Component.text("[☠]", NamedTextColor.RED) return Component.text("[☠]", NamedTextColor.RED)
.hoverEvent(HoverEvent.showText(Component.text("Vogelfreie Spieler dürfen ohne Grund angegriffen werden!"))); .hoverEvent(HoverEvent.showText(Component.text("Vogelfreie Spieler dürfen ohne Grund angegriffen werden!")));
} }
@@ -127,7 +128,7 @@ public class Outlawed extends Appliance {
@Override @Override
@NotNull @NotNull
protected List<Listener> eventHandlers() { protected List<Listener> listeners() {
return List.of(new OutlawedReminderListener()); return List.of(new OutlawedReminderListener());
} }
} }

View File

@@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliances.outlawed; package eu.mhsl.craftattack.spawn.appliances.gameplay.outlawed;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -7,7 +7,7 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class OutlawedCommand extends ApplianceCommand.PlayerChecked<Outlawed> { class OutlawedCommand extends ApplianceCommand.PlayerChecked<Outlawed> {
public OutlawedCommand() { public OutlawedCommand() {
super("vogelfrei"); super("vogelfrei");
} }
@@ -15,10 +15,10 @@ public class OutlawedCommand extends ApplianceCommand.PlayerChecked<Outlawed> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
try { try {
getAppliance().switchLawStatus(getPlayer()); this.getAppliance().switchLawStatus(this.getPlayer());
sender.sendMessage( sender.sendMessage(
getAppliance() this.getAppliance()
.getStatusDescription(getAppliance().getLawStatus(getPlayer())) .getStatusDescription(this.getAppliance().getLawStatus(this.getPlayer()))
); );
} catch(OutlawChangeNotPermitted e) { } catch(OutlawChangeNotPermitted e) {
sender.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); sender.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED));

View File

@@ -0,0 +1,14 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.outlawed;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class OutlawedReminderListener extends ApplianceListener<Outlawed> {
@EventHandler
public void onJoin(PlayerJoinEvent e) {
if(this.getAppliance().isOutlawed(e.getPlayer())) {
e.getPlayer().sendMessage(this.getAppliance().getStatusDescription(this.getAppliance().getLawStatus(e.getPlayer())));
}
}
}

View File

@@ -1,14 +1,16 @@
package eu.mhsl.craftattack.spawn.appliances.portableCrafting; package eu.mhsl.craftattack.spawn.appliances.gameplay.portableCrafting;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
public class OnCraftingTableUseListener extends ApplianceListener<PortableCrafting> { class OnCraftingTableUseListener extends ApplianceListener<PortableCrafting> {
@EventHandler @EventHandler
public void inInteract(PlayerInteractEvent event) { public void inInteract(PlayerInteractEvent event) {
if(!event.getAction().equals(Action.RIGHT_CLICK_AIR)) return;
if(!event.getMaterial().equals(Material.CRAFTING_TABLE)) return; if(!event.getMaterial().equals(Material.CRAFTING_TABLE)) return;
getAppliance().openFor(event.getPlayer()); this.getAppliance().openFor(event.getPlayer());
} }
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.portableCrafting; package eu.mhsl.craftattack.spawn.appliances.gameplay.portableCrafting;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -9,13 +9,18 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class PortableCrafting extends Appliance { public class PortableCrafting extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(PortableCraftingSetting.class);
}
public void openFor(Player player) { public void openFor(Player player) {
if(!Settings.instance().getSetting(player, Settings.Key.EnablePortableCrafting, Boolean.class)) return; if(!Settings.instance().getSetting(player, Settings.Key.EnablePortableCrafting, Boolean.class)) return;
player.openWorkbench(null, true); player.openWorkbench(null, true);
} }
@Override @Override
protected @NotNull List<Listener> eventHandlers() { protected @NotNull List<Listener> listeners() {
return List.of(new OnCraftingTableUseListener()); return List.of(new OnCraftingTableUseListener());
} }
} }

View File

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

View File

@@ -0,0 +1,22 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.snowballKnockback;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class SnowballKnockback extends Appliance {
public void dealSnowballKnockback(LivingEntity entity, Entity snowball) {
entity.damage(0.1);
entity.knockback(0.4, -snowball.getVelocity().getX(), -snowball.getVelocity().getZ());
}
@Override
@NotNull
protected List<Listener> listeners() {
return List.of(new SnowballKnockbackListener());
}
}

View File

@@ -0,0 +1,20 @@
package eu.mhsl.craftattack.spawn.appliances.gameplay.snowballKnockback;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.ProjectileHitEvent;
class SnowballKnockbackListener extends ApplianceListener<SnowballKnockback> {
@EventHandler
public void onSnowballHit(ProjectileHitEvent event) {
if(event.getHitEntity() == null) return;
if(!event.getEntityType().equals(EntityType.SNOWBALL)) return;
if(!(event.getHitEntity() instanceof LivingEntity hitEntity)) return;
Entity snowball = event.getEntity();
this.getAppliance().dealSnowballKnockback(hitEntity, snowball);
}
}

View File

@@ -1,45 +0,0 @@
package eu.mhsl.craftattack.spawn.appliances.hotbarRefill;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.settings.HotbarReplaceSetting;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerItemBreakEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.ItemStack;
public class ItemRefillListener extends ApplianceListener<HotbarRefill> {
private HotbarReplaceSetting.HotbarReplaceConfig getPlayerSetting(Player player) {
return Settings.instance().getSetting(
player,
Settings.Key.HotbarReplacer,
HotbarReplaceSetting.HotbarReplaceConfig.class
);
}
@EventHandler
public void blockPlace(BlockPlaceEvent event) {
ItemStack stackInHand = event.getItemInHand();
if(stackInHand.getAmount() != 1) return;
if(stackInHand.getType().getMaxDurability() > 0) return;
if(!getPlayerSetting(event.getPlayer()).onBlocks()) return;
getAppliance().handleHotbarChange(event.getPlayer(), stackInHand);
}
@EventHandler
public void onPlayerItemBreak(PlayerItemBreakEvent event) {
if(!getPlayerSetting(event.getPlayer()).onTools()) return;
getAppliance().handleHotbarChange(event.getPlayer(), event.getBrokenItem());
}
@EventHandler
public void onPlayerItemConsume(PlayerItemConsumeEvent event) {
if(!getPlayerSetting(event.getPlayer()).onConsumable()) return;
getAppliance().handleHotbarChange(event.getPlayer(), event.getItem());
}
}

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.debug; package eu.mhsl.craftattack.spawn.appliances.internal.debug;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.debug.command.AppliancesCommand; import eu.mhsl.craftattack.spawn.appliances.internal.debug.command.AppliancesCommand;
import eu.mhsl.craftattack.spawn.appliances.debug.command.UserInfoCommand; import eu.mhsl.craftattack.spawn.appliances.internal.debug.command.UserInfoCommand;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;

View File

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

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.debug.command; package eu.mhsl.craftattack.spawn.appliances.internal.debug.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.debug.Debug; import eu.mhsl.craftattack.spawn.appliances.internal.debug.Debug;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@@ -50,13 +50,13 @@ public class UserInfoCommand extends ApplianceCommand<Debug> {
.appendNewline() .appendNewline()
.append( .append(
Component 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()))) .clickEvent(ClickEvent.copyToClipboard(String.valueOf(player.getFirstPlayed())))
) )
.appendNewline() .appendNewline()
.append( .append(
Component 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()))) .clickEvent(ClickEvent.copyToClipboard(String.valueOf(player.getLastSeen())))
) )
.appendNewline() .appendNewline()

View File

@@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.appliances.titleClear; package eu.mhsl.craftattack.spawn.appliances.internal.titleClear;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -14,7 +14,7 @@ public class TitleClear extends Appliance {
@Override @Override
@NotNull @NotNull
protected List<Listener> eventHandlers() { protected List<Listener> listeners() {
return List.of( return List.of(
new TitleClearListener() new TitleClearListener()
); );

View File

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

View File

@@ -1,13 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.adminMarker; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.adminMarker;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class AdminMarker extends Appliance { public class AdminMarker extends Appliance {
public TextColor getPlayerColor(Player player) { public TextColor getPlayerColor(Player player) {
@@ -15,10 +11,4 @@ public class AdminMarker extends Appliance {
return TextColor.color(Color.AQUA.asRGB()); // TODO read permission from config return TextColor.color(Color.AQUA.asRGB()); // TODO read permission from config
return TextColor.color(Color.WHITE.asRGB()); return TextColor.color(Color.WHITE.asRGB());
} }
@Override
@NotNull
protected List<Listener> eventHandlers() {
return List.of(new AdminMarkerListener());
}
} }

View File

@@ -0,0 +1,30 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.afkTag;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import io.papermc.paper.event.player.AsyncChatEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
class AfkResetListener extends ApplianceListener<AfkTag> {
@EventHandler
public void onMove(PlayerMoveEvent event) {
this.getAppliance().resetTiming(event.getPlayer());
}
@EventHandler
public void onInteract(PlayerInteractEvent event) {
this.getAppliance().resetTiming(event.getPlayer());
}
@EventHandler
public void onChat(AsyncChatEvent event) {
this.getAppliance().resetTiming(event.getPlayer());
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().resetTiming(event.getPlayer());
}
}

View File

@@ -0,0 +1,74 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.afkTag;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.displayName.DisplayName;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.util.Ticks;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public class AfkTag extends Appliance implements DisplayName.Prefixed {
private final HashMap<UUID, Long> afkTimings = new HashMap<>();
private static final int updateIntervalSeconds = 30;
private static final int afkWhenMillis = 3 * 60 * 1000;
@Override
public void onEnable() {
Bukkit.getScheduler().runTaskTimerAsynchronously(
Main.instance(),
this::checkAfkPlayers,
Ticks.TICKS_PER_SECOND,
Ticks.TICKS_PER_SECOND * updateIntervalSeconds
);
}
public void resetTiming(Player player) {
boolean wasAfk = this.isAfk(player);
this.afkTimings.put(player.getUniqueId(), System.currentTimeMillis());
if(wasAfk) this.updateAfkPrefix(player);
}
private void checkAfkPlayers() {
this.afkTimings.keySet().stream()
.map(Bukkit::getPlayer)
.filter(Objects::nonNull)
.filter(this::isAfk)
.forEach(this::updateAfkPrefix);
}
private boolean isAfk(Player player) {
if(player.isSleeping()) return false;
long lastTimeActive = this.afkTimings.getOrDefault(player.getUniqueId(), 0L);
long timeSinceLastActive = System.currentTimeMillis() - lastTimeActive;
return timeSinceLastActive >= afkWhenMillis;
}
private void updateAfkPrefix(Player player) {
Main.instance().getAppliance(DisplayName.class).update(player);
}
@Override
public @Nullable Component getNamePrefix(Player player) {
if(this.isAfk(player)) return Component.text("[ᵃᶠᵏ]", NamedTextColor.GRAY)
.hoverEvent(HoverEvent.showText(Component.text("Der Spieler ist AFK")));
return null;
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new AfkResetListener());
}
}

View File

@@ -1,9 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.chatMention; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.settings.ChatMentionSetting;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
@@ -56,11 +55,12 @@ public class ChatMention extends Appliance {
@Override @Override
public void onEnable() { public void onEnable() {
refreshPlayers(); Settings.instance().declareSetting(ChatMentionSetting.class);
this.refreshPlayers();
} }
@Override @Override
protected @NotNull List<Listener> eventHandlers() { protected @NotNull List<Listener> listeners() {
return List.of(new ChatMentionListener()); return List.of(new ChatMentionListener());
} }
} }

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.chatMessages; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEvent;
@@ -12,8 +13,13 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class ChatMessages extends Appliance { public class ChatMessages extends Appliance {
@Override
public void onEnable() {
Settings.instance().declareSetting(ShowJoinAndLeaveMessagesSetting.class);
}
public Component getReportablePlayerName(Player player) { 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) { public Component addReportActions(Component message, String username) {
@@ -24,7 +30,7 @@ public class ChatMessages extends Appliance {
@Override @Override
@NotNull @NotNull
protected List<Listener> eventHandlers() { protected List<Listener> listeners() {
return List.of(new ChatMessagesListener()); return List.of(new ChatMessagesListener());
} }
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.chatMessages; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.util.IteratorUtil; import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import io.papermc.paper.event.player.AsyncChatEvent; import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -9,25 +9,26 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Optional; import java.util.Optional;
public class ChatMessagesListener extends ApplianceListener<ChatMessages> { class ChatMessagesListener extends ApplianceListener<ChatMessages> {
@EventHandler @EventHandler
public void onPlayerChatEvent(AsyncChatEvent event) { public void onPlayerChatEvent(AsyncChatEvent event) {
event.renderer( event.renderer(
(source, sourceDisplayName, message, viewer) -> (source, sourceDisplayName, message, viewer) ->
Component.text("") Component.text("")
.append(getAppliance().getReportablePlayerName(source)) .append(this.getAppliance().getReportablePlayerName(source))
.append(Component.text(" > ").color(TextColor.color(Color.GRAY.asRGB()))) .append(Component.text(" > ").color(TextColor.color(Color.GRAY.asRGB())))
.append(message).color(TextColor.color(Color.SILVER.asRGB())) .append(message).color(TextColor.color(Color.SILVER.asRGB()))
); );
} }
@EventHandler @EventHandler(priority = EventPriority.HIGH)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
event.joinMessage(null); event.joinMessage(null);
IteratorUtil.onlinePlayers(player -> { IteratorUtil.onlinePlayers(player -> {
@@ -35,7 +36,7 @@ public class ChatMessagesListener extends ApplianceListener<ChatMessages> {
player.sendMessage( player.sendMessage(
Component Component
.text(">>> ").color(NamedTextColor.GREEN) .text(">>> ").color(NamedTextColor.GREEN)
.append(getAppliance().getReportablePlayerName(event.getPlayer())) .append(this.getAppliance().getReportablePlayerName(event.getPlayer()))
); );
}); });
} }
@@ -48,7 +49,7 @@ public class ChatMessagesListener extends ApplianceListener<ChatMessages> {
player.sendMessage( player.sendMessage(
Component Component
.text("<<< ").color(NamedTextColor.RED) .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.settings.settings; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.spawn.appliances.settings.CategorizedSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.appliances.settings.SettingCategory; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.settings.datatypes.BoolSetting;
import org.bukkit.Material; import org.bukkit.Material;
public class ShowJoinAndLeaveMessagesSetting extends BoolSetting implements CategorizedSetting { public class ShowJoinAndLeaveMessagesSetting extends BoolSetting implements CategorizedSetting {
@@ -33,6 +33,6 @@ public class ShowJoinAndLeaveMessagesSetting extends BoolSetting implements Cate
@Override @Override
public SettingCategory category() { public SettingCategory category() {
return SettingCategory.Chat; return SettingCategory.Visuals;
} }
} }

View File

@@ -0,0 +1,75 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.displayName;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.gameplay.outlawed.Outlawed;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.adminMarker.AdminMarker;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.afkTag.AfkTag;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.sleepTag.SleepTag;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.yearRank.YearRank;
import eu.mhsl.craftattack.spawn.util.server.Floodgate;
import eu.mhsl.craftattack.spawn.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.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.logging.Level;
public class DisplayName extends Appliance {
public interface Prefixed {
@Nullable
Component getNamePrefix(Player player);
}
public void update(Player player) {
TextColor playerColor = this.queryAppliance(AdminMarker.class).getPlayerColor(player);
List<Supplier<Prefixed>> prefixes = List.of(
() -> this.queryAppliance(Outlawed.class),
() -> this.queryAppliance(YearRank.class),
() -> this.queryAppliance(AfkTag.class),
() -> this.queryAppliance(SleepTag.class)
);
ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text();
prefixes.stream()
.map(prefixed -> prefixed.get().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);
}
}
}

View File

@@ -0,0 +1,13 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.displayName;
import eu.mhsl.craftattack.spawn.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,12 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class ApplyPendingRewardsListener extends ApplianceListener<Event> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().applyPendingRewards(event.getPlayer());
}
}

View File

@@ -1,13 +1,16 @@
package eu.mhsl.craftattack.spawn.appliances.event; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer; import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.api.client.repositories.EventRepository;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.command.*; import eu.mhsl.craftattack.spawn.appliances.gameplay.customAdvancements.Advancements;
import eu.mhsl.craftattack.spawn.appliances.event.listener.ApplyPendingRewardsListener; import eu.mhsl.craftattack.spawn.appliances.gameplay.customAdvancements.CustomAdvancements;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.command.*;
import eu.mhsl.craftattack.spawn.util.IteratorUtil; import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.util.api.HttpStatus;
import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager; import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager;
import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder; import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder;
import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener; import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener;
@@ -25,12 +28,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
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.*; import java.util.*;
public class Event extends Appliance { public class Event extends Appliance {
@@ -55,7 +52,6 @@ public class Event extends Appliance {
private boolean isOpen = false; private boolean isOpen = false;
private AdvertisementStatus advertiseStatus = AdvertisementStatus.BEFORE; private AdvertisementStatus advertiseStatus = AdvertisementStatus.BEFORE;
private UUID roomId; private UUID roomId;
private final HttpClient eventServerClient = HttpClient.newHttpClient();
private final List<Reward> pendingRewards = new ArrayList<>(); private final List<Reward> pendingRewards = new ArrayList<>();
record RewardConfiguration(String memorialMaterial, String memorialTitle, String memorialLore, List<UUID> memorials, record RewardConfiguration(String memorialMaterial, String memorialTitle, String memorialLore, List<UUID> memorials,
@@ -72,85 +68,67 @@ public class Event extends Appliance {
@Override @Override
public void onEnable() { public void onEnable() {
this.villager = new DisplayVillager.ConfigBound( this.villager = new DisplayVillager.ConfigBound(
localConfig(), this.localConfig(),
villager -> { villager -> {
villager.customName(Component.text("Events", NamedTextColor.GOLD)); villager.customName(Component.text("Events", NamedTextColor.GOLD));
villager.setProfession(Villager.Profession.LIBRARIAN); villager.setProfession(Villager.Profession.LIBRARIAN);
villager.setVillagerType(Villager.Type.SNOW); villager.setVillagerType(Villager.Type.SNOW);
} }
); );
this.isOpen = localConfig().getBoolean("enabled", false); this.isOpen = this.localConfig().getBoolean("enabled", false);
if(this.isOpen) this.roomId = UUID.fromString(localConfig().getString("roomId", "")); if(this.isOpen) this.roomId = UUID.fromString(this.localConfig().getString("roomId", ""));
} }
public void openEvent() throws URISyntaxException, IOException, InterruptedException { public void openEvent() {
if(isOpen) throw new ApplianceCommand.Error("Es läuft derzeit bereits ein Event!"); if(this.isOpen) throw new ApplianceCommand.Error("Es läuft derzeit bereits ein Event!");
HttpRequest createRoomRequest = HttpRequest.newBuilder()
.uri(new URI(localConfig().getString("api") + "/room"))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> rawResponse = eventServerClient.send(createRoomRequest, HttpResponse.BodyHandlers.ofString()); Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
if(rawResponse.statusCode() != 200) ReqResp<EventRepository.CreatedRoom> sessionResponse = this.queryRepository(EventRepository.class).createSession();
throw new ApplianceCommand.Error("Event-Server meldet Fehler: " + rawResponse.statusCode());
record Response(UUID uuid) { if(sessionResponse.status() != HttpStatus.OK)
} throw new ApplianceCommand.Error("Event-Server meldet Fehler: " + sessionResponse.status());
Response response = new Gson().fromJson(rawResponse.body(), Response.class);
isOpen = true; this.isOpen = true;
roomId = response.uuid; this.roomId = sessionResponse.data().uuid();
});
} }
public void joinEvent(Player p) { public void joinEvent(Player p) {
if(!isOpen) { if(!this.isOpen) {
p.sendMessage(Component.text("Zurzeit ist kein Event geöffnet.", NamedTextColor.RED)); p.sendMessage(Component.text("Zurzeit ist kein Event geöffnet.", NamedTextColor.RED));
return; return;
} }
if(!p.hasPermission("admin") && advertiseStatus == AdvertisementStatus.BEFORE) { if(!p.hasPermission("admin") && this.advertiseStatus == AdvertisementStatus.BEFORE) {
p.sendMessage(Component.text("Die Event befinden sich noch in der Vorbereitung.", NamedTextColor.RED)); p.sendMessage(Component.text("Die Event befinden sich noch in der Vorbereitung.", NamedTextColor.RED));
return; return;
} }
if(!p.hasPermission("admin") && advertiseStatus == AdvertisementStatus.DONE) { if(!p.hasPermission("admin") && this.advertiseStatus == AdvertisementStatus.DONE) {
p.sendMessage(Component.text("Die Events laufen bereits. Ein nachträgliches Beitreten ist nicht möglich.", NamedTextColor.RED)); p.sendMessage(Component.text("Die Events laufen bereits. Ein nachträgliches Beitreten ist nicht möglich.", NamedTextColor.RED));
return; return;
} }
try {
Main.instance().getLogger().info("Verbinde mit eventserver: " + p.getName()); Main.instance().getLogger().info("Verbinde mit eventserver: " + p.getName());
p.sendMessage(Component.text("Authentifiziere...", NamedTextColor.GREEN)); p.sendMessage(Component.text("Authentifiziere...", NamedTextColor.GREEN));
record Request(UUID player, UUID room) {
}
Request request = new Request(p.getUniqueId(), this.roomId);
HttpRequest queueRoomRequest = HttpRequest.newBuilder()
.uri(new URI(localConfig().getString("api") + "/queueRoom"))
.POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(request)))
.build();
record Response(String error) { Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
} ReqResp<EventRepository.QueueRoom.Response> queueResponse = this.queryRepository(EventRepository.class)
HttpResponse<String> rawResponse = eventServerClient.send(queueRoomRequest, HttpResponse.BodyHandlers.ofString()); .queueRoom(new EventRepository.QueueRoom(p.getUniqueId(), this.roomId));
Main.instance().getLogger().info("Response: " + rawResponse.body());
Response response = new Gson().fromJson(rawResponse.body(), Response.class);
if(rawResponse.statusCode() != 200 || response.error != null) { if(queueResponse.status() != HttpStatus.OK || queueResponse.data().error() != null) {
p.sendMessage(Component.text("Fehler beim Betreten: " + response.error, NamedTextColor.RED)); p.sendMessage(Component.text("Fehler beim Betreten: " + queueResponse.data().error(), NamedTextColor.RED));
return; return;
} }
p.sendMessage(Component.text("Betrete...", NamedTextColor.GREEN)); p.sendMessage(Component.text("Betrete...", NamedTextColor.GREEN));
PluginMessage.connect(p, localConfig().getString("connect-server-name")); PluginMessage.connect(p, this.localConfig().getString("connect-server-name"));
});
} catch(URISyntaxException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
} }
public void endEvent() { public void endEvent() {
if(!isOpen) throw new ApplianceCommand.Error("Es läuft derzeit kein Event!"); if(!this.isOpen) throw new ApplianceCommand.Error("Es läuft derzeit kein Event!");
isOpen = false; this.isOpen = false;
} }
private void rewardPlayers(RewardConfiguration rewardConfiguration) { private void rewardPlayers(RewardConfiguration rewardConfiguration) {
@@ -158,10 +136,10 @@ public class Event extends Appliance {
Reward reward = new Reward(uuid, new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.material)), amount)); Reward reward = new Reward(uuid, new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.material)), amount));
if(Bukkit.getPlayer(uuid) == null) { if(Bukkit.getPlayer(uuid) == null) {
pendingRewards.add(reward); this.pendingRewards.add(reward);
return; return;
} }
giveReward(reward); this.giveReward(reward);
}); });
rewardConfiguration.memorials.forEach(uuid -> { rewardConfiguration.memorials.forEach(uuid -> {
@@ -172,12 +150,19 @@ public class Event extends Appliance {
memorialItem.setItemMeta(meta); memorialItem.setItemMeta(meta);
Reward memorial = new Reward(uuid, memorialItem); Reward memorial = new Reward(uuid, memorialItem);
Main.instance().getAppliance(CustomAdvancements.class).grantAdvancement(Advancements.participateEvent, uuid);
if(Bukkit.getPlayer(uuid) == null) { if(Bukkit.getPlayer(uuid) == null) {
pendingRewards.add(memorial); this.pendingRewards.add(memorial);
return; return;
} }
giveReward(memorial); this.giveReward(memorial);
}); });
rewardConfiguration.rewards.keySet().stream()
.max(Comparator.comparing(rewardConfiguration.rewards::get))
.ifPresent(uuid -> Main.instance().getAppliance(CustomAdvancements.class)
.grantAdvancement(Advancements.winner, uuid));
} }
private void giveReward(Reward reward) { private void giveReward(Reward reward) {
@@ -199,10 +184,9 @@ public class Event extends Appliance {
} }
public void advertise() { public void advertise() {
advertiseCountdown.cancelIfRunning(); this.advertiseCountdown.cancelIfRunning();
this.advertiseStatus = AdvertisementStatus.ADVERTISED; this.advertiseStatus = AdvertisementStatus.ADVERTISED;
IteratorUtil.onlinePlayers(player -> { IteratorUtil.onlinePlayers(player -> player.sendMessage(
player.sendMessage(
Component.text() Component.text()
.append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline() .append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline()
.append(Component.text("Ein Event wurde gestartet!", NamedTextColor.GOLD)).appendNewline() .append(Component.text("Ein Event wurde gestartet!", NamedTextColor.GOLD)).appendNewline()
@@ -210,9 +194,8 @@ public class Event extends Appliance {
.append(Component.text("/event", NamedTextColor.AQUA)) .append(Component.text("/event", NamedTextColor.AQUA))
.append(Component.text(", um dem Event beizutreten!")).appendNewline() .append(Component.text(", um dem Event beizutreten!")).appendNewline()
.append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline() .append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline()
); ));
}); this.advertiseCountdown.start();
advertiseCountdown.start();
} }
@Override @Override
@@ -237,10 +220,10 @@ public class Event extends Appliance {
@Override @Override
@NotNull @NotNull
protected List<Listener> eventHandlers() { protected List<Listener> listeners() {
return List.of( return List.of(
new ApplyPendingRewardsListener(), new ApplyPendingRewardsListener(),
new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> joinEvent(playerInteractAtEntityEvent.getPlayer())), new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> this.joinEvent(playerInteractAtEntityEvent.getPlayer())),
new DismissInventoryOpenFromHolder(this.villager.getUniqueId()) new DismissInventoryOpenFromHolder(this.villager.getUniqueId())
); );
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.event.command; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.Event; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.Event;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -13,6 +13,6 @@ public class EventAdvertiseCommand extends ApplianceCommand<Event> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().advertise(); this.getAppliance().advertise();
} }
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.event.command; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.Event; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.Event;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -13,6 +13,6 @@ public class EventCommand extends ApplianceCommand.PlayerChecked<Event> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().joinEvent(getPlayer()); this.getAppliance().joinEvent(this.getPlayer());
} }
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.event.command; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.Event; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.Event;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -13,6 +13,6 @@ public class EventEndSessionCommand extends ApplianceCommand<Event> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
getAppliance().endEvent(); this.getAppliance().endEvent();
} }
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.event.command; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.Event; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.Event;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@@ -15,7 +15,7 @@ public class EventOpenSessionCommand extends ApplianceCommand<Event> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().openEvent(); this.getAppliance().openEvent();
sender.sendMessage(Component.text("Event-Server erfolgreich gestartet!", NamedTextColor.GREEN)); sender.sendMessage(Component.text("Event-Server gestartet!", NamedTextColor.GREEN));
} }
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.event.command; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.Event; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.event.Event;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -13,6 +13,6 @@ public class MoveEventVillagerCommand extends ApplianceCommand.PlayerChecked<Eve
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
getAppliance().villager.updateLocation(getPlayer().getLocation()); this.getAppliance().villager.updateLocation(this.getPlayer().getLocation());
} }
} }

View File

@@ -0,0 +1,77 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.feedback;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.api.client.repositories.FeedbackRepository;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.util.api.HttpStatus;
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.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class Feedback extends Appliance {
public Feedback() {
super("feedback");
}
public void requestFeedback(String eventName, List<Player> receivers, @Nullable String question) {
ReqResp<Map<UUID, String>> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls(
new FeedbackRepository.Request(eventName, receivers.stream().map(Entity::getUniqueId).toList())
);
System.out.println(response.toString());
System.out.println(response.status());
if(response.status() != HttpStatus.CREATED) throw new RuntimeException();
Component border = Component.text("-".repeat(40), NamedTextColor.GRAY);
receivers.forEach(player -> {
String feedbackUrl = response.data().get(player.getUniqueId());
if(feedbackUrl == null) {
Main.logger().warning(String.format("FeedbackUrl not found for player '%s' from backend!", player.getUniqueId()));
return;
}
ComponentBuilder<TextComponent, TextComponent.Builder> message = Component.text()
.append(border)
.appendNewline();
if(question != null) {
message
.append(Component.text(question, NamedTextColor.GREEN))
.appendNewline()
.appendNewline();
}
message
.append(Component.text("Klicke hier und gib uns Feedback, damit wir dein Spielerlebnis verbessern können!", NamedTextColor.DARK_GREEN)
.clickEvent(ClickEvent.openUrl(feedbackUrl)))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um Feedback zu geben.")))
.appendNewline()
.append(border);
player.sendMessage(message.build());
});
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(
new FeedbackCommand(),
new RequestFeedbackCommand()
);
}
}

View File

@@ -0,0 +1,30 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.feedback;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
class FeedbackCommand extends ApplianceCommand.PlayerChecked<Feedback> {
public FeedbackCommand() {
super("feedback");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
sender.sendMessage(ComponentUtil.pleaseWait());
Bukkit.getScheduler().runTaskAsynchronously(
Main.instance(),
() -> this.getAppliance().requestFeedback(
"self-issued-ingame",
List.of(this.getPlayer()),
null
)
);
}
}

View File

@@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.feedback;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
class RequestFeedbackCommand extends ApplianceCommand<Feedback> {
public RequestFeedbackCommand() {
super("requestFeedback");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
Bukkit.getScheduler().runTaskAsynchronously(
Main.instance(),
() -> this.getAppliance().requestFeedback(
"admin-issued-ingame",
new ArrayList<>(Bukkit.getOnlinePlayers()), String.join(" ", args)
)
);
}
}

View File

@@ -1,11 +1,11 @@
package eu.mhsl.craftattack.spawn.appliances.help; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.help;
import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.command.DiscordCommand; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.command.DiscordCommand;
import eu.mhsl.craftattack.spawn.appliances.help.command.HelpCommand; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.command.HelpCommand;
import eu.mhsl.craftattack.spawn.appliances.help.command.SpawnCommand; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.command.SpawnCommand;
import eu.mhsl.craftattack.spawn.appliances.help.command.TeamspeakCommand; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.command.TeamspeakCommand;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;

View File

@@ -1,8 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.help.command; package eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -13,11 +14,14 @@ public class DiscordCommand extends ApplianceCommand<Help> {
super("discord"); super("discord");
} }
private final static String discordLink = "https://discord.gg/TXxspGVanq";
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
sender.sendMessage( sender.sendMessage(
Component.text("Einen offiziellen Discord Server gibt es nicht, aber Du kannst gerne unserem Teamspeak joinen: ", NamedTextColor.GOLD) Component.text("Offizieller Discord Server: ", NamedTextColor.GOLD)
.append(Component.text("mhsl.eu", NamedTextColor.AQUA)) .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.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@@ -18,7 +18,8 @@ public class HelpCommand extends ApplianceCommand<Help> {
sender.sendMessage( sender.sendMessage(
Component.text("Willkommen auf Craftattack!", NamedTextColor.GOLD) Component.text("Willkommen auf Craftattack!", NamedTextColor.GOLD)
.appendNewline() .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.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@@ -19,8 +19,8 @@ public class SpawnCommand extends ApplianceCommand<Help> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 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!"); 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.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.help.Help; import eu.mhsl.craftattack.spawn.appliances.metaGameplay.help.Help;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@@ -17,12 +17,12 @@ public class TeamspeakCommand extends ApplianceCommand<Help> {
@Override @Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 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!"); throw new ApplianceCommand.Error("Es wurde kein Teamspeak hinterlegt!");
sender.sendMessage( sender.sendMessage(
Component.text() Component.text()
.append(Component.text("Joine unserem Teamspeak: ", NamedTextColor.GOLD)) .append(Component.text("Joine unserem Teamspeak: ", NamedTextColor.GOLD))
.append(getTeamspeakIp(getAppliance().localConfig().getString(teamspeakKey))) .append(this.getTeamspeakIp(this.getAppliance().localConfig().getString(teamspeakKey)))
); );
} }

View File

@@ -0,0 +1,69 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.Main;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.Ticks;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
public abstract class Bar {
private BossBar bossBar;
private final BukkitTask updateTask;
public Bar() {
long refreshRateInTicks = this.refresh().get(ChronoUnit.SECONDS) * Ticks.TICKS_PER_SECOND;
this.updateTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
Main.instance(),
this::update,
refreshRateInTicks,
refreshRateInTicks
);
}
public BossBar getBossBar() {
if(this.bossBar == null) this.bossBar = this.createBar();
return this.bossBar;
}
private BossBar createBar() {
return BossBar.bossBar(
this.title(),
this.correctedProgress(),
this.color(),
this.overlay()
);
}
private void update() {
if(this.bossBar == null) return;
this.beforeRefresh();
this.bossBar.name(this.title());
this.bossBar.progress(this.correctedProgress());
this.bossBar.color(this.color());
this.bossBar.overlay(this.overlay());
}
public void stopUpdate() {
this.updateTask.cancel();
}
private float correctedProgress() {
return Math.clamp(this.progress(), 0, 1);
}
protected void beforeRefresh() {
}
protected abstract Duration refresh();
protected abstract String name();
protected abstract Component title();
protected abstract float progress();
protected abstract BossBar.Color color();
protected abstract BossBar.Overlay overlay();
}

View File

@@ -0,0 +1,32 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
class InfoBarCommand extends ApplianceCommand.PlayerChecked<InfoBars> {
public InfoBarCommand() {
super("infobar");
}
@Override
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" -> 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'!");
}
}
@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 this.getAppliance().getInfoBars().stream().map(Bar::name).toList();
}
}

View File

@@ -0,0 +1,92 @@
package eu.mhsl.craftattack.spawn.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.metaGameplay.infoBars.bars.MsptBar;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.bars.PlayerCounterBar;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.bars.TpsBar;
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 java.util.ArrayList;
import java.util.List;
public class InfoBars extends Appliance {
private final NamespacedKey infoBarKey = new NamespacedKey(Main.instance(), "infobars");
private final List<Bar> infoBars = List.of(
new TpsBar(),
new MsptBar(),
new PlayerCounterBar()
);
public void showAll(Player player) {
this.getStoredBars(player).forEach(bar -> this.show(player, bar));
}
public void hideAll(Player player) {
this.getStoredBars(player).forEach(bar -> this.hide(player, bar));
this.setStoredBars(player, List.of());
}
public void show(Player player, String bar) {
this.validateBarName(bar);
List<String> existingBars = new ArrayList<>(this.getStoredBars(player));
existingBars.add(bar);
player.showBossBar(this.getBarByName(bar).getBossBar());
this.setStoredBars(player, existingBars);
}
public void hide(Player player, String bar) {
this.validateBarName(bar);
List<String> existingBars = new ArrayList<>(this.getStoredBars(player));
existingBars.remove(bar);
player.hideBossBar(this.getBarByName(bar).getBossBar());
this.setStoredBars(player, existingBars);
}
private List<String> getStoredBars(Player player) {
PersistentDataContainer container = player.getPersistentDataContainer();
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(this.infoBarKey, PersistentDataType.LIST.strings(), bars);
}
private Bar getBarByName(String name) {
return this.infoBars.stream()
.filter(bar -> bar.name().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
private void validateBarName(String name) {
if(this.getBarByName(name) == null)
throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name));
}
public List<Bar> getInfoBars() {
return this.infoBars;
}
@Override
public void onDisable() {
this.infoBars.forEach(Bar::stopUpdate);
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new ShowPreviousBarsListener());
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(new InfoBarCommand());
}
}

View File

@@ -0,0 +1,12 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.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

@@ -0,0 +1,57 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.util.statistics.ServerMonitor;
import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.time.Duration;
public class MsptBar extends Bar {
@Override
protected Duration refresh() {
return Duration.ofSeconds(3);
}
@Override
protected String name() {
return "mspt";
}
@Override
protected Component title() {
return Component.text()
.append(Component.text("M"))
.append(Component.text("illi", NamedTextColor.GRAY))
.append(Component.text("S"))
.append(Component.text("econds ", NamedTextColor.GRAY))
.append(Component.text("P"))
.append(Component.text("er ", NamedTextColor.GRAY))
.append(Component.text("T"))
.append(Component.text("ick", NamedTextColor.GRAY))
.append(Component.text(": "))
.append(Component.text(String.format("%.2f", this.currentMSPT()), ColorUtil.msptColor(this.currentMSPT())))
.build();
}
@Override
protected float progress() {
return this.currentMSPT() / 50f;
}
@Override
protected BossBar.Color color() {
return BossBar.Color.BLUE;
}
@Override
protected BossBar.Overlay overlay() {
return BossBar.Overlay.PROGRESS;
}
private float currentMSPT() {
return ServerMonitor.getServerMSPT();
}
}

View File

@@ -0,0 +1,56 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.appliances.tooling.playerlimit.PlayerLimit;
import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
import java.time.Duration;
public class PlayerCounterBar extends Bar {
@Override
protected Duration refresh() {
return Duration.ofSeconds(3);
}
@Override
protected String name() {
return "playerCounter";
}
@Override
protected Component title() {
TextColor color = ColorUtil.mapGreenToRed(this.getCurrentPlayerCount(), 0, this.getMaxPlayerCount(), true);
return Component.text()
.append(Component.text("Spieler online: "))
.append(Component.text(this.getCurrentPlayerCount(), color))
.build();
}
@Override
protected float progress() {
return (float) this.getCurrentPlayerCount() / this.getMaxPlayerCount();
}
@Override
protected BossBar.Color color() {
return BossBar.Color.BLUE;
}
@Override
protected BossBar.Overlay overlay() {
return BossBar.Overlay.PROGRESS;
}
private int getCurrentPlayerCount() {
return Bukkit.getOnlinePlayers().size();
}
private int getMaxPlayerCount() {
return Main.instance().getAppliance(PlayerLimit.class).getLimit();
}
}

View File

@@ -0,0 +1,55 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import java.time.Duration;
public class TpsBar extends Bar {
@Override
protected Duration refresh() {
return Duration.ofSeconds(3);
}
@Override
protected String name() {
return "tps";
}
@Override
protected Component title() {
return Component.text()
.append(Component.text("T"))
.append(Component.text("icks ", NamedTextColor.GRAY))
.append(Component.text("P"))
.append(Component.text("er ", NamedTextColor.GRAY))
.append(Component.text("S"))
.append(Component.text("econds", NamedTextColor.GRAY))
.append(Component.text(": "))
.append(Component.text(String.format("%.2f", this.currentTps()), ColorUtil.tpsColor(this.currentTps())))
.build();
}
@Override
protected float progress() {
return this.currentTps() / 20;
}
@Override
protected BossBar.Color color() {
return BossBar.Color.BLUE;
}
@Override
protected BossBar.Overlay overlay() {
return BossBar.Overlay.NOTCHED_20;
}
private float currentTps() {
return (float) Bukkit.getTPS()[0];
}
}

View File

@@ -0,0 +1,70 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.optionLinks;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.ServerLinks;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.net.URI;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Level;
@SuppressWarnings("UnstableApiUsage")
public class OptionLinks extends Appliance {
record ComponentSupplier() {
}
record UriSupplier(Player player) {
}
record UriConsumer(String uri) {
}
record SuppliedLink(Function<ComponentSupplier, Component> component, Function<UriSupplier, UriConsumer> uri) {
}
List<SuppliedLink> links = List.of(
new SuppliedLink(
componentSupplier -> Component.text("CraftAttack Homepage", NamedTextColor.GOLD),
uriSupplier -> new UriConsumer("https://mhsl.eu/craftattack")
),
new SuppliedLink(
componentSupplier -> Component.text("Regeln", NamedTextColor.GOLD),
uriSupplier -> new UriConsumer("https://mhsl.eu/craftattack/rules")
)
);
@Override
public void onEnable() {
Bukkit.getServer().getServerLinks().getLinks()
.forEach(serverLink -> Bukkit.getServer().getServerLinks().removeLink(serverLink));
}
public void setServerLinks(Player player) {
ServerLinks playerLinks = Bukkit.getServerLinks().copy();
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
this.links.forEach(suppliedLink -> {
Component component = suppliedLink.component.apply(new ComponentSupplier());
String uri = suppliedLink.uri.apply(new UriSupplier(player)).uri;
try {
playerLinks.addLink(component, URI.create(uri));
} catch(IllegalArgumentException e) {
Main.logger().log(Level.INFO, String.format("Failed to create OptionLink '%s' for player '%s'", uri, player.getName()), e);
}
});
player.sendLinks(playerLinks);
});
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new UpdateLinksListener());
}
}

View File

@@ -0,0 +1,12 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.optionLinks;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class UpdateLinksListener extends ApplianceListener<OptionLinks> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().setServerLinks(event.getPlayer());
}
}

View File

@@ -0,0 +1,19 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.packSelect;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
class ChangePackCommand extends ApplianceCommand.PlayerChecked<PackSelect> {
public static final String commandName = "texturepack";
public ChangePackCommand() {
super(commandName);
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
this.getAppliance().openPackInventory(this.getPlayer());
}
}

View File

@@ -0,0 +1,99 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.packSelect;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.mhsl.craftattack.spawn.appliance.CachedApplianceSupplier;
import eu.mhsl.craftattack.spawn.util.inventory.HeadBuilder;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import net.kyori.adventure.resource.ResourcePackInfo;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.*;
import java.util.stream.Collectors;
public class PackConfiguration extends CachedApplianceSupplier<PackSelect> {
public record Pack(UUID id, String name, String description, String author, ResourcePackInfo info, String icon) {
public ItemStack buildItem() {
ItemStack stack = HeadBuilder.getCustomTextureHead(this.icon);
ItemMeta meta = stack.getItemMeta();
meta.itemName(Component.text(this.id.toString()));
meta.displayName(Component.text(this.name(), NamedTextColor.GOLD));
List<Component> lore = new ArrayList<>();
lore.add(Component.text("Autor: ", NamedTextColor.DARK_GRAY).append(Component.text(this.author)));
lore.add(Component.text(" "));
lore.addAll(
ComponentUtil.lineBreak(this.description())
.map(s -> Component.text(s, NamedTextColor.GRAY))
.toList()
);
lore.add(Component.text(" "));
meta.lore(lore);
stack.setItemMeta(meta);
return stack;
}
public boolean equalsItem(ItemStack other) {
String itemName = PlainTextComponentSerializer.plainText().serialize(other.getItemMeta().itemName());
try {
return UUID.fromString(itemName).equals(this.id);
} catch(IllegalArgumentException ignored) {
return false;
}
}
}
public record PackList(List<Pack> packs) {
public Optional<Pack> findFromItem(ItemStack itemStack) {
return this.packs.stream()
.filter(pack -> pack.equalsItem(itemStack))
.findFirst();
}
}
public record SerializedPackList(List<UUID> packs) {
}
private final PackList packList;
private final Gson gson = new GsonBuilder().create();
private PackConfiguration() {
this.packList = new PackList(new ArrayList<>());
}
private PackConfiguration(String data) {
SerializedPackList serializedData = this.gson.fromJson(data, SerializedPackList.class);
var availablePackMap = this.getAppliance().availablePacks.packs().stream()
.collect(Collectors.toMap(PackConfiguration.Pack::id, pack -> pack));
this.packList = new PackList(
serializedData.packs().stream()
.map(availablePackMap::get)
.filter(Objects::nonNull)
.collect(Collectors.toList())
);
if(this.packList.packs().isEmpty())
throw new IllegalArgumentException("Serialized data does not contain any valid data!");
}
public static PackConfiguration deserialize(String data) {
return new PackConfiguration(data);
}
public static PackConfiguration empty() {
return new PackConfiguration();
}
public String serialize() {
return this.gson.toJson(new SerializedPackList(this.packList.packs().stream().map(Pack::id).toList()));
}
public List<Pack> getPackList() {
return this.packList.packs();
}
}

View File

@@ -0,0 +1,171 @@
package eu.mhsl.craftattack.spawn.appliances.metaGameplay.packSelect;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.CachedApplianceSupplier;
import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.util.inventory.ItemBuilder;
import eu.mhsl.craftattack.spawn.util.inventory.PlaceholderItems;
import eu.mhsl.craftattack.spawn.util.world.InteractSounds;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
public class PackConfigurationInventory extends CachedApplianceSupplier<PackSelect> {
private final PackConfiguration packConfiguration;
private final Player inventoryOwner;
private final Inventory inventory;
private final ItemStack reset = ItemBuilder.of(Material.BARRIER)
.displayName(Component.text("Zurücksetzen", NamedTextColor.RED))
.lore("Alle gewählten Texturepacks werden entfernt")
.build();
private final ItemStack info = ItemBuilder.of(Material.OAK_HANGING_SIGN)
.displayName(Component.text("Texturepacks", NamedTextColor.GOLD))
.lore(
"Wähle aus der oberen Liste eine oder mehrere Texturepacks aus. " +
"Ändere die anzuwendende Reihenfolge durch Links/Rechtsklick in der unteren Liste. " +
"Klicke auf Speichern um die Änderungen anzuwenden!"
)
.build();
private final ItemStack save = ItemBuilder.of(Material.GREEN_WOOL)
.displayName(Component.text("Anwenden", NamedTextColor.GREEN))
.lore("Die Ausgewählten Texturepacks werden in der entsprechenden Reihenfolge angewandt. Das Anwenden kann durch den Download je nach Internetgeschwindigkeit eine Weile dauern.")
.build();
private final ItemStack unusedSlot = ItemBuilder.of(Material.LIME_STAINED_GLASS_PANE)
.displayName(Component.text("Freier Slot", NamedTextColor.GREEN))
.lore("Klicke auf ein Texturepack um es hinzuzufügen.")
.build();
public PackConfigurationInventory(PackConfiguration packConfiguration, Player inventoryOwner) {
this.packConfiguration = packConfiguration;
this.inventoryOwner = inventoryOwner;
this.inventory = Bukkit.createInventory(null, 9 * 6, Component.text("Texturepacks"));
this.draw();
}
public Inventory getInventory() {
return this.inventory;
}
public void handleClick(@Nullable ItemStack clickedItem, int slot, ClickType clickType) {
if(clickedItem == null) return;
if(clickedItem.equals(this.reset)) this.reset();
if(clickedItem.equals(this.save)) this.apply();
if(slot >= 9 && slot < 9 * 4) {
this.getAppliance().availablePacks.findFromItem(clickedItem)
.ifPresent(this::toggle);
}
if(slot >= 9 * 5) {
this.getAppliance().availablePacks.findFromItem(clickedItem)
.ifPresent(pack -> {
switch(clickType) {
case RIGHT -> this.move(pack, true);
case LEFT -> this.move(pack, false);
case MIDDLE -> this.toggle(pack);
}
});
}
}
private void move(PackConfiguration.Pack pack, boolean moveToRight) {
List<PackConfiguration.Pack> packs = this.packConfiguration.getPackList();
int index = packs.indexOf(pack);
if(index != -1) {
int newIndex = moveToRight ? index + 1 : index - 1;
if(newIndex >= 0 && newIndex < packs.size()) Collections.swap(packs, index, newIndex);
}
InteractSounds.of(this.inventoryOwner).click();
this.draw();
}
private void toggle(PackConfiguration.Pack pack) {
if(this.packConfiguration.getPackList().contains(pack)) {
this.packConfiguration.getPackList().remove(pack);
} else {
this.packConfiguration.getPackList().add(pack);
}
InteractSounds.of(this.inventoryOwner).click();
this.draw();
}
private void reset() {
this.packConfiguration.getPackList().clear();
InteractSounds.of(this.inventoryOwner).delete();
this.draw();
}
private void apply() {
this.inventoryOwner.closeInventory();
InteractSounds.of(this.inventoryOwner).success();
Bukkit.getScheduler().runTask(Main.instance(), () -> this.getAppliance().setPack(this.inventoryOwner, this.packConfiguration));
}
private void draw() {
this.inventory.clear();
this.inventory.setItem(0, this.packConfiguration.getPackList().isEmpty() ? PlaceholderItems.grayStainedGlassPane : this.reset);
IteratorUtil.times(3, () -> this.inventory.addItem(PlaceholderItems.grayStainedGlassPane));
this.inventory.setItem(4, this.info);
IteratorUtil.times(3, () -> this.inventory.addItem(PlaceholderItems.grayStainedGlassPane));
this.inventory.setItem(8, this.save);
IteratorUtil.iterateListInGlobal(
9,
this.getAppliance().availablePacks.packs().stream()
.filter(pack -> !this.packConfiguration.getPackList().contains(pack))
.limit(9 * 3)
.map(pack -> {
ItemBuilder stack = ItemBuilder.of(pack.buildItem());
if(this.packConfiguration.getPackList().contains(pack)) stack.glint();
return stack.build();
})
.toList(),
this.inventory::setItem
);
IntStream.range(9 * 4, 9 * 5)
.forEach(slot -> this.inventory.setItem(slot, PlaceholderItems.grayStainedGlassPane));
IteratorUtil.iterateListInGlobal(
9 * 5,
IteratorUtil.expandList(
IntStream.range(0, Math.min(this.packConfiguration.getPackList().size(), 9))
.mapToObj(i -> {
PackConfiguration.Pack pack = this.packConfiguration.getPackList().get(i);
ItemBuilder builder = ItemBuilder.of(pack.buildItem());
builder
.displayName(existing -> Component.text(String.format("#%s ", i + 1), NamedTextColor.LIGHT_PURPLE).append(existing))
.appendLore(Component.text("➡ Rechtsklick um nach Rechts zu verschieben", NamedTextColor.AQUA))
.appendLore(Component.text("⬅ Linksklick um nach Links zu verschieben", NamedTextColor.AQUA))
.appendLore(Component.text("\uD83D\uDDD1 Mausradklick um zu entfernen", NamedTextColor.AQUA));
return builder.build();
})
.toList(),
9,
this.unusedSlot
),
this.inventory::setItem
);
}
}

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