forked from SteamWar/SteamWar
Merge pull request 'Refactor leaderboard management' (#166) from Lobby/refactor-leaderboard into main
Reviewed-on: SteamWar/SteamWar#166
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.useDb
|
||||||
|
import org.jetbrains.exposed.v1.core.SortOrder
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.count
|
||||||
|
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
|
||||||
|
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
|
||||||
|
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.lessSubQuery
|
||||||
|
import org.jetbrains.exposed.v1.dao.CompositeEntity
|
||||||
|
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
||||||
|
import org.jetbrains.exposed.v1.javatime.CurrentTimestamp
|
||||||
|
import org.jetbrains.exposed.v1.javatime.timestamp
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.select
|
||||||
|
|
||||||
|
object LeaderboardTable : CompositeIdTable("Leaderboard") {
|
||||||
|
val userId = reference("UserId", SteamwarUserTable)
|
||||||
|
val name = varchar("Name", 64).entityId()
|
||||||
|
val time = long("Time")
|
||||||
|
val updatedAt = timestamp("UpdatedAt").defaultExpression(CurrentTimestamp)
|
||||||
|
val bestTime = bool("BestTime")
|
||||||
|
}
|
||||||
|
|
||||||
|
class Leaderboard(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||||
|
companion object : CompositeEntityClass<Leaderboard>(LeaderboardTable) {
|
||||||
|
@JvmStatic
|
||||||
|
fun getLeaderboard(name: String) = useDb {
|
||||||
|
find { LeaderboardTable.name eq name }.orderBy(LeaderboardTable.time to SortOrder.ASC).limit(5).toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getPlayerTime(user: SteamwarUser, name: String) = useDb {
|
||||||
|
findById(CompositeID {
|
||||||
|
it[LeaderboardTable.userId] = user.id.value
|
||||||
|
it[LeaderboardTable.name] = name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getPlayerPlacement(user: SteamwarUser, name: String) = useDb {
|
||||||
|
LeaderboardTable.select(LeaderboardTable.time.count())
|
||||||
|
.where {
|
||||||
|
(LeaderboardTable.name eq name) and (LeaderboardTable.time lessSubQuery LeaderboardTable.select(
|
||||||
|
LeaderboardTable.time
|
||||||
|
).where { (LeaderboardTable.userId eq user.id.value) and (LeaderboardTable.name eq name) })
|
||||||
|
}
|
||||||
|
.firstOrNull()?.get(LeaderboardTable.time.count())?.toInt() ?: Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun upsert(userId: Int, name: String, time: Long, bestTime: Boolean) = useDb {
|
||||||
|
findByIdAndUpdate(CompositeID {
|
||||||
|
it[LeaderboardTable.userId] = userId
|
||||||
|
it[LeaderboardTable.name] = name
|
||||||
|
}) {
|
||||||
|
it.time = time
|
||||||
|
it.bestTime = bestTime
|
||||||
|
} ?: new(
|
||||||
|
CompositeID {
|
||||||
|
it[LeaderboardTable.userId] = userId
|
||||||
|
it[LeaderboardTable.name] = name
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
this.time = time
|
||||||
|
this.bestTime = bestTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val user by LeaderboardTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
|
||||||
|
val name by LeaderboardTable.name
|
||||||
|
var time by LeaderboardTable.time
|
||||||
|
var updatedAt by LeaderboardTable.updatedAt
|
||||||
|
var bestTime by LeaderboardTable.bestTime
|
||||||
|
}
|
||||||
@@ -22,8 +22,9 @@ package de.steamwar.lobby.boatrace;
|
|||||||
import de.steamwar.entity.REntity;
|
import de.steamwar.entity.REntity;
|
||||||
import de.steamwar.entity.REntityServer;
|
import de.steamwar.entity.REntityServer;
|
||||||
import de.steamwar.lobby.LobbySystem;
|
import de.steamwar.lobby.LobbySystem;
|
||||||
import de.steamwar.lobby.util.Leaderboard;
|
import de.steamwar.lobby.util.LeaderboardManager;
|
||||||
import de.steamwar.sql.UserConfig;
|
import de.steamwar.sql.Leaderboard;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
@@ -43,16 +44,18 @@ import org.bukkit.scheduler.BukkitTask;
|
|||||||
|
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
|
|
||||||
import static de.steamwar.lobby.util.Leaderboard.renderTime;
|
import static de.steamwar.lobby.util.LeaderboardManager.renderTime;
|
||||||
|
|
||||||
public class BoatRace implements EventListener, Listener {
|
public class BoatRace implements EventListener, Listener {
|
||||||
|
|
||||||
|
private static final String CONFIG_KEY = "lobby@boatrace";
|
||||||
|
|
||||||
private static final double MIN_HEIGHT = 4.3;
|
private static final double MIN_HEIGHT = 4.3;
|
||||||
|
|
||||||
public static final REntityServer boatNpcServer;
|
public static final REntityServer boatNpcServer;
|
||||||
|
|
||||||
private static boolean oneNotStarted = false;
|
private static boolean oneNotStarted = false;
|
||||||
private static final Leaderboard leaderboard;
|
private static final LeaderboardManager leaderboard;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
boatNpcServer = new REntityServer();
|
boatNpcServer = new REntityServer();
|
||||||
@@ -65,7 +68,7 @@ public class BoatRace implements EventListener, Listener {
|
|||||||
new BoatRace(player);
|
new BoatRace(player);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
leaderboard = new Leaderboard(boatNpcServer, "lobby@boatrace", BoatRacePositions.LEADERBOARD, 5);
|
leaderboard = new LeaderboardManager(boatNpcServer, CONFIG_KEY, BoatRacePositions.LEADERBOARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
@@ -123,12 +126,11 @@ public class BoatRace implements EventListener, Listener {
|
|||||||
HandlerList.unregisterAll(this);
|
HandlerList.unregisterAll(this);
|
||||||
task.cancel();
|
task.cancel();
|
||||||
LobbySystem.getMessage().send("BOAT_RACE_TIME", player, renderTime(time));
|
LobbySystem.getMessage().send("BOAT_RACE_TIME", player, renderTime(time));
|
||||||
String conf = UserConfig.getConfig(player.getUniqueId(), "lobby@boatrace");
|
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
||||||
long best = Long.parseLong(conf == null ? String.valueOf(Long.MAX_VALUE) : conf);
|
long best = leaderboard.getPlayerTime(user);
|
||||||
if (time < best) {
|
if (time < best) {
|
||||||
LobbySystem.getMessage().send("BOAT_RACE_NEW_BEST", player);
|
LobbySystem.getMessage().send("BOAT_RACE_NEW_BEST", player);
|
||||||
UserConfig.updatePlayerConfig(player.getUniqueId(), "lobby@boatrace", String.valueOf(time));
|
leaderboard.updateBestTime(user, time);
|
||||||
leaderboard.update();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1);
|
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1);
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ package de.steamwar.lobby.jumpandrun;
|
|||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
import de.steamwar.lobby.LobbySystem;
|
import de.steamwar.lobby.LobbySystem;
|
||||||
import de.steamwar.lobby.listener.PlayerSpawn;
|
import de.steamwar.lobby.listener.PlayerSpawn;
|
||||||
import de.steamwar.lobby.util.Leaderboard;
|
import de.steamwar.lobby.util.LeaderboardManager;
|
||||||
import de.steamwar.sql.UserConfig;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import net.md_5.bungee.api.ChatMessageType;
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
@@ -58,7 +58,7 @@ public class JumpAndRun implements Listener {
|
|||||||
private static final Map<Player, Long> CLICKED = new HashMap<>();
|
private static final Map<Player, Long> CLICKED = new HashMap<>();
|
||||||
private static final Map<Player, Integer> CLICKED_COUNT = new HashMap<>();
|
private static final Map<Player, Integer> CLICKED_COUNT = new HashMap<>();
|
||||||
|
|
||||||
private static final Leaderboard LEADERBOARD = new Leaderboard(LobbySystem.getEntityServer(false), JUMP_AND_RUN_CONFIG, new Location(Bukkit.getWorlds().get(0), 2338.5, 42.5, 1231.5), 5);
|
private static final LeaderboardManager LEADERBOARD = new LeaderboardManager(LobbySystem.getEntityServer(false), JUMP_AND_RUN_CONFIG, new Location(Bukkit.getWorlds().get(0), 2338.5, 42.5, 1231.5));
|
||||||
|
|
||||||
{
|
{
|
||||||
Bukkit.getScheduler().runTaskTimer(LobbySystem.getInstance(), () -> {
|
Bukkit.getScheduler().runTaskTimer(LobbySystem.getInstance(), () -> {
|
||||||
@@ -161,18 +161,14 @@ public class JumpAndRun implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateJumpAndRunTime(Player player, long time) {
|
private void updateJumpAndRunTime(Player player, long time) {
|
||||||
String jumpAndRunTimeConfig = UserConfig.getConfig(player.getUniqueId(), JUMP_AND_RUN_CONFIG);
|
long best = LEADERBOARD.getPlayerTime(SteamwarUser.get(player.getUniqueId()));
|
||||||
if (jumpAndRunTimeConfig == null) {
|
if (time < best) {
|
||||||
UserConfig.updatePlayerConfig(player.getUniqueId(), JUMP_AND_RUN_CONFIG, time + "");
|
if (best != Long.MAX_VALUE) {
|
||||||
} else {
|
|
||||||
long jumpAndRunTime = Long.parseLong(jumpAndRunTimeConfig);
|
|
||||||
if (time < jumpAndRunTime) {
|
|
||||||
SimpleDateFormat format = new SimpleDateFormat(LobbySystem.getMessage().parse("JUMP_AND_RUN_TIME", player), Locale.ROOT);
|
SimpleDateFormat format = new SimpleDateFormat(LobbySystem.getMessage().parse("JUMP_AND_RUN_TIME", player), Locale.ROOT);
|
||||||
String parsed = format.format(new Date(jumpAndRunTime - time));
|
String parsed = format.format(new Date(best - time));
|
||||||
LobbySystem.getMessage().sendPrefixless("JUMP_AND_RUN_PERSONAL_BEST", player, parsed);
|
LobbySystem.getMessage().sendPrefixless("JUMP_AND_RUN_PERSONAL_BEST", player, parsed);
|
||||||
UserConfig.updatePlayerConfig(player.getUniqueId(), JUMP_AND_RUN_CONFIG, time + "");
|
|
||||||
LEADERBOARD.update();
|
|
||||||
}
|
}
|
||||||
|
LEADERBOARD.updateBestTime(SteamwarUser.get(player.getUniqueId()), time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-44
@@ -23,9 +23,8 @@ import de.steamwar.entity.RArmorStand;
|
|||||||
import de.steamwar.entity.REntity;
|
import de.steamwar.entity.REntity;
|
||||||
import de.steamwar.entity.REntityServer;
|
import de.steamwar.entity.REntityServer;
|
||||||
import de.steamwar.lobby.LobbySystem;
|
import de.steamwar.lobby.LobbySystem;
|
||||||
|
import de.steamwar.sql.Leaderboard;
|
||||||
import de.steamwar.sql.SteamwarUser;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import de.steamwar.sql.internal.Statement;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@@ -39,25 +38,18 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class Leaderboard implements Listener {
|
public class LeaderboardManager implements Listener {
|
||||||
|
|
||||||
private static final Statement LEADERBOARD = new Statement("SELECT User, CAST(Value as integer) AS Time from UserConfig WHERE Config = ? ORDER BY CAST(Value as integer) ASC LIMIT ?");
|
|
||||||
private static final Statement PLAYER_TIME = new Statement("SELECT CAST(Value as integer) AS Time FROM UserConfig WHERE Config = ? AND User = ?");
|
|
||||||
private static final Statement PLAYER_PLACEMENT = new Statement("SELECT COUNT(*) AS Placement FROM UserConfig WHERE Config = ? AND CAST(Value as integer) < (SELECT CAST(Value as integer) AS Time FROM UserConfig WHERE Config = ? AND User = ?)");
|
|
||||||
|
|
||||||
private final REntityServer server;
|
private final REntityServer server;
|
||||||
private final String configKey;
|
private final String configKey;
|
||||||
private final Location location;
|
private final Location location;
|
||||||
private final int best;
|
|
||||||
private long bestTime;
|
private long bestTime;
|
||||||
private final List<REntity> entities = new ArrayList<>();
|
private final List<REntity> entities = new ArrayList<>();
|
||||||
private final Map<Integer, REntityServer> playerPlacements = new HashMap<>();
|
private final Map<Integer, REntityServer> playerPlacements = new HashMap<>();
|
||||||
|
|
||||||
public Leaderboard(REntityServer server, String configKey, Location location, int best) {
|
public LeaderboardManager(REntityServer server, String configKey, Location location) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.configKey = configKey;
|
this.configKey = configKey;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.best = best;
|
|
||||||
Bukkit.getPluginManager().registerEvents(this, LobbySystem.getInstance());
|
Bukkit.getPluginManager().registerEvents(this, LobbySystem.getInstance());
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@@ -65,20 +57,20 @@ public class Leaderboard implements Listener {
|
|||||||
public void update() {
|
public void update() {
|
||||||
entities.forEach(REntity::die);
|
entities.forEach(REntity::die);
|
||||||
entities.clear();
|
entities.clear();
|
||||||
List<LeaderboardEntry> leaderboard = getLeaderboard();
|
List<Leaderboard> leaderboard = getLeaderboard();
|
||||||
if (leaderboard.isEmpty()) return;
|
if (leaderboard.isEmpty()) return;
|
||||||
bestTime = leaderboard.get(0).time;
|
bestTime = leaderboard.get(0).getTime();
|
||||||
for (int i = 0; i < leaderboard.size(); i++) {
|
for (int i = 0; i < leaderboard.size(); i++) {
|
||||||
LeaderboardEntry entry = leaderboard.get(i);
|
Leaderboard entry = leaderboard.get(i);
|
||||||
RArmorStand entity = new RArmorStand(server, location.clone().add(0, (leaderboard.size() - i - 1) * 0.32, 0), RArmorStand.Size.MARKER);
|
RArmorStand entity = new RArmorStand(server, location.clone().add(0, (leaderboard.size() - i - 1) * 0.32, 0), RArmorStand.Size.MARKER);
|
||||||
SteamwarUser user = SteamwarUser.byId(entry.user);
|
SteamwarUser user = SteamwarUser.byId(entry.getUser());
|
||||||
String color = "§7";
|
String color = "§7";
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
color = "§6§l";
|
color = "§6§l";
|
||||||
} else if (i < 3) {
|
} else if (i < 3) {
|
||||||
color = "§e";
|
color = "§e";
|
||||||
}
|
}
|
||||||
entity.setDisplayName(calcName(user, color, i + 1, entry.time));
|
entity.setDisplayName(calcName(user, color, i + 1, entry.getTime()));
|
||||||
entity.setInvisible(true);
|
entity.setInvisible(true);
|
||||||
entities.add(entity);
|
entities.add(entity);
|
||||||
}
|
}
|
||||||
@@ -135,32 +127,27 @@ public class Leaderboard implements Listener {
|
|||||||
return st.toString();
|
return st.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LeaderboardEntry> getLeaderboard() {
|
private boolean isNewBestTime(long time) {
|
||||||
return LEADERBOARD.select(resultSet -> {
|
return time < bestTime;
|
||||||
List<LeaderboardEntry> leaderboard = new ArrayList<>();
|
|
||||||
while (resultSet.next()) {
|
|
||||||
leaderboard.add(new LeaderboardEntry(resultSet.getInt("User"), resultSet.getLong("Time")));
|
|
||||||
}
|
|
||||||
return leaderboard;
|
|
||||||
}, configKey, best);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getPlayerTime(SteamwarUser user) {
|
public void updateBestTime(SteamwarUser user, long time) {
|
||||||
return PLAYER_TIME.select(resultSet -> {
|
Leaderboard.upsert(user.getId(), configKey, time, isNewBestTime(time));
|
||||||
if (!resultSet.next()) {
|
update();
|
||||||
return Long.MAX_VALUE;
|
}
|
||||||
}
|
|
||||||
return resultSet.getLong("Time");
|
private List<Leaderboard> getLeaderboard() {
|
||||||
}, configKey, user.getId());
|
return Leaderboard.getLeaderboard(configKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPlayerTime(SteamwarUser user) {
|
||||||
|
Leaderboard lb = Leaderboard.getPlayerTime(user, configKey);
|
||||||
|
if(lb != null) return lb.getTime();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPlayerPlacement(SteamwarUser user) {
|
private int getPlayerPlacement(SteamwarUser user) {
|
||||||
return PLAYER_PLACEMENT.select(resultSet -> {
|
return Leaderboard.getPlayerPlacement(user, configKey);
|
||||||
if (!resultSet.next()) {
|
|
||||||
return Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
return resultSet.getInt("Placement");
|
|
||||||
}, configKey, configKey, user.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String renderTime(long time) {
|
public static String renderTime(long time) {
|
||||||
@@ -178,13 +165,6 @@ public class Leaderboard implements Listener {
|
|||||||
time % 1000);
|
time % 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
private class LeaderboardEntry {
|
|
||||||
|
|
||||||
private final int user;
|
|
||||||
private final long time;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
SteamwarUser steamwarUser = SteamwarUser.get(event.getPlayer().getUniqueId());
|
SteamwarUser steamwarUser = SteamwarUser.get(event.getPlayer().getUniqueId());
|
||||||
Reference in New Issue
Block a user