mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-24 15:42:53 +08:00
commit
d44793359d
@ -197,4 +197,8 @@ javadoc {
|
|||||||
if(JavaVersion.current().isJava9Compatible()) {
|
if(JavaVersion.current().isJava9Compatible()) {
|
||||||
options.addBooleanOption('html5', true)
|
options.addBooleanOption('html5', true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
dependsOn "generateProto"
|
||||||
}
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.Mail;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Command(label = "sendmail", usage = "sendmail <userId|all|help> [templateId]",
|
||||||
|
description = "Sends mail to the specified user. The usage of this command changes based on it's composition state.", permission = "server.sendmail")
|
||||||
|
public class SendMailCommand implements CommandHandler {
|
||||||
|
|
||||||
|
// TODO: You should be able to do /sendmail and then just send subsequent messages until you finish
|
||||||
|
// However, due to the current nature of the command system, I don't think this is possible without rewriting
|
||||||
|
// the command system (again). For now this will do
|
||||||
|
|
||||||
|
// Key = User that is constructing the mail.
|
||||||
|
private static HashMap<Integer, MailBuilder> mailBeingConstructed = new HashMap<Integer, MailBuilder>();
|
||||||
|
|
||||||
|
// Yes this is awful and I hate it.
|
||||||
|
@Override
|
||||||
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
|
int senderId;
|
||||||
|
if(sender != null) {
|
||||||
|
senderId = sender.getUid();
|
||||||
|
} else {
|
||||||
|
senderId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mailBeingConstructed.containsKey(senderId)) {
|
||||||
|
switch (args.size()) {
|
||||||
|
case 1 -> {
|
||||||
|
MailBuilder mailBuilder;
|
||||||
|
switch (args.get(0).toLowerCase()) {
|
||||||
|
case "help" -> {
|
||||||
|
CommandHandler.sendMessage(sender, this.getClass().getAnnotation(Command.class).description() + "\nUsage: " + this.getClass().getAnnotation(Command.class).usage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
|
||||||
|
default -> {
|
||||||
|
if (DatabaseHelper.getPlayerById(Integer.parseInt(args.get(0))) != null) {
|
||||||
|
mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail());
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
CommandHandler.sendMessage(sender, "The user with an id of '" + args.get(0) + "' does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mailBeingConstructed.put(senderId, mailBuilder);
|
||||||
|
CommandHandler.sendMessage(sender, "Starting composition of message.\nPlease use `/sendmail <title>` to continue.\nYou can use `/sendmail stop` at any time");
|
||||||
|
}
|
||||||
|
case 2 -> CommandHandler.sendMessage(sender, "Mail templates coming soon implemented...");
|
||||||
|
default -> CommandHandler.sendMessage(sender, "Invalid arguments.\nUsage `/sendmail <userId|all|help> [templateId]`");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MailBuilder mailBuilder = mailBeingConstructed.get(senderId);
|
||||||
|
|
||||||
|
if (args.size() >= 1) {
|
||||||
|
switch (args.get(0).toLowerCase()) {
|
||||||
|
case "stop" -> {
|
||||||
|
mailBeingConstructed.remove(senderId);
|
||||||
|
CommandHandler.sendMessage(sender, "Message sending cancelled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "finish" -> {
|
||||||
|
if (mailBuilder.constructionStage == 3) {
|
||||||
|
if (mailBuilder.sendToAll == false) {
|
||||||
|
Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail);
|
||||||
|
CommandHandler.sendMessage(sender, "Message sent to user " + mailBuilder.recipient + "!");
|
||||||
|
} else {
|
||||||
|
for (GenshinPlayer player : DatabaseHelper.getAllPlayers()) {
|
||||||
|
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), true).sendMail(mailBuilder.mail);
|
||||||
|
}
|
||||||
|
CommandHandler.sendMessage(sender, "Message sent to all users!");
|
||||||
|
}
|
||||||
|
mailBeingConstructed.remove(senderId);
|
||||||
|
} else {
|
||||||
|
CommandHandler.sendMessage(sender, "Message composition not at final stage.\nPlease use `/sendmail " + getConstructionArgs(mailBuilder.constructionStage) + "` or `/sendmail stop` to cancel");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "help" -> {
|
||||||
|
CommandHandler.sendMessage(sender, "Please use `/sendmail " + getConstructionArgs(mailBuilder.constructionStage) + "`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
switch (mailBuilder.constructionStage) {
|
||||||
|
case 0 -> {
|
||||||
|
String title = String.join(" ", args.subList(0, args.size()));
|
||||||
|
mailBuilder.mail.mailContent.title = title;
|
||||||
|
CommandHandler.sendMessage(sender, "Message title set as '" + title + "'.\nUse '/sendmail <content>' to continue.");
|
||||||
|
mailBuilder.constructionStage++;
|
||||||
|
}
|
||||||
|
case 1 -> {
|
||||||
|
String contents = String.join(" ", args.subList(0, args.size()));
|
||||||
|
mailBuilder.mail.mailContent.content = contents;
|
||||||
|
CommandHandler.sendMessage(sender, "Message contents set as '" + contents + "'.\nUse '/sendmail <sender>' to continue.");
|
||||||
|
mailBuilder.constructionStage++;
|
||||||
|
}
|
||||||
|
case 2 -> {
|
||||||
|
String msgSender = String.join(" ", args.subList(0, args.size()));
|
||||||
|
mailBuilder.mail.mailContent.sender = msgSender;
|
||||||
|
CommandHandler.sendMessage(sender, "Message sender set as '" + msgSender + "'.\nUse '/sendmail <itemId|itemName|finish> [amount] [level]' to continue.");
|
||||||
|
mailBuilder.constructionStage++;
|
||||||
|
}
|
||||||
|
case 3 -> {
|
||||||
|
// Literally just copy-pasted from the give command lol.
|
||||||
|
int item, lvl, amount = 1;
|
||||||
|
switch (args.size()) {
|
||||||
|
default -> { // *No args*
|
||||||
|
CommandHandler.sendMessage(sender, "Usage: give [player] <itemId|itemName> [amount]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 1 -> { // <itemId|itemName>
|
||||||
|
try {
|
||||||
|
item = Integer.parseInt(args.get(0));
|
||||||
|
lvl = 1;
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
// TODO: Parse from item name using GM Handbook.
|
||||||
|
CommandHandler.sendMessage(sender, "Invalid item id.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2 -> { // <itemId|itemName> [amount]
|
||||||
|
lvl = 1;
|
||||||
|
item = Integer.parseInt(args.get(0));
|
||||||
|
amount = Integer.parseInt(args.get(1));
|
||||||
|
}
|
||||||
|
case 3 -> { // <itemId|itemName> [amount] [level]
|
||||||
|
try {
|
||||||
|
item = Integer.parseInt(args.get(0));
|
||||||
|
amount = Integer.parseInt(args.get(1));
|
||||||
|
lvl = Integer.parseInt(args.get(2));
|
||||||
|
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
// TODO: Parse from item name using GM Handbook.
|
||||||
|
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl));
|
||||||
|
CommandHandler.sendMessage(sender, String.format("Attached %s of %s (level %s) to the message.\nContinue adding more items or use `/sendmail finish` to send the message.", amount, item, lvl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CommandHandler.sendMessage(sender, "Invalid arguments \n Please use `/sendmail " + getConstructionArgs(mailBuilder.constructionStage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConstructionArgs(int stage) {
|
||||||
|
switch (stage) {
|
||||||
|
case 0 -> {
|
||||||
|
return "<title>";
|
||||||
|
}
|
||||||
|
case 1 -> {
|
||||||
|
return "<message>";
|
||||||
|
}
|
||||||
|
case 2 -> {
|
||||||
|
return "<sender>";
|
||||||
|
|
||||||
|
}
|
||||||
|
case 3 -> {
|
||||||
|
return "<itemId|itemName|finish> [amount] [level]";
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
Thread.dumpStack();
|
||||||
|
return "ERROR: invalid construction stage " + stage + ". Check console for stacktrace.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MailBuilder {
|
||||||
|
public int recipient;
|
||||||
|
public boolean sendToAll;
|
||||||
|
public int constructionStage;
|
||||||
|
public Mail mail;
|
||||||
|
|
||||||
|
public MailBuilder(int recipient, Mail mail) {
|
||||||
|
this.recipient = recipient;
|
||||||
|
this.sendToAll = false;
|
||||||
|
this.constructionStage = 0;
|
||||||
|
this.mail = mail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailBuilder(boolean sendToAll, Mail mail) {
|
||||||
|
if (sendToAll) {
|
||||||
|
this.recipient = 0;
|
||||||
|
this.sendToAll = true;
|
||||||
|
this.constructionStage = 0;
|
||||||
|
this.mail = mail;
|
||||||
|
} else {
|
||||||
|
Grasscutter.getLogger().error("Please use MailBuilder(int, mail) when not sending to all");
|
||||||
|
Thread.dumpStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -90,6 +90,10 @@ public final class DatabaseHelper {
|
|||||||
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).delete().getDeletedCount() > 0;
|
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).delete().getDeletedCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<GenshinPlayer> getAllPlayers() {
|
||||||
|
return DatabaseManager.getDatastore().find(GenshinPlayer.class).stream().toList();
|
||||||
|
}
|
||||||
|
|
||||||
public static GenshinPlayer getPlayerById(int id) {
|
public static GenshinPlayer getPlayerById(int id) {
|
||||||
return DatabaseManager.getDatastore().find(GenshinPlayer.class).filter(Filters.eq("_id", id)).first();
|
return DatabaseManager.getDatastore().find(GenshinPlayer.class).filter(Filters.eq("_id", id)).first();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package emu.grasscutter.game;
|
package emu.grasscutter.game;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import dev.morphia.annotations.*;
|
import dev.morphia.annotations.*;
|
||||||
import emu.grasscutter.GenshinConstants;
|
import emu.grasscutter.GenshinConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
import emu.grasscutter.data.GenshinData;
|
import emu.grasscutter.data.GenshinData;
|
||||||
import emu.grasscutter.data.def.PlayerLevelData;
|
import emu.grasscutter.data.def.PlayerLevelData;
|
||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
@ -76,6 +80,7 @@ public class GenshinPlayer {
|
|||||||
private boolean showAvatar;
|
private boolean showAvatar;
|
||||||
private ArrayList<AvatarProfileData> shownAvatars;
|
private ArrayList<AvatarProfileData> shownAvatars;
|
||||||
private Set<Integer> rewardedLevels;
|
private Set<Integer> rewardedLevels;
|
||||||
|
private ArrayList<Mail> mail;
|
||||||
|
|
||||||
private int sceneId;
|
private int sceneId;
|
||||||
private int regionId;
|
private int regionId;
|
||||||
@ -119,6 +124,8 @@ public class GenshinPlayer {
|
|||||||
this.flyCloakList = new HashSet<>();
|
this.flyCloakList = new HashSet<>();
|
||||||
this.costumeList = new HashSet<>();
|
this.costumeList = new HashSet<>();
|
||||||
|
|
||||||
|
this.mail = new ArrayList<>();
|
||||||
|
|
||||||
this.setSceneId(3);
|
this.setSceneId(3);
|
||||||
this.setRegionId(1);
|
this.setRegionId(1);
|
||||||
this.sceneState = SceneLoadState.NONE;
|
this.sceneState = SceneLoadState.NONE;
|
||||||
@ -666,6 +673,47 @@ public class GenshinPlayer {
|
|||||||
this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString()));
|
this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------MAIL------------------------
|
||||||
|
|
||||||
|
public List<Mail> getAllMail() { return this.mail; }
|
||||||
|
|
||||||
|
public void sendMail(Mail message) {
|
||||||
|
this.mail.add(message);
|
||||||
|
this.save();
|
||||||
|
Grasscutter.getLogger().info("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
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mail getMail(int index) { return this.mail.get(index); }
|
||||||
|
public int getMailId(Mail message) {
|
||||||
|
return this.mail.indexOf(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean replaceMailByIndex(int index, Mail message) {
|
||||||
|
if(getMail(index) != null) {
|
||||||
|
this.mail.set(index, message);
|
||||||
|
this.save();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void interactWith(int gadgetEntityId) {
|
public void interactWith(int gadgetEntityId) {
|
||||||
GenshinEntity entity = getScene().getEntityById(gadgetEntityId);
|
GenshinEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||||
|
|
||||||
|
95
src/main/java/emu/grasscutter/game/Mail.java
Normal file
95
src/main/java/emu/grasscutter/game/Mail.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package emu.grasscutter.game;
|
||||||
|
|
||||||
|
import dev.morphia.annotations.Entity;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Mail {
|
||||||
|
|
||||||
|
public MailContent mailContent;
|
||||||
|
public List<MailItem> itemList;
|
||||||
|
public long sendTime;
|
||||||
|
public long expireTime;
|
||||||
|
public int importance;
|
||||||
|
public boolean isRead;
|
||||||
|
public boolean isAttachmentGot;
|
||||||
|
public int stateValue;
|
||||||
|
|
||||||
|
public Mail() {
|
||||||
|
this(new MailContent(), new ArrayList<MailItem>(), (int) Instant.now().getEpochSecond() + 604800); // TODO: add expire time to send mail command
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime) {
|
||||||
|
this(mailContent, itemList, expireTime, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime, int importance) {
|
||||||
|
this(mailContent, itemList, expireTime, importance, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mail(MailContent mailContent, List<MailItem> itemList, long expireTime, int importance, int state) {
|
||||||
|
this.mailContent = mailContent;
|
||||||
|
this.itemList = itemList;
|
||||||
|
this.sendTime = (int) Instant.now().getEpochSecond();
|
||||||
|
this.expireTime = expireTime;
|
||||||
|
this.importance = importance; // Starred mail, 0 = No star, 1 = Star.
|
||||||
|
this.isRead = false;
|
||||||
|
this.isAttachmentGot = false;
|
||||||
|
this.stateValue = state; // Different mailboxes, 1 = Default, 3 = Gift-box.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public static class MailContent {
|
||||||
|
public String title;
|
||||||
|
public String content;
|
||||||
|
public String sender;
|
||||||
|
|
||||||
|
public MailContent() {
|
||||||
|
this.title = "";
|
||||||
|
this.content = "loading...";
|
||||||
|
this.sender = "loading";
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailContent(String title, String content) {
|
||||||
|
this(title, content, "Server");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailContent(String title, String content, GenshinPlayer sender) {
|
||||||
|
this(title, content, sender.getNickname());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailContent(String title, String content, String sender) {
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public static class MailItem {
|
||||||
|
public int itemId;
|
||||||
|
public int itemCount;
|
||||||
|
public int itemLevel;
|
||||||
|
|
||||||
|
public MailItem() {
|
||||||
|
this.itemId = 11101;
|
||||||
|
this.itemCount = 1;
|
||||||
|
this.itemLevel = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailItem(int itemId) {
|
||||||
|
this(itemId, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailItem(int itemId, int itemCount) { this(itemId, itemCount, 1); }
|
||||||
|
|
||||||
|
public MailItem(int itemId, int itemCount, int itemLevel) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.itemCount = itemCount;
|
||||||
|
this.itemLevel = itemLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.Mail;
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.ChangeMailStarNotifyOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.ChangeMailStarNotify)
|
||||||
|
public class HandlerChangeMailStarNotify extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
ChangeMailStarNotifyOuterClass.ChangeMailStarNotify req = ChangeMailStarNotifyOuterClass.ChangeMailStarNotify.parseFrom(payload);
|
||||||
|
|
||||||
|
List<Mail> updatedMail = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int mailId : req.getMailIdListList()) {
|
||||||
|
Mail message = session.getPlayer().getMail(mailId);
|
||||||
|
|
||||||
|
message.importance = req.getIsStar() == true ? 1 : 0;
|
||||||
|
|
||||||
|
session.getPlayer().replaceMailByIndex(mailId, message);
|
||||||
|
updatedMail.add(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.send(new PacketMailChangeNotify(session.getPlayer(), updatedMail));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.DelMailReqOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.DeleteFriendReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketDelMailRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.DelMailReq)
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.proto.GetAllMailReqOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GetPlayerTokenReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGetAllMailRsp;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGetGachaInfoRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.GetAllMailReq)
|
||||||
|
public class HandlerGetAllMailReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
GetAllMailReqOuterClass.GetAllMailReq req = GetAllMailReqOuterClass.GetAllMailReq.parseFrom(payload);
|
||||||
|
session.send(new PacketGetAllMailRsp(session.getPlayer(), req.getIsGiftMail()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
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;
|
||||||
|
import emu.grasscutter.net.proto.GetMailItemReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGetMailItemRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.GetMailItemReq)
|
||||||
|
public class HandlerGetMailItemReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
GetMailItemReqOuterClass.GetMailItemReq req = GetMailItemReqOuterClass.GetMailItemReq.parseFrom(payload);
|
||||||
|
session.send(new PacketGetMailItemRsp(session.getPlayer(), req.getMailIdListList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.Mail;
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.GetAllMailReqOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.ReadMailNotifyOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.ReadMailNotify)
|
||||||
|
public class HandlerReadMailNotify extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
ReadMailNotifyOuterClass.ReadMailNotify req = ReadMailNotifyOuterClass.ReadMailNotify.parseFrom(payload);
|
||||||
|
|
||||||
|
List<Mail> updatedMail = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int mailId : req.getMailIdListList()) {
|
||||||
|
Mail message = session.getPlayer().getMail(mailId);
|
||||||
|
|
||||||
|
message.isRead = true;
|
||||||
|
|
||||||
|
session.getPlayer().replaceMailByIndex(mailId, message);
|
||||||
|
updatedMail.add(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.send(new PacketMailChangeNotify(session.getPlayer(), updatedMail));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.DelMailRspOuterClass.DelMailRsp;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketDelMailRsp extends GenshinPacket {
|
||||||
|
|
||||||
|
public PacketDelMailRsp(GenshinPlayer player, List<Integer> toDeleteIds) {
|
||||||
|
super(PacketOpcodes.DelMailRsp);
|
||||||
|
|
||||||
|
DelMailRsp.Builder proto = DelMailRsp.newBuilder();
|
||||||
|
|
||||||
|
List<Integer> 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));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.Mail;
|
||||||
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.GetAllMailRspOuterClass.GetAllMailRsp;
|
||||||
|
import emu.grasscutter.net.proto.ItemParamOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.MailDataOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.MailDataOuterClass.MailData;
|
||||||
|
import emu.grasscutter.net.proto.MailItemOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.MailTextContentOuterClass.MailTextContent;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketGetAllMailRsp extends GenshinPacket {
|
||||||
|
|
||||||
|
public PacketGetAllMailRsp(GenshinPlayer player, boolean isGiftMail) {
|
||||||
|
super(PacketOpcodes.GetAllMailRsp);
|
||||||
|
|
||||||
|
if (isGiftMail) {
|
||||||
|
// TODO: Gift Mail
|
||||||
|
// Make sure to send the stupid empty packet
|
||||||
|
Base64.Decoder decoder = Base64.getDecoder();
|
||||||
|
byte[] rsp = decoder.decode("IAE=");
|
||||||
|
try {
|
||||||
|
GetAllMailRsp var = GetAllMailRsp.parseFrom(rsp);
|
||||||
|
this.setData(var.toBuilder().build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (player.getAllMail().size() != 0) { // Make sure the player has mail
|
||||||
|
GetAllMailRsp.Builder proto = GetAllMailRsp.newBuilder();
|
||||||
|
List<MailData> mailDataList = new ArrayList<MailData>();
|
||||||
|
|
||||||
|
for (Mail message : player.getAllMail()) {
|
||||||
|
|
||||||
|
if(message.stateValue == 1) { // Make sure it isn't a gift
|
||||||
|
if (message.expireTime > (int) Instant.now().getEpochSecond()) { // Make sure the message isn't expired (The game won't show expired mail, but I don't want to send unnecessary information).
|
||||||
|
if(mailDataList.size() <= 1000) { // Make sure that there isn't over 1000 messages in the mailbox. (idk what will happen if there is but the game probably won't like it.)
|
||||||
|
MailTextContent.Builder mailTextContent = MailTextContent.newBuilder();
|
||||||
|
mailTextContent.setTitle(message.mailContent.title);
|
||||||
|
mailTextContent.setContent(message.mailContent.content);
|
||||||
|
mailTextContent.setSender(message.mailContent.sender);
|
||||||
|
|
||||||
|
List<MailItemOuterClass.MailItem> mailItems = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Mail.MailItem item : message.itemList) {
|
||||||
|
MailItemOuterClass.MailItem.Builder mailItem = MailItemOuterClass.MailItem.newBuilder();
|
||||||
|
ItemParamOuterClass.ItemParam.Builder itemParam = ItemParamOuterClass.ItemParam.newBuilder();
|
||||||
|
itemParam.setItemId(item.itemId);
|
||||||
|
itemParam.setCount(item.itemCount);
|
||||||
|
mailItem.setItemParam(itemParam.build());
|
||||||
|
|
||||||
|
mailItems.add(mailItem.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
MailDataOuterClass.MailData.Builder mailData = MailDataOuterClass.MailData.newBuilder();
|
||||||
|
mailData.setMailId(player.getMailId(message));
|
||||||
|
mailData.setMailTextContent(mailTextContent.build());
|
||||||
|
mailData.addAllItemList(mailItems);
|
||||||
|
mailData.setSendTime((int) message.sendTime);
|
||||||
|
mailData.setExpireTime((int) message.expireTime);
|
||||||
|
mailData.setImportance(message.importance);
|
||||||
|
mailData.setIsRead(message.isRead);
|
||||||
|
mailData.setIsAttachmentGot(message.isAttachmentGot);
|
||||||
|
mailData.setStateValue(1);
|
||||||
|
|
||||||
|
mailDataList.add(mailData.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proto.addAllMailList(mailDataList);
|
||||||
|
proto.setIsTruncated(mailDataList.size() <= 1000 ? false : true); // When enabled this will send a notification to the user telling them their inbox is full and they should delete old messages when opening the mailbox.
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
} else {
|
||||||
|
// Make sure to send the stupid empty packet
|
||||||
|
Base64.Decoder decoder = Base64.getDecoder();
|
||||||
|
byte[] rsp = decoder.decode("IAE=");
|
||||||
|
try {
|
||||||
|
GetAllMailRsp var = GetAllMailRsp.parseFrom(rsp);
|
||||||
|
this.setData(var.toBuilder().build());
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GenshinData;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.Mail;
|
||||||
|
import emu.grasscutter.game.inventory.GenshinItem;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.EquipParamOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GetMailItemRspOuterClass.GetMailItemRsp;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class PacketGetMailItemRsp extends GenshinPacket {
|
||||||
|
|
||||||
|
public PacketGetMailItemRsp(GenshinPlayer player, List<Integer> mailList) {
|
||||||
|
super(PacketOpcodes.GetMailItemRsp);
|
||||||
|
|
||||||
|
List<Mail> claimedMessages = new ArrayList<>();
|
||||||
|
List<EquipParamOuterClass.EquipParam> claimedItems = new ArrayList<>();
|
||||||
|
|
||||||
|
GetMailItemRsp.Builder proto = GetMailItemRsp.newBuilder();
|
||||||
|
|
||||||
|
for (int mailId : mailList) {
|
||||||
|
Mail message = player.getMail(mailId);
|
||||||
|
|
||||||
|
for(Mail.MailItem mailItem : message.itemList) {
|
||||||
|
EquipParamOuterClass.EquipParam.Builder item = EquipParamOuterClass.EquipParam.newBuilder();
|
||||||
|
int promoteLevel = 0;
|
||||||
|
if (mailItem.itemLevel > 20) { // 20/40
|
||||||
|
promoteLevel = 1;
|
||||||
|
} else if (mailItem.itemLevel > 40) { // 40/50
|
||||||
|
promoteLevel = 2;
|
||||||
|
} else if (mailItem.itemLevel > 50) { // 50/60
|
||||||
|
promoteLevel = 3;
|
||||||
|
} else if (mailItem.itemLevel > 60) { // 60/70
|
||||||
|
promoteLevel = 4;
|
||||||
|
} else if (mailItem.itemLevel > 70) { // 70/80
|
||||||
|
promoteLevel = 5;
|
||||||
|
} else if (mailItem.itemLevel > 80) { // 80/90
|
||||||
|
promoteLevel = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setItemId(mailItem.itemId);
|
||||||
|
item.setItemNum(mailItem.itemCount);
|
||||||
|
item.setItemLevel(mailItem.itemLevel);
|
||||||
|
item.setPromoteLevel(promoteLevel);
|
||||||
|
claimedItems.add(item.build());
|
||||||
|
|
||||||
|
GenshinItem genshinItem = new GenshinItem(GenshinData.getItemDataMap().get(mailItem.itemId));
|
||||||
|
genshinItem.setCount(mailItem.itemCount);
|
||||||
|
genshinItem.setLevel(mailItem.itemLevel);
|
||||||
|
genshinItem.setPromoteLevel(promoteLevel);
|
||||||
|
player.getInventory().addItem(genshinItem);
|
||||||
|
player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.MailAttachment));
|
||||||
|
}
|
||||||
|
|
||||||
|
message.isAttachmentGot = true;
|
||||||
|
claimedMessages.add(message);
|
||||||
|
|
||||||
|
player.replaceMailByIndex(mailId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
proto.addAllMailIdList(claimedMessages.stream().map(message -> player.getMailId(message)).collect(Collectors.toList()));
|
||||||
|
proto.addAllItemList(claimedItems);
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
player.getSession().send(new PacketMailChangeNotify(player, claimedMessages)); // For some reason you have to also send the MailChangeNotify packet
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.Mail;
|
||||||
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketMailChangeNotify extends GenshinPacket {
|
||||||
|
|
||||||
|
public PacketMailChangeNotify(GenshinPlayer player, Mail message) {
|
||||||
|
this (player, new ArrayList<Mail>(){{add(message);}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketMailChangeNotify(GenshinPlayer player, List<Mail> mailList) {
|
||||||
|
this(player, mailList, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketMailChangeNotify(GenshinPlayer player, List<Mail> mailList, List<Integer> delMailIdList) {
|
||||||
|
super(PacketOpcodes.MailChangeNotify);
|
||||||
|
|
||||||
|
MailChangeNotifyOuterClass.MailChangeNotify.Builder proto = MailChangeNotifyOuterClass.MailChangeNotify.newBuilder();
|
||||||
|
|
||||||
|
if (mailList != null) {
|
||||||
|
for (Mail message : mailList) {
|
||||||
|
MailTextContentOuterClass.MailTextContent.Builder mailTextContent = MailTextContentOuterClass.MailTextContent.newBuilder();
|
||||||
|
mailTextContent.setTitle(message.mailContent.title);
|
||||||
|
mailTextContent.setContent(message.mailContent.content);
|
||||||
|
mailTextContent.setSender(message.mailContent.sender);
|
||||||
|
|
||||||
|
List<MailItemOuterClass.MailItem> mailItems = new ArrayList<MailItemOuterClass.MailItem>();
|
||||||
|
|
||||||
|
for (Mail.MailItem item : message.itemList) {
|
||||||
|
MailItemOuterClass.MailItem.Builder mailItem = MailItemOuterClass.MailItem.newBuilder();
|
||||||
|
ItemParamOuterClass.ItemParam.Builder itemParam = ItemParamOuterClass.ItemParam.newBuilder();
|
||||||
|
itemParam.setItemId(item.itemId);
|
||||||
|
itemParam.setCount(item.itemCount);
|
||||||
|
mailItem.setItemParam(itemParam.build());
|
||||||
|
|
||||||
|
mailItems.add(mailItem.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
MailDataOuterClass.MailData.Builder mailData = MailDataOuterClass.MailData.newBuilder();
|
||||||
|
mailData.setMailId(player.getMailId(message));
|
||||||
|
mailData.setMailTextContent(mailTextContent.build());
|
||||||
|
mailData.addAllItemList(mailItems);
|
||||||
|
mailData.setSendTime((int) message.sendTime);
|
||||||
|
mailData.setExpireTime((int) message.expireTime);
|
||||||
|
mailData.setImportance(message.importance);
|
||||||
|
mailData.setIsRead(message.isRead);
|
||||||
|
mailData.setIsAttachmentGot(message.isAttachmentGot);
|
||||||
|
mailData.setStateValue(message.stateValue);
|
||||||
|
|
||||||
|
proto.addMailList(mailData.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(delMailIdList != null) {
|
||||||
|
proto.addAllDelMailIdList(delMailIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,12 @@ import emu.grasscutter.game.GenshinPlayer;
|
|||||||
import emu.grasscutter.net.packet.GenshinPacket;
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason;
|
import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason;
|
||||||
|
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
|
||||||
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify;
|
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify;
|
||||||
|
|
||||||
public class PacketPlayerApplyEnterMpResultNotify extends GenshinPacket {
|
public class PacketPlayerApplyEnterMpResultNotify extends GenshinPacket {
|
||||||
|
|
||||||
public PacketPlayerApplyEnterMpResultNotify(GenshinPlayer target, boolean isAgreed, PlayerApplyEnterMpResultNotify.Reason reason) {
|
public PacketPlayerApplyEnterMpResultNotify(GenshinPlayer target, boolean isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason reason) {
|
||||||
super(PacketOpcodes.PlayerApplyEnterMpResultNotify);
|
super(PacketOpcodes.PlayerApplyEnterMpResultNotify);
|
||||||
|
|
||||||
PlayerApplyEnterMpResultNotify proto = PlayerApplyEnterMpResultNotify.newBuilder()
|
PlayerApplyEnterMpResultNotify proto = PlayerApplyEnterMpResultNotify.newBuilder()
|
||||||
|
Loading…
Reference in New Issue
Block a user