mirror of
https://github.com/Melledy/Nebula.git
synced 2025-12-13 12:54:36 +01:00
Implement boss blitz server leaderboard
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package emu.nebula.database;
|
package emu.nebula.database;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import emu.nebula.Config.DatabaseInfo;
|
import emu.nebula.Config.DatabaseInfo;
|
||||||
@@ -27,6 +28,8 @@ import dev.morphia.*;
|
|||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import dev.morphia.mapping.Mapper;
|
import dev.morphia.mapping.Mapper;
|
||||||
import dev.morphia.mapping.MapperOptions;
|
import dev.morphia.mapping.MapperOptions;
|
||||||
|
import dev.morphia.query.FindOptions;
|
||||||
|
import dev.morphia.query.Sort;
|
||||||
import dev.morphia.query.filters.Filters;
|
import dev.morphia.query.filters.Filters;
|
||||||
import dev.morphia.query.updates.UpdateOperators;
|
import dev.morphia.query.updates.UpdateOperators;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -159,6 +162,14 @@ public final class DatabaseManager {
|
|||||||
public <T> Stream<T> getObjects(Class<T> cls) {
|
public <T> Stream<T> getObjects(Class<T> cls) {
|
||||||
return getDatastore().find(cls).stream();
|
return getDatastore().find(cls).stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> List<T> getSortedObjects(Class<T> cls, String filter, int limit) {
|
||||||
|
var options = new FindOptions()
|
||||||
|
.sort(Sort.descending(filter))
|
||||||
|
.limit(limit);
|
||||||
|
|
||||||
|
return getDatastore().find(cls).iterator(options).toList();
|
||||||
|
}
|
||||||
|
|
||||||
public <T> void save(T obj) {
|
public <T> void save(T obj) {
|
||||||
getDatastore().save(obj, INSERT_OPTIONS);
|
getDatastore().save(obj, INSERT_OPTIONS);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import emu.nebula.GameConstants;
|
|||||||
import emu.nebula.Nebula;
|
import emu.nebula.Nebula;
|
||||||
import emu.nebula.game.gacha.GachaModule;
|
import emu.nebula.game.gacha.GachaModule;
|
||||||
import emu.nebula.game.player.PlayerModule;
|
import emu.nebula.game.player.PlayerModule;
|
||||||
|
import emu.nebula.game.scoreboss.ScoreBossModule;
|
||||||
import emu.nebula.net.GameSession;
|
import emu.nebula.net.GameSession;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
@@ -24,6 +25,7 @@ public class GameContext implements Runnable {
|
|||||||
// Modules
|
// Modules
|
||||||
private final PlayerModule playerModule;
|
private final PlayerModule playerModule;
|
||||||
private final GachaModule gachaModule;
|
private final GachaModule gachaModule;
|
||||||
|
private final ScoreBossModule scoreBossModule;
|
||||||
|
|
||||||
// Game loop
|
// Game loop
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
@@ -37,6 +39,7 @@ public class GameContext implements Runnable {
|
|||||||
// Setup game modules
|
// Setup game modules
|
||||||
this.playerModule = new PlayerModule(this);
|
this.playerModule = new PlayerModule(this);
|
||||||
this.gachaModule = new GachaModule(this);
|
this.gachaModule = new GachaModule(this);
|
||||||
|
this.scoreBossModule = new ScoreBossModule(this);
|
||||||
|
|
||||||
// Run game loop
|
// Run game loop
|
||||||
this.scheduler = Executors.newScheduledThreadPool(1);
|
this.scheduler = Executors.newScheduledThreadPool(1);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package emu.nebula.game.scoreboss;
|
package emu.nebula.game.scoreboss;
|
||||||
|
|
||||||
|
import emu.nebula.Nebula;
|
||||||
import emu.nebula.data.GameData;
|
import emu.nebula.data.GameData;
|
||||||
import emu.nebula.data.resources.ScoreBossControlDef;
|
import emu.nebula.data.resources.ScoreBossControlDef;
|
||||||
import emu.nebula.game.player.Player;
|
import emu.nebula.game.player.Player;
|
||||||
@@ -11,6 +12,9 @@ public class ScoreBossManager extends PlayerManager {
|
|||||||
private int levelId;
|
private int levelId;
|
||||||
private long buildId;
|
private long buildId;
|
||||||
|
|
||||||
|
private boolean checkedDatabase;
|
||||||
|
private ScoreBossRankEntry ranking;
|
||||||
|
|
||||||
public ScoreBossManager(Player player) {
|
public ScoreBossManager(Player player) {
|
||||||
super(player);
|
super(player);
|
||||||
}
|
}
|
||||||
@@ -23,6 +27,15 @@ public class ScoreBossManager extends PlayerManager {
|
|||||||
return GameData.getScoreBossControlDataTable().get(this.getControlId());
|
return GameData.getScoreBossControlDataTable().get(this.getControlId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScoreBossRankEntry getRanking() {
|
||||||
|
if (this.ranking == null && !this.checkedDatabase) {
|
||||||
|
this.ranking = Nebula.getGameDatabase().getObjectByUid(ScoreBossRankEntry.class, this.getPlayerUid());
|
||||||
|
this.checkedDatabase = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ranking;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean apply(int levelId, long buildId) {
|
public boolean apply(int levelId, long buildId) {
|
||||||
// Get level
|
// Get level
|
||||||
var control = getControlData();
|
var control = getControlData();
|
||||||
@@ -44,7 +57,38 @@ public class ScoreBossManager extends PlayerManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void settle(int star, int score) {
|
public boolean settle(int stars, int score) {
|
||||||
// TODO
|
// Get level
|
||||||
|
var control = getControlData();
|
||||||
|
if (control == null || !control.getLevelGroup().contains(this.getLevelId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get build
|
||||||
|
var build = getPlayer().getStarTowerManager().getBuildById(this.getBuildId());
|
||||||
|
if (build == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ranking from database
|
||||||
|
this.getRanking();
|
||||||
|
|
||||||
|
// Create ranking if its not in the database
|
||||||
|
if (this.ranking == null) {
|
||||||
|
this.ranking = new ScoreBossRankEntry(this.getPlayer(), this.getControlId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settle
|
||||||
|
this.ranking.settle(this.getPlayer(), build, this.getLevelId(), stars, score);
|
||||||
|
|
||||||
|
// Save ranking
|
||||||
|
this.ranking.save();
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
this.levelId = 0;
|
||||||
|
this.buildId = 0;
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/main/java/emu/nebula/game/scoreboss/ScoreBossModule.java
Normal file
52
src/main/java/emu/nebula/game/scoreboss/ScoreBossModule.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package emu.nebula.game.scoreboss;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.nebula.Nebula;
|
||||||
|
import emu.nebula.game.GameContext;
|
||||||
|
import emu.nebula.game.GameContextModule;
|
||||||
|
import emu.nebula.proto.ScoreBossRank.ScoreBossRankData;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class ScoreBossModule extends GameContextModule {
|
||||||
|
private long lastUpdate;
|
||||||
|
private long nextUpdate;
|
||||||
|
private List<ScoreBossRankData> ranking;
|
||||||
|
|
||||||
|
public ScoreBossModule(GameContext context) {
|
||||||
|
super(context);
|
||||||
|
this.nextUpdate = -1;
|
||||||
|
this.ranking = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<ScoreBossRankData> getRanking() {
|
||||||
|
if (System.currentTimeMillis() > this.nextUpdate) {
|
||||||
|
this.updateRanking();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ranking;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache ranking so we dont query the database too much
|
||||||
|
private void updateRanking() {
|
||||||
|
// Clear
|
||||||
|
this.ranking.clear();
|
||||||
|
|
||||||
|
// Get from database
|
||||||
|
var list = Nebula.getGameDatabase().getSortedObjects(ScoreBossRankEntry.class, "score", 50);
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
// Get rank entry and set proto
|
||||||
|
var entry = list.get(i);
|
||||||
|
entry.setRank(i + 1);
|
||||||
|
|
||||||
|
// Add to ranking
|
||||||
|
this.ranking.add(entry.toProto());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextUpdate = System.currentTimeMillis() + 1000;
|
||||||
|
this.lastUpdate = Nebula.getCurrentTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
171
src/main/java/emu/nebula/game/scoreboss/ScoreBossRankEntry.java
Normal file
171
src/main/java/emu/nebula/game/scoreboss/ScoreBossRankEntry.java
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package emu.nebula.game.scoreboss;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import dev.morphia.annotations.Entity;
|
||||||
|
import dev.morphia.annotations.Id;
|
||||||
|
|
||||||
|
import emu.nebula.database.GameDatabaseObject;
|
||||||
|
import emu.nebula.game.player.Player;
|
||||||
|
import emu.nebula.game.tower.StarTowerBuild;
|
||||||
|
import emu.nebula.game.character.Character;
|
||||||
|
import emu.nebula.proto.Public.HonorInfo;
|
||||||
|
import emu.nebula.proto.ScoreBossRank.ScoreBossRankChar;
|
||||||
|
import emu.nebula.proto.ScoreBossRank.ScoreBossRankData;
|
||||||
|
import emu.nebula.proto.ScoreBossRank.ScoreBossRankTeam;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Entity(value = "score_boss_rank", useDiscriminator = false)
|
||||||
|
public class ScoreBossRankEntry implements GameDatabaseObject {
|
||||||
|
@Id
|
||||||
|
private int playerUid;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private int level;
|
||||||
|
private int headIcon;
|
||||||
|
private int titlePrefix;
|
||||||
|
private int titleSuffix;
|
||||||
|
private int[] honor;
|
||||||
|
private int score;
|
||||||
|
|
||||||
|
private int controlId;
|
||||||
|
private Map<Integer, ScoreBossTeamEntry> teams;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private transient int rank;
|
||||||
|
|
||||||
|
@Deprecated // Morphia only
|
||||||
|
public ScoreBossRankEntry() {
|
||||||
|
this.rank = 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreBossRankEntry(Player player, int controlId) {
|
||||||
|
this.playerUid = player.getUid();
|
||||||
|
this.controlId = controlId;
|
||||||
|
this.teams = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Player player) {
|
||||||
|
this.name = player.getName();
|
||||||
|
this.level = player.getLevel();
|
||||||
|
this.headIcon = player.getHeadIcon();
|
||||||
|
this.titlePrefix = player.getTitlePrefix();
|
||||||
|
this.titleSuffix = player.getTitleSuffix();
|
||||||
|
this.honor = player.getHonor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void settle(Player player, StarTowerBuild build, int level, int stars, int score) {
|
||||||
|
// Update player data
|
||||||
|
this.update(player);
|
||||||
|
|
||||||
|
// Set team entry
|
||||||
|
var team = new ScoreBossTeamEntry(player, build, stars, score);
|
||||||
|
this.getTeams().put(level, team);
|
||||||
|
|
||||||
|
// Calculate score
|
||||||
|
this.score = 0;
|
||||||
|
|
||||||
|
for (var t : this.getTeams().values()) {
|
||||||
|
this.score += t.getLevelScore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proto
|
||||||
|
|
||||||
|
public ScoreBossRankData toProto() {
|
||||||
|
var proto = ScoreBossRankData.newInstance()
|
||||||
|
.setId(this.getPlayerUid())
|
||||||
|
.setNickName(this.getName())
|
||||||
|
.setWorldClass(this.getLevel())
|
||||||
|
.setHeadIcon(this.getHeadIcon())
|
||||||
|
.setScore(this.getScore())
|
||||||
|
.setTitlePrefix(this.getTitlePrefix())
|
||||||
|
.setTitleSuffix(this.getTitleSuffix())
|
||||||
|
.setRank(this.getRank());
|
||||||
|
|
||||||
|
for (int id : this.getHonor()) {
|
||||||
|
proto.addHonors(HonorInfo.newInstance().setId(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var team : this.getTeams().values()) {
|
||||||
|
proto.addTeams(team.toProto());
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra classes
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Entity(useDiscriminator = false)
|
||||||
|
public static class ScoreBossTeamEntry {
|
||||||
|
private int buildId;
|
||||||
|
private int buildScore;
|
||||||
|
private int stars;
|
||||||
|
private int levelScore;
|
||||||
|
private List<ScoreBossCharEntry> characters;
|
||||||
|
|
||||||
|
@Deprecated // Morphia only
|
||||||
|
public ScoreBossTeamEntry() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreBossTeamEntry(Player player, StarTowerBuild build, int stars, int score) {
|
||||||
|
this.buildId = build.getUid();
|
||||||
|
this.buildScore = build.getScore();
|
||||||
|
this.stars = stars;
|
||||||
|
this.levelScore = score;
|
||||||
|
this.characters = new ArrayList<>();
|
||||||
|
|
||||||
|
for (var charId : build.getCharIds()) {
|
||||||
|
var character = player.getCharacters().getCharacterById(charId);
|
||||||
|
if (character != null) {
|
||||||
|
this.getCharacters().add(new ScoreBossCharEntry(character));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreBossRankTeam toProto() {
|
||||||
|
var proto = ScoreBossRankTeam.newInstance()
|
||||||
|
.setBuildScore(this.getBuildScore())
|
||||||
|
.setLevelScore(this.getLevelScore());
|
||||||
|
|
||||||
|
for (var c : this.getCharacters()) {
|
||||||
|
proto.addChars(c.toProto());
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Entity(useDiscriminator = false)
|
||||||
|
public static class ScoreBossCharEntry {
|
||||||
|
private int id;
|
||||||
|
private int level;
|
||||||
|
|
||||||
|
@Deprecated // Morphia only
|
||||||
|
public ScoreBossCharEntry() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreBossCharEntry(Character character) {
|
||||||
|
this.id = character.getCharId();
|
||||||
|
this.level = character.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreBossRankChar toProto() {
|
||||||
|
var proto = ScoreBossRankChar.newInstance()
|
||||||
|
.setId(this.getId())
|
||||||
|
.setLevel(this.getLevel());
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,29 @@ public class HandlerScoreBossRankReq extends NetHandler {
|
|||||||
var rsp = ScoreBossRankInfo.newInstance()
|
var rsp = ScoreBossRankInfo.newInstance()
|
||||||
.setLastRefreshTime(Nebula.getCurrentTime());
|
.setLastRefreshTime(Nebula.getCurrentTime());
|
||||||
|
|
||||||
|
// Get self
|
||||||
|
var self = session.getPlayer().getScoreBossManager().getRanking();
|
||||||
|
|
||||||
|
if (self != null) {
|
||||||
|
rsp.setSelf(self.toProto());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ranking
|
||||||
|
var ranking = Nebula.getGameContext().getScoreBossModule().getRanking();
|
||||||
|
|
||||||
|
for (var entry : ranking) {
|
||||||
|
// Check self
|
||||||
|
if (self != null && self.getPlayerUid() == entry.getId()) {
|
||||||
|
rsp.getMutableSelf().setRank(entry.getRank());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to ranking
|
||||||
|
rsp.addRank(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set total
|
||||||
|
rsp.setTotal(ranking.size());
|
||||||
|
|
||||||
// Encode and send
|
// Encode and send
|
||||||
return session.encodeMsg(NetMsgId.score_boss_rank_succeed_ack, rsp);
|
return session.encodeMsg(NetMsgId.score_boss_rank_succeed_ack, rsp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ public class HandlerScoreBossSettleReq extends NetHandler {
|
|||||||
var req = ScoreBossSettleReq.parseFrom(message);
|
var req = ScoreBossSettleReq.parseFrom(message);
|
||||||
|
|
||||||
// Settle
|
// Settle
|
||||||
session.getPlayer().getScoreBossManager().settle(req.getStar(), req.getScore());
|
boolean success = session.getPlayer().getScoreBossManager().settle(req.getStar(), req.getScore());
|
||||||
|
|
||||||
|
if (success == false) {
|
||||||
|
return session.encodeMsg(NetMsgId.score_boss_settle_failed_ack);
|
||||||
|
}
|
||||||
|
|
||||||
// Build response
|
// Build response
|
||||||
var rsp = ScoreBossSettleResp.newInstance();
|
var rsp = ScoreBossSettleResp.newInstance();
|
||||||
|
|||||||
Reference in New Issue
Block a user