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