diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index 2a247b46e..97bd6d739 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -12,6 +12,7 @@ import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.player.Player; public final class DatabaseHelper { @@ -166,6 +167,7 @@ public final class DatabaseHelper { public static List getInventoryItems(Player player) { return DatabaseManager.getDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } + public static List getFriends(Player player) { return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } @@ -219,6 +221,19 @@ public final class DatabaseHelper { public static void saveGachaRecord(GachaRecord gachaRecord){ DatabaseManager.getDatastore().save(gachaRecord); } + + public static List getAllMail(Player player) { + return DatabaseManager.getDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); + } + + public static void saveMail(Mail mail) { + DatabaseManager.getDatastore().save(mail); + } + + public static boolean deleteMail(Mail mail) { + DeleteResult result = DatabaseManager.getDatastore().delete(mail); + return result.wasAcknowledged(); + } public static char AWJVN = 'e'; } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 2376451db..90ff17238 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -18,6 +18,7 @@ import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.player.Player; public final class DatabaseManager { @@ -29,7 +30,7 @@ public final class DatabaseManager { private static Datastore dispatchDatastore; private static final Class[] mappedClasses = new Class[] { - DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class + DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class, Mail.class }; public static Datastore getDatastore() { diff --git a/src/main/java/emu/grasscutter/game/mail/Mail.java b/src/main/java/emu/grasscutter/game/mail/Mail.java index 19fd79adc..286db6ef6 100644 --- a/src/main/java/emu/grasscutter/game/mail/Mail.java +++ b/src/main/java/emu/grasscutter/game/mail/Mail.java @@ -1,15 +1,22 @@ package emu.grasscutter.game.mail; import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import dev.morphia.annotations.Indexed; +import dev.morphia.annotations.Transient; +import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.player.Player; import java.time.Instant; import java.util.ArrayList; import java.util.List; -@Entity -public class Mail { +import org.bson.types.ObjectId; +@Entity(value = "mail", useDiscriminator = false) +public class Mail { + @Id private ObjectId id; + @Indexed private int ownerUid; public MailContent mailContent; public List itemList; public long sendTime; @@ -18,6 +25,7 @@ public class Mail { public boolean isRead; public boolean isAttachmentGot; public int stateValue; + @Transient private boolean shouldDelete; public Mail() { this(new MailContent(), new ArrayList(), (int) Instant.now().getEpochSecond() + 604800); // TODO: add expire time to send mail command @@ -42,7 +50,19 @@ public class Mail { this.stateValue = state; // Different mailboxes, 1 = Default, 3 = Gift-box. } - @Entity + public ObjectId getId() { + return id; + } + + public int getOwnerUid() { + return ownerUid; + } + + public void setOwnerUid(int ownerUid) { + this.ownerUid = ownerUid; + } + + @Entity public static class MailContent { public String title; public String content; @@ -93,4 +113,12 @@ public class Mail { this.itemLevel = itemLevel; } } + + public void save() { + if (this.expireTime * 1000 < System.currentTimeMillis()) { + DatabaseHelper.deleteMail(this); + } else { + DatabaseHelper.saveMail(this); + } + } } diff --git a/src/main/java/emu/grasscutter/game/mail/MailHandler.java b/src/main/java/emu/grasscutter/game/mail/MailHandler.java new file mode 100644 index 000000000..9d7839fb5 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/mail/MailHandler.java @@ -0,0 +1,104 @@ +package emu.grasscutter.game.mail; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.player.PlayerReceiveMailEvent; +import emu.grasscutter.server.packet.send.PacketDelMailRsp; +import emu.grasscutter.server.packet.send.PacketMailChangeNotify; + +public class MailHandler { + private final Player player; + private final List mail; + + public MailHandler(Player player) { + this.player = player; + this.mail = new ArrayList<>(); + } + + public Player getPlayer() { + return player; + } + + public List getMail() { + return mail; + } + + // ---------------------MAIL------------------------ + + public void sendMail(Mail message) { + // Call mail receive event. + PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this.getPlayer(), message); event.call(); + if(event.isCanceled()) return; message = event.getMessage(); + + message.setOwnerUid(this.getPlayer().getUid()); + + this.mail.add(message); + + Grasscutter.getLogger().debug("Mail sent to user [" + this.getPlayer().getUid() + ":" + this.getPlayer().getNickname() + "]!"); + + if (this.getPlayer().isOnline()) { + this.getPlayer().sendPacket(new PacketMailChangeNotify(this.getPlayer(), message)); + } // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline + } + + public boolean deleteMail(int mailId) { + Mail message = getMailById(mailId); + + if (message != null) { + this.getMail().remove(mailId); + message.expireTime = 0; + message.save(); + + return true; + } + + return false; + } + + public void deleteMail(List mailList) { + List sortedMailList = new ArrayList<>(); + sortedMailList.addAll(mailList); + Collections.sort(sortedMailList, Collections.reverseOrder()); + + List deleted = new ArrayList<>(); + + for (int id : sortedMailList) { + if (this.deleteMail(id)) { + deleted.add(id); + } + } + + player.getSession().send(new PacketDelMailRsp(player, deleted)); + player.getSession().send(new PacketMailChangeNotify(player, null, deleted)); + } + + public Mail getMailById(int index) { return this.mail.get(index); } + + public int getMailIndex(Mail message) { + return this.mail.indexOf(message); + } + + public boolean replaceMailByIndex(int index, Mail message) { + if(getMailById(index) != null) { + this.mail.set(index, message); + message.save(); + return true; + } else { + return false; + } + } + + public void loadFromDatabase() { + List mailList = DatabaseHelper.getAllMail(this.getPlayer()); + + for (Mail mail : mailList) { + this.getMail().add(mail); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 13f2674f2..671e33269 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -19,6 +19,7 @@ import emu.grasscutter.game.gacha.PlayerGachaInfo; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.mail.Mail; +import emu.grasscutter.game.mail.MailHandler; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.shop.ShopLimit; @@ -78,14 +79,14 @@ public class Player { @Transient private AvatarStorage avatars; @Transient private Inventory inventory; @Transient private FriendsList friendsList; - + @Transient private MailHandler mailHandler; + private TeamManager teamManager; private PlayerGachaInfo gachaInfo; private PlayerProfile playerProfile; private boolean showAvatar; private ArrayList shownAvatars; private Set rewardedLevels; - private ArrayList mail; private ArrayList shopLimit; private int sceneId; @@ -118,6 +119,7 @@ public class Player { this.inventory = new Inventory(this); this.avatars = new AvatarStorage(this); this.friendsList = new FriendsList(this); + this.mailHandler = new MailHandler(this); this.pos = new Position(); this.rotation = new Position(); this.properties = new HashMap<>(); @@ -133,8 +135,6 @@ public class Player { this.flyCloakList = new HashSet<>(); this.costumeList = new HashSet<>(); - this.mail = new ArrayList<>(); - this.setSceneId(3); this.setRegionId(1); this.sceneState = SceneLoadState.NONE; @@ -437,6 +437,10 @@ public class Player { return this.friendsList; } + public MailHandler getMailHandler() { + return mailHandler; + } + public int getEnterSceneToken() { return enterSceneToken; } @@ -725,47 +729,24 @@ public class Player { // ---------------------MAIL------------------------ - public List getAllMail() { return this.mail; } + public List getAllMail() { return this.getMailHandler().getMail(); } public void sendMail(Mail message) { - // Call mail receive event. - PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this, message); event.call(); - if(event.isCanceled()) return; message = event.getMessage(); - - this.mail.add(message); - this.save(); - Grasscutter.getLogger().debug("Mail sent to user [" + this.getUid() + ":" + this.getNickname() + "]!"); - if(this.isOnline()) { - this.sendPacket(new PacketMailChangeNotify(this, message)); - } // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline + this.getMailHandler().sendMail(message); } public boolean deleteMail(int mailId) { - Mail message = getMail(mailId); - - if(message != null) { - int index = getMailId(message); - message.expireTime = (int) Instant.now().getEpochSecond(); // Just set the mail as expired for now. I don't want to implement a counter specifically for an account... - this.replaceMailByIndex(index, message); - return true; - } - - return false; + return this.getMailHandler().deleteMail(mailId); } - public Mail getMail(int index) { return this.mail.get(index); } + public Mail getMail(int index) { return this.getMailHandler().getMailById(index); } + public int getMailId(Mail message) { - return this.mail.indexOf(message); + return this.getMailHandler().getMailIndex(message); } public boolean replaceMailByIndex(int index, Mail message) { - if(getMail(index) != null) { - this.mail.set(index, message); - this.save(); - return true; - } else { - return false; - } + return this.getMailHandler().replaceMailByIndex(index, message); } public void interactWith(int gadgetEntityId) { @@ -1015,6 +996,7 @@ public class Player { this.getAvatars().postLoad(); this.getFriendsList().loadFromDatabase(); + this.getMailHandler().loadFromDatabase(); // Create world World world = new World(this); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDelMailReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDelMailReq.java index 4c6473996..2c95cc307 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDelMailReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDelMailReq.java @@ -1,5 +1,6 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.Grasscutter; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -14,8 +15,8 @@ public class HandlerDelMailReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { DelMailReqOuterClass.DelMailReq req = DelMailReqOuterClass.DelMailReq.parseFrom(payload); - - session.send(new PacketDelMailRsp(session.getPlayer(), req.getMailIdListList())); + + session.getPlayer().getMailHandler().deleteMail(req.getMailIdListList()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDelMailRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDelMailRsp.java index e8348a144..7b618e8d0 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDelMailRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDelMailRsp.java @@ -13,17 +13,10 @@ public class PacketDelMailRsp extends BasePacket { public PacketDelMailRsp(Player player, List toDeleteIds) { super(PacketOpcodes.DelMailRsp); - DelMailRsp.Builder proto = DelMailRsp.newBuilder(); - - List deletedIds = new ArrayList<>(); - - for(int mailId : toDeleteIds) { - if(player.deleteMail(mailId)) { - deletedIds.add(mailId); - } - } - - this.setData(proto.build()); - player.getSession().send(new PacketMailChangeNotify(player, null, deletedIds)); + DelMailRsp proto = DelMailRsp.newBuilder() + .addAllMailIdList(toDeleteIds) + .build(); + + this.setData(proto); } } \ No newline at end of file