implemented PlayTimer
This commit is contained in:
@ -0,0 +1,95 @@
|
||||
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.playTimer;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.VaroTeam;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PlayTimer extends Appliance {
|
||||
public static final int PLAYTIME_MINUTES = 30;
|
||||
|
||||
private final Map<String, Integer> joinTickets = new HashMap<>();
|
||||
private final Path saveFile = Paths.get(Main.instance().getDataFolder() + "playtime.json");
|
||||
|
||||
public PlayTimer() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private void load() {
|
||||
if (!Files.exists(this.saveFile)) return;
|
||||
try (Reader reader = Files.newBufferedReader(this.saveFile)) {
|
||||
Type type = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
Map<String, Object> data = new Gson().fromJson(reader, type);
|
||||
@SuppressWarnings("unchecked") Map<String, Double> ticketMap = (Map<String, Double>) data.get("tickets");
|
||||
|
||||
if (ticketMap != null) {
|
||||
for (Map.Entry<String, Double> entry : ticketMap.entrySet()) {
|
||||
this.joinTickets.put(entry.getKey(), entry.getValue().intValue());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Main.logger().warning("Failed reading playtime from teams: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void save() {
|
||||
try {
|
||||
Files.createDirectories(this.saveFile.getParent());
|
||||
try (Writer writer = Files.newBufferedWriter(this.saveFile)) {
|
||||
new Gson().toJson(Map.of(
|
||||
"tickets", this.joinTickets
|
||||
), writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Main.logger().warning("Failed to save playtime for teams: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementAll() {
|
||||
this.joinTickets.replaceAll((n, v) -> this.joinTickets.get(n) + 1);
|
||||
this.save();
|
||||
}
|
||||
public void setTickets(VaroTeam team, int amount) {
|
||||
this.joinTickets.put(team.name, amount);
|
||||
}
|
||||
|
||||
public int getTickets(VaroTeam team) {
|
||||
return this.joinTickets.getOrDefault(team.name, 1);
|
||||
}
|
||||
|
||||
public boolean tryConsumeTicket(VaroTeam team) {
|
||||
String teamName = team.name;
|
||||
int current = this.joinTickets.getOrDefault(teamName, 1);
|
||||
|
||||
if (current <= 0) return false;
|
||||
|
||||
this.joinTickets.put(teamName, current - 1);
|
||||
this.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<ApplianceCommand<?>> commands() {
|
||||
return List.of(new PlayTimerCommand());
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.playTimer;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams;
|
||||
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.VaroTeam;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class PlayTimerCommand extends ApplianceCommand<PlayTimer> {
|
||||
public PlayTimerCommand() {
|
||||
super("playTimer");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
if (args.length < 3) throw new Error("Usage: playTimer <user|team> <identifier> <get|set> [amount]");
|
||||
|
||||
String mode = args[0].toLowerCase();
|
||||
String identifier = args[1];
|
||||
String action = args[2].toLowerCase();
|
||||
|
||||
Teams teamAppliance = Main.instance().getAppliance(Teams.class);
|
||||
VaroTeam team = switch (mode) {
|
||||
case "user" -> {
|
||||
OfflinePlayer player = Bukkit.getOfflinePlayer(identifier);
|
||||
try {
|
||||
yield teamAppliance.getTeamFromPlayer(player.getUniqueId());
|
||||
} catch(NoSuchElementException e) {
|
||||
throw new Error("Dieser Spieler konnte keinem Team zugeordnet werden!");
|
||||
}
|
||||
}
|
||||
case "team" -> {
|
||||
VaroTeam targetTeam = teamAppliance.findTeamByName(identifier);
|
||||
if (targetTeam == null) throw new Error("Team nicht gefunden.");
|
||||
yield targetTeam;
|
||||
}
|
||||
default -> throw new Error("Ungültiger Modus: " + mode + ". Erlaubt: user | team");
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
case "get" -> {
|
||||
int ticketCount = this.getAppliance().getTickets(team);
|
||||
sender.sendMessage(String.format("Team %s hat %d tickets!", team.name, ticketCount));
|
||||
}
|
||||
case "set" -> {
|
||||
if (args.length < 4) throw new Error("Usage: playTimer <user|team> <identifier> set <amount>");
|
||||
int amount = Integer.parseInt(args[3]);
|
||||
this.getAppliance().setTickets(team, amount);
|
||||
sender.sendMessage("Tickets wurden gesetzt!");
|
||||
}
|
||||
default -> throw new Error("Ungültige Aktion: " + action + ". Erlaubt: get | set");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
List<VaroTeam> teams = Main.instance().getAppliance(Teams.class).getAllTeams();
|
||||
|
||||
return switch (args.length) {
|
||||
case 1 -> Stream.of("user", "team")
|
||||
.filter(opt -> opt.startsWith(args[0].toLowerCase()))
|
||||
.toList();
|
||||
|
||||
case 2 -> {
|
||||
if (args[0].equalsIgnoreCase("user")) {
|
||||
yield Bukkit.getOnlinePlayers().stream()
|
||||
.map(Player::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
.toList();
|
||||
} else if (args[0].equalsIgnoreCase("team")) {
|
||||
yield teams.stream()
|
||||
.map(team -> team.name)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
.toList();
|
||||
} else {
|
||||
yield List.of();
|
||||
}
|
||||
}
|
||||
|
||||
case 3 -> Stream.of("get", "set")
|
||||
.filter(opt -> opt.startsWith(args[2].toLowerCase()))
|
||||
.toList();
|
||||
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -30,10 +30,15 @@ class ConnectivityChangeListener extends ApplianceListener<Teams> {
|
||||
public void onLeave(PlayerQuitEvent event) {
|
||||
if(event.getReason().equals(PlayerQuitEvent.QuitReason.KICKED)) {
|
||||
Main.logger().info(String.format(
|
||||
"Player %s left the Server. The 'teamLeave' enforcement was skipped, since the QuitReqson is 'kicked'",
|
||||
"Player %s left the Server. The 'teamLeave' enforcement was skipped, since the QuitReason is 'kicked'",
|
||||
event.getPlayer().getName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
VaroTeam team = Main.instance().getAppliance(Teams.class).getTeamFromPlayer(event.getPlayer().getUniqueId());
|
||||
Main.logger().info(String.format("Team %s got a Strike, because they %s left early!", team.name, event.getPlayer().getName()));
|
||||
// TODO: strike team
|
||||
this.getAppliance().enforceTeamLeave(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName.DisplayName;
|
||||
import eu.mhsl.craftattack.spawn.varo.api.repositories.TeamRepository;
|
||||
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.joinProtection.JoinProtection;
|
||||
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.playTimer.PlayTimer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
@ -56,7 +57,7 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable VaroTeam findTeamByName(String name) {
|
||||
public @Nullable VaroTeam findTeamByName(String name) {
|
||||
for (VaroTeam team : this.teams) {
|
||||
if (team.name.equals(name)) return team;
|
||||
}
|
||||
@ -75,15 +76,51 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
public List<VaroTeam> getAllTeams() {
|
||||
return this.teams;
|
||||
}
|
||||
|
||||
public void enforceTeamJoin(Player joinedPlayer) {
|
||||
DisconnectInfo disconnectInfo = new DisconnectInfo(
|
||||
DisconnectInfo teamNotCompleteInfo = new DisconnectInfo(
|
||||
"Teampartner nicht beigetreten",
|
||||
"Deine Verbindung wurde getrennt, da dein Teampartner keine Verbindung zum Server hergestellt hat!",
|
||||
"Bitte sorge dafür, dass alle anderen Teammitglieder eine einwandfreie Internetverbindung haben und melde dich im Zweifel bei einem Admin!",
|
||||
joinedPlayer.getUniqueId()
|
||||
);
|
||||
|
||||
DisconnectInfo teamNoPlaytime = new DisconnectInfo(
|
||||
"Keine Spielzeit verfügbar",
|
||||
"Deine Verbindung wurde getrennt, da dein Team keine verbleibende Spielzeit auf dem Server hat!",
|
||||
"Falls dies ein Fehler ist, melde dich bitte bei einem Admin!",
|
||||
joinedPlayer.getUniqueId()
|
||||
);
|
||||
|
||||
VaroTeam team = this.getTeamFromPlayer(joinedPlayer.getUniqueId());
|
||||
|
||||
PlayTimer playTimer = Main.instance().getAppliance(PlayTimer.class);
|
||||
boolean isAllowed = playTimer.tryConsumeTicket(team);
|
||||
if(!isAllowed) {
|
||||
Main.logger().warning(String.format("Team %s joined, but got denied from Ticketing. Team will be kicked!", team.name));
|
||||
team.kickTeam(teamNoPlaytime);
|
||||
return;
|
||||
}
|
||||
|
||||
int leftTickets = playTimer.getTickets(team);
|
||||
Main.logger().info(String.format("Player %s joined. There are %d tickets left!", joinedPlayer.getName(), leftTickets));
|
||||
String playtimeOverview = String.format(
|
||||
"Dein Team hat noch %d Beitritte, also %dx%d=%d Minuten übrig.",
|
||||
leftTickets,
|
||||
leftTickets,
|
||||
PlayTimer.PLAYTIME_MINUTES,
|
||||
leftTickets * PlayTimer.PLAYTIME_MINUTES
|
||||
);
|
||||
joinedPlayer.sendMessage(Component.text(
|
||||
leftTickets == 0
|
||||
? String.format("Dein Team hat ab jetzt %d Minuten Spielzeit!", PlayTimer.PLAYTIME_MINUTES)
|
||||
: playtimeOverview,
|
||||
NamedTextColor.GREEN
|
||||
));
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(
|
||||
Main.instance(),
|
||||
() -> team.members.stream()
|
||||
@ -93,7 +130,14 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
|
||||
return p == null || !p.isOnline();
|
||||
})
|
||||
.findAny()
|
||||
.ifPresent(member -> team.kickTeam(disconnectInfo)),
|
||||
.ifPresentOrElse(
|
||||
member -> team.kickTeam(teamNotCompleteInfo),
|
||||
() -> Bukkit.getScheduler().runTaskLater(
|
||||
Main.instance(),
|
||||
team::timeOverKick,
|
||||
Ticks.TICKS_PER_SECOND * 60 * PlayTimer.PLAYTIME_MINUTES
|
||||
)
|
||||
),
|
||||
Ticks.TICKS_PER_SECOND * (JoinProtection.resistanceDuration / 2)
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
|
||||
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.fightDetector.FightDetector;
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -62,4 +65,28 @@ public class VaroTeam {
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(player -> player.kick(disconnectInfo.getComponent()));
|
||||
}
|
||||
|
||||
public void timeOverKick() {
|
||||
boolean isInFight = Main.instance().getAppliance(FightDetector.class).isInFight(this);
|
||||
if(isInFight) {
|
||||
Main.logger().info(String.format("Cannot kick Team %s because it is in a fight!", this.name));
|
||||
Bukkit.getScheduler().runTaskLater(
|
||||
Main.instance(),
|
||||
this::timeOverKick,
|
||||
Ticks.TICKS_PER_SECOND * 15
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Main.logger().info(String.format("Kicking Team %s because time is up!", this.name));
|
||||
|
||||
DisconnectInfo timeOverInfo = new DisconnectInfo(
|
||||
"Die Zeit ist abgelaufen!",
|
||||
"Deine Spielzeit ist vorüber. Falls dir noch weitere Zeit zusteht kannst du jetzt eine Pause machen und anschließend erneut beitreten.",
|
||||
"Falls du Fragen hast, melde dich bitte bei einem Admin!",
|
||||
UUID.nameUUIDFromBytes("".getBytes())
|
||||
);
|
||||
|
||||
this.kickTeam(timeOverInfo);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user