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) {
|
public void onLeave(PlayerQuitEvent event) {
|
||||||
if(event.getReason().equals(PlayerQuitEvent.QuitReason.KICKED)) {
|
if(event.getReason().equals(PlayerQuitEvent.QuitReason.KICKED)) {
|
||||||
Main.logger().info(String.format(
|
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()
|
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());
|
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.common.appliances.metaGameplay.displayName.DisplayName;
|
||||||
import eu.mhsl.craftattack.spawn.varo.api.repositories.TeamRepository;
|
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.joinProtection.JoinProtection;
|
||||||
|
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.playTimer.PlayTimer;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
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) {
|
for (VaroTeam team : this.teams) {
|
||||||
if (team.name.equals(name)) return team;
|
if (team.name.equals(name)) return team;
|
||||||
}
|
}
|
||||||
@ -75,15 +76,51 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
|
|||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<VaroTeam> getAllTeams() {
|
||||||
|
return this.teams;
|
||||||
|
}
|
||||||
|
|
||||||
public void enforceTeamJoin(Player joinedPlayer) {
|
public void enforceTeamJoin(Player joinedPlayer) {
|
||||||
DisconnectInfo disconnectInfo = new DisconnectInfo(
|
DisconnectInfo teamNotCompleteInfo = new DisconnectInfo(
|
||||||
"Teampartner nicht beigetreten",
|
"Teampartner nicht beigetreten",
|
||||||
"Deine Verbindung wurde getrennt, da dein Teampartner keine Verbindung zum Server hergestellt hat!",
|
"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!",
|
"Bitte sorge dafür, dass alle anderen Teammitglieder eine einwandfreie Internetverbindung haben und melde dich im Zweifel bei einem Admin!",
|
||||||
joinedPlayer.getUniqueId()
|
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());
|
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(
|
Bukkit.getScheduler().scheduleSyncDelayedTask(
|
||||||
Main.instance(),
|
Main.instance(),
|
||||||
() -> team.members.stream()
|
() -> team.members.stream()
|
||||||
@ -93,7 +130,14 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
|
|||||||
return p == null || !p.isOnline();
|
return p == null || !p.isOnline();
|
||||||
})
|
})
|
||||||
.findAny()
|
.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)
|
Ticks.TICKS_PER_SECOND * (JoinProtection.resistanceDuration / 2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams;
|
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.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.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -62,4 +65,28 @@ public class VaroTeam {
|
|||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEach(player -> player.kick(disconnectInfo.getComponent()));
|
.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