Move files around

This commit is contained in:
Alessandro Proto 2025-03-15 08:16:56 +01:00
parent d3838f6a78
commit 1592d0b2a4
242 changed files with 13063 additions and 0 deletions

View file

@ -0,0 +1,127 @@
package me.alexdevs.solstice.modules;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.api.module.ModuleEntrypoint;
import me.alexdevs.solstice.modules.admin.AdminModule;
import me.alexdevs.solstice.modules.afk.AfkModule;
import me.alexdevs.solstice.modules.autoAnnouncement.AutoAnnouncementModule;
import me.alexdevs.solstice.modules.item.ItemModule;
import me.alexdevs.solstice.modules.jail.JailModule;
import me.alexdevs.solstice.modules.kit.KitModule;
import me.alexdevs.solstice.modules.miscellaneous.MiscellaneousModule;
import me.alexdevs.solstice.modules.note.NoteModule;
import me.alexdevs.solstice.modules.notifications.NotificationsModule;
import me.alexdevs.solstice.modules.placeholders.PlaceholdersModule;
import me.alexdevs.solstice.modules.powertool.PowerToolModule;
import me.alexdevs.solstice.modules.restart.RestartModule;
import me.alexdevs.solstice.modules.back.BackModule;
import me.alexdevs.solstice.modules.ban.BanModule;
import me.alexdevs.solstice.modules.broadcast.BroadcastModule;
import me.alexdevs.solstice.modules.commandSpy.CommandSpyModule;
import me.alexdevs.solstice.modules.customName.CustomNameModule;
import me.alexdevs.solstice.modules.enderchest.EnderChestModule;
import me.alexdevs.solstice.modules.experiments.ExperimentsModule;
import me.alexdevs.solstice.modules.extinguish.ExtinguishModule;
import me.alexdevs.solstice.modules.feed.FeedModule;
import me.alexdevs.solstice.modules.fly.FlyModule;
import me.alexdevs.solstice.modules.rtp.RTPModule;
import me.alexdevs.solstice.modules.sign.SignModule;
import me.alexdevs.solstice.modules.god.GodModule;
import me.alexdevs.solstice.modules.hat.HatModule;
import me.alexdevs.solstice.modules.heal.HealModule;
import me.alexdevs.solstice.modules.helpOp.HelpOpModule;
import me.alexdevs.solstice.modules.home.HomeModule;
import me.alexdevs.solstice.modules.ignite.IgniteModule;
import me.alexdevs.solstice.modules.ignore.IgnoreModule;
import me.alexdevs.solstice.modules.info.InfoModule;
import me.alexdevs.solstice.modules.inventorySee.InventorySeeModule;
import me.alexdevs.solstice.modules.kick.KickModule;
import me.alexdevs.solstice.modules.mail.MailModule;
import me.alexdevs.solstice.modules.mute.MuteModule;
import me.alexdevs.solstice.modules.near.NearModule;
import me.alexdevs.solstice.modules.seen.SeenModule;
import me.alexdevs.solstice.modules.skull.SkullModule;
import me.alexdevs.solstice.modules.smite.SmiteModule;
import me.alexdevs.solstice.modules.spawn.SpawnModule;
import me.alexdevs.solstice.modules.staffChat.StaffChatModule;
import me.alexdevs.solstice.modules.styling.StylingModule;
import me.alexdevs.solstice.modules.sudo.SudoModule;
import me.alexdevs.solstice.modules.suicide.SuicideModule;
import me.alexdevs.solstice.modules.tablist.TabListModule;
import me.alexdevs.solstice.modules.teleportHere.TeleportHereModule;
import me.alexdevs.solstice.modules.teleportOffline.TeleportOfflineModule;
import me.alexdevs.solstice.modules.teleportPosition.TeleportPositionModule;
import me.alexdevs.solstice.modules.teleportRequest.TeleportRequestModule;
import me.alexdevs.solstice.modules.tell.TellModule;
import me.alexdevs.solstice.modules.timeBar.TimeBarModule;
import me.alexdevs.solstice.modules.trash.TrashModule;
import me.alexdevs.solstice.modules.utilities.UtilitiesModule;
import me.alexdevs.solstice.modules.warp.WarpModule;
import java.util.HashSet;
import java.util.List;
public class ModuleProvider implements ModuleEntrypoint {
private static final List<? extends ModuleBase> modules = List.of(
new AdminModule(),
new AfkModule(),
new AutoAnnouncementModule(),
new RestartModule(),
new BackModule(),
new BanModule(),
new BroadcastModule(),
new CommandSpyModule(),
new CustomNameModule(),
new EnderChestModule(),
new ExperimentsModule(),
new ExtinguishModule(),
new FeedModule(),
new FlyModule(),
new SignModule(),
new GodModule(),
new HatModule(),
new HealModule(),
new HelpOpModule(),
new HomeModule(),
new IgniteModule(),
new IgnoreModule(),
new InfoModule(),
new InventorySeeModule(),
new ItemModule(),
new JailModule(),
new KickModule(),
new KitModule(),
new MailModule(),
new MiscellaneousModule(),
new MuteModule(),
new NearModule(),
new NoteModule(),
new NotificationsModule(),
new PlaceholdersModule(),
new PowerToolModule(),
new RTPModule(),
new SeenModule(),
new SkullModule(),
new SmiteModule(),
new SpawnModule(),
new StaffChatModule(),
new StylingModule(),
new SudoModule(),
new SuicideModule(),
new TabListModule(),
new TeleportHereModule(),
new TeleportOfflineModule(),
new TeleportPositionModule(),
new TeleportRequestModule(),
new TellModule(),
new TimeBarModule(),
new TrashModule(),
new UtilitiesModule(),
new WarpModule()
);
@Override
public HashSet<ModuleBase> register() {
return new HashSet<>(modules);
}
}

View file

@ -0,0 +1,43 @@
package me.alexdevs.solstice.modules.admin;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.events.PlayerConnectionEvents;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.lucko.fabric.api.permissions.v0.Permissions;
public class AdminModule extends ModuleBase {
public static final String ID = "admin";
public AdminModule() {
super(ID);
}
@Override
public void init() {
PlayerConnectionEvents.WHITELIST_BYPASS.register(profile -> {
try {
return Permissions.check(profile, getWhitelistBypassPermission(), false).get();
} catch (Exception e) {
Solstice.LOGGER.error("Error checking whitelist bypass for profile {}", profile.getId(), e);
}
return false;
});
PlayerConnectionEvents.FULL_SERVER_BYPASS.register(profile -> {
try {
return Permissions.check(profile, getFullServerBypassPermission(), false).get();
} catch (Exception e) {
Solstice.LOGGER.error("Error checking full server bypass for profile {}", profile.getId(), e);
}
return false;
});
}
public String getWhitelistBypassPermission() {
return getPermissionNode("bypass.whitelist");
}
public String getFullServerBypassPermission() {
return getPermissionNode("bypass.fullserver");
}
}

View file

@ -0,0 +1,361 @@
package me.alexdevs.solstice.modules.afk;
import eu.pb4.placeholders.api.PlaceholderContext;
import eu.pb4.placeholders.api.PlaceholderResult;
import eu.pb4.placeholders.api.Placeholders;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.api.events.CommandEvents;
import me.alexdevs.solstice.api.events.PlayerActivityEvents;
import me.alexdevs.solstice.api.events.SolsticeEvents;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.api.text.Format;
import me.alexdevs.solstice.modules.afk.commands.ActiveTimeCommand;
import me.alexdevs.solstice.modules.afk.commands.AfkCommand;
import me.alexdevs.solstice.modules.afk.data.*;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.*;
import net.fabricmc.fabric.api.message.v1.ServerMessageEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class AfkModule extends ModuleBase.Toggleable {
public static final String ID = "afk";
public static final double sprintSpeed = 0.280617;
public static final double walkSpeed = 0.215859;
public static final double sneakSpeed = 0.0841;
public static final int LEADERBOARD_SIZE = 10;
public enum AfkTriggerReason {
MANUAL,
MOVEMENT,
LOOK_CHANGE,
CHAT_MESSAGE,
COMMAND,
BLOCK_ATTACK,
BLOCK_INTERACT,
ENTITY_ATTACK,
ENTITY_INTERACT,
ITEM_USE,
}
private final Map<UUID, PlayerActivityState> activities = new ConcurrentHashMap<>();
public AfkModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, AfkConfig.class, AfkConfig::new);
Solstice.localeManager.registerModule(ID, AfkLocale.MODULE);
Solstice.playerData.registerData(ID, AfkPlayerData.class, AfkPlayerData::new);
Solstice.serverData.registerData(ID, AfkServerData.class, AfkServerData::new);
this.commands.add(new AfkCommand(this));
this.commands.add(new ActiveTimeCommand(this));
Placeholders.register(ResourceLocation.fromNamespaceAndPath(Solstice.MOD_ID, "afk"), (context, arg) -> {
if (!context.hasPlayer())
return PlaceholderResult.invalid("No player!");
var player = context.player();
if (isPlayerAfk(player))
return PlaceholderResult.value(Format.parse(getConfig().tag));
else
return PlaceholderResult.value("");
});
SolsticeEvents.READY.register((instance, server) -> {
Solstice.scheduler.scheduleAtFixedRate(this::updateActiveTime, 0, 1, TimeUnit.SECONDS);
calculateLeaderboard();
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
activities.put(handler.getPlayer().getUUID(), new PlayerActivityState(handler.getPlayer(), server.getTickCount()));
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
activities.remove(handler.getPlayer().getUUID());
});
ServerTickEvents.END_SERVER_TICK.register(this::tick);
PlayerActivityEvents.AFK.register((player) -> {
var config = getConfig();
if (player.serverLevel().canSleepThroughNights()) {
player.serverLevel().updateSleepingPlayerList();
}
Solstice.LOGGER.info("{} is AFK. Active time: {} seconds.", player.getGameProfile().getName(), getActiveTime(player.getUUID()));
if (!config.announce)
return;
var playerContext = PlaceholderContext.of(player);
Solstice.getInstance().broadcast(locale().get("goneAfk", playerContext));
});
PlayerActivityEvents.AFK_RETURN.register((player, reason) -> {
var config = getConfig();
if (player.serverLevel().canSleepThroughNights()) {
player.serverLevel().updateSleepingPlayerList();
}
Solstice.LOGGER.info("{} is no longer AFK due to {}. Active time: {} seconds.", player.getGameProfile().getName(), reason.name(), getActiveTime(player.getUUID()));
if (!config.announce)
return;
var playerContext = PlaceholderContext.of(player);
Solstice.getInstance().broadcast(locale().get("returnAfk", playerContext));
});
registerTriggers();
}
public AfkServerData getServerData() {
return Solstice.serverData.getData(AfkServerData.class);
}
private void updateActiveTime() {
var activePlayers = Solstice.server.getPlayerList().getPlayers()
.stream().filter(player -> !isPlayerAfk(player));
activePlayers.forEach(player -> {
var activity = activities.computeIfAbsent(player.getUUID(), uuid -> new PlayerActivityState(player, player.getServer().getTickCount()));
if (!activity.activeTimeEnabled)
return;
var playerData = getPlayerData(player.getUUID());
playerData.activeTime++;
tryInsertLeaderboard(player, playerData.activeTime);
});
}
private void tryInsertLeaderboard(ServerPlayer player, int activeTime) {
var serverData = getServerData();
var leaderboard = serverData.leaderboard;
// if in list, update
var entry = leaderboard.stream().filter(e -> e.uuid().equals(player.getUUID())).findFirst();
if (entry.isPresent()) {
entry.get().activeTime(activeTime);
entry.get().name(player.getGameProfile().getName());
leaderboard.sort((o1, o2) -> Integer.compare(o2.activeTime(), o1.activeTime()));
return;
}
// if not in list, insert
var smallest = leaderboard.stream().min(Comparator.comparingInt(LeaderboardEntry::activeTime));
if (smallest.isPresent()) {
if (smallest.get().activeTime() < activeTime) {
leaderboard.remove(smallest.get());
leaderboard.add(new LeaderboardEntry(player.getGameProfile().getName(), player.getUUID(), activeTime));
leaderboard.sort((o1, o2) -> Integer.compare(o2.activeTime(), o1.activeTime()));
}
} else {
leaderboard.add(new LeaderboardEntry(player.getGameProfile().getName(), player.getUUID(), activeTime));
}
}
private void tick(MinecraftServer server) {
var config = getConfig();
if (!config.enable)
return;
server.getPlayerList().getPlayers().forEach(player -> {
var activity = activities.computeIfAbsent(player.getUUID(), uuid -> new PlayerActivityState(player, server.getTickCount()));
var curLocation = new ServerLocation(player);
var oldLocation = activity.location;
activity.location = curLocation;
var delta = curLocation.getDelta(oldLocation);
var horizontalDelta = new Vec3(delta.x(), 0, delta.z());
var speed = horizontalDelta.length();
// Suppose the player in a vehicle will look around, so we only check for movement when not in a vehicle.
if (player.getVehicle() == null) {
// Defeats some anti-afk stuff, like pools. Works best when no lag.
if ((player.isShiftKeyDown() && speed >= sneakSpeed) || (player.isSprinting() && speed >= sprintSpeed) || (speed >= walkSpeed)) {
if (getConfig().triggers.onMovement) {
clearAfk(player, AfkTriggerReason.MOVEMENT);
}
}
}
// Looking around requires player input
if (curLocation.getPitch() != oldLocation.getPitch() || curLocation.getYaw() != oldLocation.getYaw()) {
if (getConfig().triggers.onLookChange) {
clearAfk(player, AfkTriggerReason.LOOK_CHANGE);
}
}
var ticks = server.getTickCount();
if (activity.lastUpdate < ticks - config.timeTrigger * 20) {
if (!activity.isAfk && activity.afkEnabled) {
activity.isAfk = true;
PlayerActivityEvents.AFK.invoker().onAfk(player);
}
}
});
}
public AfkConfig getConfig() {
return Solstice.configManager.getData(AfkConfig.class);
}
public AfkPlayerData getPlayerData(UUID playerUuid) {
return Solstice.playerData.get(playerUuid).getData(AfkPlayerData.class);
}
public boolean isPlayerAfk(ServerPlayer player) {
return activities.containsKey(player.getUUID()) && activities.get(player.getUUID()).isAfk;
}
public void setPlayerAfk(ServerPlayer player, boolean isAfk) {
if (!activities.containsKey(player.getUUID()))
return;
var config = getConfig();
var activity = activities.get(player.getUUID());
if (isAfk) {
activity.lastUpdate = activity.lastUpdate - (config.timeTrigger * 20);
} else {
clearAfk(player, AfkTriggerReason.MANUAL);
}
}
public int getActiveTime(UUID playerUuid) {
return getPlayerData(playerUuid).activeTime;
}
private void calculateLeaderboard() {
var serverData = getServerData();
if (!serverData.forceCalculateLeaderboard)
return;
serverData.forceCalculateLeaderboard = false;
var userCache = Solstice.getUserCache();
var temp = new ArrayList<LeaderboardEntry>();
for (var name : userCache.getAllNames()) {
var profile = userCache.getByName(name);
if (profile.isEmpty())
continue;
var playerData = Solstice.playerData.get(profile.get().getId()).getData(AfkPlayerData.class);
if (playerData.activeTime > 0) {
var entry = new LeaderboardEntry(profile.get().getName(), profile.get().getId(), playerData.activeTime);
temp.add(entry);
}
}
temp.sort((o1, o2) -> Integer.compare(o2.activeTime(), o1.activeTime()));
serverData.leaderboard.clear();
for (var i = 0; i < Math.min(temp.size(), LEADERBOARD_SIZE); i++) {
serverData.leaderboard.add(temp.get(i));
}
var onlinePlayers = Solstice.server.getPlayerList().getPlayers().stream().map(Entity::getUUID).toList();
Solstice.playerData.disposeMissing(onlinePlayers);
}
public List<LeaderboardEntry> getActiveTimeLeaderboard() {
var serverData = getServerData();
return Collections.unmodifiableList(serverData.leaderboard);
}
public List<ServerPlayer> getCurrentActivePlayers() {
return Solstice.server.getPlayerList().getPlayers().stream().filter(player -> !isPlayerAfk(player)).toList();
}
private void clearAfk(ServerPlayer player, AfkTriggerReason reason) {
if (!activities.containsKey(player.getUUID()))
return;
var activity = activities.get(player.getUUID());
activity.lastUpdate = Solstice.server.getTickCount();
if (!activity.afkEnabled)
return;
if (activity.isAfk) {
activity.isAfk = false;
PlayerActivityEvents.AFK_RETURN.invoker().onAfkReturn(player, reason);
}
}
private void registerTriggers() {
AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> {
if (getConfig().triggers.onBlockAttack) {
clearAfk((ServerPlayer) player, AfkTriggerReason.BLOCK_ATTACK);
}
return InteractionResult.PASS;
});
AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
if (getConfig().triggers.onEntityAttack) {
clearAfk((ServerPlayer) player, AfkTriggerReason.ENTITY_ATTACK);
}
return InteractionResult.PASS;
});
UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
if (getConfig().triggers.onBlockInteract) {
clearAfk((ServerPlayer) player, AfkTriggerReason.BLOCK_INTERACT);
}
return InteractionResult.PASS;
});
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
if (getConfig().triggers.onEntityInteract) {
clearAfk((ServerPlayer) player, AfkTriggerReason.ENTITY_INTERACT);
}
return InteractionResult.PASS;
});
UseItemCallback.EVENT.register((player, world, hand) -> {
if (getConfig().triggers.onItemUse) {
clearAfk((ServerPlayer) player, AfkTriggerReason.ITEM_USE);
}
return InteractionResultHolder.pass(player.getItemInHand(hand));
});
ServerMessageEvents.ALLOW_CHAT_MESSAGE.register((message, sender, params) -> {
if (getConfig().triggers.onChat) {
clearAfk(sender, AfkTriggerReason.CHAT_MESSAGE);
}
return true;
});
CommandEvents.ALLOW_COMMAND.register((source, command) -> {
if (!source.isPlayer())
return true;
if (getConfig().triggers.onCommand) {
clearAfk(source.getPlayer(), AfkTriggerReason.COMMAND);
}
return true;
});
}
}

View file

@ -0,0 +1,122 @@
package me.alexdevs.solstice.modules.afk.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.command.TimeSpan;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.afk.AfkModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
public class ActiveTimeCommand extends ModCommand<AfkModule> {
public ActiveTimeCommand(AfkModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("activetime");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require(true))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var activeTime = module.getActiveTime(player.getUUID());
var longSpan = TimeSpan.toLongString(activeTime);
var map = Map.of(
"activeTime", Component.nullToEmpty(longSpan),
"player", player.getName()
);
context.getSource().sendSuccess(() -> module.locale().get("yourActiveTime", map), false);
return 1;
})
.then(Commands.literal("player")
.requires(require("others", 1))
.then(Commands.argument("player", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.executes(context -> {
var profile = LocalGameProfile.getProfile(context, "player");
var activeTime = module.getActiveTime(profile.getId());
if (activeTime == 0) {
context.getSource().sendSuccess(() -> module.locale().get("neverPlayed"), false);
return 0;
}
var longSpan = TimeSpan.toLongString(activeTime);
var map = Map.of(
"activeTime", Component.nullToEmpty(longSpan),
"player", Component.nullToEmpty(profile.getName())
);
context.getSource().sendSuccess(() -> module.locale().get("playerActiveTime", map), false);
return 1;
})
))
.then(Commands.literal("leaderboard")
.requires(require("leaderboard", true))
.executes(context -> {
var leaderboard = module.getActiveTimeLeaderboard();
var text = Component.empty();
text.append(module.locale().get("leaderboardHeader"));
var index = 0;
for (var entry : leaderboard) {
text.append("\n");
index++;
var map = Map.of(
"index", Component.nullToEmpty(String.valueOf(index)),
"player", Component.nullToEmpty(entry.name()),
"uuid", Component.nullToEmpty(entry.uuid().toString()),
"time", Component.nullToEmpty(TimeSpan.toLongString(entry.activeTime())),
"shortTime", Component.nullToEmpty(TimeSpan.toShortString(entry.activeTime())),
"seconds", Component.nullToEmpty(String.valueOf(entry.activeTime()))
);
text.append(module.locale().get("leaderboardEntry", map));
}
context.getSource().sendSuccess(() -> text, false);
return 1;
})
)
.then(Commands.literal("set")
.requires(require("set", 3))
.then(Commands.argument("player", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.then(Commands.argument("time", TimeSpan.timeSpan())
.suggests(TimeSpan::suggest)
.executes(context -> {
var profile = LocalGameProfile.getProfile(context, "player");
var time = TimeSpan.getTimeSpan(context, "time");
var data = module.getPlayerData(profile.getId());
data.activeTime = time;
var map = Map.of(
"player", Component.nullToEmpty(profile.getName()),
"time", Component.nullToEmpty(TimeSpan.toLongString(time))
);
context.getSource().sendSuccess(() -> module.locale().get("activeTimeSet", map), true);
return 1;
})
))
);
}
}

View file

@ -0,0 +1,33 @@
package me.alexdevs.solstice.modules.afk.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.afk.AfkModule;
import net.minecraft.commands.CommandSourceStack;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class AfkCommand extends ModCommand<AfkModule> {
public AfkCommand(AfkModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("afk");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
module.setPlayerAfk(player, !module.isPlayerAfk(player));
return 1;
});
}
}

View file

@ -0,0 +1,52 @@
package me.alexdevs.solstice.modules.afk.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ConfigSerializable
public class AfkConfig {
@Comment("Enable the AFK functionality. Requires server restart.")
public boolean enable = true;
@Comment("Announce in chat when a player goes or return from AFK.")
public boolean announce = true;
@Comment("AFK triggers after the player has been inactive for the following seconds. Defaults to 300 seconds.")
public int timeTrigger = 300;
@Comment("This tag is displayed with `solstice:afk` placeholder when the player is AFK.")
public String tag = "<gray>[AFK]</gray> ";
@Comment("These triggers clear the AFK status. Events regarding entities, blocks or item usage may be triggered by fake players.")
public AfkTriggers triggers = new AfkTriggers();
@ConfigSerializable
public static class AfkTriggers {
@Comment("Movement is triggered when the velocity threshold is met.")
public boolean onMovement = true;
@Comment("Look change is triggered when the player yaw and/or pitch change.")
public boolean onLookChange = true;
@Comment("Trigger on chat messages sent by the player.")
public boolean onChat = true;
@Comment("Trigger on commands.")
public boolean onCommand = true;
@Comment("Trigger when a block is being attacked (left click).")
public boolean onBlockAttack = true;
@Comment("Trigger when a block is being interacted with (right click).")
public boolean onBlockInteract = true;
@Comment("Trigger when an entity is attacked.")
public boolean onEntityAttack = true;
@Comment("Trigger when an entity is interacted with.")
public boolean onEntityInteract = true;
@Comment("Trigger when an item is used.")
public boolean onItemUse = true;
}
}

View file

@ -0,0 +1,17 @@
package me.alexdevs.solstice.modules.afk.data;
import java.util.Map;
public class AfkLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("goneAfk", "<gray>%player:displayname% is now AFK</gray>"),
Map.entry("returnAfk", "<gray>%player:displayname% is no longer AFK</gray>"),
Map.entry("yourActiveTime", "<gold>Your active time is <yellow>${activeTime}</yellow>.</gold>"),
Map.entry("playerActiveTime", "<gold><yellow>${player}</yellow>'s active time is <yellow>${activeTime}</yellow>.</gold>"),
Map.entry("neverPlayed", "<gold>This player never played.</gold>"),
Map.entry("notFound", "<gold>Player not found.</gold>"),
Map.entry("leaderboardHeader", "<gold>Active Time Leaderboard:</gold>"),
Map.entry("leaderboardEntry", "<gold> <gold>${index}.</gold> <yellow>${player}</yellow>: <gray>${time}</gray></gold>"),
Map.entry("activeTimeSet", "<gold>Set <yellow>${player}</yellow>'s active time to <yellow>${time}</yellow>!</gold>")
);
}

View file

@ -0,0 +1,8 @@
package me.alexdevs.solstice.modules.afk.data;
import com.google.gson.annotations.Expose;
public class AfkPlayerData {
@Expose
public int activeTime = 0;
}

View file

@ -0,0 +1,9 @@
package me.alexdevs.solstice.modules.afk.data;
import java.util.ArrayList;
import java.util.List;
public class AfkServerData {
public boolean forceCalculateLeaderboard = true;
public List<LeaderboardEntry> leaderboard = new ArrayList<>();
}

View file

@ -0,0 +1,35 @@
package me.alexdevs.solstice.modules.afk.data;
import java.util.UUID;
public class LeaderboardEntry {
protected String name;
protected final UUID uuid;
protected int activeTime;
public LeaderboardEntry(String name, UUID uuid, int activeTime) {
this.name = name;
this.uuid = uuid;
this.activeTime = activeTime;
}
public String name() {
return name;
}
public void name(String name) {
this.name = name;
}
public UUID uuid() {
return uuid;
}
public int activeTime() {
return activeTime;
}
public void activeTime(int activeTime) {
this.activeTime = activeTime;
}
}

View file

@ -0,0 +1,23 @@
package me.alexdevs.solstice.modules.afk.data;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.modules.afk.AfkModule;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.server.level.ServerPlayer;
public class PlayerActivityState {
public ServerLocation location;
public int lastUpdate;
public boolean isAfk;
public boolean afkEnabled;
public boolean activeTimeEnabled;
public PlayerActivityState(ServerPlayer player, int lastUpdate) {
this.location = new ServerLocation(player);
this.lastUpdate = lastUpdate;
this.isAfk = false;
this.afkEnabled = Permissions.check(player, Solstice.MOD_ID + "." + AfkModule.ID + ".base", true);
this.activeTimeEnabled = Permissions.check(player, Solstice.MOD_ID + "." + AfkModule.ID + ".activetime", true);
}
}

View file

@ -0,0 +1,79 @@
package me.alexdevs.solstice.modules.autoAnnouncement;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.events.SolsticeEvents;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.api.text.Format;
import me.alexdevs.solstice.modules.autoAnnouncement.data.AutoAnnouncementConfig;
import me.lucko.fabric.api.permissions.v0.Permissions;
import java.util.Random;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class AutoAnnouncementModule extends ModuleBase.Toggleable {
public static final String ID = "autoannouncement";
private ScheduledFuture<?> scheduledFuture = null;
private int currentLine = 0;
public AutoAnnouncementModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, AutoAnnouncementConfig.class, AutoAnnouncementConfig::new);
SolsticeEvents.READY.register((instance, server) -> {
setup();
});
SolsticeEvents.RELOAD.register(instance -> {
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
setup();
});
}
private void setup() {
currentLine = 0;
if (getConfig().enable) {
scheduledFuture = Solstice.scheduler.scheduleAtFixedRate(this::announce, getConfig().delay, getConfig().delay, TimeUnit.SECONDS);
}
}
public AutoAnnouncementConfig getConfig() {
return Solstice.configManager.getData(AutoAnnouncementConfig.class);
}
public void announce() {
var lines = getConfig().announcements;
if (lines.isEmpty())
return;
if (getConfig().pickRandomly) {
currentLine = new Random().nextInt(lines.size());
}
currentLine %= lines.size();
var line = lines.get(currentLine);
currentLine++;
Solstice.server.getPlayerList().getPlayers().forEach(player -> {
if (line.permission() != null) {
var result = line.result();
if (result == null)
result = true;
if (Permissions.check(player, line.permission()) != result) {
return;
}
}
var playerContext = PlaceholderContext.of(player);
player.sendSystemMessage(Format.parse(line.text(), playerContext));
});
}
}

View file

@ -0,0 +1,31 @@
package me.alexdevs.solstice.modules.autoAnnouncement.data;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.ArrayList;
import java.util.List;
@ConfigSerializable
public class AutoAnnouncementConfig {
@Comment("Enable automatic announcements functionality.")
public boolean enable = true;
@Comment("Pick the next announcement randomly, else linearly.")
public boolean pickRandomly = false;
// every 5 mins
@Comment("Send announcement every X seconds. Defaults to 300 seconds.")
public int delay = 300;
@Comment("Announcement list. Announcements can have a permission as condition. If result is true, the permission has to be granted, else the permission has to be denied (or unset).")
public ArrayList<Announcement> announcements = new ArrayList<>(List.of(
new Announcement("Tip! <gray>Solstice is open-source! Contribute on <url:'https://github.com/Ale32bit/Solstice'><blue>GitHub</blue></url>!</gray>"),
new Announcement("Fun fact! <gray>This announcement is only visible to players that do not have the 'solstice.example' permission granted!</gray>", "solstice.example", false)
));
@ConfigSerializable
public record Announcement(String text, @Nullable String permission, @Nullable Boolean result) {
public Announcement(String text) {
this(text, null, null);
}
}
}

View file

@ -0,0 +1,44 @@
package me.alexdevs.solstice.modules.back;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.api.events.PlayerTeleportCallback;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.back.commands.BackCommand;
import me.alexdevs.solstice.modules.back.data.BackLocale;
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.server.level.ServerPlayer;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BackModule extends ModuleBase.Toggleable {
public static final String ID = "back";
public final ConcurrentHashMap<UUID, ServerLocation> lastPlayerPositions = new ConcurrentHashMap<>();
public BackModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, BackLocale.MODULE);
commands.add(new BackCommand(this));
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> lastPlayerPositions.remove(handler.getPlayer().getUUID()));
PlayerTeleportCallback.EVENT.register((player, origin, destination) -> lastPlayerPositions.put(player.getUUID(), origin));
ServerLivingEntityEvents.AFTER_DEATH.register((entity, damageSource) -> {
if (entity.isAlwaysTicking()) {
try {
var player = (ServerPlayer) entity;
lastPlayerPositions.put(entity.getUUID(), new ServerLocation(player));
} catch (ClassCastException e) {
// They were, in fact, not a player.
}
}
});
}
}

View file

@ -0,0 +1,52 @@
package me.alexdevs.solstice.modules.back.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.locale.Locale;
import me.alexdevs.solstice.modules.back.BackModule;
import net.minecraft.commands.CommandSourceStack;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class BackCommand extends ModCommand<BackModule> {
private final Locale locale = Solstice.localeManager.getLocale(BackModule.ID);
public BackCommand(BackModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("back");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var playerContext = PlaceholderContext.of(player);
var lastPosition = module.lastPlayerPositions.get(player.getUUID());
if (lastPosition == null) {
context.getSource().sendSuccess(() -> locale.get(
"noPosition",
playerContext
), false);
return 1;
}
context.getSource().sendSuccess(() -> locale.get(
"teleporting",
playerContext
), false);
lastPosition.teleport(player);
return 1;
});
}
}

View file

@ -0,0 +1,10 @@
package me.alexdevs.solstice.modules.back.data;
import java.util.Map;
public class BackLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("teleporting", "<gold>Teleporting to previous position...</gold>"),
Map.entry("noPosition", "<red>There is no position to return back to.</red>")
);
}

View file

@ -0,0 +1,25 @@
package me.alexdevs.solstice.modules.ban;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.ban.commands.BanCommand;
import me.alexdevs.solstice.modules.ban.commands.TempBanCommand;
import me.alexdevs.solstice.modules.ban.commands.UnbanCommand;
import me.alexdevs.solstice.modules.ban.data.BanLocale;
public class BanModule extends ModuleBase.Toggleable {
public static final String ID = "ban";
public BanModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, BanLocale.MODULE);
commands.add(new BanCommand(this));
commands.add(new TempBanCommand(this));
commands.add(new UnbanCommand(this));
}
}

View file

@ -0,0 +1,92 @@
package me.alexdevs.solstice.modules.ban.commands;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.module.Utils;
import me.alexdevs.solstice.modules.ban.BanModule;
import me.alexdevs.solstice.modules.ban.formatters.BanMessageFormatter;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.GameProfileArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.server.players.UserBanListEntry;
import me.alexdevs.solstice.api.text.Format;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class BanCommand extends ModCommand<BanModule> {
public static final SimpleCommandExceptionType ALREADY_BANNED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.ban.failed"));
public BanCommand(BanModule module) {
super(module);
}
static int execute(CommandContext<CommandSourceStack> context, Collection<GameProfile> targets, @Nullable String reason, @Nullable Date expiryDate) throws CommandSyntaxException {
var source = context.getSource();
var server = source.getServer();
var banList = server.getPlayerList().getBans();
var banCounter = 0;
for (GameProfile target : targets) {
if (banList.isBanned(target)) {
continue;
}
var banEntry = new UserBanListEntry(target, null, source.getTextName(), expiryDate, reason);
banList.add(banEntry);
banCounter++;
var playerContext = PlaceholderContext.of(target, server);
source.sendSuccess(() -> Component.translatable("commands.ban.success", Component.nullToEmpty(target.getName()), Format.parse(banEntry.getReason(), playerContext)), true);
var serverPlayerEntity = source.getServer().getPlayerList().getPlayer(target.getId());
if (serverPlayerEntity != null) {
serverPlayerEntity.connection.disconnect(BanMessageFormatter.format(target, banEntry));
}
}
if (banCounter == 0) {
throw ALREADY_BANNED_EXCEPTION.create();
} else {
return banCounter;
}
}
@Override
public void register(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext commandRegistry, Commands.CommandSelection environment) {
Utils.removeCommands(dispatcher, "ban");
super.register(dispatcher, commandRegistry, environment);
}
@Override
public List<String> getNames() {
return List.of("ban");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(3))
.then(argument("targets", GameProfileArgument.gameProfile())
.executes(context -> execute(context, GameProfileArgument.getGameProfiles(context, "targets"), null, null))
.then(argument("reason", StringArgumentType.greedyString())
.executes(context -> execute(context, GameProfileArgument.getGameProfiles(context, "targets"), StringArgumentType.getString(context, "reason"), null))));
}
}

View file

@ -0,0 +1,57 @@
package me.alexdevs.solstice.modules.ban.commands;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.command.TimeSpan;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.ban.BanModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.GameProfileArgument;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class TempBanCommand extends ModCommand<BanModule> {
public TempBanCommand(BanModule module) {
super(module);
}
private static Date getDateFromNow(int seconds) {
var now = new Date();
var c = Calendar.getInstance();
c.setTime(now);
c.add(Calendar.SECOND, seconds);
return c.getTime();
}
@Override
public List<String> getNames() {
return List.of("tempban");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require("tempban", 3))
.then(argument("targets", GameProfileArgument.gameProfile())
.then(argument("duration", StringArgumentType.string())
.suggests(TimeSpan::suggest)
.executes(context -> execute(context, GameProfileArgument.getGameProfiles(context, "targets"), null, TimeSpan.getTimeSpan(context, "duration")))
.then(argument("reason", StringArgumentType.greedyString())
.executes(context -> execute(context, GameProfileArgument.getGameProfiles(context, "targets"), StringArgumentType.getString(context, "reason"), TimeSpan.getTimeSpan(context, "duration"))))));
}
private int execute(CommandContext<CommandSourceStack> context, Collection<GameProfile> targets, String reason, int duration) throws CommandSyntaxException {
var expiryDate = getDateFromNow(duration);
return BanCommand.execute(context, targets, reason, expiryDate);
}
}

View file

@ -0,0 +1,69 @@
package me.alexdevs.solstice.modules.ban.commands;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.module.Utils;
import me.alexdevs.solstice.modules.ban.BanModule;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.GameProfileArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import java.util.Collection;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class UnbanCommand extends ModCommand<BanModule> {
private static final SimpleCommandExceptionType ALREADY_UNBANNED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.pardon.failed"));
public UnbanCommand(BanModule module) {
super(module);
}
@Override
public void register(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext commandRegistry, Commands.CommandSelection environment) {
Utils.removeCommands(dispatcher, "pardon");
super.register(dispatcher, commandRegistry, environment);
}
@Override
public List<String> getNames() {
return List.of("unban", "pardon");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(3))
.then(Commands.argument("targets", GameProfileArgument.gameProfile())
.suggests((context, builder) -> SharedSuggestionProvider.suggest((context.getSource()).getServer().getPlayerList().getBans().getUserList(), builder))
.executes(context -> execute(context, GameProfileArgument.getGameProfiles(context, "targets"))));
}
private int execute(CommandContext<CommandSourceStack> context, Collection<GameProfile> targets) throws CommandSyntaxException {
var banList = context.getSource().getServer().getPlayerList().getBans();
var source = context.getSource();
var pardonCount = 0;
for (GameProfile profile : targets) {
if (banList.isBanned(profile)) {
banList.remove(profile);
pardonCount++;
source.sendSuccess(() -> Component.translatable("commands.pardon.success", Component.nullToEmpty(profile.getName())), true);
}
}
if (pardonCount == 0) {
throw ALREADY_UNBANNED_EXCEPTION.create();
} else {
return pardonCount;
}
}
}

View file

@ -0,0 +1,10 @@
package me.alexdevs.solstice.modules.ban.data;
import java.util.Map;
public class BanLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("banMessageFormat", "<red>You are banned from this server:</red>\n\n${reason}"),
Map.entry("tempBanMessageFormat", "<red>You are temporarily banned from this server:</red>\n\n${reason}\n\n<gray>Expires: ${expiry_date}</gray>")
);
}

View file

@ -0,0 +1,33 @@
package me.alexdevs.solstice.modules.ban.formatters;
import com.mojang.authlib.GameProfile;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.modules.ban.BanModule;
import net.minecraft.network.chat.Component;
import net.minecraft.server.players.UserBanListEntry;
import me.alexdevs.solstice.core.coreModule.CoreModule;
import me.alexdevs.solstice.api.text.Format;
import java.text.SimpleDateFormat;
import java.util.Map;
public class BanMessageFormatter {
public static Component format(GameProfile profile, UserBanListEntry entry) {
var locale = Solstice.modules.getModule(BanModule.class).locale();
var coreConfig = CoreModule.getConfig();
var df = new SimpleDateFormat(coreConfig.dateTimeFormat);
var context = PlaceholderContext.of(profile, Solstice.server);
var expiryDate = Component.nullToEmpty(entry.getExpires() != null ? df.format(entry.getExpires()) : "never");
Map<String, Component> placeholders = Map.of(
"reason", Format.parse(entry.getReason(), context),
"creation_date", Component.nullToEmpty(df.format(entry.getCreated())),
"expiry_date", expiryDate,
"source", Component.nullToEmpty(entry.getSource())
);
var format = entry.getExpires() != null ? locale.raw("tempBanMessageFormat") : locale.raw("banMessageFormat");
return Format.parse(format, context, placeholders);
}
}

View file

@ -0,0 +1,23 @@
package me.alexdevs.solstice.modules.broadcast;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.broadcast.commands.BroadcastCommand;
import me.alexdevs.solstice.modules.broadcast.commands.PlainBroadcastCommand;
import me.alexdevs.solstice.modules.broadcast.data.BroadcastConfig;
public class BroadcastModule extends ModuleBase.Toggleable {
public static final String ID = "broadcast";
public BroadcastModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, BroadcastConfig.class, BroadcastConfig::new);
commands.add(new BroadcastCommand(this));
commands.add(new PlainBroadcastCommand(this));
}
}

View file

@ -0,0 +1,50 @@
package me.alexdevs.solstice.modules.broadcast.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.broadcast.BroadcastModule;
import me.alexdevs.solstice.modules.broadcast.data.BroadcastConfig;
import net.minecraft.commands.CommandSourceStack;
import me.alexdevs.solstice.api.text.Format;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class BroadcastCommand extends ModCommand<BroadcastModule> {
public BroadcastCommand(BroadcastModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("broadcast", "bc");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.then(argument("message", StringArgumentType.greedyString())
.executes(context -> {
var config = Solstice.configManager.getData(BroadcastConfig.class);
var message = StringArgumentType.getString(context, "message");
var serverContext = PlaceholderContext.of(context.getSource().getServer());
var placeholders = Map.of(
"message", Format.parse(message, serverContext)
);
Solstice.getInstance().broadcast(Format.parse(config.format, placeholders));
return 1;
}));
}
}

View file

@ -0,0 +1,42 @@
package me.alexdevs.solstice.modules.broadcast.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.broadcast.BroadcastModule;
import net.minecraft.commands.CommandSourceStack;
import me.alexdevs.solstice.api.text.Format;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class PlainBroadcastCommand extends ModCommand<BroadcastModule> {
public PlainBroadcastCommand(BroadcastModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("plainbroadcast", "pbc");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require("plain", 2))
.then(argument("message", StringArgumentType.greedyString())
.executes(context -> {
var message = StringArgumentType.getString(context, "message");
var serverContext = PlaceholderContext.of(context.getSource().getServer());
Solstice.getInstance().broadcast(Format.parse(message, serverContext));
return 1;
}));
}
}

View file

@ -0,0 +1,10 @@
package me.alexdevs.solstice.modules.broadcast.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ConfigSerializable
public class BroadcastConfig {
@Comment("Format to use when broadcasting a message.")
public String format = "<red>[</red><gold>Broadcast</gold><red>]</red> ${message}";
}

View file

@ -0,0 +1,61 @@
package me.alexdevs.solstice.modules.commandSpy;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.events.CommandEvents;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.commandSpy.data.CommandSpyConfig;
import me.alexdevs.solstice.modules.commandSpy.data.CommandSpyLocale;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.network.chat.Component;
import java.util.Map;
public class CommandSpyModule extends ModuleBase.Toggleable {
public static final String ID = "commandspy";
public CommandSpyModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, CommandSpyConfig.class, CommandSpyConfig::new);
Solstice.localeManager.registerModule(ID, CommandSpyLocale.MODULE);
CommandEvents.ALLOW_COMMAND.register((source, command) -> {
if (!source.isPlayer())
return true;
Solstice.LOGGER.info("{}: /{}", source.getTextName(), command);
var parts = command.split("\\s");
if (parts.length >= 1) {
var cmd = parts[0];
if (isIgnored(cmd)) {
return true;
}
}
var player = source.getPlayer();
var players = source.getServer().getPlayerList().getPlayers();
var placeholders = Map.of("player", Component.nullToEmpty(player.getGameProfile().getName()), "command", Component.nullToEmpty(command));
var message = locale().get("spyFormat", placeholders);
for (var pl : players) {
var commandSpyEnabled = Permissions.check(pl, this.getPermissionNode("base"));
if (commandSpyEnabled && !pl.getUUID().equals(player.getUUID())) {
pl.displayClientMessage(message, false);
}
}
return true;
});
}
public boolean isIgnored(String command) {
if(!isEnabled())
return false;
return Solstice.configManager.getData(CommandSpyConfig.class).ignoredCommands.contains(command);
}
}

View file

@ -0,0 +1,15 @@
package me.alexdevs.solstice.modules.commandSpy.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.ArrayList;
import java.util.List;
@ConfigSerializable
public class CommandSpyConfig {
@Comment("Commands to ignore.")
public ArrayList<String> ignoredCommands = new ArrayList<>(List.of(
"tell", "w", "msg", "dm", "r", "staffchat", "sc", "helpop", "sos"
));
}

View file

@ -0,0 +1,9 @@
package me.alexdevs.solstice.modules.commandSpy.data;
import java.util.Map;
public class CommandSpyLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("spyFormat", "\uD83D\uDC41 <dark_gray>${player}:</dark_gray> <gray>/${command}</gray>")
);
}

View file

@ -0,0 +1,98 @@
package me.alexdevs.solstice.modules.customName;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.api.text.Format;
import me.alexdevs.solstice.api.text.RawPlaceholder;
import me.alexdevs.solstice.integrations.LuckPermsIntegration;
import me.alexdevs.solstice.modules.customName.commands.NicknameCommand;
import me.alexdevs.solstice.modules.customName.data.CustomNameConfig;
import me.alexdevs.solstice.modules.customName.data.CustomNamePlayerData;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.level.ServerPlayer;
import java.util.Map;
public class CustomNameModule extends ModuleBase.Toggleable {
public static final String ID = "customname";
public CustomNameModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, CustomNameConfig.class, CustomNameConfig::new);
Solstice.playerData.registerData(ID, CustomNamePlayerData.class, CustomNamePlayerData::new);
commands.add(new NicknameCommand(this));
}
public String fetchUsernameFormat(ServerPlayer player) {
var formats = Solstice.configManager.getData(CustomNameConfig.class).nameFormats;
String format = null;
for (var f : formats) {
if (LuckPermsIntegration.isInGroup(player, f.group())) {
format = f.format();
break;
}
}
var isOperator = player.getServer().getPlayerList().isOp(player.getGameProfile());
if (format == null) {
format = "${username}";
for (var f : formats) {
if (isOperator && f.group().equals("operator")) {
format = f.format();
break;
}
if (f.group().equals("default")) {
format = f.format();
break;
}
}
}
return format;
}
public String getResolvedUsername(ServerPlayer player) {
var format = fetchUsernameFormat(player);
var playerData = Solstice.playerData.get(player).getData(CustomNamePlayerData.class);
var name = playerData.nickname == null ? player.getGameProfile().getName() : playerData.nickname;
var prefix = LuckPermsIntegration.getPrefix(player);
var suffix = LuckPermsIntegration.getSuffix(player);
if (prefix == null)
prefix = "";
if (suffix == null)
suffix = "";
Map<String, String> placeholders = Map.of(
"name", name,
"prefix", prefix,
"suffix", suffix
);
return RawPlaceholder.parse(format, placeholders);
}
public MutableComponent getNameForPlayer(ServerPlayer player) {
var name = getResolvedUsername(player);
var playerContext = PlaceholderContext.of(player);
return Format.parse(name, playerContext).copy();
}
public void setCustomName(ServerPlayer player, String name) {
var playerData = Solstice.playerData.get(player).getData(CustomNamePlayerData.class);
playerData.nickname = name;
}
public void clearCustomName(ServerPlayer player) {
var playerData = Solstice.playerData.get(player).getData(CustomNamePlayerData.class);
playerData.nickname = null;
}
}

View file

@ -0,0 +1,77 @@
package me.alexdevs.solstice.modules.customName.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.customName.CustomNameModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class NicknameCommand extends ModCommand<CustomNameModule> {
public NicknameCommand(CustomNameModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("nickname", "nick");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.then(literal("clear")
.executes(context -> executeClear(context, null))
)
.then(argument("nickname", StringArgumentType.string())
.executes(context -> execute(context, StringArgumentType.getString(context, "nickname"), null))
)
.then(argument("player", EntityArgument.player())
.requires(require("others", 2))
.then(literal("clear")
.executes(context -> executeClear(context, EntityArgument.getPlayer(context, "player")))
)
.then(argument("nickname", StringArgumentType.string())
.executes(context -> execute(context, StringArgumentType.getString(context, "nickname"), EntityArgument.getPlayer(context, "player")))
)
);
}
private int execute(CommandContext<CommandSourceStack> context, String nickname, @Nullable ServerPlayer player) throws CommandSyntaxException {
if (player == null) {
player = context.getSource().getPlayerOrException();
}
module.setCustomName(player, nickname);
var name = player.getGameProfile().getName();
context.getSource().sendSuccess(() -> Component.literal(String.format("Changed %s's nickname", name)), true);
return 1;
}
private int executeClear(CommandContext<CommandSourceStack> context, @Nullable ServerPlayer player) throws CommandSyntaxException {
if (player == null) {
player = context.getSource().getPlayerOrException();
}
module.clearCustomName(player);
var name = player.getGameProfile().getName();
context.getSource().sendSuccess(() -> Component.literal(String.format("Cleared %s's nickname", name)), true);
return 1;
}
}

View file

@ -0,0 +1,20 @@
package me.alexdevs.solstice.modules.customName.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.ArrayList;
import java.util.List;
@ConfigSerializable
public class CustomNameConfig {
@Comment("Customize player display names based on their LuckPerms group. Priority is determined by the list order: first comes before last.")
public ArrayList<NameFormat> nameFormats = new ArrayList<>(List.of(
new NameFormat("admin", "${prefix}<red>${name}</red>${suffix}"),
new NameFormat("default", "${prefix}<green>${name}</green>${suffix}")
));
@ConfigSerializable
public record NameFormat(String group, String format) {
}
}

View file

@ -0,0 +1,7 @@
package me.alexdevs.solstice.modules.customName.data;
import org.jetbrains.annotations.Nullable;
public class CustomNamePlayerData {
public @Nullable String nickname;
}

View file

@ -0,0 +1,21 @@
package me.alexdevs.solstice.modules.enderchest;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.enderchest.commands.EnderChestCommand;
import me.alexdevs.solstice.modules.enderchest.data.EnderChestLocale;
public class EnderChestModule extends ModuleBase.Toggleable {
public static final String ID = "enderchest";
public EnderChestModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, EnderChestLocale.LOCALE);
commands.add(new EnderChestCommand(this));
}
}

View file

@ -0,0 +1,120 @@
package me.alexdevs.solstice.modules.enderchest.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.utils.PlayerUtils;
import me.alexdevs.solstice.modules.enderchest.EnderChestModule;
import me.alexdevs.solstice.modules.inventorySee.ImmutableSlot;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.Stats;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.inventory.Slot;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class EnderChestCommand extends ModCommand<EnderChestModule> {
public EnderChestCommand(EnderChestModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("enderchest", "ec");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
player.awardStat(Stats.OPEN_ENDERCHEST);
open(player, player.getEnderChestInventory(), Component.translatable("container.enderchest"), true, () -> {
});
return 1;
})
.then(argument("player", StringArgumentType.word())
.requires(require("others", 2))
.suggests(LocalGameProfile::suggest)
.executes(context -> {
final var source = context.getSource();
var player = source.getPlayerOrException();
var profile = LocalGameProfile.getProfile(context, "player");
Permissions.check(profile, getPermissionNode("exempt"), 3, source.getServer()).thenAccept(exempt -> {
if (exempt) {
source.sendSuccess(() -> module.locale().get("exempt"), false);
return;
}
var isOnline = PlayerUtils.isOnline(profile.getId());
if (!isOnline && !Permissions.check(player, getPermissionNode("offline"), 3)) {
source.sendSuccess(() -> module.locale().get("offlineNotAllowed"), false);
return;
}
ServerPlayer targetPlayer;
if (isOnline) {
targetPlayer = source.getServer().getPlayerList().getPlayer(profile.getId());
} else {
targetPlayer = PlayerUtils.loadOfflinePlayer(profile);
}
var inventory = targetPlayer.getEnderChestInventory();
var canEdit = Permissions.check(player, getPermissionNode("edit"), 3);
var map = Map.of(
"player", Component.nullToEmpty(profile.getName())
);
var title = module.locale().get("title", map);
open(player, inventory, title, canEdit, () -> {
if(!isOnline) {
PlayerUtils.saveOfflinePlayer(targetPlayer);
}
});
source.sendSuccess(() -> module.locale().get("opened", map), true);
});
return 1;
}));
}
private void open(ServerPlayer player, PlayerEnderChestContainer inventory, Component title, boolean canEdit, Runnable onClose) {
var container = new SimpleGui(MenuType.GENERIC_9x3, player, false) {
@Override
public void onClose() {
onClose.run();
}
};
for (var i = 0; i < inventory.getContainerSize(); i++) {
Slot slot;
if (canEdit) {
slot = new Slot(inventory, i, 0, 0);
} else {
slot = new ImmutableSlot(inventory, i, 0, 0);
}
container.setSlotRedirect(i, slot);
}
container.setTitle(title);
container.open();
}
}

View file

@ -0,0 +1,11 @@
package me.alexdevs.solstice.modules.enderchest.data;
import java.util.Map;
public class EnderChestLocale {
public static final Map<String, String> LOCALE = Map.ofEntries(
Map.entry("title", "${player}'s Ender Chest"),
Map.entry("exempt", "<red>You cannot open this Ender Chest because the player is exempt.</red>"),
Map.entry("opened", "<gold>Opened <yellow>${player}</yellow>'s Ender Chest.</gold>")
);
}

View file

@ -0,0 +1,31 @@
package me.alexdevs.solstice.modules.experiments;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.experiments.commands.FlagsCommand;
import me.alexdevs.solstice.modules.experiments.commands.TimeSpanCommand;
import java.util.Collection;
import java.util.List;
public class ExperimentsModule extends ModuleBase {
public static final boolean ENABLED = false;
public static final String ID = "experiments";
public ExperimentsModule() {
super(ID);
}
@Override
public void init() {
commands.add(new TimeSpanCommand(this));
commands.add(new FlagsCommand(this));
}
@Override
public Collection<? extends ModCommand<?>> getCommands() {
if (!ENABLED) return List.of();
return commands;
}
}

View file

@ -0,0 +1,59 @@
package me.alexdevs.solstice.modules.experiments.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.command.Flags;
import me.alexdevs.solstice.api.command.flags.ArgumentFlag;
import me.alexdevs.solstice.api.command.flags.DoubleFlag;
import me.alexdevs.solstice.api.command.flags.Flag;
import me.alexdevs.solstice.api.command.flags.StringFlag;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.experiments.ExperimentsModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import java.util.List;
public class FlagsCommand extends ModCommand<ExperimentsModule> {
public FlagsCommand(ExperimentsModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("flags");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require(true))
.then(Commands.argument("flags", StringArgumentType.greedyString())
.executes(context -> {
var source = context.getSource();
var testFlag = Flag.of("test");
var stringFlag = new StringFlag("string", List.of('s'));
var numberFlag = new DoubleFlag("number", List.of('n'));
Flags.parse(StringArgumentType.getString(context, "flags"), testFlag, stringFlag, numberFlag);
for (var flag : List.of(testFlag, stringFlag, numberFlag)) {
if (flag.isUsed()) {
if (flag.acceptsValue() && flag instanceof ArgumentFlag<?> argFlag) {
var value = argFlag.getValue();
source.sendSystemMessage(Component.nullToEmpty(String.format("Flag %s: %s", flag.getName(), value)));
} else {
source.sendSystemMessage(Component.nullToEmpty(String.format("Flag %s", flag.getName())));
}
}
}
return 1;
})
);
}
}

View file

@ -0,0 +1,42 @@
package me.alexdevs.solstice.modules.experiments.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.command.TimeSpan;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.experiments.ExperimentsModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class TimeSpanCommand extends ModCommand<ExperimentsModule> {
public TimeSpanCommand(ExperimentsModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("timespan");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.then(argument("timespan", StringArgumentType.string())
.suggests(TimeSpan::suggest)
.executes(this::execute));
}
private int execute(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
var timespan = TimeSpan.getTimeSpan(context, "timespan");
context.getSource().sendSuccess(() -> Component.nullToEmpty(String.format("Got %s (%d)", TimeSpan.toShortString(timespan), timespan)), false);
return 1;
}
}

View file

@ -0,0 +1,17 @@
package me.alexdevs.solstice.modules.extinguish;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.extinguish.commands.ExtinguishCommand;
public class ExtinguishModule extends ModuleBase.Toggleable {
public static final String ID = "extinguish";
public ExtinguishModule() {
super(ID);
}
@Override
public void init() {
commands.add(new ExtinguishCommand(this));
}
}

View file

@ -0,0 +1,56 @@
package me.alexdevs.solstice.modules.extinguish.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.extinguish.ExtinguishModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class ExtinguishCommand extends ModCommand<ExtinguishModule> {
public ExtinguishCommand(ExtinguishModule module) {
super(module);
}
private static int execute(CommandContext<CommandSourceStack> context, @Nullable Collection<ServerPlayer> players) throws CommandSyntaxException {
var source = context.getSource();
if (players == null) {
extinguish(source, source.getPlayerOrException());
return 1;
} else {
for (ServerPlayer player : players) {
extinguish(source, player);
}
return players.size();
}
}
private static void extinguish(CommandSourceStack source, ServerPlayer player) {
player.clearFire();
source.sendSuccess(() -> Component.literal("Extinguished ").append(source.getDisplayName()), true);
}
@Override
public List<String> getNames() {
return List.of("extinguish", "ex");
}
public LiteralArgumentBuilder<CommandSourceStack> command(String command) {
return literal(command)
.requires(require(2))
.executes(context -> execute(context, null))
.then(argument("players", EntityArgument.players())
.executes(context -> execute(context, EntityArgument.getPlayers(context, "players"))));
}
}

View file

@ -0,0 +1,17 @@
package me.alexdevs.solstice.modules.feed;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.feed.commands.FeedCommand;
public class FeedModule extends ModuleBase.Toggleable {
public static final String ID = "feed";
public FeedModule() {
super(ID);
}
@Override
public void init() {
commands.add(new FeedCommand(this));
}
}

View file

@ -0,0 +1,61 @@
package me.alexdevs.solstice.modules.feed.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.feed.FeedModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class FeedCommand extends ModCommand<FeedModule> {
private static final int MAX_FOOD_LEVEL = 20;
public FeedCommand(FeedModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("feed");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.executes(context -> execute(context, null))
.then(argument("targets", EntityArgument.players())
.requires(require("others", 2))
.executes(context -> execute(context, EntityArgument.getPlayers(context, "targets"))));
}
private int execute(CommandContext<CommandSourceStack> context, @Nullable Collection<ServerPlayer> targets) throws CommandSyntaxException {
if (targets == null) {
var player = context.getSource().getPlayerOrException();
feed(context, player);
return 1;
} else {
for (var target : targets) {
feed(context, target);
}
return targets.size();
}
}
private void feed(CommandContext<CommandSourceStack> context, ServerPlayer player) {
player.getFoodData().setFoodLevel(MAX_FOOD_LEVEL);
player.getFoodData().setSaturation(MAX_FOOD_LEVEL);
context.getSource().sendSuccess(() -> Component.literal("Fed ").append(player.getDisplayName()), true);
}
}

View file

@ -0,0 +1,35 @@
package me.alexdevs.solstice.modules.fly;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.fly.commands.FlyCommand;
import me.alexdevs.solstice.modules.fly.data.FlyLocale;
import me.alexdevs.solstice.modules.fly.data.FlyPlayerData;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
public class FlyModule extends ModuleBase.Toggleable {
public static final String ID = "fly";
public FlyModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, FlyLocale.MODULE);
Solstice.playerData.registerData(ID, FlyPlayerData.class, FlyPlayerData::new);
commands.add(new FlyCommand(this));
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
var player = handler.getPlayer();
var data = Solstice.playerData.get(player).getData(FlyPlayerData.class);
if(data.flightEnabled) {
var abilities = player.getAbilities();
abilities.mayfly = true;
player.onUpdateAbilities();
}
});
}
}

View file

@ -0,0 +1,81 @@
package me.alexdevs.solstice.modules.fly.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.fly.FlyModule;
import me.alexdevs.solstice.modules.fly.data.FlyPlayerData;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class FlyCommand extends ModCommand<FlyModule> {
public FlyCommand(FlyModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("fly");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(3))
.executes(context -> execute(context, null))
.then(argument("player", EntityArgument.player())
.requires(require("others", 3))
.executes(context -> execute(context, EntityArgument.getPlayer(context, "player")))
);
}
private int execute(CommandContext<CommandSourceStack> context, @Nullable ServerPlayer player) throws CommandSyntaxException {
var forOther = player != null;
if (player == null) {
player = context.getSource().getPlayerOrException();
}
var abilities = player.getAbilities();
abilities.mayfly = !abilities.mayfly;
player.onUpdateAbilities();
var data = Solstice.playerData.get(player).getData(FlyPlayerData.class);
data.flightEnabled = abilities.mayfly;
Component text;
var sourceContext = PlaceholderContext.of(context.getSource());
if (forOther) {
var placeholders = Map.of(
"player", player.getDisplayName()
);
if (abilities.mayfly) {
text = module.locale().get("enabledForOther", sourceContext, placeholders);
} else {
text = module.locale().get("disabledForOther", sourceContext, placeholders);
}
} else {
if (abilities.mayfly) {
text = module.locale().get("enabled", sourceContext);
} else {
text = module.locale().get("disabled", sourceContext);
}
}
context.getSource().sendSuccess(() -> text, forOther);
return 1;
}
}

View file

@ -0,0 +1,12 @@
package me.alexdevs.solstice.modules.fly.data;
import java.util.Map;
public class FlyLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("enabled", "<green>Flight enabled</green>"),
Map.entry("disabled", "<gold>Flight disabled</gold>"),
Map.entry("enabledForOther", "<green>Flight enabled for ${player}</green>"),
Map.entry("disabledForOther", "<gold>Flight disabled for ${player}</gold>")
);
}

View file

@ -0,0 +1,5 @@
package me.alexdevs.solstice.modules.fly.data;
public class FlyPlayerData {
public boolean flightEnabled = false;
}

View file

@ -0,0 +1,35 @@
package me.alexdevs.solstice.modules.god;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.god.commands.GodCommand;
import me.alexdevs.solstice.modules.god.data.GodLocale;
import me.alexdevs.solstice.modules.god.data.GodPlayerData;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
public class GodModule extends ModuleBase.Toggleable {
public static final String ID = "god";
public GodModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, GodLocale.MODULE);
Solstice.playerData.registerData(ID, GodPlayerData.class, GodPlayerData::new);
commands.add(new GodCommand(this));
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
var player = handler.getPlayer();
var data = Solstice.playerData.get(player).getData(GodPlayerData.class);
if(data.invulnerabilityEnabled) {
var abilities = player.getAbilities();
abilities.invulnerable = true;
player.onUpdateAbilities();
}
});
}
}

View file

@ -0,0 +1,81 @@
package me.alexdevs.solstice.modules.god.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.god.GodModule;
import me.alexdevs.solstice.modules.god.data.GodPlayerData;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class GodCommand extends ModCommand<GodModule> {
public GodCommand(GodModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("god");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(3))
.executes(context -> execute(context, null))
.then(argument("player", EntityArgument.player())
.requires(require("others", 3))
.executes(context -> execute(context, EntityArgument.getPlayer(context, "player")))
);
}
private int execute(CommandContext<CommandSourceStack> context, @Nullable ServerPlayer player) throws CommandSyntaxException {
var forOther = player != null;
if (player == null) {
player = context.getSource().getPlayerOrException();
}
var abilities = player.getAbilities();
abilities.invulnerable = !abilities.invulnerable;
player.onUpdateAbilities();
var data = Solstice.playerData.get(player).getData(GodPlayerData.class);
data.invulnerabilityEnabled = abilities.invulnerable;
Component text;
var sourceContext = PlaceholderContext.of(context.getSource());
if (forOther) {
var placeholders = Map.of(
"player", player.getDisplayName()
);
if (abilities.invulnerable) {
text = module.locale().get("enabledForOther", sourceContext, placeholders);
} else {
text = module.locale().get("disabledForOther", sourceContext, placeholders);
}
} else {
if (abilities.invulnerable) {
text = module.locale().get("enabled", sourceContext);
} else {
text = module.locale().get("disabled", sourceContext);
}
}
context.getSource().sendSuccess(() -> text, forOther);
return 1;
}
}

View file

@ -0,0 +1,12 @@
package me.alexdevs.solstice.modules.god.data;
import java.util.Map;
public class GodLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("enabled", "<green>Invincibility enabled</green>"),
Map.entry("disabled", "<gold>Invincibility disabled</gold>"),
Map.entry("enabledForOther", "<green>Invincibility enabled for ${player}</green>"),
Map.entry("disabledForOther", "<gold>Invincibility disabled for ${player}</gold>")
);
}

View file

@ -0,0 +1,5 @@
package me.alexdevs.solstice.modules.god.data;
public class GodPlayerData {
public boolean invulnerabilityEnabled = false;
}

View file

@ -0,0 +1,58 @@
package me.alexdevs.solstice.modules.hat;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.hat.commands.HatCommand;
import me.alexdevs.solstice.modules.hat.data.HatConfig;
import me.alexdevs.solstice.modules.hat.data.HatLocale;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import java.util.List;
import java.util.stream.Stream;
public class HatModule extends ModuleBase.Toggleable {
public static final String ID = "hat";
public HatModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, HatConfig.class, HatConfig::new);
Solstice.localeManager.registerModule(ID, HatLocale.MODULE);
commands.add(new HatCommand(this));
}
public HatConfig getConfig() {
return Solstice.configManager.getData(HatConfig.class);
}
public List<String> getConfigTags() {
return getConfig().filter.stream().filter(s -> s.startsWith("#")).toList();
}
public List<String> getConfigItems() {
return getConfig().filter.stream().filter(s -> !s.startsWith("#")).toList();
}
public boolean isInFilter(String key) {
if (key.startsWith("#")) {
return getConfigTags().contains(key);
} else {
return getConfigItems().contains(key);
}
}
public boolean isInFilter(Stream<TagKey<Item>> stream) {
var tags = getConfigTags().stream().map(t -> t.substring(1)).toList();
var iter = stream.iterator();
while (iter.hasNext()) {
var tag = iter.next();
if(tags.contains(tag.location().toString()))
return true;
}
return false;
}
}

View file

@ -0,0 +1,63 @@
package me.alexdevs.solstice.modules.hat.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.hat.HatModule;
import net.minecraft.commands.CommandSourceStack;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class HatCommand extends ModCommand<HatModule> {
public HatCommand(HatModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("hat");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var handStack = player.getMainHandItem();
if (handStack.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("emptyStack"), false);
return 0;
}
var config = module.getConfig();
var itemId = handStack.getItemHolder().unwrapKey().get().location().toString();
var tags = handStack.getTags();
if (config.whitelistFilter) {
if(!module.isInFilter(itemId) && !module.isInFilter(tags)) {
context.getSource().sendSuccess(() -> module.locale().get("notAllowed"), false);
return 0;
}
} else {
if(module.isInFilter(itemId) || module.isInFilter(tags)) {
context.getSource().sendSuccess(() -> module.locale().get("notAllowed"), false);
return 0;
}
}
//handStack.streamTags().toList().get(0).id().toString();
var inventory = player.getInventory();
var oldHeadStack = inventory.armor.get(3); // head slot
inventory.setItem(inventory.selected, oldHeadStack.copyAndClear());
inventory.armor.set(3, handStack.copyAndClear());
context.getSource().sendSuccess(() -> module.locale().get("success"), false);
return 1;
});
}
}

View file

@ -0,0 +1,18 @@
package me.alexdevs.solstice.modules.hat.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.List;
@ConfigSerializable
public class HatConfig {
@Comment("Make the filter setting act as a whitelist instead of a blacklist.")
public boolean whitelistFilter = false;
@Comment("Items & tags to allow/deny. See the 'whitelist-filter' setting to change the behaviour of this list.\nUse '#' as prefix to filter as tag.")
public List<String> filter = List.of(
"#c:shulker_boxes"
);
}

View file

@ -0,0 +1,11 @@
package me.alexdevs.solstice.modules.hat.data;
import java.util.Map;
public class HatLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("emptyStack", "<red>You are not holding any item!</red>"),
Map.entry("success", "<green>Check out your new hat!</green>"),
Map.entry("notAllowed", "<gold>You cannot wear this item!</gold>")
);
}

View file

@ -0,0 +1,17 @@
package me.alexdevs.solstice.modules.heal;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.heal.commands.HealCommand;
public class HealModule extends ModuleBase.Toggleable {
public static final String ID = "heal";
public HealModule() {
super(ID);
}
@Override
public void init() {
commands.add(new HealCommand(this));
}
}

View file

@ -0,0 +1,66 @@
package me.alexdevs.solstice.modules.heal.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.heal.HealModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class HealCommand extends ModCommand<HealModule> {
public HealCommand(HealModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("heal");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.executes(context -> execute(context, null))
.then(argument("targets", EntityArgument.entities())
.requires(require("others", 2))
.executes(context -> execute(context, EntityArgument.getEntities(context, "targets"))));
}
private int execute(CommandContext<CommandSourceStack> context, @Nullable Collection<? extends Entity> targets) throws CommandSyntaxException {
if (targets == null) {
var player = context.getSource().getPlayerOrException();
heal(context, player);
return 1;
} else {
var healedCount = 0;
for (var target : targets) {
if (target instanceof LivingEntity livingEntity) {
healedCount++;
heal(context, livingEntity);
}
}
if (healedCount == 0) {
context.getSource().sendFailure(Component.nullToEmpty("There are no living entities in the selector"));
}
return healedCount;
}
}
private void heal(CommandContext<CommandSourceStack> context, LivingEntity entity) {
entity.setHealth(entity.getMaxHealth());
context.getSource().sendSuccess(() -> Component.literal("Healed ").append(entity.getDisplayName()), true);
}
}

View file

@ -0,0 +1,21 @@
package me.alexdevs.solstice.modules.helpOp;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.helpOp.commands.HelpOpCommand;
import me.alexdevs.solstice.modules.helpOp.data.HelpOpLocale;
public class HelpOpModule extends ModuleBase.Toggleable {
public static final String ID = "helpop";
public HelpOpModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, HelpOpLocale.MODULE);
commands.add(new HelpOpCommand(this));
}
}

View file

@ -0,0 +1,64 @@
package me.alexdevs.solstice.modules.helpOp.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.locale.Locale;
import me.alexdevs.solstice.modules.helpOp.HelpOpModule;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class HelpOpCommand extends ModCommand<HelpOpModule> {
private final Locale locale = Solstice.localeManager.getLocale(HelpOpModule.ID);
public HelpOpCommand(HelpOpModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("helpop", "sos");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.then(argument("message", StringArgumentType.greedyString())
.executes(context -> {
var source = context.getSource();
var sourceContext = PlaceholderContext.of(source);
var message = StringArgumentType.getString(context, "message");
var placeholders = Map.of(
"message", Component.nullToEmpty(message)
);
var requestMessage = locale.get(
"helpRequestMessage",
sourceContext,
placeholders
);
source.getServer().sendSystemMessage(requestMessage);
source.getServer().getPlayerList().getPlayers().forEach(player -> {
if (Permissions.check(player, getPermissionNode("operator"), 1)) {
player.sendSystemMessage(requestMessage);
}
});
source.sendSuccess(() -> locale.get("helpRequestFeedback", sourceContext, placeholders), false);
return 1;
}));
}
}

View file

@ -0,0 +1,10 @@
package me.alexdevs.solstice.modules.helpOp.data;
import java.util.Map;
public class HelpOpLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("helpRequestMessage", "❗ <gold>[%player:displayname%]</gold> <gray>${message}</gray>"),
Map.entry("helpRequestFeedback", "<gold>Help request sent: </gold><gray>${message}</gray>")
);
}

View file

@ -0,0 +1,39 @@
package me.alexdevs.solstice.modules.home;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.home.commands.*;
import me.alexdevs.solstice.modules.home.data.HomeConfig;
import me.alexdevs.solstice.modules.home.data.HomeLocale;
import me.alexdevs.solstice.modules.home.data.HomePlayerData;
import java.util.UUID;
public class HomeModule extends ModuleBase.Toggleable {
public static final String ID = "home";
public HomeModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, HomeConfig.class, HomeConfig::new);
Solstice.playerData.registerData(ID, HomePlayerData.class, HomePlayerData::new);
Solstice.localeManager.registerModule(ID, HomeLocale.MODULE);
commands.add(new HomeCommand(this));
commands.add(new SetHomeCommand(this));
commands.add(new HomesCommand(this));
commands.add(new DeleteHomeCommand(this));
commands.add(new HomeOtherCommand(this));
}
public HomePlayerData getData(UUID player) {
return Solstice.playerData.get(player).getData(HomePlayerData.class);
}
public HomeConfig getConfig() {
return Solstice.configManager.getData(HomeConfig.class);
}
}

View file

@ -0,0 +1,74 @@
package me.alexdevs.solstice.modules.home.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.home.HomeModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class DeleteHomeCommand extends ModCommand<HomeModule> {
public DeleteHomeCommand(HomeModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("delhome");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> execute(context, "home"))
.then(argument("name", StringArgumentType.word())
.suggests((context, builder) -> {
if (!context.getSource().isPlayer())
return SharedSuggestionProvider.suggest(new String[]{}, builder);
var data = module.getData(context.getSource().getPlayer().getUUID());
return SharedSuggestionProvider.suggest(data.homes.keySet().stream(), builder);
})
.executes(context -> execute(context, StringArgumentType.getString(context, "name"))));
}
private int execute(CommandContext<CommandSourceStack> context, String name) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var data = module.getData(player.getUUID());
var playerContext = PlaceholderContext.of(player);
var placeholders = Map.of(
"home", Component.nullToEmpty(name)
);
if (!data.homes.containsKey(name)) {
context.getSource().sendSuccess(() -> module.locale().get(
"homeNotFound",
playerContext,
placeholders
), false);
return 1;
}
data.homes.remove(name);
context.getSource().sendSuccess(() -> module.locale().get(
"homeDeleted",
playerContext,
placeholders
), false);
return 1;
}
}

View file

@ -0,0 +1,79 @@
package me.alexdevs.solstice.modules.home.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.home.HomeModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class HomeCommand extends ModCommand<HomeModule> {
public HomeCommand(HomeModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("home");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> execute(context, "home"))
.then(argument("name", StringArgumentType.word())
.suggests((context, builder) -> {
if (!context.getSource().isPlayer())
return SharedSuggestionProvider.suggest(new String[]{}, builder);
var data = module.getData(context.getSource().getPlayer().getUUID());
return SharedSuggestionProvider.suggest(data.homes.keySet().stream(), builder);
})
.executes(context -> execute(context, StringArgumentType.getString(context, "name"))));
}
private int execute(CommandContext<CommandSourceStack> context, String name) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var data = module.getData(player.getUUID());
var playerContext = PlaceholderContext.of(player);
var placeholders = Map.of(
"home", Component.nullToEmpty(name)
);
if (!data.homes.containsKey(name)) {
context.getSource().sendSuccess(() ->
module.locale().get(
"homeNotFound",
playerContext,
placeholders
), false);
return 0;
}
context.getSource().sendSuccess(() ->
module.locale().get(
"teleporting",
playerContext,
placeholders
), false);
var homePosition = data.homes.get(name);
homePosition.teleport(player);
return 1;
}
}

View file

@ -0,0 +1,76 @@
package me.alexdevs.solstice.modules.home.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.home.HomeModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class HomeOtherCommand extends ModCommand<HomeModule> {
public HomeOtherCommand(HomeModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("homeother");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require("others", 2))
.then(argument("player", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.executes(context -> execute(context, "home"))
.then(argument("name", StringArgumentType.word())
.executes(context -> execute(context, StringArgumentType.getString(context, "name")))));
}
private int execute(CommandContext<CommandSourceStack> context, String name) throws CommandSyntaxException {
var sourcePlayer = context.getSource().getPlayerOrException();
var profile = LocalGameProfile.getProfile(context, "player");
var playerContext = PlaceholderContext.of(context.getSource().getPlayer());
var data = module.getData(profile.getId());
var placeholders = Map.of(
"home", Component.nullToEmpty(name),
"owner", Component.nullToEmpty(profile.getName())
);
if (!data.homes.containsKey(name)) {
context.getSource().sendSuccess(() ->
module.locale().get(
"homeNotFound",
playerContext,
placeholders
), false);
return 1;
}
context.getSource().sendSuccess(() ->
module.locale().get(
"teleportingOther",
playerContext,
placeholders
), true);
var homePosition = data.homes.get(name);
homePosition.teleport(sourcePlayer);
return 1;
}
}

View file

@ -0,0 +1,133 @@
package me.alexdevs.solstice.modules.home.commands;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.home.HomeModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class HomesCommand extends ModCommand<HomeModule> {
public HomesCommand(HomeModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("homes");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(this::execute)
.then(argument("player", StringArgumentType.word())
.requires(require("others", 2))
.suggests(LocalGameProfile::suggest)
.executes(context -> executeOthers(context, LocalGameProfile.getProfile(context, "player"))));
}
private int execute(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var data = module.getData(player.getUUID());
var homeList = data.homes.keySet().stream().toList();
var playerContext = PlaceholderContext.of(player);
if (homeList.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get(
"noHomes",
playerContext
), false);
return 1;
}
var listText = Component.empty();
var comma = module.locale().get("homesComma");
for (var i = 0; i < homeList.size(); i++) {
if (i > 0) {
listText = listText.append(comma);
}
var placeholders = Map.of(
"home", Component.nullToEmpty(homeList.get(i))
);
listText = listText.append(module.locale().get(
"homesFormat",
playerContext,
placeholders
));
}
var placeholders = Map.of(
"homeList", (Component) listText
);
context.getSource().sendSuccess(() -> module.locale().get(
"homeList",
playerContext,
placeholders
), false);
return homeList.size();
}
private int executeOthers(CommandContext<CommandSourceStack> context, GameProfile profile) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var playerContext = PlaceholderContext.of(player);
var data = module.getData(profile.getId());
var homeList = data.homes.keySet().stream().toList();
if (homeList.isEmpty()) {
var placeholders = Map.of(
"owner", Component.nullToEmpty(profile.getName())
);
context.getSource().sendSuccess(() -> module.locale().get(
"noHomesOther",
playerContext,
placeholders
), false);
return 1;
}
var listText = Component.empty();
var comma = module.locale().get("homesComma");
for (var i = 0; i < homeList.size(); i++) {
if (i > 0) {
listText = listText.append(comma);
}
var placeholders = Map.of(
"home", Component.nullToEmpty(homeList.get(i)),
"owner", Component.nullToEmpty(profile.getName())
);
listText = listText.append(module.locale().get(
"homesFormatOther",
playerContext,
placeholders
));
}
var placeholders = Map.of(
"homeList", listText,
"owner", Component.nullToEmpty(profile.getName())
);
context.getSource().sendSuccess(() -> module.locale().get(
"homeListOther",
playerContext,
placeholders
), false);
return homeList.size();
}
}

View file

@ -0,0 +1,109 @@
package me.alexdevs.solstice.modules.home.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.home.HomeModule;
import me.alexdevs.solstice.api.text.Components;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class SetHomeCommand extends ModCommand<HomeModule> {
public SetHomeCommand(HomeModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("sethome");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> execute(context,
"home",
false))
.then(argument("name", StringArgumentType.word())
.executes(context -> execute(context,
StringArgumentType.getString(context, "name"),
false))
.then(literal("force")
.executes(context -> execute(context,
StringArgumentType.getString(context, "name"),
true))));
}
private int execute(CommandContext<CommandSourceStack> context, String name, boolean forced) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var data = module.getData(player.getUUID());
var homes = data.homes;
var playerContext = PlaceholderContext.of(player);
var placeholders = Map.of(
"home", Component.nullToEmpty(name),
"forceSetButton", Components.button(
module.locale().raw("forceSetLabel"),
module.locale().raw("forceSetHover"),
"/sethome " + name + " force"
)
);
var exists = homes.containsKey(name);
if (exists && !forced) {
var text = module.locale().get(
"homeExists",
playerContext,
placeholders
);
context.getSource().sendSuccess(() -> text, false);
return 0;
}
homes.remove(name);
var groups = module.getConfig().homes;
var maxHomes = Integer.MIN_VALUE;
for(var entry : groups.entrySet()) {
var group = entry.getKey();
if(Permissions.check(player, "group." + group)) {
maxHomes = Math.max(maxHomes, entry.getValue());
}
}
var allowUnlimited = Permissions.check(player, getPermissionNode("unlimited"), 3);
if (!allowUnlimited && homes.size() >= maxHomes) {
context.getSource().sendSuccess(() -> module.locale().get(
"maxHomesReached",
playerContext,
placeholders
), false);
return 0;
}
var homePosition = new ServerLocation(player);
homes.put(name, homePosition);
context.getSource().sendSuccess(() -> module.locale().get(
"homeSetSuccess",
playerContext,
placeholders
), false);
return 1;
}
}

View file

@ -0,0 +1,16 @@
package me.alexdevs.solstice.modules.home.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.Map;
@ConfigSerializable
public class HomeConfig {
@Comment("The amount of homes a player can set based on their permission group. <group> = <max homes>.\nUse the permission 'solstice.home.unlimited' to bypass this limit.")
public Map<String, Integer> homes = Map.of(
"default", 5,
"vip", 10
);
}

View file

@ -0,0 +1,24 @@
package me.alexdevs.solstice.modules.home.data;
import java.util.Map;
public class HomeLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("teleporting", "<gold>Teleporting to <yellow>${home}</yellow></gold>"),
Map.entry("teleportingOther", "<gold>Teleporting to <yellow>${owner}'s ${home}</yellow></gold>"),
Map.entry("homeExists", "<gold>You already have set this home.</gold>\n ${forceSetButton}"),
Map.entry("homeNotFound", "<red>The home <yellow>${home}</yellow> does not exist!</red>"),
Map.entry("maxHomesReached", "<red>You have reached the maximum amount of homes!</red>"),
Map.entry("homeSetSuccess", "<gold>New home <yellow>${home}</yellow> set!</gold>"),
Map.entry("forceSetLabel", "<yellow>Force set home</yellow>"),
Map.entry("forceSetHover", "Click to force setting new home"),
Map.entry("homeDeleted", "<gold>Home <yellow>${home}</yellow> deleted!</gold>"),
Map.entry("homeList", "<gold>Your homes: ${homeList}</gold>"),
Map.entry("homeListOther", "<gold><yellow>${owner}</yellow>'s homes: ${homeList}</gold>"),
Map.entry("homesFormat", "<run_cmd:'/home ${home}'><hover:'Click to teleport'><yellow>${home}</yellow></hover></run_cmd>"),
Map.entry("homesFormatOther", "<run_cmd:'/homeother ${owner} ${home}'><hover:'Click to teleport'><yellow>${home}</yellow></hover></run_cmd>"),
Map.entry("homesComma", "<gold>, </gold>"),
Map.entry("noHomes", "<gold>You did not set any home so far.</gold>"),
Map.entry("noHomesOther", "<gold><yellow>${owner}</yellow> did not set any home so far.</gold>")
);
}

View file

@ -0,0 +1,11 @@
package me.alexdevs.solstice.modules.home.data;
import me.alexdevs.solstice.api.ServerLocation;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import java.util.concurrent.ConcurrentHashMap;
@ConfigSerializable
public class HomePlayerData {
public ConcurrentHashMap<String, ServerLocation> homes = new ConcurrentHashMap<>();
}

View file

@ -0,0 +1,17 @@
package me.alexdevs.solstice.modules.ignite;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.ignite.commands.IgniteCommand;
public class IgniteModule extends ModuleBase.Toggleable {
public static final String ID = "ignite";
public IgniteModule() {
super(ID);
}
@Override
public void init() {
commands.add(new IgniteCommand(this));
}
}

View file

@ -0,0 +1,70 @@
package me.alexdevs.solstice.modules.ignite.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.ignite.IgniteModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class IgniteCommand extends ModCommand<IgniteModule> {
public static final int defaultTicks = 200; // 10 seconds
public IgniteCommand(IgniteModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("ignite");
}
public LiteralArgumentBuilder<CommandSourceStack> command(String command) {
return literal(command)
.requires(require(2))
.executes(context -> execute(context, null, null))
.then(argument("players", EntityArgument.players())
.executes(context -> execute(context, EntityArgument.getPlayers(context, "players"), null))
.then(argument("ticks", IntegerArgumentType.integer(0))
.executes(context ->
execute(context, EntityArgument.getPlayers(context, "players"), IntegerArgumentType.getInteger(context, "ticks"))
)
)
);
}
private int execute(CommandContext<CommandSourceStack> context, @Nullable Collection<ServerPlayer> players, @Nullable Integer ticks) throws CommandSyntaxException {
var source = context.getSource();
if (players == null) {
ignite(source, source.getPlayerOrException(), ticks);
return 1;
} else {
for (ServerPlayer player : players) {
ignite(source, player, ticks);
}
return players.size();
}
}
private void ignite(CommandSourceStack source, ServerPlayer player, @Nullable Integer ticks) {
if (ticks == null) {
player.setRemainingFireTicks(defaultTicks);
source.sendSuccess(() -> Component.literal("Ignited ").append(source.getDisplayName()), true);
} else {
player.setRemainingFireTicks(ticks);
source.sendSuccess(() -> Component.literal("Ignited ").append(source.getDisplayName()).append(Component.nullToEmpty(String.format(" for %d ticks", ticks))), true);
}
}
}

View file

@ -0,0 +1,42 @@
package me.alexdevs.solstice.modules.ignore;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.ignore.commands.IgnoreCommand;
import me.alexdevs.solstice.modules.ignore.commands.IgnoreListCommand;
import me.alexdevs.solstice.modules.ignore.data.IgnoreLocale;
import me.alexdevs.solstice.modules.ignore.data.IgnorePlayerData;
import me.alexdevs.solstice.modules.styling.StylingModule;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.server.level.ServerPlayer;
import java.util.UUID;
public class IgnoreModule extends ModuleBase.Toggleable {
public static final String ID = "ignore";
public IgnoreModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, IgnoreLocale.MODULE);
Solstice.playerData.registerData(ID, IgnorePlayerData.class, IgnorePlayerData::new);
commands.add(new IgnoreCommand(this));
commands.add(new IgnoreListCommand(this));
}
@Override
public boolean isEnabled() {
return super.isEnabled() && Solstice.modules.getModule(StylingModule.class).isEnabled();
}
public IgnorePlayerData getPlayerData(UUID playerUuid) {
return Solstice.playerData.get(playerUuid).getData(IgnorePlayerData.class);
}
public boolean isIgnoring(ServerPlayer player, ServerPlayer target) {
return getPlayerData(player.getUUID()).ignoredPlayers.contains(target.getUUID()) && !Permissions.check(target, this.getPermissionNode("exempt"), 2);
}
}

View file

@ -0,0 +1,76 @@
package me.alexdevs.solstice.modules.ignore.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.ignore.IgnoreModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class IgnoreCommand extends ModCommand<IgnoreModule> {
public IgnoreCommand(IgnoreModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("ignore");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.then(argument("target", StringArgumentType.word())
.suggests((context, builder) -> {
var player = context.getSource().getPlayerOrException();
var playerManager = context.getSource().getServer().getPlayerList();
return SharedSuggestionProvider.suggest(Arrays.stream(playerManager.getPlayerNamesArray()).filter(s -> !s.equals(player.getGameProfile().getName())), builder);
})
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var targetName = StringArgumentType.getString(context, "target");
context.getSource().getServer().getProfileCache().getAsync(targetName).thenAcceptAsync(profileOpt -> {
var playerContext = PlaceholderContext.of(player);
if (profileOpt.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("playerNotFound", playerContext), false);
return;
}
var profile = profileOpt.get();
if (profile.getId().equals(player.getGameProfile().getId())) {
context.getSource().sendSuccess(() -> module.locale().get("targetIsSelf", playerContext), false);
return;
}
var playerData = module.getPlayerData(player.getUUID());
var map = Map.of("targetName", Component.nullToEmpty(profile.getName()));
if (playerData.ignoredPlayers.contains(profile.getId())) {
playerData.ignoredPlayers.remove(profile.getId());
context.getSource().sendSuccess(() -> module.locale().get("unblockedPlayer", playerContext, map), false);
} else {
playerData.ignoredPlayers.add(profile.getId());
context.getSource().sendSuccess(() -> module.locale().get("blockedPlayer", playerContext, map), false);
}
});
return 1;
}));
}
}

View file

@ -0,0 +1,79 @@
package me.alexdevs.solstice.modules.ignore.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.ignore.IgnoreModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.literal;
public class IgnoreListCommand extends ModCommand<IgnoreModule> {
public IgnoreListCommand(IgnoreModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("ignorelist");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var playerData = module.getPlayerData(player.getUUID());
var ignoreList = playerData.ignoredPlayers;
var playerContext = PlaceholderContext.of(player);
if (ignoreList.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("ignoreListEmpty",
playerContext
), false);
return 1;
}
var listText = Component.empty();
var comma = module.locale().get("ignoreListComma");
for (var i = 0; i < ignoreList.size(); i++) {
if (i > 0) {
listText = listText.append(comma);
}
String playerName;
var gameProfile = context.getSource().getServer().getProfileCache().get(ignoreList.get(i));
if (gameProfile.isPresent()) {
playerName = gameProfile.get().getName();
} else {
playerName = ignoreList.get(i).toString();
}
var placeholders = Map.of(
"player", Component.nullToEmpty(playerName)
);
listText = listText.append(module.locale().get(
"ignoreListFormat",
playerContext,
placeholders
));
}
var placeholders = Map.of(
"playerList", (Component) listText
);
context.getSource().sendSuccess(() -> module.locale().get(
"ignoreList",
playerContext,
placeholders
), false);
return 1;
});
}
}

View file

@ -0,0 +1,16 @@
package me.alexdevs.solstice.modules.ignore.data;
import java.util.Map;
public class IgnoreLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("playerNotFound", "<red>Could not find this player</red>"),
Map.entry("targetIsSelf", "<red>You cannot ignore yourself.</red>"),
Map.entry("blockedPlayer", "<yellow>${targetName}</yellow> <gold>is now ignored.</gold>"),
Map.entry("unblockedPlayer", "<yellow>${targetName}</yellow> <green>is no longer ignored.</green>"),
Map.entry("ignoreList", "<gold>Ignored players: ${playerList}</gold>"),
Map.entry("ignoreListFormat", "<run_cmd:'/ignore ${player}'><hover:'Click to unblock'><yellow>${player}</yellow></hover></run_cmd>"),
Map.entry("ignoreListComma", "<gold>, </gold>"),
Map.entry("ignoreListEmpty", "<gold>You are not ignoring anyone at the moment.</gold>")
);
}

View file

@ -0,0 +1,8 @@
package me.alexdevs.solstice.modules.ignore.data;
import java.util.ArrayList;
import java.util.UUID;
public class IgnorePlayerData {
public ArrayList<UUID> ignoredPlayers = new ArrayList<>();
}

View file

@ -0,0 +1,135 @@
package me.alexdevs.solstice.modules.info;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.Paths;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.api.text.Format;
import me.alexdevs.solstice.modules.info.commands.InfoCommand;
import me.alexdevs.solstice.modules.info.commands.MotdCommand;
import me.alexdevs.solstice.modules.info.commands.RulesCommand;
import me.alexdevs.solstice.modules.info.data.InfoConfig;
import me.alexdevs.solstice.modules.info.data.InfoLocale;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
public class InfoModule extends ModuleBase.Toggleable {
public static final String ID = "info";
private static final String[] startingPages = new String[]{
"motd.txt",
"rules.txt",
"formatting.txt"
};
public final String nameFilterRegex = "[^a-z0-9-]";
private final Path infoDir;
public InfoModule() {
super(ID);
infoDir = Paths.configDirectory.resolve("info");
}
@Override
public void init() {
Solstice.configManager.registerData(ID, InfoConfig.class, InfoConfig::new);
Solstice.localeManager.registerModule(ID, InfoLocale.MODULE);
commands.add(new InfoCommand(this));
commands.add(new MotdCommand(this));
commands.add(new RulesCommand(this));
if (!infoDir.toFile().isDirectory()) {
if (!infoDir.toFile().mkdirs()) {
Solstice.LOGGER.error("Couldn't create info directory");
return;
}
var classLoader = Solstice.class.getClassLoader();
var infoDirBase = "assets/" + Solstice.MOD_ID + "/info/";
for (var name : startingPages) {
var outputPath = infoDir.resolve(name);
try (var inputStream = classLoader.getResourceAsStream(infoDirBase + name)) {
if (inputStream == null) {
Solstice.LOGGER.warn("Missing {} info file in resources, skipping", name);
continue;
}
var content = inputStream.readAllBytes();
Files.write(outputPath, content);
} catch (IOException e) {
Solstice.LOGGER.error("Could not read info file {} from resources", name, e);
}
}
}
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
if (getConfig().enableMotd) {
if (!exists("motd")) {
Solstice.LOGGER.warn("Could not send MOTD because info/motd.txt does not exist!");
return;
}
Solstice.nextTick(() -> {
var motd = buildMotd(PlaceholderContext.of(handler.getPlayer()));
handler.getPlayer().sendSystemMessage(motd);
});
}
});
}
public InfoConfig getConfig() {
return Solstice.configManager.getData(InfoConfig.class);
}
public Component buildMotd(PlaceholderContext context) {
return getPage("motd", context);
}
private String sanitize(String name) {
return name.toLowerCase().replaceAll(nameFilterRegex, "");
}
public Collection<String> enumerate() {
return Arrays.stream(Objects.requireNonNull(infoDir.toFile().listFiles()))
.map(f -> f.getName().replace(".txt", "")).toList();
}
public boolean exists(String name) {
name = sanitize(name);
var infoFile = infoDir.resolve(name + ".txt");
return infoFile.toFile().exists();
}
public Component getPage(String name, @Nullable PlaceholderContext context) {
name = sanitize(name);
if (!exists(name)) {
return locale().get("pageNotFound");
}
var infoFile = infoDir.resolve(name + ".txt");
try {
// Use readAllLines instead readString to avoid \r chars; looking at you, Windows.
var lines = Files.readAllLines(infoFile, StandardCharsets.UTF_8);
StringBuilder content = new StringBuilder();
for (var line : lines) {
content.append(line).append("\n");
}
var output = content.toString().trim();
if (context != null)
return Format.parse(output, context);
else
return Component.nullToEmpty(output);
} catch (IOException e) {
Solstice.LOGGER.error("Could not read info file", e);
return locale().get("pageError");
}
}
}

View file

@ -0,0 +1,95 @@
package me.alexdevs.solstice.modules.info.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.module.Utils;
import me.alexdevs.solstice.modules.info.InfoModule;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class InfoCommand extends ModCommand<InfoModule> {
public InfoCommand(InfoModule module) {
super(module);
}
@Override
public void register(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext commandRegistry, Commands.CommandSelection environment) {
// WorldEdit's /info -> /tool info
Utils.removeCommands(dispatcher, "info");
super.register(dispatcher, commandRegistry, environment);
}
@Override
public List<String> getNames() {
return List.of("info", "pages");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(true))
.executes(context -> {
var source = context.getSource();
var pageList = module.enumerate();
var sourceContext = PlaceholderContext.of(source);
if (pageList.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get(
"noPages",
sourceContext
), false);
return 1;
}
var listText = Component.empty();
var comma = module.locale().get("pagesComma");
var list = pageList.stream().toList();
for (var i = 0; i < list.size(); i++) {
if (i > 0) {
listText = listText.append(comma);
}
var placeholders = Map.of(
"page", Component.nullToEmpty(list.get(i))
);
listText = listText.append(module.locale().get(
"pagesFormat",
sourceContext,
placeholders
));
}
var placeholders = Map.of(
"pageList", (Component) listText
);
context.getSource().sendSuccess(() -> module.locale().get(
"pageList",
sourceContext,
placeholders
), false);
return 1;
})
.then(argument("page", StringArgumentType.word())
.suggests((context, builder) -> SharedSuggestionProvider.suggest(module.enumerate(), builder))
.executes(context -> {
var sourceContext = PlaceholderContext.of(context.getSource());
var page = module.getPage(StringArgumentType.getString(context, "page"), sourceContext);
context.getSource().sendSuccess(() -> page, false);
return 1;
}));
}
}

View file

@ -0,0 +1,34 @@
package me.alexdevs.solstice.modules.info.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.info.InfoModule;
import net.minecraft.commands.CommandSourceStack;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class MotdCommand extends ModCommand<InfoModule> {
public MotdCommand(InfoModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("motd");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require("motd", true))
.executes(context -> {
var sourceContext = PlaceholderContext.of(context.getSource());
context.getSource().sendSystemMessage(module.buildMotd(sourceContext));
return 1;
});
}
}

View file

@ -0,0 +1,33 @@
package me.alexdevs.solstice.modules.info.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.info.InfoModule;
import net.minecraft.commands.CommandSourceStack;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class RulesCommand extends ModCommand<InfoModule> {
public RulesCommand(InfoModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("rules");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require("rules", true))
.executes(context -> {
var sourceContext = PlaceholderContext.of(context.getSource());
var rules = module.getPage("rules", sourceContext);
context.getSource().sendSuccess(() -> rules, false);
return 1;
});
}
}

View file

@ -0,0 +1,10 @@
package me.alexdevs.solstice.modules.info.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ConfigSerializable
public class InfoConfig {
@Comment("Send the 'Message Of The Day' to the player when joining the server. Content is in the 'config/solstice/info/motd.txt' file.")
public boolean enableMotd = true;
}

View file

@ -0,0 +1,14 @@
package me.alexdevs.solstice.modules.info.data;
import java.util.Map;
public class InfoLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("pageNotFound", "<red>This page does not exist!</red>"),
Map.entry("pageError", "<red>There was an error opening the info page.</red>"),
Map.entry("pageList", "<gold>Available pages: ${pageList}</gold>"),
Map.entry("pagesFormat", "<run_cmd:'/info ${page}'><hover:'Click to read'><yellow>${page}</yellow></hover></run_cmd>"),
Map.entry("pagesComma", "<gold>, </gold>"),
Map.entry("noPages ", "<gold>There are no pages so far.</gold>")
);
}

View file

@ -0,0 +1,28 @@
package me.alexdevs.solstice.modules.inventorySee;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
public class ImmutableSlot extends Slot {
public ImmutableSlot(Container inventory, int index, int x, int y) {
super(inventory, index, x, y);
}
@Override
public boolean mayPickup(Player playerEntity) {
return false;
}
@Override
public boolean mayPlace(ItemStack stack) {
return false;
}
@Override
public boolean allowModification(Player player) {
return false;
}
}

View file

@ -0,0 +1,21 @@
package me.alexdevs.solstice.modules.inventorySee;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.inventorySee.commands.InventorySeeCommand;
import me.alexdevs.solstice.modules.inventorySee.data.InventorySeeLocale;
public class InventorySeeModule extends ModuleBase.Toggleable {
public static final String ID = "inventorysee";
public InventorySeeModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, InventorySeeLocale.MODULE);
commands.add(new InventorySeeCommand(this));
}
}

View file

@ -0,0 +1,215 @@
package me.alexdevs.solstice.modules.inventorySee.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import dev.emi.trinkets.api.TrinketsApi;
import eu.pb4.sgui.api.gui.SimpleGui;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.utils.PlayerUtils;
import me.alexdevs.solstice.integrations.TrinketsIntegration;
import me.alexdevs.solstice.modules.inventorySee.ImmutableSlot;
import me.alexdevs.solstice.modules.inventorySee.InventorySeeModule;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class InventorySeeCommand extends ModCommand<InventorySeeModule> {
public InventorySeeCommand(InventorySeeModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("invsee", "inventorysee");
}
private static final LinkedHashMap<Integer, MenuType<ChestMenu>> invSizes = new LinkedHashMap<>();
static {
invSizes.put(9, MenuType.GENERIC_9x1);
invSizes.put(18, MenuType.GENERIC_9x2);
invSizes.put(27, MenuType.GENERIC_9x3);
invSizes.put(36, MenuType.GENERIC_9x4);
invSizes.put(45, MenuType.GENERIC_9x5);
invSizes.put(54, MenuType.GENERIC_9x6);
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require(2))
.then(argument("player", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.executes(context -> {
var source = context.getSource();
var player = source.getPlayerOrException();
var targetProfile = LocalGameProfile.getProfile(context, "player");
var targetOnline = PlayerUtils.isOnline(targetProfile.getId());
if (!targetOnline && !Permissions.check(player, getPermissionNode("offline"), 3)) {
source.sendSuccess(() -> module.locale().get("offlineNotAllowed"), false);
return 0;
}
ServerPlayer target;
if (targetOnline) {
target = context.getSource().getServer().getPlayerList().getPlayer(targetProfile.getId());
if (Permissions.check(target, getPermissionNode("exempt"), 3)) {
source.sendSuccess(() -> module.locale().get("exempt"), false);
return 0;
}
} else {
target = PlayerUtils.loadOfflinePlayer(targetProfile);
if (Permissions.check(targetProfile, getPermissionNode("exempt"), 3, source.getServer()).getNow(false)) {
source.sendSuccess(() -> module.locale().get("exempt"), false);
return 0;
}
}
var canEdit = Permissions.check(player, getPermissionNode("edit"), 3);
var targetInventory = target.getInventory();
var container = new SimpleGui(MenuType.GENERIC_9x5, player, false) {
@Override
public void onClose() {
if (!targetOnline) {
PlayerUtils.saveOfflinePlayer(target);
}
}
};
for (var i = 0; i < targetInventory.getContainerSize(); i++) {
Slot slot;
if (canEdit) {
slot = new Slot(targetInventory, i, 0, 0);
} else {
slot = new ImmutableSlot(targetInventory, i, 0, 0);
}
container.setSlotRedirect(i, slot);
}
var barrier = new ItemStack(Items.BLACK_STAINED_GLASS_PANE);
barrier.set(DataComponents.CUSTOM_NAME, Component.literal(""));
for (var i = targetInventory.getContainerSize(); i < container.getSize(); i++) {
container.setSlot(i, barrier);
}
container.setTitle(target.getName());
container.open();
var map = Map.of(
"user", Component.nullToEmpty(target.getGameProfile().getName())
);
source.sendSuccess(() -> module.locale().get("openedInventory", map), true);
return 1;
})
.then(literal("trinkets")
.executes(context -> {
var source = context.getSource();
var player = source.getPlayerOrException();
var targetProfile = LocalGameProfile.getProfile(context, "player");
var targetOnline = PlayerUtils.isOnline(targetProfile.getId());
if (!targetOnline && !Permissions.check(player, getPermissionNode("offline"), 3)) {
source.sendSuccess(() -> module.locale().get("offlineNotAllowed"), false);
return 0;
}
ServerPlayer target;
if (targetOnline) {
target = context.getSource().getServer().getPlayerList().getPlayer(targetProfile.getId());
if (Permissions.check(target, getPermissionNode("exempt"), 3)) {
source.sendSuccess(() -> module.locale().get("exempt"), false);
return 0;
}
} else {
target = PlayerUtils.loadOfflinePlayer(targetProfile);
if (Permissions.check(targetProfile, getPermissionNode("exempt"), 3, source.getServer()).getNow(false)) {
source.sendSuccess(() -> module.locale().get("exempt"), false);
return 0;
}
}
if (!TrinketsIntegration.isAvailable()) {
source.sendSuccess(() -> module.locale().get("trinketsNotInstalled"), false);
return 0;
}
var canEdit = Permissions.check(player, getPermissionNode("edit"), 3);
var trinkets = TrinketsApi.getTrinketComponent(target).orElse(null);
var slots = new ArrayList<Slot>();
for (var group : trinkets.getInventory().values()) {
for (var inventory : group.values()) {
for (var i = 0; i < inventory.getContainerSize(); i++) {
Slot slot;
if (canEdit) {
slot = new Slot(inventory, i, 0, 0);
} else {
slot = new ImmutableSlot(inventory, i, 0, 0);
}
slots.add(slot);
}
}
}
var size = slots.size();
MenuType<ChestMenu> handlerType = null;
for (var entry : invSizes.entrySet()) {
handlerType = entry.getValue();
if (size <= entry.getKey()) {
break;
}
}
var container = new SimpleGui(handlerType, player, false) {
@Override
public void onClose() {
if (!targetOnline) {
PlayerUtils.saveOfflinePlayer(target);
}
}
};
for (var i = 0; i < slots.size(); i++) {
var slot = slots.get(i);
container.setSlotRedirect(i, slot);
}
var barrier = new ItemStack(Items.BLACK_STAINED_GLASS_PANE);
barrier.set(DataComponents.CUSTOM_NAME, Component.literal(""));
for (var i = size; i < container.getSize(); i++) {
container.setSlot(i, barrier);
}
container.setTitle(target.getName());
container.open();
var map = Map.of(
"user", Component.nullToEmpty(target.getGameProfile().getName())
);
source.sendSuccess(() -> module.locale().get("openedTrinkets", map), true);
return 1;
}))
);
}
}

View file

@ -0,0 +1,14 @@
package me.alexdevs.solstice.modules.inventorySee.data;
import java.util.Map;
public class InventorySeeLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("exempt", "<gold>You cannot open this inventory because the user is exempt.</gold>"),
Map.entry("openedInventory", "<gold>Opened <yellow>${user}</yellow>'s inventory.</gold>"),
Map.entry("openedTrinkets", "<gold>Opened <yellow>${user}</yellow>'s trinkets inventory.</gold>"),
Map.entry("trinketsNotInstalled", "<gold>Trinkets not available because the mod is missing.</gold>"),
Map.entry("playerNotFound", "<gold>Player not found!</gold>"),
Map.entry("offlineNotAllowed", "<gold>You cannot open offline player inventories.</gold>")
);
}

View file

@ -0,0 +1,26 @@
package me.alexdevs.solstice.modules.item;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.item.commands.ItemLoreCommand;
import me.alexdevs.solstice.modules.item.commands.ItemNameCommand;
import me.alexdevs.solstice.modules.item.commands.MoreCommand;
import me.alexdevs.solstice.modules.item.commands.RepairCommand;
import me.alexdevs.solstice.modules.item.data.ItemLocale;
public class ItemModule extends ModuleBase.Toggleable {
public static final String ID = "item";
public ItemModule() {
super(ID);
}
@Override
public void init() {
Solstice.localeManager.registerModule(ID, ItemLocale.MODULE);
commands.add(new ItemLoreCommand(this));
commands.add(new ItemNameCommand(this));
commands.add(new RepairCommand(this));
commands.add(new MoreCommand(this));
}
}

View file

@ -0,0 +1,75 @@
package me.alexdevs.solstice.modules.item.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.text.Format;
import me.alexdevs.solstice.modules.item.ItemModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.component.ItemLore;
import java.util.ArrayList;
import java.util.List;
public class ItemLoreCommand extends ModCommand<ItemModule> {
public ItemLoreCommand(ItemModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("lore", "itemlore");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require("lore", 2))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var item = player.getMainHandItem();
if (item.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noItem"), false);
return 0;
}
item.remove(DataComponents.LORE);
context.getSource().sendSuccess(() -> module.locale().get("loreCleared"), false);
return 1;
})
.then(Commands.argument("lore", StringArgumentType.greedyString())
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var item = player.getMainHandItem();
var itemLore = StringArgumentType.getString(context, "lore");
if (item.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noItem"), false);
return 0;
}
var playerContext = PlaceholderContext.of(player);
var list = new ArrayList<Component>();
for(var line : itemLore.split("\\\\n")) {
list.add(Format.parse(line, playerContext));
}
item.set(DataComponents.LORE, new ItemLore(list));
context.getSource().sendSuccess(() -> module.locale().get("loreSet"), false);
return 1;
})
);
}
}

View file

@ -0,0 +1,64 @@
package me.alexdevs.solstice.modules.item.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import eu.pb4.placeholders.api.PlaceholderContext;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.api.text.Format;
import me.alexdevs.solstice.modules.item.ItemModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.core.component.DataComponents;
import java.util.List;
public class ItemNameCommand extends ModCommand<ItemModule> {
public ItemNameCommand(ItemModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("itemname");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require("name", 2))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var item = player.getMainHandItem();
if(item.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noItem"), false);
return 0;
}
item.remove(DataComponents.CUSTOM_NAME);
context.getSource().sendSuccess(() -> module.locale().get("nameCleared"), false);
return 1;
})
.then(Commands.argument("name", StringArgumentType.greedyString())
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var item = player.getMainHandItem();
var itemName = StringArgumentType.getString(context, "name");
if(item.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noItem"), false);
return 0;
}
var playerContext = PlaceholderContext.of(player);
item.set(DataComponents.CUSTOM_NAME, Format.parse(itemName, playerContext));
context.getSource().sendSuccess(() -> module.locale().get("nameSet"), false);
return 1;
})
);
}
}

View file

@ -0,0 +1,40 @@
package me.alexdevs.solstice.modules.item.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.item.ItemModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import java.util.List;
public class MoreCommand extends ModCommand<ItemModule> {
public MoreCommand(ItemModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("more", "stack");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require("more", 2))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var item = player.getMainHandItem();
if(item.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noItem"), false);
return 0;
}
item.setCount(item.getMaxStackSize());
context.getSource().sendSuccess(() -> module.locale().get("stackRefilled"), false);
return 1;
});
}
}

View file

@ -0,0 +1,54 @@
package me.alexdevs.solstice.modules.item.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.item.ItemModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.component.DataComponents;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class RepairCommand extends ModCommand<ItemModule> {
public RepairCommand(ItemModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("repair", "repairitem");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return literal(name)
.requires(require("repair", 2))
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var item = player.getMainHandItem();
if(item.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noItem"), false);
return 0;
}
if(!item.isDamageableItem()) {
context.getSource().sendSuccess(() -> module.locale().get("notRepairable"), false);
return 0;
}
if(item.isDamaged()) {
// Removes the NBT tag altogether instead of just setting it to 0
item.remove(DataComponents.DAMAGE);
context.getSource().sendSuccess(() -> module.locale().get("repaired"), false);
return 1;
} else {
context.getSource().sendSuccess(() -> module.locale().get("alreadyRepaired"), false);
return 0;
}
});
}
}

View file

@ -0,0 +1,17 @@
package me.alexdevs.solstice.modules.item.data;
import java.util.Map;
public class ItemLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("noItem", "<gold>You are not holding any item!</gold>"),
Map.entry("nameSet", "<green>Successfully set the new item name!</green>"),
Map.entry("nameCleared", "<gold>Item name cleared!</gold>"),
Map.entry("loreSet", "<green>Successfully set the new item lore!</green>"),
Map.entry("loreCleared", "<gold>Item lore cleared!</gold>"),
Map.entry("repaired", "<green>Item repaired!</green>"),
Map.entry("alreadyRepaired", "<gold>This item is already repaired!</gold>"),
Map.entry("notRepairable", "<gold>This item is not repairable!</gold>"),
Map.entry("stackRefilled", "<green>Item stack refilled!</green>")
);
}

View file

@ -0,0 +1,230 @@
package me.alexdevs.solstice.modules.jail;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.api.command.TimeSpan;
import me.alexdevs.solstice.api.events.CommandEvents;
import me.alexdevs.solstice.api.module.ModuleBase;
import me.alexdevs.solstice.modules.jail.commands.CheckJailCommand;
import me.alexdevs.solstice.modules.jail.commands.JailCommand;
import me.alexdevs.solstice.modules.jail.commands.JailsCommand;
import me.alexdevs.solstice.modules.jail.commands.UnjailCommand;
import me.alexdevs.solstice.modules.jail.data.JailConfig;
import me.alexdevs.solstice.modules.jail.data.JailLocale;
import me.alexdevs.solstice.modules.jail.data.JailPlayerData;
import me.alexdevs.solstice.modules.jail.data.JailServerData;
import me.alexdevs.solstice.modules.spawn.SpawnModule;
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.player.*;
import net.fabricmc.fabric.api.message.v1.ServerMessageEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class JailModule extends ModuleBase.Toggleable {
public static final String ID = "jail";
public JailModule() {
super(ID);
}
@Override
public void init() {
Solstice.configManager.registerData(ID, JailConfig.class, JailConfig::new);
Solstice.localeManager.registerModule(ID, JailLocale.MODULE);
Solstice.playerData.registerData(ID, JailPlayerData.class, JailPlayerData::new);
Solstice.serverData.registerData(ID, JailServerData.class, JailServerData::new);
commands.add(new JailsCommand(this));
commands.add(new JailCommand(this));
commands.add(new UnjailCommand(this));
commands.add(new CheckJailCommand(this));
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
Solstice.nextTick(() -> {
var data = getPlayer(handler.getPlayer().getUUID());
if (data.jailed) {
sendToJail(handler.getPlayer());
} else if (data.teleportToPreviousLocation) {
unjailPlayer(handler.getPlayer().getUUID());
}
});
});
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, player, alive) -> {
if (isPlayerJailed(player.getUUID())) {
sendToJail(player);
}
});
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
Solstice.scheduler.scheduleAtFixedRate(this::checkJailedPlayers, 0, 1, TimeUnit.SECONDS);
});
CommandEvents.ALLOW_COMMAND.register((source, command) -> {
if (!source.isPlayer())
return true;
if (isPlayerJailed(source.getPlayer().getUUID())) {
var config = getConfig();
var cmd = command.split(" ")[0];
var canRun = config.allowedCommands.contains(cmd);
if (!canRun) {
source.sendSuccess(() -> locale().get("cannotRunCommands"), false);
}
return canRun;
}
return true;
});
AttackBlockCallback.EVENT.register((player, world, hand, blockPos, direction) -> {
if (isPlayerJailed(player.getUUID())) {
player.sendSystemMessage(locale().get("cannotBreakBlocks"));
return InteractionResult.FAIL;
}
return InteractionResult.PASS;
});
AttackEntityCallback.EVENT.register((player, world, hand, entity, entityHitResult) -> {
if (isPlayerJailed(player.getUUID())) {
player.sendSystemMessage(locale().get("cannotAttackEntities"));
return InteractionResult.FAIL;
}
return InteractionResult.PASS;
});
PlayerBlockBreakEvents.BEFORE.register((world, player, blockPos, blockState, blockEntity) -> {
if (isPlayerJailed(player.getUUID())) {
player.sendSystemMessage(locale().get("cannotBreakBlocks"));
return false;
}
return true;
});
UseBlockCallback.EVENT.register((player, world, hand, blockHitResult) -> {
if (isPlayerJailed(player.getUUID())) {
player.sendSystemMessage(locale().get("cannotUseBlocks"));
return InteractionResult.FAIL;
}
return InteractionResult.PASS;
});
UseEntityCallback.EVENT.register((player, world, hand, entity, entityHitResult) -> {
if (isPlayerJailed(player.getUUID())) {
player.sendSystemMessage(locale().get("cannotUseEntities"));
return InteractionResult.FAIL;
}
return InteractionResult.PASS;
});
UseItemCallback.EVENT.register((player, world, hand) -> {
var stack = player.getItemInHand(hand);
if (isPlayerJailed(player.getUUID())) {
player.sendSystemMessage(locale().get("cannotUseItems"));
return InteractionResultHolder.fail(stack);
}
return InteractionResultHolder.pass(stack);
});
ServerMessageEvents.ALLOW_CHAT_MESSAGE.register((signedMessage, player, parameters) -> {
if (isPlayerJailed(player.getUUID())) {
var config = getConfig();
if(config.mute) {
player.sendSystemMessage(locale().get("cannotSpeak"));
return false;
}
}
return true;
});
}
private void checkJailedPlayers() {
// run on server thread
Solstice.nextTick(() -> {
var players = Solstice.server.getPlayerList().getPlayers();
for (var player : players) {
var data = getPlayer(player.getUUID());
if (isPlayerJailed(player.getUUID()) && data.jailTime > 0) {
if (data.jailedOn != null && data.jailedOn.getTime() + (data.jailTime * 1000L) < System.currentTimeMillis()) {
unjailPlayer(player.getUUID());
}
}
}
});
}
public JailConfig getConfig() {
return Solstice.configManager.getData(JailConfig.class);
}
public Map<String, ServerLocation> getJails() {
return Solstice.serverData.getData(JailServerData.class).jails;
}
public JailPlayerData getPlayer(UUID uuid) {
return Solstice.playerData.get(uuid).getData(JailPlayerData.class);
}
public boolean isPlayerJailed(UUID uuid) {
return getPlayer(uuid).jailed;
}
public void sendToJail(ServerPlayer player) {
Solstice.nextTick(() -> {
var data = getPlayer(player.getUUID());
var jails = getJails();
var jail = jails.get(data.jailName);
if (jail != null) {
jail.teleport(player);
var map = Map.of(
"player", player.getName(),
"jail", Component.nullToEmpty(data.jailName),
"duration", Component.nullToEmpty(TimeSpan.toLongString(data.jailTime)),
"reason", Component.nullToEmpty(data.jailReason)
);
Component text;
if (data.jailTime > 0) {
if (data.jailReason != null) {
text = locale().get("playerJailedForWithReason", map);
} else {
text = locale().get("playerJailedFor", map);
}
} else {
text = locale().get("playerJailed", map);
}
player.displayClientMessage(text, false);
}
});
}
public void unjailPlayer(UUID uuid) {
var data = getPlayer(uuid);
data.jailed = false;
data.teleportToPreviousLocation = true;
var player = Solstice.server.getPlayerList().getPlayer(uuid);
if (player != null) {
data.teleportToPreviousLocation = false;
player.sendSystemMessage(locale().get("playerUnjailed"));
if (data.previousLocation != null) {
data.previousLocation.teleport(player);
} else {
var spawnModule = Solstice.modules.getModule(SpawnModule.class);
spawnModule.getGlobalSpawnPosition().teleport(player);
}
}
}
}

View file

@ -0,0 +1,103 @@
package me.alexdevs.solstice.modules.jail.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.command.TimeSpan;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.core.coreModule.data.CoreConfig;
import me.alexdevs.solstice.modules.jail.JailModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class CheckJailCommand extends ModCommand<JailModule> {
public CheckJailCommand(JailModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("checkjail");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require(2))
.then(Commands.argument("user", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.executes(context -> {
var user = LocalGameProfile.getProfile(context, "user");
var data = module.getPlayer(user.getId());
if (!data.jailed) {
context.getSource().sendSuccess(() -> module.locale().get("notJailed"), false);
return 0;
}
String operator;
if (new UUID(0, 0).equals(data.jailedBy)) {
operator = "Server";
} else {
var opProfile = context.getSource().getServer().getProfileCache().get(data.jailedBy);
if (opProfile.isPresent()) {
operator = opProfile.get().getName();
} else {
operator = data.jailedBy != null ? data.jailedBy.toString() : "Unknown";
}
}
String reason;
if (data.jailReason != null) {
reason = data.jailReason;
} else {
reason = module.locale().raw("infoJailReasonEmpty");
}
String duration;
if (data.jailTime == 0) {
duration = module.locale().raw("infoJailedForEmpty");
} else {
duration = TimeSpan.toLongString(data.jailTime);
}
var coreConfig = Solstice.configManager.getData(CoreConfig.class);
var df = new SimpleDateFormat(coreConfig.dateTimeFormat);
var map = Map.of(
"player", Component.nullToEmpty(user.getName()),
"jail", Component.nullToEmpty(data.jailName),
"operator", Component.nullToEmpty(operator),
"reason", Component.nullToEmpty(reason),
"duration", Component.nullToEmpty(duration),
"date", Component.nullToEmpty(df.format(data.jailedOn))
);
var text = Component.empty();
text.append(module.locale().get("infoHeader", map));
text.append("\n");
text.append(module.locale().get("infoJailedAt", map));
text.append("\n");
text.append(module.locale().get("infoJailedBy", map));
text.append("\n");
text.append(module.locale().get("infoJailReason", map));
text.append("\n");
text.append(module.locale().get("infoJailedFor", map));
text.append("\n");
text.append(module.locale().get("infoJailedOn", map));
context.getSource().sendSuccess(() -> text, false);
return 1;
})
);
}
}

View file

@ -0,0 +1,132 @@
package me.alexdevs.solstice.modules.jail.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.alexdevs.solstice.Solstice;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.command.TimeSpan;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.core.coreModule.data.CorePlayerData;
import me.alexdevs.solstice.modules.jail.JailModule;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class JailCommand extends ModCommand<JailModule> {
public JailCommand(JailModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("jail");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require("jail", 2))
.then(Commands.argument("user", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.then(Commands.argument("jail", StringArgumentType.word())
.suggests(this::suggestJails)
.executes(context -> execute(context, 0, null))
.then(Commands.argument("duration", TimeSpan.timeSpan())
.suggests(TimeSpan::suggest)
.executes(context -> execute(context, TimeSpan.getTimeSpan(context, "duration"), null))
.then(Commands.argument("reason", StringArgumentType.greedyString())
.executes(context -> execute(context, TimeSpan.getTimeSpan(context, "duration"), StringArgumentType.getString(context, "reason")))
)
)
)
);
}
private int execute(CommandContext<CommandSourceStack> context, int seconds, @Nullable String reason) throws CommandSyntaxException {
var source = context.getSource();
var profile = LocalGameProfile.getProfile(context, "user");
var data = module.getPlayer(profile.getId());
var coreData = Solstice.playerData.get(profile.getId()).getData(CorePlayerData.class);
if (data.jailed) {
source.sendSuccess(() -> module.locale().get("alreadyJailed"), false);
return 0;
}
var jailName = StringArgumentType.getString(context, "jail");
var jails = module.getJails();
if (!jails.containsKey(jailName)) {
source.sendSuccess(() -> module.locale().get("jailNotFound"), false);
return 0;
}
Permissions.check(profile, getPermissionNode("exempt")).thenAccept(granted -> {
if (granted) {
source.sendSuccess(() -> module.locale().get("playerExempt"), false);
return;
}
var player = source.getServer().getPlayerList().getPlayer(profile.getId());
data.jailed = true;
data.jailedBy = source.isPlayer() ? source.getPlayer().getUUID() : new UUID(0L, 0L);
data.jailedOn = new Date();
data.jailName = jailName;
data.jailTime = seconds;
data.jailReason = reason;
if (player != null) {
data.previousLocation = new ServerLocation(player);
} else {
data.previousLocation = coreData.logoffPosition;
}
var map = Map.of(
"player", Component.nullToEmpty(profile.getName()),
"jail", Component.nullToEmpty(jailName),
"duration", Component.nullToEmpty(TimeSpan.toLongString(seconds)),
"reason", Component.nullToEmpty(reason)
);
Component text;
if (seconds > 0) {
if (reason != null) {
text = module.locale().get("jailedForWithReason", map);
} else {
text = module.locale().get("jailedFor", map);
}
} else {
text = module.locale().get("jailed", map);
}
source.sendSuccess(() -> text, true);
if (player != null) {
module.sendToJail(player);
}
});
return 1;
}
private CompletableFuture<Suggestions> suggestJails(CommandContext<CommandSourceStack> context, SuggestionsBuilder builder) {
var jails = module.getJails().keySet().stream();
return SharedSuggestionProvider.suggest(jails, builder);
}
}

View file

@ -0,0 +1,149 @@
package me.alexdevs.solstice.modules.jail.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.alexdevs.solstice.api.ServerLocation;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.jail.JailModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class JailsCommand extends ModCommand<JailModule> {
public JailsCommand(JailModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("jails");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require(2))
.executes(this::listJails)
.then(Commands.literal("set")
.requires(require("set", 3))
.then(Commands.argument("name", StringArgumentType.word())
.executes(this::createJail)
))
.then(Commands.literal("delete")
.requires(require("set", 3))
.then(Commands.argument("name", StringArgumentType.word())
.suggests(this::suggestJails)
.executes(this::deleteJail)
))
.then(Commands.literal("tp")
.requires(require("tp", 2))
.then(Commands.argument("name", StringArgumentType.word())
.suggests(this::suggestJails)
.executes(this::teleport)
));
}
private CompletableFuture<Suggestions> suggestJails(CommandContext<CommandSourceStack> context, SuggestionsBuilder builder) {
var jails = module.getJails().keySet().stream();
return SharedSuggestionProvider.suggest(jails, builder);
}
private int listJails(CommandContext<CommandSourceStack> context) {
var jails = module.getJails().keySet().stream().toList();
if(jails.isEmpty()) {
context.getSource().sendSuccess(() -> module.locale().get("noJails"), false);
return 0;
}
var comma = module.locale().get("comma");
var list = Component.empty();
for(var i = 0; i < jails.size(); i++) {
if(i > 0) {
list.append(comma);
}
list.append(module.locale().get("listEntry", Map.of(
"jail", Component.nullToEmpty(jails.get(i))
)));
}
context.getSource().sendSuccess(() -> module.locale().get("jailList", Map.of(
"list", list
)), false);
return 1;
}
private int createJail(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var jailName = StringArgumentType.getString(context, "name");
var position = new ServerLocation(player);
var jails = module.getJails();
if (jails.containsKey(jailName)) {
context.getSource().sendSuccess(() -> module.locale().get("jailAlreadyExists"), false);
return 0;
}
jails.put(jailName, position);
var map = Map.of(
"jail", Component.nullToEmpty(jailName)
);
context.getSource().sendSuccess(() -> module.locale().get("created", map), true);
return 1;
}
private int deleteJail(CommandContext<CommandSourceStack> context) {
var jailName = StringArgumentType.getString(context, "name");
var jails = module.getJails();
if (!jails.containsKey(jailName)) {
context.getSource().sendSuccess(() -> module.locale().get("jailNotFound"), false);
return 0;
}
jails.remove(jailName);
var map = Map.of(
"jail", Component.nullToEmpty(jailName)
);
context.getSource().sendSuccess(() -> module.locale().get("deleted", map), true);
return 1;
}
private int teleport(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var jailName = StringArgumentType.getString(context, "name");
var jails = module.getJails();
if(!jails.containsKey(jailName)) {
context.getSource().sendSuccess(() -> module.locale().get("jailNotFound"), false);
return 0;
}
var jail = jails.get(jailName);
var map = Map.of(
"jail", Component.nullToEmpty(jailName)
);
context.getSource().sendSuccess(() -> module.locale().get("teleporting", map), true);
jail.teleport(player);
return 1;
}
}

View file

@ -0,0 +1,51 @@
package me.alexdevs.solstice.modules.jail.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.alexdevs.solstice.api.command.LocalGameProfile;
import me.alexdevs.solstice.api.module.ModCommand;
import me.alexdevs.solstice.modules.jail.JailModule;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import java.util.List;
import java.util.Map;
public class UnjailCommand extends ModCommand<JailModule> {
public UnjailCommand(JailModule module) {
super(module);
}
@Override
public List<String> getNames() {
return List.of("unjail");
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> command(String name) {
return Commands.literal(name)
.requires(require("unjail", 2))
.then(Commands.argument("user", StringArgumentType.word())
.suggests(LocalGameProfile::suggest)
.executes(context -> {
var profile = LocalGameProfile.getProfile(context, "user");
var data = module.getPlayer(profile.getId());
if (!data.jailed) {
context.getSource().sendSuccess(() -> module.locale().get("notJailed"), false);
return 0;
}
module.unjailPlayer(profile.getId());
var map = Map.of(
"player", Component.nullToEmpty(profile.getName())
);
context.getSource().sendSuccess(() -> module.locale().get("unjailed", map), false);
return 1;
})
);
}
}

View file

@ -0,0 +1,21 @@
package me.alexdevs.solstice.modules.jail.data;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.List;
@ConfigSerializable
public class JailConfig {
@Comment("List of commands the jailed players can execute.")
public List<String> allowedCommands = List.of(
"afk",
"ignore",
"msg", "tell", "w", "dm", "r", "reply",
"mail",
"info", "motd", "rules"
);
@Comment("Mute jailed players. They will not be able to send chat messages.")
public boolean mute = false;
}

View file

@ -0,0 +1,49 @@
package me.alexdevs.solstice.modules.jail.data;
import java.util.Map;
public class JailLocale {
public static final Map<String, String> MODULE = Map.ofEntries(
Map.entry("jailNotFound", "<gold>This jail does not exist.</gold>"),
Map.entry("jailAlreadyExists", "<gold>A jail with this name already exists.</gold>"),
Map.entry("teleporting", "<gold>Teleporting to <yellow>${jail}</yellow>...</gold>"),
Map.entry("created", "<green>New jail <yellow>${jail}</yellow> created!</green>"),
Map.entry("deleted", "<gold>Jail <yellow>${jail}</yellow> deleted!</gold>"),
Map.entry("notJailed", "<gold>This user is not jailed!</gold>"),
Map.entry("alreadyJailed", "<gold>This player is already jailed!</gold>"),
Map.entry("jailed", "<gold><yellow>${player}</yellow> has been jailed at <yellow>${jail}</yellow> indefinitely!</gold>"),
Map.entry("jailedFor", "<gold><yellow>${player}</yellow> has been jailed at <yellow>${jail}</yellow> for <yellow>${duration}</yellow>!</gold>"),
Map.entry("jailedForWithReason", "<gold><yellow>${player}</yellow> has been jailed at <yellow>${jail}</yellow> for <yellow>${duration}</yellow> with reason: <yellow>${reason}</yellow></gold>"),
Map.entry("playerJailed", "<red>You have been jailed!</red>"),
Map.entry("playerJailedFor", "<red>You have been jailed for <yellow>${duration}</yellow>!</red>"),
Map.entry("playerJailedForWithReason", "<red>You have been jailed for <yellow>${duration}</yellow> with reason: <yellow>${reason}</yellow></red>"),
Map.entry("playerUnjailed", "<green>You have been unjailed!</green>"),
Map.entry("unjailed", "<gold>Unjailed <yellow>${player}</yellow>!</gold>"),
Map.entry("cannotRunCommands", "<red>You are not allowed to run this command in jail!</red>"),
Map.entry("cannotBreakBlocks", "<red>You are not allowed to break blocks in jail!</red>"),
Map.entry("cannotAttackEntities", "<red>You are not allowed to attack entities in jail!</red>"),
Map.entry("cannotUseBlocks", "<red>You are not allowed to use blocks in jail!</red>"),
Map.entry("cannotUseEntities", "<red>You are not allowed to interact with entities in jail!</red>"),
Map.entry("cannotUseItems", "<red>You are not allowed to use items in jail!</red>"),
Map.entry("cannotSpeak", "<red>You are not allowed to speak in jail!</red>"),
Map.entry("playerExempt", "<gold>This player is exempt from being jailed!</gold>"),
Map.entry("jailList", "<gold>Jails: ${list}.</gold>"),
Map.entry("listEntry", "<yellow>${jail}</yellow>"),
Map.entry("comma", "<gold>, </gold>"),
Map.entry("noJails", "<gold>There are no jails yet.</gold>"),
Map.entry("infoHeader", "<gold><yellow>${player}</yellow> Jail Information:</gold>"),
Map.entry("infoJailedAt", "<gold>Jailed at: <yellow>${jail}</yellow></gold>"),
Map.entry("infoJailedBy", "<gold>Jailed by: <yellow>${operator}</yellow></gold>"),
Map.entry("infoJailReason", "<gold>Reason: <yellow>${reason}</yellow></gold>"),
Map.entry("infoJailReasonEmpty", "No reason"),
Map.entry("infoJailedFor", "<gold>Jail time: <yellow>${duration}</yellow></gold>"),
Map.entry("infoJailedForEmpty", "Indefinitely"),
Map.entry("infoJailedOn", "<gold>Jailed on: <yellow>${date}</yellow></gold>")
);
}

View file

@ -0,0 +1,18 @@
package me.alexdevs.solstice.modules.jail.data;
import me.alexdevs.solstice.api.ServerLocation;
import org.jetbrains.annotations.Nullable;
import java.util.Date;
import java.util.UUID;
public class JailPlayerData {
public boolean jailed = false;
public @Nullable String jailName = null;
public @Nullable UUID jailedBy = null;
public @Nullable Date jailedOn = null;
public int jailTime = 0;
public @Nullable String jailReason = null;
public @Nullable ServerLocation previousLocation = null;
public boolean teleportToPreviousLocation = false;
}

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