Compare commits

..

152 Commits

Author SHA1 Message Date
dd1518fce4 split Event into Scorable and Event; removed AbstractEvent; added list, select and unselect commands 2025-12-12 19:30:21 +01:00
04cb233604 WIP: moved scores and score updates to EventScoreboardBuilder 2025-12-12 12:09:25 +01:00
2ff95f8450 changed scoreboard package name to lowercase 2025-12-02 12:52:18 +01:00
6ed48895ca made appliance ignore destruct when no listeners 2025-12-01 22:29:13 +01:00
bff8cf24cd added AbstractEvent and basic Scoreboard functionality 2025-12-01 22:28:15 +01:00
212f84b6de added start, stop and getScore methods to Event 2025-11-30 18:07:15 +01:00
f7430c8fc8 added possibility to disable appliances 2025-11-28 21:08:27 +01:00
c81a2d2161 started big events 2025-11-28 16:52:42 +01:00
2087b4c379 Merge pull request 'develop-bloodmoon' (#10) from develop-bloodmoon into master
Reviewed-on: #10
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2025-11-23 14:03:06 +00:00
4d9548aafc resolved pr comments 2025-11-23 15:00:16 +01:00
4c63800189 Merge branch 'master' into develop-bloodmoon 2025-11-23 12:58:45 +00:00
b8725bc0f2 made bossbar shrink until end of bloodmoon, fixed horde spawning 2025-11-23 13:58:02 +01:00
c90d767f0f added todos 2025-11-21 09:12:17 +01:00
143f4ee8eb adjusted bloodmoon settings 2025-11-21 09:03:43 +01:00
ba2befb467 refactored event hierarchy; replaced SpawnEvent with direct Event implementation, added StrikeUpdateListener and refactored whitelist profile update logic 2025-11-16 12:02:49 +01:00
7a2b9b9763 updated Paper API dependency to 1.21.10-R0.1-SNAPSHOT across all modules 2025-11-16 12:00:58 +01:00
0b9dc5358d added HTTP hooks framework with actions for signup, report, and strike events; introduced SpawnEvent support for event broadcasting 2025-11-15 12:50:01 +01:00
448e9472db updated outlawed info messages 2025-11-09 20:36:21 +01:00
933c4c0db0 refactored InfoBars handling logic; replaced persistent data with direct visibility control 2025-11-09 19:32:44 +01:00
f27474016a added @Flags annotation to Appliance, disabled DeviceFingerprinting appliance by default 2025-11-09 19:31:25 +01:00
17e5b2e049 Merge branch 'develop-backendUpdate' 2025-11-09 19:21:39 +01:00
d3512cb2eb Merge remote-tracking branch 'origin/master' 2025-11-09 19:13:34 +01:00
7b19bfd39e Merge branch 'develop-deviceFingerprinting' 2025-11-09 19:13:18 +01:00
0ab67bb426 refactored strike handling logic; added ban determination and updated whitelist integration 2025-11-09 19:13:08 +01:00
29a362b580 updated default shortcut setting value 2025-11-09 16:08:28 +01:00
a7f298682b Merge pull request 'develop-statisticsApi' (#9) from develop-statisticsApi into master
Reviewed-on: #9
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2025-11-08 18:36:41 +00:00
895a51e71a Merge branch 'master' into develop-statisticsApi 2025-11-08 18:28:10 +00:00
4a5c24235b added player display name to StatisticsResponse 2025-11-07 21:46:16 +01:00
62c0250049 added null value check for material 2025-11-07 21:27:33 +01:00
b4ccc3c4c8 added statistics appliance in craftattack 2025-11-07 19:47:32 +01:00
239094971c Revert "simplified event message handling logic in ChatMessagesListener"
This reverts commit db13a9f0a2.
2025-11-02 14:18:43 +01:00
53dbeff829 merged automatic start with hordes per player 2025-11-01 17:43:35 +01:00
b5da06fd49 Merge remote-tracking branch 'origin/develop-bloodmoon' into develop-bloodmoon
# Conflicts:
#	craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/Bloodmoon.java
2025-11-01 17:32:36 +01:00
b55035f1f0 added timings and announcements for automatic bloodmoon start 2025-11-01 17:29:39 +01:00
469cd19b55 added AntiBoatFreecam to detect and notify admins of illegal boat yaw behavior 2025-10-27 17:54:19 +01:00
91a28b4500 Merge branch 'master' into develop-backendUpdate 2025-10-27 17:11:01 +01:00
e745ff4721 added AntiFormattedBook to detect and sanitize illegal book formatting 2025-10-27 17:10:44 +01:00
23af3ff784 Merge branch 'master' into develop-backendUpdate 2025-10-27 16:14:24 +01:00
bc5c9a2a13 added AntiIllegalBundlePicker to track and notify admins on illegal bundle interactions 2025-10-27 16:02:13 +01:00
c220479052 WIP: refactored report and feedback systems; updated repository models, APIs, and component utilities 2025-10-27 14:25:44 +01:00
0565f5de9e changed bloodmoon setting description 2025-10-20 16:28:00 +02:00
ff81c91661 changed horde spawning to delay for each player 2025-10-20 16:27:10 +02:00
1182cb2ed6 added todos 2025-10-19 21:35:03 +02:00
045f31d4ca removed horde announcement message and unnecessary copy of player location, added setting feedback when bloodmoon active 2025-10-19 21:26:24 +02:00
263fd85df7 fixed multiple horde tasks being started 2025-10-19 18:56:12 +02:00
d70b025502 added hordes, disabled setting when bloodmoon is active 2025-10-19 18:45:55 +02:00
78f87d97f0 Merge remote-tracking branch 'origin/master' 2025-10-19 12:54:35 +02:00
db13a9f0a2 simplified event message handling logic in ChatMessagesListener 2025-10-19 12:54:31 +02:00
e14c87c2fb added mob effects and Setting 2025-10-19 01:18:27 +02:00
9433495a52 added random drops and boss bar for Bloodmoon 2025-10-18 19:01:09 +02:00
4955e94306 started bloodmoon appliance 2025-10-17 19:38:10 +02:00
09abfefe33 added PhantomReducer 2025-10-17 18:39:05 +02:00
bd3546abc8 changed report appliance to craftattack 2025-10-17 18:00:15 +02:00
8a7a0453ce updated Whitelist api for new Backend 2025-10-17 17:28:25 +02:00
64d0d817c0 Merge pull request 'added IronGolemAnimation' (#8) from develop-animatedIronGolem into master
Reviewed-on: #8
Reviewed-by: Lars Neuhaus <larslukasneuhaus@gmx.de>
2025-10-14 22:42:52 +00:00
713561bf07 adjusted Iron Golem animation viewer calculation to account for block distance 2025-10-12 23:07:09 +02:00
4be3e528b1 fixed spawn reason check for Iron Golem spawning logic in NaturalIronGolemSpawnEvent 2025-10-12 23:05:19 +02:00
53fca580f3 added IronGolemAnimation 2025-10-12 23:04:05 +02:00
20fb4bf9fb added example local Gradle tasks for deploying and uploading plugins 2025-10-12 21:31:35 +02:00
c42d259909 Merge pull request 'added RecoveryCompass' (#7) from develop-recoveryCompass into master
Reviewed-on: #7
Reviewed-by: Lars Neuhaus <larslukasneuhaus@gmx.de>
2025-10-11 00:42:05 +00:00
5c82c8d6da resolved pr comments 2025-10-11 00:04:09 +02:00
5910847172 added RecoveryCompass 2025-10-10 23:39:29 +02:00
aad1fcafa6 added FingerprintData class and improved device fingerprinting logic 2025-10-05 15:46:45 +02:00
9fca7430a8 implemented working fingerprinting prototype 2025-10-05 13:24:47 +02:00
7c254707c1 removed permanent coordinate broadcasting 2025-10-03 17:47:23 +02:00
9ee5f6e419 updated InfoBars to use Bukkit scheduler for data container updates; enhanced CoordinateDisplay with Unicode icons and adjusted text styling 2025-10-03 17:38:29 +02:00
e49c3b1987 added category support to CoordinateDisplaySetting to align with gameplay setting structure 2025-10-03 17:06:56 +02:00
5ca4c70a41 added CoordinateDisplay with settings preferences, directional updates, and time display 2025-10-03 17:06:20 +02:00
040cae6cd1 updated InfoBars: localized names, dynamic coloring, and progress clamping adjustments 2025-10-03 15:49:17 +02:00
324defc4a8 removed InfoBarCommand and integrated InfoBars with Settings preference handling 2025-10-03 15:30:36 +02:00
dc0003b91e Merge branch 'master-antiGrief' 2025-10-01 19:12:01 +02:00
d4a3c798f8 added LocatorBar preferences 2025-10-01 19:11:16 +02:00
c88c2ab6aa updated dependencies 2025-09-28 13:22:42 +02:00
32a20cd4c5 added antiGrief command 2025-09-28 12:59:18 +02:00
d7cc141b94 added antiGrief inhabited chunk time calculation 2025-09-27 07:42:13 +02:00
16d7347fd0 antigrief false positives tweaks 2025-09-27 07:30:23 +02:00
fdf3b5c73f smaller lavaCast detector to prevent natural flow detection 2025-09-21 13:31:21 +02:00
74f17e1b6d WIP: AntiGrief 2025-09-21 01:54:20 +02:00
0d18b81399 changed coloring of VaroRank 2025-09-21 01:06:29 +02:00
1fa5fdfeb7 tweaked settings defaults 2025-09-20 20:35:02 +02:00
3bec5f4cbd added confirmation on outlawed change 2025-09-20 20:30:47 +02:00
d38871eac9 added MendingReducer tweak 2025-09-20 14:43:19 +02:00
238df2feff added armadilloExpFarm tweaks 2025-09-20 13:01:41 +02:00
e752d7f73b moved AntiAutoTotem to common project 2025-09-20 12:51:48 +02:00
b0df982be3 made antiSignEdit message clearer 2025-09-20 12:16:26 +02:00
dc1b5957f6 WIP: different method for grief detection 2025-09-20 11:35:19 +02:00
4068eae5bb Merge branch 'master' into master-antiGrief 2025-08-10 12:10:32 +02:00
b1e3e99cb8 added AntiAutoTotem 2025-08-10 12:04:46 +02:00
b3787983d5 added advancement for MinecartBlocks 2025-08-10 09:58:16 +02:00
43ef28499b added MinecartBlocks 2025-08-10 00:04:12 +02:00
6e1ef4fd7c added VaroRank 2025-08-09 22:15:01 +02:00
ec2d243b7b fixed bug in HotBarRefill when using Blocks 2025-07-19 22:32:47 +02:00
ef6f34c2b2 added interaction sounds to settings 2025-07-19 16:36:31 +02:00
977f4ff4ec project dependency update 2025-07-19 16:27:39 +02:00
337727b0f0 fixed bug in FightDetector 2025-07-19 15:57:58 +02:00
44dae51e1c fixed playtimer 2025-06-24 21:23:05 +02:00
035864631d changed behavior to spawn in survival mode 2025-06-24 20:09:36 +02:00
f3b884058e code cleanup shrinkingborder 2025-06-23 20:34:48 +02:00
03d4f4e6d8 fixed bug in ShrinkingBorder 2025-06-23 20:28:13 +02:00
7422a89d98 fixed bug in fight detector 2025-06-23 19:52:30 +02:00
3590a5d278 finalized strikesystem 2025-06-22 14:20:45 +02:00
15ac47b314 auto playtime increment 2025-06-22 11:59:46 +02:00
af644a71ee ticketing enable and disable 2025-06-22 11:57:46 +02:00
0ce69f207f fixed bugs in strike handling 2025-06-22 10:59:38 +02:00
76297bb3af WIP: basic strike handling 2025-06-22 10:34:27 +02:00
1aad8f07c4 various bugfixes 2025-06-21 23:16:30 +02:00
f26f4ed56a cleanup 2025-06-21 21:35:31 +02:00
831eacaf47 added verbose logging for api requests
added autostrike for early leave
2025-06-21 21:22:49 +02:00
c71a2567bd fixed adminmarker handling api data wrong 2025-06-21 20:18:32 +02:00
72e88ce491 added spawnpoint for varo 2025-06-21 18:51:37 +02:00
66d84f4677 projectstart for varo 2025-06-21 18:15:25 +02:00
427aed9a7e fixed bug in teamtasks 2025-06-21 17:55:52 +02:00
0d1e6070ce updated playtimer and teamtasks 2025-06-21 17:18:47 +02:00
220fb9e229 moved existing spawning behavior to craftattack 2025-06-21 11:41:13 +02:00
9acac488f2 added api for querying admin-players 2025-06-21 11:38:09 +02:00
d71c0d768e configured shrinkingBorder for production use 2025-06-21 11:31:16 +02:00
9ef4c2e96b added playtimer ticket api 2025-06-20 17:07:53 +02:00
5d33d2aff7 updated adminmarker 2025-06-20 14:29:46 +02:00
3f1065fd3a added teamlist command 2025-06-19 23:49:48 +02:00
aa868deeca added team task management 2025-06-19 21:41:43 +02:00
b6c298cec3 unlimited admin access 2025-06-19 01:18:14 +02:00
8f5a96dc31 changed report text 2025-06-19 00:54:40 +02:00
2824c1053b WIP: report implementation for varo 2025-06-19 00:40:49 +02:00
ccf383cdb5 fixed configuration file not saving correctly 2025-06-15 18:55:17 +02:00
fce9449b7e implemented PlayTimer 2025-06-15 18:42:49 +02:00
69e971f618 Teams corrections
full implementation of FightDetector
2025-06-11 21:36:22 +02:00
b1f188dece generic tweaks
started implementation of FightDetector
2025-06-09 13:52:39 +02:00
a4289d5ac9 periodic team fetch 2025-05-30 22:00:42 +02:00
1fef363c50 Merge remote-tracking branch 'origin/master' 2025-05-30 18:35:14 +02:00
558e6f84f1 api header support, team api integration 2025-05-30 18:35:11 +02:00
bdbb8b5824 api header support, team api integration 2025-05-30 18:34:49 +02:00
8093a4a644 various changes for team management 2025-05-30 12:44:48 +02:00
50147a06e2 added removal of forbidden items in containers 2025-04-13 20:52:03 +02:00
a52476650e registered missing listener for DisplayName 2025-04-12 20:42:32 +02:00
0e5e841527 Merge branch 'master-shrinkingBorder' 2025-04-11 20:44:00 +02:00
ea5279dd82 Merge branch 'master-netherPrevent' 2025-04-11 20:43:08 +02:00
32cbbe6c51 made displayName independent of other appliances 2025-04-11 19:16:13 +02:00
9544c953a2 fixed shrinking border warning 2025-04-09 23:05:46 +02:00
34df173940 changed integer setting constructor to correctly use maximum 2025-04-09 22:32:36 +02:00
ca99e6cfef added integer setting 2025-04-09 16:50:29 +02:00
b0414ae6ab added warning with corresponding setting 2025-04-09 00:08:01 +02:00
c28d34ab88 added shrinking border to config 2025-04-08 19:03:22 +02:00
9bae26044a added shrinking border appliance 2025-04-08 19:00:49 +02:00
d1b5d81fa7 moved tablist to common and made project title configurable 2025-04-08 15:10:23 +02:00
e37e410542 moved report appliance to common 2025-04-08 15:04:50 +02:00
956d2717d8 Merge remote-tracking branch 'origin/master' 2025-04-08 11:49:31 +02:00
ef7232e687 fixed missing countdown for JoinProtection 2025-04-08 11:49:25 +02:00
ff31215295 added option for end prevent 2025-04-07 23:48:06 +02:00
a4a254ebbe removed unnecessary listeners 2025-04-07 22:33:01 +02:00
71d9faa9f4 added nether prevent 2025-04-07 22:17:54 +02:00
859733e3dd finalized JoinProtection 2025-04-07 19:15:35 +02:00
092d33beb3 prototype for grief detection 2025-03-29 22:03:12 +01:00
337 changed files with 7338 additions and 848 deletions

View File

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

View File

@@ -1,14 +1,15 @@
package eu.mhsl.craftattack.core.util.api; package eu.mhsl.craftattack.spawn.common.api;
import eu.mhsl.craftattack.core.config.Configuration; import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.util.Objects; import java.util.Objects;
public class WebsiteApiUtil { public class CraftAttackApi {
private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("api")); private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("api"));
public final static String basePath = apiConfig.getString("baseurl"); public final static String basePath = apiConfig.getString("baseurl");
public final static String apiSecret = apiConfig.getString("secret"); public final static String apiSecret = apiConfig.getString("secret");
@@ -25,4 +26,8 @@ public class WebsiteApiUtil {
public static void withAuthorizationSecret(URIBuilder builder) { public static void withAuthorizationSecret(URIBuilder builder) {
builder.addParameter("secret", apiSecret); builder.addParameter("secret", apiSecret);
} }
public static void withAuthorizationHeader(HttpRequest.Builder builder) {
builder.header("Authorization", apiSecret);
}
} }

View File

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

View File

@@ -0,0 +1,26 @@
package eu.mhsl.craftattack.spawn.common.api.repositories;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi;
import java.util.UUID;
public class CraftAttackReportRepository extends ReportRepository {
public CraftAttackReportRepository() {
super(CraftAttackApi.getBaseUri(), new RequestModifier(null, CraftAttackApi::withAuthorizationHeader));
}
public ReqResp<PlayerReports> queryReports(UUID player) {
return this.get(
"users/%s/reports".formatted(player.toString()),
PlayerReports.class
);
}
public ReqResp<ReportUrl> createReport(ReportCreationInfo data) {
return this.post(
"reports",
data,
ReportUrl.class
);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
package eu.mhsl.craftattack.spawn.common.appliances.gameplay.cordinateDisplay;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
public class CoordinateChangedListener extends ApplianceListener<CoordinateDisplay> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().updateEnabled(event.getPlayer());
}
@EventHandler
public void onMove(PlayerMoveEvent event) {
if(!this.getAppliance().isEnabled(event.getPlayer())) return;
boolean hasChangedOrientation = this.getAppliance().hasChangedDirection(event.getFrom(), event.getTo());
if(!event.hasChangedBlock() && !hasChangedOrientation) return;
this.getAppliance().sendCoordinates(event.getPlayer());
}
}

View File

@@ -0,0 +1,93 @@
package eu.mhsl.craftattack.spawn.common.appliances.gameplay.cordinateDisplay;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.util.text.DataSizeConverter;
import eu.mhsl.craftattack.spawn.core.util.world.WorldUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class CoordinateDisplay extends Appliance {
Map<Player, CoordinateDisplaySetting.CoordinateDisplayConfiguration> enabledPlayers = new WeakHashMap<>();
@Override
public void onEnable() {
Settings.instance().declareSetting(CoordinateDisplaySetting.class);
Settings.instance().addChangeListener(CoordinateDisplaySetting.class, this::updateEnabled);
}
public void updateEnabled(Player player) {
CoordinateDisplaySetting.CoordinateDisplayConfiguration configuration = Settings.instance().getSetting(
player,
Settings.Key.CoordinateDisplay,
CoordinateDisplaySetting.CoordinateDisplayConfiguration.class
);
this.enabledPlayers.put(player, configuration);
}
public boolean isEnabled(Player player) {
return Optional.ofNullable(this.enabledPlayers.get(player))
.map(CoordinateDisplaySetting.CoordinateDisplayConfiguration::anyEnabled)
.orElse(false);
}
public void sendCoordinates(Player player) {
CoordinateDisplaySetting.CoordinateDisplayConfiguration config = this.enabledPlayers.get(player);
List<Component> components = new ArrayList<>();
if (config.coordinates()) {
components.add(Component.text("\uD83C\uDF0E ", NamedTextColor.GOLD));
components.add(Component.text(String.format(
"%d %d %d",
player.getLocation().getBlockX(),
player.getLocation().getBlockY(),
player.getLocation().getBlockZ()
)));
}
if (config.direction()) {
if (!components.isEmpty()) {
components.add(Component.text(" | ", NamedTextColor.GRAY));
}
components.add(Component.text("\uD83E\uDDED ", NamedTextColor.GOLD));
components.add(Component.text(DataSizeConverter.getCardinalDirection(player.getLocation())));
}
if (config.time()) {
if (!components.isEmpty()) {
components.add(Component.text(" | ", NamedTextColor.GRAY));
}
components.add(Component.text("", NamedTextColor.GOLD));
components.add(Component.text(WorldUtils.getGameTime(player.getWorld())));
}
if (!components.isEmpty()) {
Component actionBar = Component.empty();
for (Component component : components) {
actionBar = actionBar.append(component);
}
player.sendActionBar(actionBar);
}
}
public boolean hasChangedDirection(Location previous, Location next) {
return !Objects.equals(
DataSizeConverter.getCardinalDirection(previous),
DataSizeConverter.getCardinalDirection(next)
);
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(
new CoordinateChangedListener()
);
}
}

View File

@@ -0,0 +1,53 @@
package eu.mhsl.craftattack.spawn.common.appliances.gameplay.cordinateDisplay;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.MultiBoolSetting;
import org.bukkit.Material;
public class CoordinateDisplaySetting extends MultiBoolSetting<CoordinateDisplaySetting.CoordinateDisplayConfiguration> implements CategorizedSetting {
public CoordinateDisplaySetting() {
super(Settings.Key.CoordinateDisplay);
}
@Override
public SettingCategory category() {
return SettingCategory.Gameplay;
}
public record CoordinateDisplayConfiguration(
@DisplayName("Koordinaten") boolean coordinates,
@DisplayName("Richtung") boolean direction,
@DisplayName("Zeit") boolean time
) {
public boolean anyEnabled() {
return this.coordinates || this.direction || this.time;
}
}
@Override
protected String title() {
return "Koordinatenanzeige";
}
@Override
protected String description() {
return "Zeige deine aktuelle Position über der Hotbar an";
}
@Override
protected Material icon() {
return Material.RECOVERY_COMPASS;
}
@Override
protected CoordinateDisplayConfiguration defaultValue() {
return new CoordinateDisplayConfiguration(false, false, false);
}
@Override
public Class<?> dataType() {
return CoordinateDisplayConfiguration.class;
}
}

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.debug; package eu.mhsl.craftattack.spawn.common.appliances.internal.debug;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.AppliancesCommand; import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.AppliancesCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.UserInfoCommand; import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.UserInfoCommand;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command; package eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.Debug; import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.Debug;
import eu.mhsl.craftattack.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.core.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;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command; package eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.Debug; import eu.mhsl.craftattack.spawn.common.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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.titleClear; package eu.mhsl.craftattack.spawn.common.appliances.internal.titleClear;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.internal.titleClear; package eu.mhsl.craftattack.spawn.common.appliances.internal.titleClear;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;

View File

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

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;

View File

@@ -1,10 +1,10 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.core.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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.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;

View File

@@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.core.util.IteratorUtil; import eu.mhsl.craftattack.spawn.core.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;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@@ -30,7 +30,9 @@ class ChatMessagesListener extends ApplianceListener<ChatMessages> {
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
boolean wasHidden = event.joinMessage() == null;
event.joinMessage(null); event.joinMessage(null);
if(wasHidden) return;
IteratorUtil.onlinePlayers(player -> { IteratorUtil.onlinePlayers(player -> {
if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return; if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return;
player.sendMessage( player.sendMessage(
@@ -43,7 +45,9 @@ class ChatMessagesListener extends ApplianceListener<ChatMessages> {
@EventHandler @EventHandler
public void onPlayerLeave(PlayerQuitEvent event) { public void onPlayerLeave(PlayerQuitEvent event) {
boolean wasHidden = event.quitMessage() == null;
event.quitMessage(null); event.quitMessage(null);
if(wasHidden) return;
IteratorUtil.onlinePlayers(player -> { IteratorUtil.onlinePlayers(player -> {
if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return; if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return;
player.sendMessage( player.sendMessage(

View File

@@ -1,14 +1,9 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed.Outlawed; import eu.mhsl.craftattack.spawn.core.util.server.Floodgate;
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.adminMarker.AdminMarker; import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.afkTag.AfkTag;
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.sleepTag.SleepTag;
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.yearRank.YearRank;
import eu.mhsl.craftattack.core.util.server.Floodgate;
import eu.mhsl.craftattack.core.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;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
@@ -16,11 +11,12 @@ import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
public class DisplayName extends Appliance { public class DisplayName extends Appliance {
@@ -29,18 +25,33 @@ public class DisplayName extends Appliance {
Component getNamePrefix(Player player); Component getNamePrefix(Player player);
} }
public interface Colored {
@Nullable
TextColor getNameColor(Player player);
}
public void update(Player player) { public void update(Player player) {
TextColor playerColor = this.queryAppliance(AdminMarker.class).getPlayerColor(player); List<Colored> coloring = Main.instance().getAppliances().stream()
List<Supplier<Prefixed>> prefixes = List.of( .filter(appliance -> appliance instanceof Colored)
() -> this.queryAppliance(Outlawed.class), .map(appliance -> (Colored) appliance)
() -> this.queryAppliance(YearRank.class), .toList();
() -> this.queryAppliance(AfkTag.class),
() -> this.queryAppliance(SleepTag.class) if(coloring.size() > 1) throw new IllegalStateException(
"There are two or more appliances which provide coloring for player names. This is currently not supported!"
); );
TextColor playerColor = coloring.isEmpty()
? NamedTextColor.WHITE
: coloring.getFirst().getNameColor(player);
List<Prefixed> prefixes = Main.instance().getAppliances().stream()
.filter(appliance -> appliance instanceof Prefixed)
.map(appliance -> (Prefixed) appliance)
.toList();
ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text(); ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text();
prefixes.stream() prefixes.stream()
.map(prefixed -> prefixed.get().getNamePrefix(player)) .map(prefixed -> prefixed.getNamePrefix(player))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.forEach(prefix -> playerName .forEach(prefix -> playerName
.append(prefix) .append(prefix)
@@ -72,4 +83,9 @@ public class DisplayName extends Appliance {
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage); Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
} }
} }
@Override
protected @NotNull List<Listener> listeners() {
return List.of(new DisplayNameUpdateListener());
}
} }

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.DiscordCommand; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.DiscordCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.HelpCommand; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.HelpCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.SpawnCommand; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.SpawnCommand;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help; import eu.mhsl.craftattack.spawn.common.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.event.ClickEvent;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help; import eu.mhsl.craftattack.spawn.common.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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help; import eu.mhsl.craftattack.spawn.common.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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.Help; import eu.mhsl.craftattack.spawn.common.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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.Ticks; import net.kyori.adventure.util.Ticks;
@@ -13,6 +13,7 @@ import java.time.temporal.ChronoUnit;
public abstract class Bar { public abstract class Bar {
private BossBar bossBar; private BossBar bossBar;
private final BukkitTask updateTask; private final BukkitTask updateTask;
public static String name;
public Bar() { public Bar() {
long refreshRateInTicks = this.refresh().get(ChronoUnit.SECONDS) * Ticks.TICKS_PER_SECOND; long refreshRateInTicks = this.refresh().get(ChronoUnit.SECONDS) * Ticks.TICKS_PER_SECOND;
@@ -32,7 +33,7 @@ public abstract class Bar {
private BossBar createBar() { private BossBar createBar() {
return BossBar.bossBar( return BossBar.bossBar(
this.title(), this.title(),
this.correctedProgress(), this.clampedProgress(),
this.color(), this.color(),
this.overlay() this.overlay()
); );
@@ -43,7 +44,7 @@ public abstract class Bar {
this.beforeRefresh(); this.beforeRefresh();
this.bossBar.name(this.title()); this.bossBar.name(this.title());
this.bossBar.progress(this.correctedProgress()); this.bossBar.progress(this.clampedProgress());
this.bossBar.color(this.color()); this.bossBar.color(this.color());
this.bossBar.overlay(this.overlay()); this.bossBar.overlay(this.overlay());
} }
@@ -52,7 +53,7 @@ public abstract class Bar {
this.updateTask.cancel(); this.updateTask.cancel();
} }
private float correctedProgress() { private float clampedProgress() {
return Math.clamp(this.progress(), 0, 1); return Math.clamp(this.progress(), 0, 1);
} }

View File

@@ -1,32 +0,0 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.core.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,49 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.MultiBoolSetting;
import org.bukkit.Material;
public class InfoBarSetting extends MultiBoolSetting<InfoBarSetting.InfoBarConfiguration> implements CategorizedSetting {
public InfoBarSetting() {
super(Settings.Key.InfoBars);
}
@Override
public SettingCategory category() {
return SettingCategory.Misc;
}
public record InfoBarConfiguration(
@DisplayName("Millisekunden pro Tick") boolean mspt,
@DisplayName("Spieler online") boolean playerCounter,
@DisplayName("Ticks pro Sekunde") boolean tps
) {}
@Override
protected String title() {
return "Informationsleisten";
}
@Override
protected String description() {
return "Wähle anzuzeigende Informationsleisten aus";
}
@Override
protected Material icon() {
return Material.COMMAND_BLOCK;
}
@Override
protected InfoBarConfiguration defaultValue() {
return new InfoBarConfiguration(false, false, false);
}
@Override
public Class<?> dataType() {
return InfoBarConfiguration.class;
}
}

View File

@@ -1,62 +1,38 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.MsptBar; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.MsptBar;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.PlayerCounterBar; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.PlayerCounterBar;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.TpsBar; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.TpsBar;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class InfoBars extends Appliance { public class InfoBars extends Appliance {
private final NamespacedKey infoBarKey = new NamespacedKey(Main.instance(), "infobars");
private final List<Bar> infoBars = List.of( private final List<Bar> infoBars = List.of(
new TpsBar(), new TpsBar(),
new MsptBar(), new MsptBar(),
new PlayerCounterBar() new PlayerCounterBar()
); );
public void showAll(Player player) { public void showAllEnabled(Player player) {
this.getStoredBars(player).forEach(bar -> this.show(player, bar)); InfoBarSetting.InfoBarConfiguration config = Settings.instance().getSetting(player, Settings.Key.InfoBars, InfoBarSetting.InfoBarConfiguration.class);
this.setVisible(player, MsptBar.name, config.mspt());
this.setVisible(player, PlayerCounterBar.name, config.playerCounter());
this.setVisible(player, TpsBar.name, config.tps());
} }
public void hideAll(Player player) { public void setVisible(Player player, String bar, boolean visible) {
this.getStoredBars(player).forEach(bar -> this.hide(player, bar));
this.setStoredBars(player, List.of());
}
public void show(Player player, String bar) {
this.validateBarName(bar); this.validateBarName(bar);
List<String> existingBars = new ArrayList<>(this.getStoredBars(player)); if(visible) {
existingBars.add(bar); player.showBossBar(this.getBarByName(bar).getBossBar());
player.showBossBar(this.getBarByName(bar).getBossBar()); } else {
this.setStoredBars(player, existingBars); player.hideBossBar(this.getBarByName(bar).getBossBar());
} }
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) { private Bar getBarByName(String name) {
@@ -71,8 +47,10 @@ public class InfoBars extends Appliance {
throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name)); throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name));
} }
public List<Bar> getInfoBars() { @Override
return this.infoBars; public void onEnable() {
Settings.instance().declareSetting(InfoBarSetting.class);
Settings.instance().addChangeListener(InfoBarSetting.class, this::showAllEnabled);
} }
@Override @Override
@@ -84,9 +62,4 @@ public class InfoBars extends Appliance {
protected @NotNull List<Listener> listeners() { protected @NotNull List<Listener> listeners() {
return List.of(new ShowPreviousBarsListener()); return List.of(new ShowPreviousBarsListener());
} }
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(new InfoBarCommand());
}
} }

View File

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

View File

@@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.core.util.statistics.ServerMonitor; import eu.mhsl.craftattack.spawn.core.util.statistics.ServerMonitor;
import eu.mhsl.craftattack.core.util.text.ColorUtil; import eu.mhsl.craftattack.spawn.core.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.bossbar.BossBar;
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;
@@ -10,6 +10,8 @@ import net.kyori.adventure.text.format.NamedTextColor;
import java.time.Duration; import java.time.Duration;
public class MsptBar extends Bar { public class MsptBar extends Bar {
public static String name = "msptd";
@Override @Override
protected Duration refresh() { protected Duration refresh() {
return Duration.ofSeconds(3); return Duration.ofSeconds(3);
@@ -17,7 +19,7 @@ public class MsptBar extends Bar {
@Override @Override
protected String name() { protected String name() {
return "mspt"; return name;
} }
@Override @Override
@@ -25,10 +27,10 @@ public class MsptBar extends Bar {
return Component.text() return Component.text()
.append(Component.text("M")) .append(Component.text("M"))
.append(Component.text("illi", NamedTextColor.GRAY)) .append(Component.text("illi", NamedTextColor.GRAY))
.append(Component.text("S")) .append(Component.text("s"))
.append(Component.text("econds ", NamedTextColor.GRAY)) .append(Component.text("ekunden ", NamedTextColor.GRAY))
.append(Component.text("P")) .append(Component.text("p"))
.append(Component.text("er ", NamedTextColor.GRAY)) .append(Component.text("ro ", NamedTextColor.GRAY))
.append(Component.text("T")) .append(Component.text("T"))
.append(Component.text("ick", NamedTextColor.GRAY)) .append(Component.text("ick", NamedTextColor.GRAY))
.append(Component.text(": ")) .append(Component.text(": "))
@@ -43,7 +45,7 @@ public class MsptBar extends Bar {
@Override @Override
protected BossBar.Color color() { protected BossBar.Color color() {
return BossBar.Color.BLUE; return this.currentMSPT() <= 50 ? BossBar.Color.GREEN : BossBar.Color.RED;
} }
@Override @Override

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit.PlayerLimit; import eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit.PlayerLimit;
import eu.mhsl.craftattack.core.util.text.ColorUtil; import eu.mhsl.craftattack.spawn.core.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
@@ -12,6 +12,8 @@ import org.bukkit.Bukkit;
import java.time.Duration; import java.time.Duration;
public class PlayerCounterBar extends Bar { public class PlayerCounterBar extends Bar {
public static String name = "playerCounter";
@Override @Override
protected Duration refresh() { protected Duration refresh() {
return Duration.ofSeconds(3); return Duration.ofSeconds(3);
@@ -19,7 +21,7 @@ public class PlayerCounterBar extends Bar {
@Override @Override
protected String name() { protected String name() {
return "playerCounter"; return name;
} }
@Override @Override
@@ -38,7 +40,10 @@ public class PlayerCounterBar extends Bar {
@Override @Override
protected BossBar.Color color() { protected BossBar.Color color() {
return BossBar.Color.BLUE; int freeSlots = this.getMaxPlayerCount() - this.getCurrentPlayerCount();
return freeSlots <= 0
? BossBar.Color.RED
: freeSlots < 5 ? BossBar.Color.YELLOW : BossBar.Color.GREEN;
} }
@Override @Override

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.Bar;
import eu.mhsl.craftattack.core.util.text.ColorUtil; import eu.mhsl.craftattack.spawn.core.util.text.ColorUtil;
import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.bossbar.BossBar;
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;
@@ -10,6 +10,8 @@ import org.bukkit.Bukkit;
import java.time.Duration; import java.time.Duration;
public class TpsBar extends Bar { public class TpsBar extends Bar {
public static String name = "tps";
@Override @Override
protected Duration refresh() { protected Duration refresh() {
return Duration.ofSeconds(3); return Duration.ofSeconds(3);
@@ -17,7 +19,7 @@ public class TpsBar extends Bar {
@Override @Override
protected String name() { protected String name() {
return "tps"; return name;
} }
@Override @Override
@@ -25,10 +27,10 @@ public class TpsBar extends Bar {
return Component.text() return Component.text()
.append(Component.text("T")) .append(Component.text("T"))
.append(Component.text("icks ", NamedTextColor.GRAY)) .append(Component.text("icks ", NamedTextColor.GRAY))
.append(Component.text("P")) .append(Component.text("p"))
.append(Component.text("er ", NamedTextColor.GRAY)) .append(Component.text("ro ", NamedTextColor.GRAY))
.append(Component.text("S")) .append(Component.text("S"))
.append(Component.text("econds", NamedTextColor.GRAY)) .append(Component.text("ekunde", NamedTextColor.GRAY))
.append(Component.text(": ")) .append(Component.text(": "))
.append(Component.text(String.format("%.2f", this.currentTps()), ColorUtil.tpsColor(this.currentTps()))) .append(Component.text(String.format("%.2f", this.currentTps()), ColorUtil.tpsColor(this.currentTps())))
.build(); .build();
@@ -41,7 +43,9 @@ public class TpsBar extends Bar {
@Override @Override
protected BossBar.Color color() { protected BossBar.Color color() {
return BossBar.Color.BLUE; return this.currentTps() >= 18
? BossBar.Color.GREEN
: this.currentTps() >= 15 ? BossBar.Color.YELLOW : BossBar.Color.RED;
} }
@Override @Override

View File

@@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateMessageCommand; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateMessageCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateReplyCommand; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateReplyCommand;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@@ -1,10 +1,12 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.common.api.repositories.ReportRepository;
import eu.mhsl.craftattack.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.api.client.repositories.ReportRepository; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.common.api.repositories.CraftAttackReportRepository;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.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;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
@@ -18,7 +20,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function;
public class Report extends Appliance { public class Report extends Appliance {
public static Component helpText() { public static Component helpText() {
@@ -36,7 +40,7 @@ public class Report extends Appliance {
} }
public void reportToUnknown(@NotNull Player issuer) { public void reportToUnknown(@NotNull Player issuer) {
ReportRepository.ReportCreationInfo request = new ReportRepository.ReportCreationInfo(issuer.getUniqueId(), null, ""); CraftAttackReportRepository.ReportCreationInfo request = new CraftAttackReportRepository.ReportCreationInfo(issuer.getUniqueId(), null, "");
Bukkit.getScheduler().runTaskAsynchronously( Bukkit.getScheduler().runTaskAsynchronously(
Main.instance(), Main.instance(),
() -> this.createReport(issuer, request) () -> this.createReport(issuer, request)
@@ -62,21 +66,22 @@ public class Report extends Appliance {
} }
private void createReport(Player issuer, ReportRepository.ReportCreationInfo reportRequest) { private void createReport(Player issuer, ReportRepository.ReportCreationInfo reportRequest) {
ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(ReportRepository.class) ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(CraftAttackReportRepository.class) // TODO: Besser machen!!
.createReport(reportRequest); .createReport(reportRequest);
switch(createdReport.status()) { switch(createdReport.status()) {
case 200: // varo-endpoint specific
case 201: case 201:
issuer.sendMessage( issuer.sendMessage(
Component.text() Component.text()
.append(Component.text("\\/".repeat(20), NamedTextColor.DARK_GRAY)) .append(Component.text("\\/".repeat(20), NamedTextColor.DARK_GRAY))
.appendNewline() .appendNewline()
.append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden!", NamedTextColor.GOLD)) .append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden:", NamedTextColor.GOLD))
.appendNewline() .appendNewline()
.appendNewline() .appendNewline()
.append( .append(
Component Component
.text(createdReport.data().url(), NamedTextColor.GRAY) // URL mit Weltkugel-Emoji .text(createdReport.data().url(), NamedTextColor.GRAY)
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url())) .clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url()))
) )
.appendNewline() .appendNewline()
@@ -112,7 +117,7 @@ public class Report extends Appliance {
} }
public void queryReports(Player issuer) { public void queryReports(Player issuer) {
ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(ReportRepository.class) ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(CraftAttackReportRepository.class) // TODO: Besser machen!!
.queryReports(issuer.getUniqueId()); .queryReports(issuer.getUniqueId());
if(userReports.status() != 200) { if(userReports.status() != 200) {
@@ -126,43 +131,50 @@ public class Report extends Appliance {
return; return;
} }
List<ReportRepository.PlayerReports.Report> reports = userReports Function<List<ReportRepository.PlayerReports.Report>, List<ReportRepository.PlayerReports.Report>> filterClosed = reports -> reports.stream()
.data() .filter(report -> Objects.equals(report.status(), ReportRepository.PlayerReports.Report.Status.closed))
.from_self() .toList();
.stream()
.filter(report -> !report.draft())
.toList()
.reversed();
if(reports.isEmpty()) { List<ReportRepository.PlayerReports.Report> reportsToOthers = filterClosed.apply(userReports.data().from_self()).reversed();
issuer.sendMessage( List<ReportRepository.PlayerReports.Report> reportsToSelf = filterClosed.apply(userReports.data().to_self()).reversed();
Component.text()
.append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY))
);
return;
}
ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text() ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text()
.append(Component.newline()) .append(Component.text(
.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD)) !reportsToSelf.isEmpty()
.appendNewline(); ? "Du wurdest insgesamt %d mal von anderen Spielern gemeldet.".formatted(reportsToSelf.size())
: "Du wurdest von keinem anderen Spieler gemeldet.",
NamedTextColor.GOLD)
);
reports.forEach(report -> { component.appendNewline();
component
.append(Component.text(" - ", NamedTextColor.WHITE)) component.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD));
.append( reportsToOthers.forEach(report -> {
report.reported() != null Component button = Component.text("[\uD83D\uDC41/\uD83D\uDD8A]")
? Component.text(report.reported().username(), NamedTextColor.WHITE)
: Component.text("Unbekannt", NamedTextColor.YELLOW)
)
.append(Component.text(String.format(": %s", report.subject()), NamedTextColor.GRAY))
.clickEvent(ClickEvent.openUrl(report.url())) .clickEvent(ClickEvent.openUrl(report.url()))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD))); .hoverEvent(HoverEvent.showText(ComponentUtil.clickLink(report.url())));
component.appendNewline();
Component reportedDisplayName = report.reported() != null
? Component.text(Optional.ofNullable(Bukkit.getOfflinePlayer(report.reported()).getName()).orElse(report.reported().toString()), NamedTextColor.WHITE)
: Component.text("Unbekannt", NamedTextColor.YELLOW);
component
.appendNewline()
.append(Component.text(" \u27A1 ", NamedTextColor.GRAY))
.append(button)
.append(Component.text(" du gegen ", NamedTextColor.GRAY))
.append(reportedDisplayName)
.append(Component.text(String.format(": %s", report.reason()), NamedTextColor.GRAY));
}); });
if(reportsToOthers.isEmpty()) {
component
.appendNewline()
.append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY));
}
issuer.sendMessage(component.build()); issuer.sendMessage(component.build());
} }

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;

View File

@@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@@ -1,11 +1,12 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.Setting; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.Setting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.OpenSettingsShortcutListener; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.OpenSettingsShortcutListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.SettingsInventoryListener; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.SettingsInventoryListener;
import eu.mhsl.craftattack.spawn.core.util.world.InteractSounds;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -16,6 +17,7 @@ import org.jetbrains.annotations.NotNull;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Settings extends Appliance { public class Settings extends Appliance {
@@ -33,6 +35,11 @@ public class Settings extends Appliance {
ChatMentions, ChatMentions,
DoubleDoors, DoubleDoors,
KnockDoors, KnockDoors,
BorderWarning,
LocatorBar,
InfoBars,
CoordinateDisplay,
Bloodmoon
} }
public static Settings instance() { public static Settings instance() {
@@ -56,6 +63,16 @@ public class Settings extends Appliance {
private final WeakHashMap<Player, OpenSettingsInventory> openSettingsInventories = new WeakHashMap<>(); private final WeakHashMap<Player, OpenSettingsInventory> openSettingsInventories = new WeakHashMap<>();
private final WeakHashMap<Player, List<Setting<?>>> settingsCache = new WeakHashMap<>(); private final WeakHashMap<Player, List<Setting<?>>> settingsCache = new WeakHashMap<>();
protected final Map<Class<? extends Setting<?>>, Consumer<Player>> changeListeners = new WeakHashMap<>();
public <TDataType extends Setting<?>> void addChangeListener(Class<TDataType> setting, Consumer<Player> listener) {
this.changeListeners.merge(setting, listener, Consumer::andThen);
}
public <TDataType extends Setting<?>> void invokeChangeListener(Player player, Class<TDataType> setting) {
Optional.ofNullable(this.changeListeners.get(setting))
.ifPresent(listener -> listener.accept(player));
}
private List<Setting<?>> getSettings(Player player) { private List<Setting<?>> getSettings(Player player) {
if(this.settingsCache.containsKey(player)) return this.settingsCache.get(player); if(this.settingsCache.containsKey(player)) return this.settingsCache.get(player);
@@ -135,6 +152,7 @@ public class Settings extends Appliance {
} }
player.openInventory(inventory); player.openInventory(inventory);
InteractSounds.of(player).open();
this.openSettingsInventories.put(player, new OpenSettingsInventory(inventory, settings)); this.openSettingsInventories.put(player, new OpenSettingsInventory(inventory, settings));
} }
@@ -142,9 +160,9 @@ public class Settings extends Appliance {
int countOfUncategorized = (int) settings.stream() int countOfUncategorized = (int) settings.stream()
.filter(setting -> !(setting instanceof CategorizedSetting)) .filter(setting -> !(setting instanceof CategorizedSetting))
.count(); .count();
int rowsOfUncategorized = (int) Math.ceil((double) countOfUncategorized / 9); int invSizeForUncategorized = (int) Math.ceil((double) countOfUncategorized / 9) * 9;
int rowsOfCategorized = Arrays.stream(SettingCategory.values()) int invSizeForCategorized = Arrays.stream(SettingCategory.values())
.map(settingCategory -> settings.stream() .map(settingCategory -> settings.stream()
.filter(setting -> setting instanceof CategorizedSetting) .filter(setting -> setting instanceof CategorizedSetting)
.map(setting -> (CategorizedSetting) setting) .map(setting -> (CategorizedSetting) setting)
@@ -154,17 +172,18 @@ public class Settings extends Appliance {
.reduce(Integer::sum) .reduce(Integer::sum)
.orElse(1) * 9; .orElse(1) * 9;
int rows = rowsOfUncategorized + rowsOfCategorized; int invSize = invSizeForUncategorized + invSizeForCategorized;
if(rows % 9 != 0) throw new IllegalStateException( if(invSize % 9 != 0) throw new IllegalStateException(
String.format("Failed to calculate settings inventory size. %d is not an multiple of 9", rows) String.format("Failed to calculate settings inventory size. %d is not an multiple of 9", invSize)
); );
return rows; return invSize;
} }
public void onSettingsClose(Player player) { public void onSettingsClose(Player player) {
if(!this.openSettingsInventories.containsKey(player)) return; if(!this.openSettingsInventories.containsKey(player)) return;
this.openSettingsInventories.remove(player); this.openSettingsInventories.remove(player);
player.updateInventory(); player.updateInventory();
InteractSounds.of(player).close();
} }
public boolean hasSettingsNotOpen(Player player) { public boolean hasSettingsNotOpen(Player player) {

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -25,7 +25,7 @@ public class SettingsShortcutSetting extends BoolSetting implements CategorizedS
@Override @Override
protected Boolean defaultValue() { protected Boolean defaultValue() {
return false; return true;
} }
@Override @Override

View File

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

View File

@@ -1,8 +1,9 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.util.world.InteractSounds;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@@ -17,7 +18,7 @@ import org.bukkit.persistence.PersistentDataContainer;
import java.util.List; import java.util.List;
public abstract class Setting<TDataType> { public abstract class Setting<TDataType> {
TDataType state; protected TDataType state;
private final Settings.Key key; private final Settings.Key key;
public Setting(Settings.Key key) { public Setting(Settings.Key key) {
@@ -25,7 +26,7 @@ public abstract class Setting<TDataType> {
} }
public NamespacedKey getNamespacedKey() { public NamespacedKey getNamespacedKey() {
return new NamespacedKey(Main.instance(), this.key.name()); return new NamespacedKey(Settings.class.getSimpleName().toLowerCase(), this.key.name().toLowerCase());
} }
public Settings.Key getKey() { public Settings.Key getKey() {
@@ -33,12 +34,32 @@ public abstract class Setting<TDataType> {
} }
public void initializeFromPlayer(Player p) { public void initializeFromPlayer(Player p) {
this.fromStorage(p.getPersistentDataContainer()); PersistentDataContainer dataContainer = p.getPersistentDataContainer();
try {
this.fromStorage(dataContainer);
} catch(IllegalArgumentException e) {
Main.logger().warning(String.format(
"Could not load state of setting %s from player %s: '%s'\n Did the datatype of the setting change?",
this.getNamespacedKey(),
e.getMessage(),
p.getName()
));
dataContainer.remove(this.getNamespacedKey());
this.fromStorage(dataContainer);
Main.logger().info(String.format(
"Restoring defaults of setting %s of player %s",
this.getNamespacedKey(),
p.getName()
));
}
} }
public void triggerChange(Player p, ClickType clickType) { public void triggerChange(Player p, ClickType clickType) {
if(clickType.equals(ClickType.DOUBLE_CLICK)) return;
this.change(p, clickType); this.change(p, clickType);
InteractSounds.of(p).click();
this.toStorage(p.getPersistentDataContainer(), this.state()); this.toStorage(p.getPersistentDataContainer(), this.state());
Settings.instance().invokeChangeListener(p, this.getClass());
} }
public ItemStack buildItem() { public ItemStack buildItem() {

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerSwapHandItemsEvent; import org.bukkit.event.player.PlayerSwapHandItemsEvent;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;

View File

@@ -1,13 +1,13 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.tablist; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.tablist;
import eu.mhsl.craftattack.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report.Report; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.report.Report;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.core.util.IteratorUtil; import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import eu.mhsl.craftattack.core.util.statistics.NetworkMonitor; import eu.mhsl.craftattack.spawn.core.util.statistics.NetworkMonitor;
import eu.mhsl.craftattack.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.core.util.text.RainbowComponent; import eu.mhsl.craftattack.spawn.core.util.text.RainbowComponent;
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.util.Ticks; import net.kyori.adventure.util.Ticks;
@@ -23,7 +23,8 @@ import java.util.List;
public class Tablist extends Appliance { public class Tablist extends Appliance {
private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 7 ", 7, 3); private final String projectTitle = this.localConfig().getString("projectTitle", "Title not configured");
private final RainbowComponent serverName = new RainbowComponent(String.format(" %s ", this.projectTitle), 7, 3);
private NetworkMonitor networkMonitor; private NetworkMonitor networkMonitor;
private OperatingSystemMXBean systemMonitor; private OperatingSystemMXBean systemMonitor;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.tablist; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.tablist;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;

View File

@@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.tablist; package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.tablist;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory; import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;

View File

@@ -0,0 +1,62 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiAutoTotem;
import eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform.AcInform;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
public class AntiAutoTotem extends Appliance {
public void checkTotemUse(Player player) {
PlayerInventory playerInv = player.getInventory();
Supplier<List<Material>> getHeldItems = () -> List.of(
playerInv.getItemInMainHand().getType(),
playerInv.getItemInOffHand().getType()
);
Function<Player, Boolean> isCurrentlyHoldingTotem = (p) -> getHeldItems.get().contains(Material.TOTEM_OF_UNDYING);
if(!isCurrentlyHoldingTotem.apply(player)) return;
if(getHeldItems.get().stream().allMatch(material -> material.equals(Material.TOTEM_OF_UNDYING))) return;
AtomicInteger tickCounter = new AtomicInteger();
Bukkit.getScheduler().runTaskTimer(
Main.instance(),
(task) -> {
if(tickCounter.incrementAndGet() > 10) {
task.cancel();
return;
}
if(isCurrentlyHoldingTotem.apply(player)) {
task.cancel();
Main.instance().getAppliance(AcInform.class).notifyAdmins(
"internal",
player.getName(),
"antiAutoTotem",
(float) tickCounter.get()
);
}
},
1,
1
);
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(
new OnTotemUseListener()
);
}
}

View File

@@ -0,0 +1,15 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiAutoTotem;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityResurrectEvent;
class OnTotemUseListener extends ApplianceListener<AntiAutoTotem> {
@EventHandler
public void onTotem(EntityResurrectEvent event) {
if(event.isCancelled()) return;
if(!(event.getEntity() instanceof Player player)) return;
this.getAppliance().checkTotemUse(player);
}
}

View File

@@ -0,0 +1,51 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiBoatFreecam;
import eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform.AcInform;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import org.bukkit.Bukkit;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("unused")
public class AntiBoatFreecam extends Appliance {
private static final float MAX_YAW_OFFSET = 106.0f;
private final Map<Player, Float> violatedPlayers = new HashMap<>();
public AntiBoatFreecam() {
Bukkit.getScheduler().runTaskTimerAsynchronously(
Main.instance(),
() -> Bukkit.getOnlinePlayers().forEach(player -> {
if(!(player.getVehicle() instanceof Boat boat)) return;
if(!boat.getPassengers().getFirst().equals(player)) return;
float playerYaw = player.getYaw();
float boatYaw = boat.getYaw();
float yawDelta = wrapDegrees(playerYaw - boatYaw);
if(Math.abs(yawDelta) <= MAX_YAW_OFFSET) return;
this.violatedPlayers.merge(player, 1f, Float::sum);
float violationCount = this.violatedPlayers.get(player);
if(violationCount != 1 && violationCount % 100 != 0) return;
Main.instance().getAppliance(AcInform.class).notifyAdmins(
"internal",
player.getName(),
"illegalBoatLookYaw",
violationCount
);
}),
1L,
1L
);
}
private static float wrapDegrees(float deg) {
deg = deg % 360f;
if (deg >= 180f) deg -= 360f;
if (deg < -180f) deg += 360f;
return deg;
}
}

View File

@@ -0,0 +1,66 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiFormattedBook;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import net.kyori.adventure.text.*;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.event.Listener;
import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
public class AntiFormattedBook extends Appliance {
private static final char SECTION = '\u00A7';
public boolean containsFormatting(BookMeta meta) {
if (this.hasFormattingDeep(meta.title())) return true;
if (this.hasFormattingDeep(meta.author())) return true;
for (Component c : meta.pages()) {
if (this.hasFormattingDeep(c)) return true;
}
return false;
}
private boolean hasFormattingDeep(@Nullable Component component) {
if(component == null) return false;
if (this.hastFormatting(component)) return true;
if (component instanceof TextComponent tc && tc.content().indexOf(SECTION) >= 0) return true;
if (component instanceof NBTComponent<?, ?> nbt) {
if (nbt.separator() != null && this.hasFormattingDeep(nbt.separator())) return true;
}
for (Component child : component.children()) {
if (this.hasFormattingDeep(child)) return true;
}
return false;
}
private boolean hastFormatting(Component component) {
Style style = component.style();
TextColor color = style.color();
if (color != null) return true;
if (style.font() != null) return true;
if (style.insertion() != null && !Objects.requireNonNull(style.insertion()).isEmpty()) return true;
for (var decoration : style.decorations().entrySet()) {
if (decoration.getValue() == TextDecoration.State.TRUE) return true;
}
return style.hoverEvent() != null || style.clickEvent() != null;
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(
new BookEditListener()
);
}
}

View File

@@ -0,0 +1,36 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiFormattedBook;
import eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform.AcInform;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerEditBookEvent;
import org.bukkit.inventory.meta.BookMeta;
import java.util.List;
class BookEditListener extends ApplianceListener<AntiFormattedBook> {
@EventHandler
public void onBookEdit(PlayerEditBookEvent event) {
Player player = event.getPlayer();
BookMeta meta = event.getNewBookMeta();
if (this.getAppliance().containsFormatting(meta)) {
Main.instance().getAppliance(AcInform.class).notifyAdmins(
"internal",
player.getName(),
"illegalBookFormatting",
1f
);
BookMeta sanitized = meta.clone();
sanitized.title(null);
sanitized.author(null);
//noinspection ResultOfMethodCallIgnored
sanitized.pages(List.of(Component.empty()));
event.setNewBookMeta(sanitized);
}
}
}

View File

@@ -0,0 +1,78 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiIllegalBundlePicker;
import eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform.AcInform;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BundleMeta;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.stream.Collectors;
public class AntiIllegalBundlePicker extends Appliance {
private static final int visibleSlotsInBundle = 9;
public void trackBundle(InventoryClickEvent event) {
ItemStack bundle = Objects.requireNonNull(event.getCurrentItem());
final int rawSlot = event.getRawSlot();
final Player player = (Player) event.getWhoClicked();
final InventoryView view = event.getView();
final List<ItemStack> before = this.getBundleContents(bundle);
Bukkit.getScheduler().runTask(Main.instance(), () -> {
ItemStack afterStack = view.getItem(rawSlot);
if(afterStack == null || afterStack.getType() != Material.BUNDLE) return;
List<ItemStack> after = this.getBundleContents(afterStack);
int removedSlotIndex = this.findRemoved(before, after);
if(removedSlotIndex >= visibleSlotsInBundle) {
Main.instance().getAppliance(AcInform.class).notifyAdmins(
"internal",
player.getName(),
"illegalBundlePick",
(float) removedSlotIndex
);
}
});
}
private int findRemoved(@NotNull List<ItemStack> before, @NotNull List<ItemStack> after) {
for (int i = 0; i < Math.max(before.size(), after.size()); i++) {
ItemStack a = i < after.size() ? after.get(i) : null;
ItemStack b = i < before.size() ? before.get(i) : null;
if (b == null && a == null) continue;
if (b == null) throw new IllegalStateException("Size of bundle was smaller before pickup Action");
if (a == null) return i;
if (!a.isSimilar(b)) return i;
if (a.getAmount() != b.getAmount()) return i;
}
throw new IllegalStateException("Failed to find picked Item in bundle");
}
private List<ItemStack> getBundleContents(@NotNull ItemStack bundle) {
if (bundle.getType() != Material.BUNDLE)
throw new IllegalStateException("ItemStack is not a bundle");
BundleMeta meta = (BundleMeta) bundle.getItemMeta();
return meta.getItems().stream()
.map(ItemStack::clone)
.collect(Collectors.toCollection(ArrayList::new));
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(
new OnBundlePickListener()
);
}
}

View File

@@ -0,0 +1,18 @@
package eu.mhsl.craftattack.spawn.common.appliances.security.antiIllegalBundlePicker;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
class OnBundlePickListener extends ApplianceListener<AntiIllegalBundlePicker> {
@EventHandler
public void onBundlePick(InventoryClickEvent event) {
if(!event.getAction().equals(InventoryAction.PICKUP_FROM_BUNDLE)) return;
final ItemStack bundle = event.getCurrentItem();
if (bundle == null || bundle.getType() != Material.BUNDLE) return;
this.getAppliance().trackBundle(event);
}
}

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform; package eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform; package eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat; package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat; package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute; package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute; package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.core.util.text.DataSizeConverter; import eu.mhsl.craftattack.spawn.core.util.text.DataSizeConverter;
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;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute; package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@@ -0,0 +1,177 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.deviceFingerprinting;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import net.kyori.adventure.resource.ResourcePackInfo;
import net.kyori.adventure.resource.ResourcePackRequest;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
import org.jetbrains.annotations.NotNull;
import spark.Response;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.*;
@Appliance.Flags(enabled = false)
public class DeviceFingerprinting extends Appliance {
public record PackInfo(@NotNull String url, @NotNull UUID uuid, @NotNull String hash) {
private static final String failingUrl = "http://127.0.0.1:0";
public PackInfo asFailing() {
return new PackInfo(failingUrl, this.uuid, this.hash);
}
}
public enum PackStatus {
UNCACHED,
CACHED,
INVALID;
public static PackStatus fromBukkitStatus(PlayerResourcePackStatusEvent.Status status) {
return switch(status) {
case DISCARDED -> CACHED;
case FAILED_DOWNLOAD -> UNCACHED;
default -> INVALID;
};
}
}
public enum PlayerStatus {
PREPARATION,
TESTING,
FINISHED,
NEW
}
private List<PackInfo> packs;
private final Map<Player, FingerprintData> fingerprints = new WeakHashMap<>();
private final UUID basePackId = UUID.randomUUID();
@Override
public void onEnable() {
this.packs = this.readPacksFromConfig();
}
public void startFingerprinting(Player player) {
this.fingerprints.put(player, FingerprintData.create(player));
Main.logger().info(String.format("Sending base ressource-pack with id '%s' to '%s'%n", this.basePackId, player.getName()));
this.sendPack(player, new PackInfo("http://localhost:8080/api/devicefingerprinting/base.zip", this.basePackId, "3296e8bdd30b4f7cffd11c780a1dc70da2948e71"));
}
public void onPackUpdate(Player player, UUID packId, PlayerResourcePackStatusEvent.Status status) {
if(!this.fingerprints.containsKey(player)) return;
FingerprintData playerFingerprint = this.fingerprints.get(player);
if(!playerFingerprint.isInTestingOrPreparation()) return;
if(packId.equals(this.basePackId)) {
Main.logger().info(String.format("Base pack for '%s' updated: '%s'", player.getName(), status));
if(status != PlayerResourcePackStatusEvent.Status.ACCEPTED) return;
Main.logger().info(String.format("Base pack loaded successfully, sending now all packs to '%s'...", player.getName()));
playerFingerprint.setTesting();
this.packs.forEach(pack -> this.sendPack(player, pack.asFailing()));
return;
}
PackInfo pack = this.packs.stream()
.filter(packInfo -> Objects.equals(packInfo.uuid, packId))
.findAny()
.orElse(null);
if(pack == null) return;
int packIndex = this.packs.indexOf(pack);
List<PackStatus> pendingPacks = playerFingerprint.getPendingPacks();
PackStatus newPackStatus = PackStatus.fromBukkitStatus(status);
if(newPackStatus == PackStatus.INVALID) return;
pendingPacks.set(packIndex, newPackStatus);
playerFingerprint.updateFingerprint();
if(Objects.requireNonNull(playerFingerprint.getStatus()) == PlayerStatus.NEW) {
Main.logger().info(String.format("Sending fingerprint packs to Player '%s', as it is a unseen Player!", player.getName()));
this.sendNewFingerprint(player, Objects.requireNonNull(playerFingerprint.getFingerPrint()));
}
}
private void sendNewFingerprint(Player player, long fingerprintId) {
for (int i = 0; i < this.packs.size(); i++) {
if ((fingerprintId & (1L << i)) != 0) {
PackInfo pack = this.packs.get(i);
this.sendPack(player, pack);
}
}
}
public void sendPack(Player player, PackInfo pack) {
player.sendResourcePacks(
ResourcePackRequest.resourcePackRequest()
.required(true)
.packs(ResourcePackInfo.resourcePackInfo(pack.uuid, URI.create(pack.url), pack.hash))
);
}
private List<DeviceFingerprinting.PackInfo> readPacksFromConfig() {
try (InputStreamReader reader = new InputStreamReader(Objects.requireNonNull(Main.class.getResourceAsStream("/deviceFingerprinting/packs.json")))) {
Type packListType = new TypeToken<List<DeviceFingerprinting.PackInfo>>(){}.getType();
List<DeviceFingerprinting.PackInfo> packs = new Gson().fromJson(reader, packListType);
if (packs.isEmpty()) throw new IllegalStateException("No resource packs found in packs.json.");
return packs;
} catch (IOException e) {
throw new IllegalStateException("Failed to parse packs.json.", e);
}
}
@Override
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
apiBuilder.rawGet(
"base.zip",
(request, response) -> this.servePack("base.zip", response)
);
for(int i = 0; i < this.packs.size(); i++) {
int packIndex = i;
apiBuilder.rawGet(
String.format("packs/%d", i),
(request, response) -> this.servePack(String.valueOf(packIndex), response)
);
}
}
private Object servePack(String name, Response response) {
try {
String resourcePath = String.format("/deviceFingerprinting/packs/%s", name);
var inputStream = Main.class.getResourceAsStream(resourcePath);
if (inputStream == null) {
throw new IllegalStateException("Pack file not found: " + resourcePath);
}
response.header("Content-Type", "application/zip");
response.header("Content-Disposition", String.format("attachment; filename=\"pack-%s.zip\"", name));
var outputStream = response.raw().getOutputStream();
inputStream.transferTo(outputStream);
outputStream.close();
return HttpServer.nothing;
} catch (IOException e) {
throw new RuntimeException(String.format("Failed to serve pack '%s'", name), e);
}
}
public List<PackInfo> getPacks() {
return this.packs;
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(
new PlayerJoinListener()
);
}
}

View File

@@ -0,0 +1,91 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.deviceFingerprinting;
import eu.mhsl.craftattack.spawn.core.Main;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
class FingerprintData {
public final Player player;
private DeviceFingerprinting.PlayerStatus status;
private @Nullable Long fingerPrint;
private final List<DeviceFingerprinting.PackStatus> pendingPacks;
int packCount = Main.instance().getAppliance(DeviceFingerprinting.class).getPacks().size();
private FingerprintData(Player player) {
this.player = player;
this.status = DeviceFingerprinting.PlayerStatus.PREPARATION;
this.fingerPrint = null;
this.pendingPacks = Arrays.asList(new DeviceFingerprinting.PackStatus[this.packCount]);
}
public static FingerprintData create(Player player) {
return new FingerprintData(player);
}
public void setTesting() {
this.status = DeviceFingerprinting.PlayerStatus.TESTING;
}
public void updateFingerprint() {
long fingerPrint = 0;
for (int i = 0; i < this.pendingPacks.size(); i++) {
var status = this.pendingPacks.get(i);
if(status == null) return;
switch (status) {
case CACHED:
fingerPrint |= 1L << i;
break;
case UNCACHED:
break;
default:
return;
}
}
if(fingerPrint == 0) {
this.status = DeviceFingerprinting.PlayerStatus.NEW;
this.fingerPrint = this.createNewFingerprint();
Main.logger().info(String.format("Player %s's was marked as a new Player!", this.player.getName()));
} else {
this.status = DeviceFingerprinting.PlayerStatus.FINISHED;
this.fingerPrint = fingerPrint;
}
Main.logger().info(String.format("Player %s's fingerprint is '%s'", this.player.getName(), fingerPrint));
}
private long createNewFingerprint() {
long id = 0;
Random random = new Random();
for (int i = 0; i < this.packCount / 2; i++) {
while (true) {
int bitIndex = random.nextInt(this.packCount);
if ((id & (1L << bitIndex)) == 0) {
id |= 1L << bitIndex;
break;
}
}
}
return id;
}
public List<DeviceFingerprinting.PackStatus> getPendingPacks() {
return this.pendingPacks;
}
public DeviceFingerprinting.PlayerStatus getStatus() {
return this.status;
}
public @Nullable Long getFingerPrint() {
return this.fingerPrint;
}
public boolean isInTestingOrPreparation() {
return this.status == DeviceFingerprinting.PlayerStatus.TESTING || this.status == DeviceFingerprinting.PlayerStatus.PREPARATION;
}
}

View File

@@ -0,0 +1,22 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.deviceFingerprinting;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
class PlayerJoinListener extends ApplianceListener<DeviceFingerprinting> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
this.getAppliance().startFingerprinting(event.getPlayer());
}
@EventHandler
public void onResourcePackEvent(PlayerResourcePackStatusEvent event) {
this.getAppliance().onPackUpdate(
event.getPlayer(),
event.getID(),
event.getStatus()
);
}
}

View File

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

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.endPrevent; package eu.mhsl.craftattack.spawn.common.appliances.tooling.endPrevent;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;
@@ -12,7 +12,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
class EndPreventCommand extends ApplianceCommand<EndPrevent> { class EndPreventCommand extends ApplianceCommand<EndPrevent> {
private final Map<String, Boolean> arguments = Map.of("preventEnd", true, "allowEnd", false); private final Map<String, EndPrevent.State> arguments = Map.of(
"preventEnd", EndPrevent.State.CLOSED,
"allowEnd", EndPrevent.State.OPEN,
"onlyInnerEnd", EndPrevent.State.NO_OUTER
);
public EndPreventCommand() { public EndPreventCommand() {
super("endPrevent"); super("endPrevent");
@@ -21,11 +25,11 @@ class EndPreventCommand extends ApplianceCommand<EndPrevent> {
@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 {
if(args.length == 1 && this.arguments.containsKey(args[0])) { if(args.length == 1 && this.arguments.containsKey(args[0])) {
this.getAppliance().setEndDisabled(this.arguments.get(args[0])); this.getAppliance().setEndState(this.arguments.get(args[0]));
sender.sendMessage(Component.text("Setting updated!", NamedTextColor.GREEN)); sender.sendMessage(Component.text("Setting updated!", NamedTextColor.GREEN));
} }
sender.sendMessage(Component.text( sender.sendMessage(Component.text(
String.format("The End is %s!", this.getAppliance().isEndDisabled() ? "closed" : "open"), String.format("The End is now on '%s'!", this.getAppliance().getEndPreventState().name()),
NamedTextColor.GOLD NamedTextColor.GOLD
)); ));
} }

View File

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

View File

@@ -1,23 +0,0 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.endPrevent;
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEvent;
class PreventEnderEyeUseListener extends ApplianceListener<EndPrevent> {
@EventHandler
public void onEnderEyeInteraction(PlayerInteractEvent event) {
if(event.getClickedBlock() == null) return;
if(!event.getClickedBlock().getType().equals(Material.END_PORTAL_FRAME)) return;
if(event.getItem() == null) return;
if(!event.getItem().getType().equals(Material.ENDER_EYE)) return;
if(!this.getAppliance().isEndDisabled()) return;
event.setCancelled(true);
event.getPlayer().sendActionBar(Component.text("Das End ist noch nicht freigeschaltet!", NamedTextColor.RED));
}
}

View File

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

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.kick; package eu.mhsl.craftattack.spawn.common.appliances.tooling.kick;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -19,6 +19,7 @@ class KickCommand extends ApplianceCommand<Kick> {
@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 {
if(args.length < 1) throw new Error("Es muss ein Spielername angegeben werden!");
this.getAppliance().kick( this.getAppliance().kick(
args[0], args[0],
Arrays.stream(args).skip(1).collect(Collectors.joining(" ")) Arrays.stream(args).skip(1).collect(Collectors.joining(" "))

View File

@@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance; package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.config.Configuration; import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance; package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance; package eu.mhsl.craftattack.spawn.common.appliances.tooling.maintenance;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.core.util.text.DisconnectInfo; import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;

View File

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

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan; package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan; package eu.mhsl.craftattack.spawn.common.appliances.tooling.panicBan;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.AsyncPlayerPreLoginEvent;

View File

@@ -1,8 +1,8 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit; package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.config.Configuration; import eu.mhsl.craftattack.spawn.core.config.Configuration;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit; package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit;
import eu.mhsl.craftattack.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.core.util.text.DisconnectInfo; import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
@@ -9,16 +9,16 @@ import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
class PlayerLimiterListener extends ApplianceListener<PlayerLimit> { class PlayerLimiterListener extends ApplianceListener<PlayerLimit> {
@EventHandler @EventHandler
public void onLogin(AsyncPlayerPreLoginEvent playerPreLoginEvent) { public void onLogin(AsyncPlayerPreLoginEvent playerPreLoginEvent) {
playerPreLoginEvent.kickMessage( if(Bukkit.getOnlinePlayers().size() >= this.getAppliance().getLimit()) {
new DisconnectInfo( playerPreLoginEvent.kickMessage(
"Hohe Serverauslastung", new DisconnectInfo(
"Der Server ist momentan an seiner Kapazitätsgrenze angelangt!", "Hohe Serverauslastung",
"Bitte versuche es zu einem späteren Zeitpunkt erneut.", "Der Server ist momentan an seiner Kapazitätsgrenze angelangt!",
playerPreLoginEvent.getUniqueId() "Bitte versuche es zu einem späteren Zeitpunkt erneut.",
).getComponent() playerPreLoginEvent.getUniqueId()
); ).getComponent()
);
if(Bukkit.getOnlinePlayers().size() >= this.getAppliance().getLimit())
playerPreLoginEvent.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_FULL); playerPreLoginEvent.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_FULL);
}
} }
} }

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit; package eu.mhsl.craftattack.spawn.common.appliances.tooling.playerlimit;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart; package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart; package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.util.IteratorUtil; import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import eu.mhsl.craftattack.core.util.text.Countdown; import eu.mhsl.craftattack.spawn.core.util.text.Countdown;
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;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart; package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
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;

View File

@@ -0,0 +1,5 @@
## Files originally from "TrackPack"
https://github.com/ALaggyDev/TrackPack/blob/main/README.md
Discovered by: [Laggy](https://github.com/ALaggyDev/) and [NikOverflow](https://github.com/NikOverflow)

View File

@@ -0,0 +1,33 @@
import zipfile
import hashlib
import uuid
import json
SERVER_URL = "http://localhost:8080/api/devicefingerprinting"
packs = []
def file_sha1(path):
h = hashlib.sha1()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
for i in range(0, 24):
path = f"packs/{i}"
with zipfile.ZipFile(path, mode="w") as zf:
zf.writestr(
"pack.mcmeta",
'{"pack":{"pack_format":22,"supported_formats":[22,1000],"description":"pack ' + str(i) + '"}}',
)
hash = file_sha1(path)
packs.append({
"url": f"{SERVER_URL}/packs/{i}",
"uuid": str(uuid.uuid4()),
"hash": hash
})
with open("packs.json", "w") as f:
json.dump(packs, f, indent=4)

View File

@@ -0,0 +1,122 @@
[
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/0",
"uuid": "b35f6e2f-1b50-4493-85be-fb18bd90f9bb",
"hash": "7a39af839ea6484431f7b707759546bea991d435"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/1",
"uuid": "71095b62-d5ef-4ab2-ba3b-3c1b403f5e34",
"hash": "a9192ee73df1c5cff2c188fac6e9e638a1e7b6ce"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/2",
"uuid": "a4dba0a2-f8f2-4a81-bbb2-a9a818820330",
"hash": "6b85b0eb54865dae70bbda89746d83717dc2a214"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/3",
"uuid": "79fa2dc4-8c84-45fc-a09f-d89906f0d900",
"hash": "c7abf7a316f7e8c98985e8317a8b649e824e9f79"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/4",
"uuid": "15702c9b-a22b-426d-b48a-3d65b0368e9a",
"hash": "10cd0e2c46f192deb87ac75c149827d44a713017"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/5",
"uuid": "3d702d41-8e2f-4920-8dd0-1fd2146da9fb",
"hash": "8ad517d259e800b88a38ff00ee6721d5656822f2"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/6",
"uuid": "c20a2e47-ef43-49da-a80d-adf238df3695",
"hash": "798677405a4fd678892e1cf55585c8c91f82e1e2"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/7",
"uuid": "7ce51b81-1263-4919-9f4e-bb749ffe6e2e",
"hash": "af473b8eb7572f35d307bede5f2e20f263c0d804"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/8",
"uuid": "0c70d586-fe48-4ffc-86b0-6b9ec3bfe045",
"hash": "2fb698ff88f2436637641f3b2e6792201feb5144"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/9",
"uuid": "c7af75a8-0b72-495d-a0ff-c1c40e229c13",
"hash": "cf660460798eecf451d639873cc1fedc4661db1b"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/10",
"uuid": "248dbce6-4b2a-44b5-b038-8d718b0ced99",
"hash": "a8ebe708d0f3747c76e4e5e68db5dcb561922706"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/11",
"uuid": "10979174-cb02-40eb-a754-275551ad608d",
"hash": "54961b48db1582a1a0981c8cc9be5ae0f3122cf3"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/12",
"uuid": "a361cfa7-674c-4493-a4cf-4baff851f276",
"hash": "013719dc8da79c96b45a1c5319c20bffe1a56cc9"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/13",
"uuid": "24b39bdb-ada9-40ec-9e3a-132c74b81dc6",
"hash": "206898c6b6600d2648b2d79c61fc6255b19587d9"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/14",
"uuid": "158fc5b4-be2c-4f7a-98cb-af5993adcc90",
"hash": "061b266a7c526fb3a3152a4ea70ca5592e0b503c"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/15",
"uuid": "4f9097a7-be02-48ad-919c-f292307f8490",
"hash": "45a667a0fe06246defabca14ef1271fb6db5a1ac"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/16",
"uuid": "3ce31e60-7e8a-4fb1-8c6d-da9065bea798",
"hash": "75bb12e46203d49e89aa9a826d267552372758bc"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/17",
"uuid": "cd978e5c-3de0-4ada-8ec5-3a88a305eec6",
"hash": "5b20261f7be03e83e9c52307f1408b0c5e58358c"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/18",
"uuid": "75001e58-3999-4779-a1d1-43ab161770ce",
"hash": "544420cffb6c17113c06fb49eeba892c208719d3"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/19",
"uuid": "6a7005a9-c2ca-476d-9a12-07d120ee121a",
"hash": "fcc066a4d3193b60b102e3d906ad8dc0b0fcf65b"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/20",
"uuid": "521c0d84-d82e-49ef-b096-d9b90f15aa19",
"hash": "4545835983ec7f07d02675a69181a80dc396f038"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/21",
"uuid": "c1b590c5-43fc-41e3-83c0-47f35b14f845",
"hash": "8d4c670eaefc0482734e839b72758226dde13bc3"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/22",
"uuid": "43958a18-c087-4f2b-a6ea-066231606eb1",
"hash": "004282602f7bdbb7cd7724f23aae23876f224092"
},
{
"url": "http://localhost:8080/api/devicefingerprinting/packs/23",
"uuid": "4b91ac81-9de4-4c2b-a876-47e621496d10",
"hash": "dae68eae109e08ea4c4c943905502eb331939f64"
}
]

View File

@@ -0,0 +1 @@
!base.zip

Binary file not shown.

Binary file not shown.

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