mirror of
https://github.com/Melledy/Nebula.git
synced 2026-09-19 17:19:58 +02:00
Rework and optimize inventory
`GameItem`s and `GameResource`s are now deprecated, they are stored as regular `ItemParamMap`s in the `Inventory` class instead. This is to reduce database load when loading a player.
This commit is contained in:
@@ -5,8 +5,6 @@ import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ItemDef;
|
||||
import emu.nebula.game.inventory.GameItem;
|
||||
import emu.nebula.game.inventory.GameResource;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.inventory.ItemType;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
@@ -64,14 +62,14 @@ public class CleanCommand implements CommandHandler {
|
||||
|
||||
if (all) {
|
||||
if (doItems) {
|
||||
for (GameItem item : inv.getItems().values()) {
|
||||
removeMap.add(item.getItemId(), item.getCount());
|
||||
for (var entry : inv.getItems().int2IntEntrySet()) {
|
||||
removeMap.add(entry.getIntKey(), entry.getIntValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (doResources) {
|
||||
for (GameResource res : inv.getResources().values()) {
|
||||
removeMap.add(res.getResourceId(), res.getCount());
|
||||
for (var entry : inv.getResources().int2IntEntrySet()) {
|
||||
removeMap.add(entry.getIntKey(), entry.getIntValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -212,6 +212,14 @@ public final class DatabaseManager {
|
||||
update(obj, uid, field2, value2);
|
||||
}
|
||||
|
||||
public void updateUnset(Object obj, int uid, String field) {
|
||||
var opt = new UpdateOptions().upsert(false);
|
||||
|
||||
getDatastore().find(obj.getClass())
|
||||
.filter(Filters.eq("_id", uid))
|
||||
.update(opt, UpdateOperators.unset(field));
|
||||
}
|
||||
|
||||
public void updateNested(Object obj, int uid, String filter, int filterId, String field, Object item) {
|
||||
var opt = new UpdateOptions().upsert(false);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import emu.nebula.proto.Public.Item;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Deprecated
|
||||
@Getter
|
||||
@Entity(value = "items", useDiscriminator = false)
|
||||
public class GameItem implements GameDatabaseObject {
|
||||
|
||||
@@ -12,6 +12,7 @@ import emu.nebula.proto.Public.Res;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Deprecated
|
||||
@Getter
|
||||
@Entity(value = "resources", useDiscriminator = false)
|
||||
public class GameResource implements GameDatabaseObject {
|
||||
|
||||
@@ -2,6 +2,8 @@ package emu.nebula.game.inventory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.client.model.Filters;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.nebula.GameConstants;
|
||||
@@ -20,12 +22,11 @@ import emu.nebula.proto.Public.Item;
|
||||
import emu.nebula.proto.Public.Res;
|
||||
import emu.nebula.proto.Public.Title;
|
||||
import emu.nebula.proto.Public.UI32;
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.util.ints.String2IntMap;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntCollection;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
@@ -37,6 +38,10 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
@Id
|
||||
private int uid;
|
||||
|
||||
// Items/resources
|
||||
private ItemParamMap items;
|
||||
private ItemParamMap resources;
|
||||
|
||||
// Database persistent data
|
||||
private IntSet extraSkins;
|
||||
private IntSet headIcons;
|
||||
@@ -47,13 +52,9 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
private ItemParamMap shopBuyCount;
|
||||
private String2IntMap mallBuyCount;
|
||||
|
||||
// Items/resources
|
||||
private transient Int2ObjectMap<GameResource> resources;
|
||||
private transient Int2ObjectMap<GameItem> items;
|
||||
|
||||
@Deprecated
|
||||
public Inventory() {
|
||||
this.resources = new Int2ObjectOpenHashMap<>();
|
||||
this.items = new Int2ObjectOpenHashMap<>();
|
||||
// Morphia only
|
||||
}
|
||||
|
||||
public Inventory(Player player) {
|
||||
@@ -62,6 +63,9 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
this.uid = player.getUid();
|
||||
|
||||
// Setup
|
||||
this.resources = new ItemParamMap();
|
||||
this.items = new ItemParamMap();
|
||||
|
||||
this.extraSkins = new IntOpenHashSet();
|
||||
this.headIcons = new IntOpenHashSet();
|
||||
this.titles = new IntOpenHashSet();
|
||||
@@ -266,15 +270,13 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
// Resources
|
||||
|
||||
public synchronized int getResourceCount(int id) {
|
||||
var res = this.resources.get(id);
|
||||
return res != null ? res.getCount() : 0;
|
||||
return this.resources.get(id);
|
||||
}
|
||||
|
||||
// Items
|
||||
|
||||
public synchronized int getItemCount(int id) {
|
||||
var item = this.getItems().get(id);
|
||||
return item != null ? item.getCount() : 0;
|
||||
return this.getItems().get(id);
|
||||
}
|
||||
|
||||
// Add/Remove items
|
||||
@@ -306,35 +308,37 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
// Add item
|
||||
switch (data.getItemType()) {
|
||||
case Res -> {
|
||||
var res = this.resources.get(id);
|
||||
int oldAmount = this.resources.get(id);
|
||||
int newAmount = oldAmount;
|
||||
int diff = 0;
|
||||
|
||||
if (amount > 0) {
|
||||
// Add resource
|
||||
if (res == null) {
|
||||
res = new GameResource(this.getPlayer(), id, amount);
|
||||
this.resources.put(res.getResourceId(), res);
|
||||
|
||||
diff = amount;
|
||||
} else {
|
||||
diff = res.add(amount);
|
||||
}
|
||||
newAmount = Utils.safeAdd(oldAmount, amount);
|
||||
diff = newAmount - oldAmount;
|
||||
|
||||
res.save();
|
||||
// Set
|
||||
this.resources.put(id, newAmount);
|
||||
} else {
|
||||
// Remove resource
|
||||
if (res == null) {
|
||||
break;
|
||||
}
|
||||
newAmount = Utils.safeSubtract(oldAmount, Math.abs(amount));
|
||||
diff = newAmount - oldAmount;
|
||||
|
||||
diff = res.add(amount);
|
||||
res.save();
|
||||
|
||||
if (res.getCount() < 0) {
|
||||
// Set
|
||||
if (newAmount > 0) {
|
||||
this.resources.put(id, newAmount);
|
||||
} else {
|
||||
this.resources.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Update in database
|
||||
if (newAmount > 0) {
|
||||
Nebula.getGameDatabase().update(this, this.getPlayerUid(), "resources." + id, newAmount);
|
||||
} else {
|
||||
Nebula.getGameDatabase().updateUnset(this, this.getPlayerUid(), "resources." + id);
|
||||
}
|
||||
|
||||
if (diff != 0) {
|
||||
var proto = Res.newInstance()
|
||||
.setTid(id)
|
||||
@@ -365,35 +369,37 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
// Get item
|
||||
var item = this.items.get(id);
|
||||
int oldAmount = this.items.get(id);
|
||||
int newAmount = oldAmount;
|
||||
int diff = 0;
|
||||
|
||||
if (amount > 0) {
|
||||
// Add item
|
||||
if (item == null) {
|
||||
item = new GameItem(this.getPlayer(), id, amount);
|
||||
this.items.put(item.getItemId(), item);
|
||||
|
||||
diff = amount;
|
||||
} else {
|
||||
diff = item.add(amount);
|
||||
}
|
||||
// Add resource
|
||||
newAmount = Utils.safeAdd(oldAmount, amount);
|
||||
diff = newAmount - oldAmount;
|
||||
|
||||
item.save();
|
||||
// Set
|
||||
this.items.put(id, newAmount);
|
||||
} else {
|
||||
// Remove resource
|
||||
if (item == null) {
|
||||
break;
|
||||
}
|
||||
newAmount = Utils.safeSubtract(oldAmount, Math.abs(amount));
|
||||
diff = newAmount - oldAmount;
|
||||
|
||||
diff = item.add(amount);
|
||||
item.save();
|
||||
|
||||
if (item.getCount() < 0) {
|
||||
this.resources.remove(id);
|
||||
// Set
|
||||
if (newAmount > 0) {
|
||||
this.items.put(id, newAmount);
|
||||
} else {
|
||||
this.items.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Update in database
|
||||
if (newAmount > 0) {
|
||||
Nebula.getGameDatabase().update(this, this.getPlayerUid(), "items." + id, newAmount);
|
||||
} else {
|
||||
Nebula.getGameDatabase().updateUnset(this, this.getPlayerUid(), "items." + id);
|
||||
}
|
||||
|
||||
if (diff != 0) {
|
||||
var proto = Item.newInstance()
|
||||
.setTid(id)
|
||||
@@ -893,25 +899,55 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
|
||||
// Database
|
||||
|
||||
public void loadFromDatabase() {
|
||||
@SuppressWarnings("deprecation")
|
||||
public void migrateFromDatabase() {
|
||||
var db = Nebula.getGameDatabase();
|
||||
boolean save = false;
|
||||
|
||||
db.getObjects(GameItem.class, "playerUid", getPlayerUid()).forEach(item -> {
|
||||
// Get data
|
||||
var data = GameData.getItemDataTable().get(item.getItemId());
|
||||
if (data == null) return;
|
||||
// Check if we need to handle inventory migration
|
||||
if (this.items == null) {
|
||||
this.items = new ItemParamMap();
|
||||
|
||||
// Add
|
||||
this.items.put(item.getItemId(), item);
|
||||
});
|
||||
// Get inventory items from database
|
||||
db.getObjects(GameItem.class, "playerUid", getPlayerUid()).forEach(item -> {
|
||||
// Get data
|
||||
var data = GameData.getItemDataTable().get(item.getItemId());
|
||||
if (data == null) return;
|
||||
|
||||
// Add
|
||||
this.items.put(item.getItemId(), item.getCount());
|
||||
});
|
||||
|
||||
// Delete all inventory items
|
||||
db.getDatastore().getCollection(GameItem.class).deleteMany(Filters.eq("playerUid", uid));
|
||||
|
||||
// Set to save inventory to database
|
||||
save = true;
|
||||
}
|
||||
|
||||
db.getObjects(GameResource.class, "playerUid", getPlayerUid()).forEach(res -> {
|
||||
// Get data
|
||||
var data = GameData.getItemDataTable().get(res.getResourceId());
|
||||
if (data == null) return;
|
||||
if (this.resources == null) {
|
||||
this.resources = new ItemParamMap();
|
||||
|
||||
// Add
|
||||
this.resources.put(res.getResourceId(), res);
|
||||
});
|
||||
// Get inventory resources from database
|
||||
db.getObjects(GameResource.class, "playerUid", getPlayerUid()).forEach(res -> {
|
||||
// Get data
|
||||
var data = GameData.getItemDataTable().get(res.getResourceId());
|
||||
if (data == null) return;
|
||||
|
||||
// Add
|
||||
this.resources.put(res.getResourceId(), res.getCount());
|
||||
});
|
||||
|
||||
// Delete all inventory resources
|
||||
db.getDatastore().getCollection(GameResource.class).deleteMany(Filters.eq("playerUid", uid));
|
||||
|
||||
// Set to save inventory to database
|
||||
save = true;
|
||||
}
|
||||
|
||||
// Update in database
|
||||
if (save) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,10 @@ import emu.nebula.proto.Public.CharShow;
|
||||
import emu.nebula.proto.Public.Energy;
|
||||
import emu.nebula.proto.Public.Friend;
|
||||
import emu.nebula.proto.Public.HonorInfo;
|
||||
import emu.nebula.proto.Public.Item;
|
||||
import emu.nebula.proto.Public.NewbieInfo;
|
||||
import emu.nebula.proto.Public.QuestType;
|
||||
import emu.nebula.proto.Public.Res;
|
||||
import emu.nebula.proto.Public.WorldClass;
|
||||
import emu.nebula.proto.Public.WorldClassRewardState;
|
||||
import emu.nebula.util.Utils;
|
||||
@@ -789,7 +791,7 @@ public class Player implements GameDatabaseObject {
|
||||
if (this.inventory == null) {
|
||||
this.inventory = this.loadManagerFromDatabase(Inventory.class);
|
||||
}
|
||||
this.getInventory().loadFromDatabase();
|
||||
this.getInventory().migrateFromDatabase();
|
||||
|
||||
// Load referenced classes from the database
|
||||
this.formations = this.loadManagerFromDatabase(FormationManager.class);
|
||||
@@ -952,12 +954,20 @@ public class Player implements GameDatabaseObject {
|
||||
proto.addDiscs(disc.toProto());
|
||||
}
|
||||
|
||||
for (var item : getInventory().getItems().values()) {
|
||||
proto.addItems(item.toProto());
|
||||
for (var item : getInventory().getItems().int2IntEntrySet()) {
|
||||
var info = Item.newInstance()
|
||||
.setTid(item.getIntKey())
|
||||
.setQty(item.getIntValue());
|
||||
|
||||
proto.addItems(info);
|
||||
}
|
||||
|
||||
for (var res : getInventory().getResources().values()) {
|
||||
proto.addRes(res.toProto());
|
||||
for (var res : getInventory().getResources().int2IntEntrySet()) {
|
||||
var info = Res.newInstance()
|
||||
.setTid(res.getIntKey())
|
||||
.setQty(res.getIntValue());
|
||||
|
||||
proto.addRes(info);
|
||||
}
|
||||
|
||||
// Formations
|
||||
|
||||
@@ -147,6 +147,7 @@ public class PlayerModule extends GameContextModule {
|
||||
* @param uid
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public synchronized boolean deletePlayer(int uid) {
|
||||
// Make sure player is not online when we are deleting the player
|
||||
Player player = this.getCachedPlayerByUid(uid);
|
||||
|
||||
Reference in New Issue
Block a user