Add timed boss bars, /near command
This commit is contained in:
parent
f11036a844
commit
8e7244b0b8
8 changed files with 483 additions and 1 deletions
|
@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10
|
|||
loader_version=0.16.7
|
||||
|
||||
# Mod Properties
|
||||
mod_version=1.14.3
|
||||
mod_version=1.15.0
|
||||
maven_group=cc.reconnected
|
||||
archives_base_name=rcc-server
|
||||
|
||||
|
|
|
@ -99,6 +99,10 @@ public class RccServer implements ModInitializer {
|
|||
WarpCommand.register(dispatcher, registryAccess, environment);
|
||||
SetWarpCommand.register(dispatcher, registryAccess, environment);
|
||||
DeleteWarpCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
TimeBarCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
NearCommand.register(dispatcher, registryAccess, environment);
|
||||
});
|
||||
|
||||
AfkTracker.register();
|
||||
|
@ -106,6 +110,7 @@ public class RccServer implements ModInitializer {
|
|||
BackTracker.register();
|
||||
TabList.register();
|
||||
HttpApiServer.register();
|
||||
BossBarManager.register();
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
||||
luckPerms = LuckPermsProvider.get();
|
||||
|
|
|
@ -33,4 +33,13 @@ public class RccServerConfigModel {
|
|||
));
|
||||
|
||||
public String playerTabName = "%rcc-server:afk%%player:displayname_visual%";
|
||||
|
||||
public int nearCommandMaxRange = 48;
|
||||
public int nearCommandDefaultRange = 32;
|
||||
|
||||
public boolean enableAutoRestart = true;
|
||||
public ArrayList<String> restartAt = new ArrayList<>(List.of(
|
||||
"06:00",
|
||||
"18:00"
|
||||
));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package cc.reconnected.server.api.events;
|
||||
|
||||
import cc.reconnected.server.core.BossBarManager;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
public class BossBarEvents {
|
||||
public static final Event<Start> START = EventFactory.createArrayBacked(Start.class, callbacks ->
|
||||
(timeBar, server) -> {
|
||||
for (Start callback : callbacks) {
|
||||
callback.onStart(timeBar, server);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<End> END = EventFactory.createArrayBacked(End.class, callbacks ->
|
||||
(timeBar, server) -> {
|
||||
for (End callback : callbacks) {
|
||||
callback.onEnd(timeBar, server);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<Cancel> CANCEL = EventFactory.createArrayBacked(Cancel.class, callbacks ->
|
||||
(timeBar, server) -> {
|
||||
for (Cancel callback : callbacks) {
|
||||
callback.onCancel(timeBar, server);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<Progress> PROGRESS = EventFactory.createArrayBacked(Progress.class, callbacks ->
|
||||
(timeBar, server) -> {
|
||||
for (Progress callback : callbacks) {
|
||||
callback.onProgress(timeBar, server);
|
||||
}
|
||||
});
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Start {
|
||||
void onStart(BossBarManager.TimeBar timeBar, MinecraftServer server);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface End {
|
||||
void onEnd(BossBarManager.TimeBar timeBar, MinecraftServer server);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Cancel {
|
||||
void onCancel(BossBarManager.TimeBar timeBar, MinecraftServer server);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Progress {
|
||||
void onProgress(BossBarManager.TimeBar timeBar, MinecraftServer server);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.*;
|
||||
|
||||
public class NearCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("near")
|
||||
.requires(Permissions.require("rcc.command.near", 2))
|
||||
.executes(context -> {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
return execute(context, RccServer.CONFIG.nearCommandDefaultRange(), context.getSource().getPlayer());
|
||||
})
|
||||
.then(argument("radius", IntegerArgumentType.integer(0, RccServer.CONFIG.nearCommandMaxRange()))
|
||||
.executes(context -> {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
return execute(context, IntegerArgumentType.getInteger(context, "radius"), context.getSource().getPlayer());
|
||||
}));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, int range, ServerPlayerEntity sourcePlayer) {
|
||||
var list = new ArrayList<ClosePlayers>();
|
||||
|
||||
var sourcePos = sourcePlayer.getPos();
|
||||
sourcePlayer.getServerWorld().getPlayers().forEach(targetPlayer -> {
|
||||
var targetPos = targetPlayer.getPos();
|
||||
if (!sourcePlayer.getUuid().equals(targetPlayer.getUuid()) && sourcePos.isInRange(targetPos, range)) {
|
||||
var distance = sourcePos.distanceTo(targetPos);
|
||||
list.add(new ClosePlayers(targetPlayer.getDisplayName(), distance));
|
||||
}
|
||||
});
|
||||
|
||||
if(list.isEmpty()) {
|
||||
context.getSource().sendFeedback(() -> Text.literal("There is no one near you.").formatted(Formatting.GOLD), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
list.sort(Comparator.comparingDouble(ClosePlayers::distance));
|
||||
|
||||
var text = Text.empty().append(Text.literal("Nearest players: ").formatted(Formatting.GOLD));
|
||||
var comma = Text.literal(", ").formatted(Formatting.GOLD);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
var player = list.get(i);
|
||||
if (i > 0) {
|
||||
text = text.append(comma);
|
||||
}
|
||||
text = text.append(player.displayName)
|
||||
.append(" ")
|
||||
.append(Text.literal(String.format("(%.1fm)", player.distance)).formatted(Formatting.GREEN));
|
||||
}
|
||||
|
||||
final var finalText = text;
|
||||
context.getSource().sendFeedback(() -> finalText, false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private record ClosePlayers(Text displayName, double distance) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import cc.reconnected.server.api.events.BossBarEvents;
|
||||
import cc.reconnected.server.core.BossBarManager;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.command.argument.UuidArgumentType;
|
||||
import net.minecraft.entity.boss.BossBar;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.ClickEvent;
|
||||
import net.minecraft.text.HoverEvent;
|
||||
import net.minecraft.text.Style;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.*;
|
||||
|
||||
public class TimeBarCommand {
|
||||
private static final ConcurrentHashMap<UUID, BarCommand> runningBars = new ConcurrentHashMap<>();
|
||||
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("timebar")
|
||||
.requires(Permissions.require("rcc.command.timebar", 3))
|
||||
.then(literal("start")
|
||||
.then(argument("seconds", IntegerArgumentType.integer(0))
|
||||
.then(argument("color", StringArgumentType.word())
|
||||
.suggests((context, builder) -> {
|
||||
var colors = Arrays.stream(BossBar.Color.values()).map(Enum::toString).toList();
|
||||
return CommandSource.suggestMatching(colors, builder);
|
||||
})
|
||||
.then(argument("style", StringArgumentType.word())
|
||||
.suggests((context, builder) -> {
|
||||
var styles = Arrays.stream(BossBar.Style.values()).map(Enum::toString).toList();
|
||||
return CommandSource.suggestMatching(styles, builder);
|
||||
})
|
||||
.then(argument("countdown", BoolArgumentType.bool())
|
||||
.then(argument("label", StringArgumentType.string())
|
||||
.then(argument("command", StringArgumentType.greedyString())
|
||||
.suggests((context, builder) -> dispatcher.getRoot().listSuggestions(context, builder))
|
||||
.executes(TimeBarCommand::execute))
|
||||
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("cancel")
|
||||
.then(argument("uuid", UuidArgumentType.uuid())
|
||||
.executes(TimeBarCommand::executeCancel)));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
|
||||
BossBarEvents.END.register((timeBar, server) -> {
|
||||
if (runningBars.containsKey(timeBar.getUuid())) {
|
||||
var barCommand = runningBars.get(timeBar.getUuid());
|
||||
try {
|
||||
dispatcher.execute(barCommand.command(), barCommand.source);
|
||||
} catch (CommandSyntaxException e) {
|
||||
barCommand.source.sendFeedback(() -> Text.literal(e.toString()).formatted(Formatting.RED), false);
|
||||
}
|
||||
runningBars.remove(timeBar.getUuid());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context) {
|
||||
var seconds = IntegerArgumentType.getInteger(context, "seconds");
|
||||
var colorName = StringArgumentType.getString(context, "color");
|
||||
var styleName = StringArgumentType.getString(context, "style");
|
||||
var countdown = BoolArgumentType.getBool(context, "countdown");
|
||||
var label = StringArgumentType.getString(context, "label");
|
||||
var command = StringArgumentType.getString(context, "command");
|
||||
|
||||
var color = BossBar.Color.valueOf(colorName);
|
||||
var style = BossBar.Style.valueOf(styleName);
|
||||
|
||||
var bar = BossBarManager.getInstance().startTimeBar(label, seconds, color, style, countdown);
|
||||
|
||||
runningBars.put(bar.getUuid(), new BarCommand(context.getSource(), command));
|
||||
|
||||
|
||||
context.getSource().sendFeedback(() -> Text
|
||||
.literal("New time bar created with UUID ")
|
||||
.append(Text.literal(bar.getUuid().toString()).setStyle(Style.EMPTY
|
||||
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.of("Click to copy")))
|
||||
.withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, bar.getUuid().toString())))), true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeCancel(CommandContext<ServerCommandSource> context) {
|
||||
var uuid = UuidArgumentType.getUuid(context, "uuid");
|
||||
|
||||
if (!runningBars.containsKey(uuid)) {
|
||||
context.getSource().sendFeedback(() -> Text.literal("Time bar not found!").formatted(Formatting.RED), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
runningBars.remove(uuid);
|
||||
BossBarManager.getInstance().cancelTimeBar(uuid);
|
||||
|
||||
context.getSource().sendFeedback(() -> Text.literal("Time bar canceled"), true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private record BarCommand(ServerCommandSource source, String command) {
|
||||
}
|
||||
}
|
17
src/main/java/cc/reconnected/server/core/AutoRestart.java
Normal file
17
src/main/java/cc/reconnected/server/core/AutoRestart.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package cc.reconnected.server.core;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
public class AutoRestart {
|
||||
private static MinecraftServer server;
|
||||
public static void register() {
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(s -> server = s);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void schedule() {
|
||||
|
||||
}
|
||||
}
|
191
src/main/java/cc/reconnected/server/core/BossBarManager.java
Normal file
191
src/main/java/cc/reconnected/server/core/BossBarManager.java
Normal file
|
@ -0,0 +1,191 @@
|
|||
package cc.reconnected.server.core;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.api.events.BossBarEvents;
|
||||
import cc.reconnected.server.util.Components;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.minecraft.entity.boss.BossBar;
|
||||
import net.minecraft.entity.boss.CommandBossBar;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
public class BossBarManager {
|
||||
private static BossBarManager instance;
|
||||
|
||||
public static BossBarManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static MinecraftServer server;
|
||||
private static ConcurrentLinkedDeque<TimeBar> timeBars = new ConcurrentLinkedDeque<>();
|
||||
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||
|
||||
public static void register() {
|
||||
instance = new BossBarManager();
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(s -> server = s);
|
||||
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
// every second
|
||||
if (server.getTicks() % 20 == 0) {
|
||||
for (var timeBar : timeBars) {
|
||||
var remove = timeBar.elapse();
|
||||
BossBarEvents.PROGRESS.invoker().onProgress(timeBar, server);
|
||||
|
||||
var players = server.getPlayerManager().getPlayerList();
|
||||
showBar(players, timeBar);
|
||||
|
||||
if (remove) {
|
||||
timeBars.remove(timeBar);
|
||||
BossBarEvents.END.invoker().onEnd(timeBar, server);
|
||||
hideBar(players, timeBar);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void showBar(Collection<ServerPlayerEntity> players, TimeBar timeBar) {
|
||||
timeBar.getBossBar().addPlayers(players);
|
||||
}
|
||||
|
||||
private static void hideBar(Collection<ServerPlayerEntity> players, TimeBar timeBar) {
|
||||
players.forEach(player -> {
|
||||
timeBar.getBossBar().removePlayer(player);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public TimeBar startTimeBar(String label, int seconds, BossBar.Color color, BossBar.Style style, boolean countdown) {
|
||||
var countdownBar = new TimeBar(label, seconds, countdown, color, style);
|
||||
|
||||
timeBars.add(countdownBar);
|
||||
|
||||
var players = server.getPlayerManager().getPlayerList();
|
||||
showBar(players, countdownBar);
|
||||
|
||||
BossBarEvents.START.invoker().onStart(countdownBar, server);
|
||||
|
||||
return countdownBar;
|
||||
}
|
||||
|
||||
public boolean cancelTimeBar(TimeBar timeBar) {
|
||||
var success = timeBars.remove(timeBar);
|
||||
if (success) {
|
||||
var players = server.getPlayerManager().getPlayerList();
|
||||
hideBar(players, timeBar);
|
||||
BossBarEvents.CANCEL.invoker().onCancel(timeBar, server);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean cancelTimeBar(UUID uuid) {
|
||||
var progressBar = timeBars.stream().filter(p -> p.uuid.equals(uuid)).findFirst().orElse(null);
|
||||
if (progressBar == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cancelTimeBar(progressBar);
|
||||
}
|
||||
|
||||
public static class TimeBar {
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
private final CommandBossBar bossBar;
|
||||
private final String label;
|
||||
private final int time;
|
||||
private int elapsedSeconds = 0;
|
||||
private final boolean countdown;
|
||||
|
||||
public TimeBar(String label, int time, boolean countdown, BossBar.Color color, BossBar.Style style) {
|
||||
this.bossBar = new CommandBossBar(Identifier.of(RccServer.MOD_ID, uuid.toString()), Text.of(label));
|
||||
this.bossBar.setColor(color);
|
||||
this.bossBar.setStyle(style);
|
||||
this.label = label;
|
||||
this.time = time;
|
||||
this.countdown = countdown;
|
||||
updateName();
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
public static String formatTime(int totalSeconds) {
|
||||
var hours = totalSeconds / 3600;
|
||||
var minutes = (totalSeconds / 60) % 60;
|
||||
var seconds = totalSeconds % 60;
|
||||
if(totalSeconds >= 3600) {
|
||||
return String.format("%dh%dm%ds", hours, minutes, seconds);
|
||||
}
|
||||
return String.format("%dm%ds", minutes, seconds);
|
||||
}
|
||||
|
||||
public void updateName() {
|
||||
var totalTime = formatTime(this.time);
|
||||
var elapsedTime = formatTime(this.elapsedSeconds);
|
||||
|
||||
var remaining = time - elapsedSeconds;
|
||||
var remainingTime = formatTime(remaining);
|
||||
var text = miniMessage.deserialize(label, TagResolver.resolver(
|
||||
Placeholder.parsed("total_time", totalTime),
|
||||
Placeholder.parsed("elapsed_time", elapsedTime),
|
||||
Placeholder.parsed("remaining_time", remainingTime)
|
||||
));
|
||||
|
||||
bossBar.setName(Components.toText(text));
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public CommandBossBar getBossBar() {
|
||||
return bossBar;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public int getElapsedSeconds() {
|
||||
return elapsedSeconds;
|
||||
}
|
||||
|
||||
public boolean isCountdown() {
|
||||
return countdown;
|
||||
}
|
||||
|
||||
public boolean elapse() {
|
||||
this.elapsedSeconds++;
|
||||
|
||||
updateProgress();
|
||||
updateName();
|
||||
|
||||
return this.elapsedSeconds >= this.time;
|
||||
}
|
||||
|
||||
private void updateProgress() {
|
||||
float progress = (float) elapsedSeconds / (float) time;
|
||||
if (countdown) {
|
||||
progress = 1f - progress;
|
||||
}
|
||||
|
||||
bossBar.setPercent(Math.min(
|
||||
Math.max(
|
||||
progress,
|
||||
0f),
|
||||
1f));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue