Implement saving tower records

This commit is contained in:
Melledy
2025-10-28 05:47:01 -07:00
parent 276d9f9002
commit d89acec89c
7 changed files with 207 additions and 36 deletions

View File

@@ -134,8 +134,6 @@ public class CharacterStorage extends PlayerManager {
this.characters.put(character.getCharId(), character); this.characters.put(character.getCharId(), character);
}); });
db.getObjects(GameDisc.class, "playerUid", getPlayerUid()).forEach(disc -> { db.getObjects(GameDisc.class, "playerUid", getPlayerUid()).forEach(disc -> {
// Get data // Get data
var data = GameData.getDiscDataTable().get(disc.getDiscId()); var data = GameData.getDiscDataTable().get(disc.getDiscId());

View File

@@ -44,6 +44,30 @@ public class Formation {
return this.discIds[i]; return this.discIds[i];
} }
public int getCharCount() {
int count = 0;
for (int id : this.getCharIds()) {
if (id > 0) {
count++;
}
}
return count;
}
public int getDiscCount() {
int count = 0;
for (int id : this.getDiscIds()) {
if (id > 0) {
count++;
}
}
return count;
}
// Proto // Proto
public FormationInfo toProto() { public FormationInfo toProto() {

View File

@@ -1,25 +1,38 @@
package emu.nebula.game.tower; package emu.nebula.game.tower;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.nebula.data.GameData;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.proto.PublicStarTower.BuildPotential; import emu.nebula.proto.PublicStarTower.BuildPotential;
import emu.nebula.proto.PublicStarTower.StarTowerBuildBrief;
import emu.nebula.proto.PublicStarTower.StarTowerBuildDetail;
import emu.nebula.proto.PublicStarTower.StarTowerBuildInfo; import emu.nebula.proto.PublicStarTower.StarTowerBuildInfo;
import emu.nebula.proto.PublicStarTower.TowerBuildChar; import emu.nebula.proto.PublicStarTower.TowerBuildChar;
import emu.nebula.util.Snowflake;
import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@Entity(useDiscriminator = false) @Entity(value = "star_tower_builds", useDiscriminator = false)
public class StarTowerBuild { public class StarTowerBuild implements GameDatabaseObject {
private int id; @Id
private int uid;
@Indexed
private int playerUid;
private String name; private String name;
private boolean lock; private boolean lock;
private boolean preference; private boolean preference;
private int score; private int score;
private int[] charIds;
private Int2IntMap chars;
private int[] discIds; private int[] discIds;
private Int2IntMap potentials; private Int2IntMap potentials;
private Int2IntMap subNoteSkills;
@Deprecated @Deprecated
public StarTowerBuild() { public StarTowerBuild() {
@@ -27,59 +40,78 @@ public class StarTowerBuild {
} }
public StarTowerBuild(StarTowerInstance instance) { public StarTowerBuild(StarTowerInstance instance) {
this.uid = Snowflake.newUid();
this.playerUid = instance.getPlayer().getUid();
this.name = ""; this.name = "";
this.potentials = new Int2IntOpenHashMap(); this.potentials = new Int2IntOpenHashMap();
this.subNoteSkills = new Int2IntOpenHashMap();
this.charIds = instance.getChars().stream() // Discs
.filter(c -> c.getId() > 0)
.mapToInt(c -> c.getId())
.toArray();
this.discIds = instance.getDiscs().stream() this.discIds = instance.getDiscs().stream()
.filter(d -> d.getId() > 0) .filter(d -> d.getId() > 0)
.mapToInt(d -> d.getId()) .mapToInt(d -> d.getId())
.toArray(); .toArray();
// Characters
this.chars = new Int2IntOpenHashMap();
instance.getChars().stream()
.forEach(c -> this.getChars().put(c.getId(), 0));
// Add potentials // Add potentials
for (int id : instance.getPotentials()) { for (int id : instance.getPotentials()) {
// Add to potential map
this.getPotentials().put(id, instance.getItemCount(id)); this.getPotentials().put(id, instance.getItemCount(id));
// Add to character
var potentialData = GameData.getPotentialDataTable().get(id);
if (potentialData != null) {
int charId = potentialData.getCharId();
this.getChars().put(charId, this.getChars().get(charId) + 1);
}
} }
} }
public void setId(StarTowerManager manager) {
this.id = manager.getNextBuildId();
}
// Proto // Proto
public StarTowerBuildInfo toProto() { public StarTowerBuildInfo toProto() {
var proto = StarTowerBuildInfo.newInstance(); var proto = StarTowerBuildInfo.newInstance()
.setBrief(this.toBriefProto())
// Basic data .setDetail(this.toDetailProto());
proto.getMutableBrief()
.setId(this.getId()) return proto;
.setName(this.getName()) }
.setLock(this.isLock())
.setPreference(this.isPreference()) public StarTowerBuildBrief toBriefProto() {
.setScore(this.getScore()) var proto = StarTowerBuildBrief.newInstance()
.addAllDiscIds(this.getDiscIds()); .setId(this.getUid())
.setName(this.getName())
.setLock(this.isLock())
.setPreference(this.isPreference())
.setScore(this.getScore())
.addAllDiscIds(this.getDiscIds());
// Add characters // Add characters
for (int id : charIds) { for (var character : this.getChars().int2IntEntrySet()) {
var charProto = TowerBuildChar.newInstance() var charProto = TowerBuildChar.newInstance()
.setCharId(id); .setCharId(character.getIntKey())
.setPotentialCnt(character.getIntValue());
proto.getMutableBrief().addChars(charProto); proto.addChars(charProto);
} }
// Build detail return proto;
var detail = proto.getMutableDetail(); }
public StarTowerBuildDetail toDetailProto() {
var proto = StarTowerBuildDetail.newInstance();
for (var entry : this.getPotentials().int2IntEntrySet()) { for (var entry : this.getPotentials().int2IntEntrySet()) {
var potential = BuildPotential.newInstance() var potential = BuildPotential.newInstance()
.setPotentialId(entry.getIntKey()) .setPotentialId(entry.getIntKey())
.setLevel(entry.getIntValue()); .setLevel(entry.getIntValue());
detail.getMutablePotentials().add(potential); proto.getMutablePotentials().add(potential);
} }
return proto; return proto;

View File

@@ -2,11 +2,14 @@ package emu.nebula.game.tower;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import emu.nebula.Nebula;
import emu.nebula.data.GameData; import emu.nebula.data.GameData;
import emu.nebula.database.GameDatabaseObject; import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.player.Player; import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerManager; import emu.nebula.game.player.PlayerManager;
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq; import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@@ -14,9 +17,10 @@ import lombok.Getter;
public class StarTowerManager extends PlayerManager implements GameDatabaseObject { public class StarTowerManager extends PlayerManager implements GameDatabaseObject {
@Id @Id
private int uid; private int uid;
private int lastBuildId;
private transient Int2ObjectMap<StarTowerBuild> builds;
private transient StarTowerInstance instance; private transient StarTowerInstance instance;
private transient StarTowerBuild lastBuild;
@Deprecated // Morphia only @Deprecated // Morphia only
public StarTowerManager() { public StarTowerManager() {
@@ -30,8 +34,12 @@ public class StarTowerManager extends PlayerManager implements GameDatabaseObjec
this.save(); this.save();
} }
public int getNextBuildId() { public Int2ObjectMap<StarTowerBuild> getBuilds() {
return ++this.lastBuildId; if (this.builds == null) {
this.loadFromDatabase();
}
return builds;
} }
public StarTowerInstance apply(StarTowerApplyReq req) { public StarTowerInstance apply(StarTowerApplyReq req) {
@@ -47,6 +55,11 @@ public class StarTowerManager extends PlayerManager implements GameDatabaseObjec
return null; return null;
} }
// Make sure player has at least 3 chars and 3 discs
if (formation.getCharCount() != 3 || formation.getDiscCount() < 3) {
return null;
}
// Create instance // Create instance
this.instance = new StarTowerInstance(this, data, formation, req); this.instance = new StarTowerInstance(this, data, formation, req);
@@ -55,12 +68,57 @@ public class StarTowerManager extends PlayerManager implements GameDatabaseObjec
} }
public StarTowerInstance giveUp() { public StarTowerInstance giveUp() {
// Cache instance
var instance = this.instance; var instance = this.instance;
if (instance != null) { if (instance != null) {
// Set last build
this.lastBuild = instance.getBuild();
// Clear instance
this.instance = null; this.instance = null;
} }
return instance; return instance;
} }
public boolean saveBuild(boolean delete, String name, boolean lock) {
// Sanity check
if (this.getLastBuild() == null) {
return false;
}
// Cache build and clear reference
var build = this.lastBuild;
this.lastBuild = null;
// Check if the player wants this build or not
if (delete) {
return true;
}
// Check limit
if (this.getBuilds().size() >= 50) {
return false;
}
// Add to builds
this.getBuilds().put(build.getUid(), build);
// Save build to database
build.save();
//
return true;
}
// Database
private void loadFromDatabase() {
this.builds = new Int2ObjectOpenHashMap<>();
Nebula.getGameDatabase().getObjects(StarTowerBuild.class, "playerUid", getPlayerUid()).forEach(build -> {
this.builds.put(build.getUid(), build);
});
}
} }

View File

@@ -2,6 +2,7 @@ package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler; import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgId;
import emu.nebula.proto.StarTowerBuildBriefListGet.StarTowerBuildBriefListGetResp;
import emu.nebula.net.HandlerId; import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession; import emu.nebula.net.GameSession;
@@ -10,7 +11,17 @@ public class HandlerStarTowerBuildBriefListGetReq extends NetHandler {
@Override @Override
public byte[] handle(GameSession session, byte[] message) throws Exception { public byte[] handle(GameSession session, byte[] message) throws Exception {
return this.encodeMsg(NetMsgId.star_tower_build_brief_list_get_succeed_ack); // Build response
var rsp = StarTowerBuildBriefListGetResp.newInstance();
// Add star tower builds
var builds = session.getPlayer().getStarTowerManager().getBuilds().values();
for (var build : builds) {
rsp.addBriefs(build.toBriefProto());
}
// Finish
return this.encodeMsg(NetMsgId.star_tower_build_brief_list_get_succeed_ack, rsp);
} }
} }

View File

@@ -0,0 +1,33 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.StarTowerBuildDetailGet.StarTowerBuildDetailGetReq;
import emu.nebula.proto.StarTowerBuildDetailGet.StarTowerBuildDetailGetResp;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.star_tower_build_detail_get_req)
public class HandlerStarTowerBuildDetailGetReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse request
var req = StarTowerBuildDetailGetReq.parseFrom(message);
// Get build
int buildId = (int) req.getBuildId();
var build = session.getPlayer().getStarTowerManager().getBuilds().get(buildId);
if (build == null) {
return this.encodeMsg(NetMsgId.star_tower_build_detail_get_failed_ack);
}
// Build response
var rsp = StarTowerBuildDetailGetResp.newInstance()
.setDetail(build.toDetailProto());
return this.encodeMsg(NetMsgId.star_tower_build_detail_get_succeed_ack, rsp);
}
}

View File

@@ -3,6 +3,7 @@ package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler; import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgId;
import emu.nebula.proto.StarTowerBuildWhetherSave.StarTowerBuildWhetherSaveReq; import emu.nebula.proto.StarTowerBuildWhetherSave.StarTowerBuildWhetherSaveReq;
import emu.nebula.proto.StarTowerBuildWhetherSave.StarTowerBuildWhetherSaveResp;
import emu.nebula.net.HandlerId; import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession; import emu.nebula.net.GameSession;
@@ -11,10 +12,24 @@ public class HandlerStarTowerBuildWhetherSaveReq extends NetHandler {
@Override @Override
public byte[] handle(GameSession session, byte[] message) throws Exception { public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse request
var req = StarTowerBuildWhetherSaveReq.parseFrom(message); var req = StarTowerBuildWhetherSaveReq.parseFrom(message);
// TODO // Save build
return this.encodeMsg(NetMsgId.star_tower_build_whether_save_succeed_ack); boolean result = session.getPlayer().getStarTowerManager().saveBuild(
req.getDelete(),
req.getBuildName(),
req.getLock()
);
if (!result) {
return this.encodeMsg(NetMsgId.star_tower_build_whether_save_failed_ack);
}
// Build response
var rsp = StarTowerBuildWhetherSaveResp.newInstance();
return this.encodeMsg(NetMsgId.star_tower_build_whether_save_succeed_ack, rsp);
} }
} }