90 Commits

Author SHA1 Message Date
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
296 changed files with 5521 additions and 670 deletions

View File

@@ -1,6 +1,8 @@
dependencies {
implementation project(':core')
compileOnly 'io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT'
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
compileOnly 'io.papermc.paper:paper-api:1.21.8-R0.1-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,6 +1,6 @@
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.bukkit.configuration.ConfigurationSection;
@@ -8,7 +8,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
public class WebsiteApiUtil {
public class CraftAttackApi {
private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("api"));
public final static String basePath = apiConfig.getString("baseurl");
public final static String apiSecret = apiConfig.getString("secret");

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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;
import eu.mhsl.craftattack.core.appliance.Appliance;
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.common.appliances.internal.debug.command.AppliancesCommand;
import eu.mhsl.craftattack.spawn.common.appliances.internal.debug.command.UserInfoCommand;
import org.jetbrains.annotations.NotNull;

View File

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

View File

@@ -1,6 +1,6 @@
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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;

View File

@@ -1,6 +1,6 @@
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.event.Listener;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,6 +1,6 @@
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.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;
import eu.mhsl.craftattack.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import net.kyori.adventure.sound.Sound;
import org.bukkit.Bukkit;

View File

@@ -1,10 +1,10 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention;
import eu.mhsl.craftattack.core.Main;
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@@ -1,6 +1,6 @@
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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;

View File

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

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

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help;
import eu.mhsl.craftattack.core.appliance.Appliance;
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.common.appliances.metaGameplay.help.command.DiscordCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.HelpCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.help.command.SpawnCommand;

View File

@@ -1,6 +1,6 @@
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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;

View File

@@ -1,6 +1,6 @@
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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@@ -1,6 +1,6 @@
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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@@ -1,6 +1,6 @@
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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@@ -1,6 +1,6 @@
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.text.Component;
import net.kyori.adventure.util.Ticks;
@@ -13,6 +13,7 @@ import java.time.temporal.ChronoUnit;
public abstract class Bar {
private BossBar bossBar;
private final BukkitTask updateTask;
public static String name;
public Bar() {
long refreshRateInTicks = this.refresh().get(ChronoUnit.SECONDS) * Ticks.TICKS_PER_SECOND;
@@ -32,7 +33,7 @@ public abstract class Bar {
private BossBar createBar() {
return BossBar.bossBar(
this.title(),
this.correctedProgress(),
this.clampedProgress(),
this.color(),
this.overlay()
);
@@ -43,7 +44,7 @@ public abstract class Bar {
this.beforeRefresh();
this.bossBar.name(this.title());
this.bossBar.progress(this.correctedProgress());
this.bossBar.progress(this.clampedProgress());
this.bossBar.color(this.color());
this.bossBar.overlay(this.overlay());
}
@@ -52,7 +53,7 @@ public abstract class Bar {
this.updateTask.cancel();
}
private float correctedProgress() {
private float clampedProgress() {
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,51 @@
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,11 +1,13 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars;
import eu.mhsl.craftattack.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.MsptBar;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.PlayerCounterBar;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.infoBars.bars.TpsBar;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@@ -24,39 +26,42 @@ public class InfoBars extends Appliance {
new PlayerCounterBar()
);
public void showAll(Player player) {
this.getStoredBars(player).forEach(bar -> this.show(player, bar));
public void showAllEnabled(Player player) {
this.getEnabledBars(player).forEach(bar -> this.show(player, bar));
}
public void hideAll(Player player) {
this.getStoredBars(player).forEach(bar -> this.hide(player, bar));
this.setStoredBars(player, List.of());
public void hideAllEnabled(Player player) {
this.getEnabledBars(player).forEach(bar -> this.hide(player, bar));
this.setEnabledBars(player, List.of());
}
public void show(Player player, String bar) {
this.validateBarName(bar);
List<String> existingBars = new ArrayList<>(this.getStoredBars(player));
List<String> existingBars = new ArrayList<>(this.getEnabledBars(player));
existingBars.add(bar);
player.showBossBar(this.getBarByName(bar).getBossBar());
this.setStoredBars(player, existingBars);
this.setEnabledBars(player, existingBars);
}
public void hide(Player player, String bar) {
this.validateBarName(bar);
List<String> existingBars = new ArrayList<>(this.getStoredBars(player));
List<String> existingBars = new ArrayList<>(this.getEnabledBars(player));
existingBars.remove(bar);
player.hideBossBar(this.getBarByName(bar).getBossBar());
this.setStoredBars(player, existingBars);
this.setEnabledBars(player, existingBars);
}
private List<String> getStoredBars(Player player) {
private List<String> getEnabledBars(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 void setEnabledBars(Player player, List<String> bars) {
Bukkit.getScheduler().runTask(
Main.instance(),
() -> player.getPersistentDataContainer().set(this.infoBarKey, PersistentDataType.LIST.strings(), bars)
);
}
private Bar getBarByName(String name) {
@@ -71,8 +76,16 @@ public class InfoBars extends Appliance {
throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name));
}
public List<Bar> getInfoBars() {
return this.infoBars;
@Override
public void onEnable() {
Settings.instance().declareSetting(InfoBarSetting.class);
Settings.instance().addChangeListener(InfoBarSetting.class, player -> {
this.hideAllEnabled(player);
InfoBarSetting.InfoBarConfiguration config = Settings.instance().getSetting(player, Settings.Key.InfoBars, InfoBarSetting.InfoBarConfiguration.class);
if(config.mspt()) this.show(player, MsptBar.name);
if(config.playerCounter()) this.show(player, PlayerCounterBar.name);
if(config.tps()) this.show(player, TpsBar.name);
});
}
@Override
@@ -84,9 +97,4 @@ public class InfoBars extends Appliance {
protected @NotNull List<Listener> listeners() {
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;
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
class ShowPreviousBarsListener extends ApplianceListener<InfoBars> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
// this.getAppliance().showAll(event.getPlayer());
this.getAppliance().showAllEnabled(event.getPlayer());
}
}

View File

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

View File

@@ -1,9 +1,9 @@
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.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.text.Component;
import net.kyori.adventure.text.format.TextColor;
@@ -12,6 +12,8 @@ import org.bukkit.Bukkit;
import java.time.Duration;
public class PlayerCounterBar extends Bar {
public static String name = "playerCounter";
@Override
protected Duration refresh() {
return Duration.ofSeconds(3);
@@ -19,7 +21,7 @@ public class PlayerCounterBar extends Bar {
@Override
protected String name() {
return "playerCounter";
return name;
}
@Override
@@ -38,7 +40,10 @@ public class PlayerCounterBar extends Bar {
@Override
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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
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 org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

View File

@@ -1,6 +1,6 @@
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 org.bukkit.command.Command;
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.core.api.client.ReqResp;
import eu.mhsl.craftattack.core.api.client.repositories.ReportRepository;
import eu.mhsl.craftattack.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.api.repositories.ReportRepository;
import eu.mhsl.craftattack.spawn.common.api.repositories.VaroReportRepository;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.common.api.repositories.CraftAttackReportRepository;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
@@ -36,7 +38,7 @@ public class Report extends Appliance {
}
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(
Main.instance(),
() -> this.createReport(issuer, request)
@@ -62,16 +64,17 @@ public class Report extends Appliance {
}
private void createReport(Player issuer, ReportRepository.ReportCreationInfo reportRequest) {
ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(ReportRepository.class)
ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(VaroReportRepository.class)
.createReport(reportRequest);
switch(createdReport.status()) {
case 200: // varo-endpoint specific
case 201:
issuer.sendMessage(
Component.text()
.append(Component.text("\\/".repeat(20), NamedTextColor.DARK_GRAY))
.appendNewline()
.append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden!", NamedTextColor.GOLD))
.append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden:", NamedTextColor.GOLD))
.appendNewline()
.appendNewline()
.append(
@@ -112,7 +115,7 @@ public class Report extends Appliance {
}
public void queryReports(Player issuer) {
ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(ReportRepository.class)
ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(VaroReportRepository.class)
.queryReports(issuer.getUniqueId());
if(userReports.status() != 200) {

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.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;

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.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

View File

@@ -1,11 +1,12 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings;
import eu.mhsl.craftattack.core.Main;
import eu.mhsl.craftattack.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.Setting;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.OpenSettingsShortcutListener;
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.listeners.SettingsInventoryListener;
import eu.mhsl.craftattack.spawn.core.util.world.InteractSounds;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -16,6 +17,7 @@ import org.jetbrains.annotations.NotNull;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class Settings extends Appliance {
@@ -33,6 +35,10 @@ public class Settings extends Appliance {
ChatMentions,
DoubleDoors,
KnockDoors,
BorderWarning,
LocatorBar,
InfoBars,
CoordinateDisplay
}
public static Settings instance() {
@@ -56,6 +62,16 @@ public class Settings extends Appliance {
private final WeakHashMap<Player, OpenSettingsInventory> openSettingsInventories = 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) {
if(this.settingsCache.containsKey(player)) return this.settingsCache.get(player);
@@ -135,6 +151,7 @@ public class Settings extends Appliance {
}
player.openInventory(inventory);
InteractSounds.of(player).open();
this.openSettingsInventories.put(player, new OpenSettingsInventory(inventory, settings));
}
@@ -142,9 +159,9 @@ public class Settings extends Appliance {
int countOfUncategorized = (int) settings.stream()
.filter(setting -> !(setting instanceof CategorizedSetting))
.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()
.filter(setting -> setting instanceof CategorizedSetting)
.map(setting -> (CategorizedSetting) setting)
@@ -154,17 +171,18 @@ public class Settings extends Appliance {
.reduce(Integer::sum)
.orElse(1) * 9;
int rows = rowsOfUncategorized + rowsOfCategorized;
if(rows % 9 != 0) throw new IllegalStateException(
String.format("Failed to calculate settings inventory size. %d is not an multiple of 9", rows)
int invSize = invSizeForUncategorized + invSizeForCategorized;
if(invSize % 9 != 0) throw new IllegalStateException(
String.format("Failed to calculate settings inventory size. %d is not an multiple of 9", invSize)
);
return rows;
return invSize;
}
public void onSettingsClose(Player player) {
if(!this.openSettingsInventories.containsKey(player)) return;
this.openSettingsInventories.remove(player);
player.updateInventory();
InteractSounds.of(player).close();
}
public boolean hasSettingsNotOpen(Player player) {

View File

@@ -1,6 +1,6 @@
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.CommandSender;
import org.jetbrains.annotations.NotNull;

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;
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.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.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -17,7 +18,7 @@ import org.bukkit.persistence.PersistentDataContainer;
import java.util.List;
public abstract class Setting<TDataType> {
TDataType state;
protected TDataType state;
private final Settings.Key key;
public Setting(Settings.Key key) {
@@ -25,7 +26,7 @@ public abstract class Setting<TDataType> {
}
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() {
@@ -33,12 +34,32 @@ public abstract class Setting<TDataType> {
}
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) {
if(clickType.equals(ClickType.DOUBLE_CLICK)) return;
this.change(p, clickType);
InteractSounds.of(p).click();
this.toStorage(p.getPersistentDataContainer(), this.state());
Settings.instance().invokeChangeListener(p, this.getClass());
}
public ItemStack buildItem() {

View File

@@ -1,6 +1,6 @@
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 org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;

View File

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

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.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.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

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

View File

@@ -1,6 +1,6 @@
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.CommandSender;
import org.bukkit.entity.Player;

View File

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

View File

@@ -1,6 +1,6 @@
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.CommandSender;
import org.jetbrains.annotations.NotNull;

View File

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

View File

@@ -1,7 +1,7 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute;
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.core.util.text.DataSizeConverter;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.util.text.DataSizeConverter;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

View File

@@ -1,6 +1,6 @@
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.command.Command;
import org.bukkit.command.CommandSender;

View File

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

View File

@@ -1,6 +1,6 @@
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.format.NamedTextColor;
import org.bukkit.command.Command;
@@ -12,7 +12,11 @@ import java.util.List;
import java.util.Map;
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() {
super("endPrevent");
@@ -21,11 +25,11 @@ class EndPreventCommand extends ApplianceCommand<EndPrevent> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
if(args.length == 1 && 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(
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
));
}

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;
import eu.mhsl.craftattack.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,6 +1,6 @@
package eu.mhsl.craftattack.spawn.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.command.Command;
import org.bukkit.command.CommandSender;
@@ -19,6 +19,7 @@ class KickCommand extends ApplianceCommand<Kick> {
@Override
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(
args[0],
Arrays.stream(args).skip(1).collect(Collectors.joining(" "))

View File

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

View File

@@ -1,6 +1,6 @@
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.format.NamedTextColor;
import org.bukkit.command.Command;

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
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.OfflinePlayer;
import org.bukkit.command.Command;

View File

@@ -1,6 +1,6 @@
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.player.AsyncPlayerPreLoginEvent;

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
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.format.NamedTextColor;
import org.bukkit.command.Command;

View File

@@ -1,6 +1,6 @@
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.CommandSender;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,9 +1,9 @@
package eu.mhsl.craftattack.spawn.common.appliances.tooling.restart;
import eu.mhsl.craftattack.core.appliance.Appliance;
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.core.util.IteratorUtil;
import eu.mhsl.craftattack.core.util.text.Countdown;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.core.util.text.Countdown;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;

View File

@@ -1,6 +1,6 @@
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.CommandSender;
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.

Binary file not shown.

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