Skip to content
Snippets Groups Projects
Commit c68c75e8 authored by Benjamin Elsdon's avatar Benjamin Elsdon
Browse files

Send Mail Command!

Almost done! Wooooo!
parent e30352a6
Branches
Tags
No related merge requests found
......@@ -3,30 +3,202 @@ 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")
@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) {
// This is literally so I can receive mail for some reason.
if(sender == null) {
// This is my uuid in my test server. This is just for testing.
// If someone pulled this please put your uuid to receive mail using /sendmail
// until I actually make a proper /sendmail command.
sender = Grasscutter.getGameServer().getPlayerByUid(7006);
}
sender.sendMail(new Mail(new Mail.MailContent("Test", "This is a test"),
new ArrayList<Mail.MailItem>(){{add(new Mail.MailItem(23411 ));}},
Instant.now().getEpochSecond() + 4000));
sender.dropMessage("Check your inbox");
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());
break;
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");
break;
case 2:
CommandHandler.sendMessage(sender, "Mail templates coming soon implemented...");
return;
default:
CommandHandler.sendMessage(sender, "Invalid arguments.\nUsage `/sendmail <userId|all|help> [templateId]`");
return;
}
} 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 {
// TODO: More testing required. This probably won't work for online players if DatabaseHelper.getPlayerById(string) didn't work.
for (GenshinPlayer player : DatabaseHelper.getAllPlayers()) {
player.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++;
break;
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++;
break;
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++;
break;
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;
}
break;
case 2: // <itemId|itemName> [amount]
lvl = 1;
item = Integer.parseInt(args.get(0));
amount = Integer.parseInt(args.get(1));
break;
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;
}
break;
}
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));
}
break;
}
} else {
CommandHandler.sendMessage(sender, "Invalid arguments \n Please use `/sendmail " + getConstructionArgs(mailBuilder.constructionStage));
}
}
}
public 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;
}
public static List<GenshinPlayer> getAllPlayers() {
return DatabaseManager.getDatastore().find(GenshinPlayer.class).stream().toList();
}
public static GenshinPlayer getPlayerById(int id) {
return DatabaseManager.getDatastore().find(GenshinPlayer.class).filter(Filters.eq("_id", id)).first();
}
......
......@@ -6,6 +6,7 @@ import java.util.*;
import dev.morphia.annotations.*;
import emu.grasscutter.GenshinConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GenshinData;
import emu.grasscutter.data.def.PlayerLevelData;
import emu.grasscutter.database.DatabaseHelper;
......@@ -603,10 +604,13 @@ public class GenshinPlayer {
public List<Mail> getAllMail() { return this.mail; }
public void sendMail(Mail message) {
this.mail.add(message);
message._id = this.mail.size() + 1;
this.mail.add(message);
this.save();
Grasscutter.getLogger().info("Message sent to user [" + this.getUid() + ":" + this.getNickname() + "]!");
if(this.getSession() != null) {
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) {
......
......@@ -20,15 +20,7 @@ public class Mail {
public int stateValue;
public Mail() {
_id = 1;
mailContent = new MailContent("No title set...", "No content set...");
itemList = new ArrayList<>();
sendTime = 0;
expireTime = 0;
importance = 0; // Starred mail, 0 = No star, 1 = Star.
isRead = true;
isAttachmentGot = true;
stateValue = 1; // Different mailboxes, 1 = Default, 3 = Gift-box.
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) {
......@@ -49,10 +41,10 @@ public class Mail {
this.itemList = itemList;
this.sendTime = (int) Instant.now().getEpochSecond();
this.expireTime = expireTime;
this.importance = importance;
this.importance = importance; // Starred mail, 0 = No star, 1 = Star.
this.isRead = false;
this.isAttachmentGot = false;
this.stateValue = state;
this.stateValue = state; // Different mailboxes, 1 = Default, 3 = Gift-box.
}
public int getId() {
......
......@@ -13,7 +13,6 @@ public class HandlerGetMailItemReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
Grasscutter.getLogger().info("Mail Item Req");
GetMailItemReqOuterClass.GetMailItemReq req = GetMailItemReqOuterClass.GetMailItemReq.parseFrom(payload);
session.send(new PacketGetMailItemRsp(session.getPlayer(), req.getMailIdListList()));
}
......
......@@ -39,9 +39,13 @@ public class PacketGetAllMailRsp extends GenshinPacket {
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 < 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).
Grasscutter.getLogger().info("a");
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).
Grasscutter.getLogger().info("b");
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.)
Grasscutter.getLogger().info("c");
MailTextContent.Builder mailTextContent = MailTextContent.newBuilder();
mailTextContent.setTitle(message.mailContent.title);
mailTextContent.setContent(message.mailContent.content);
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment