commit ea7abbfc845ddb8ff63005c3e27fa52f5ccca5e4
Author: coooookies <1164557342@qq.com>
Date: Tue May 3 05:14:22 2022 +0800
commmit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2d513a0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/.idea/
+/target/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..eaaf684
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 ButterCookies
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d68d534
Binary files /dev/null and b/README.md differ
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 0000000..f7b4edd
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,48 @@
+# MeaNotice - Grasscutter 定时公告插件
+MeaNotice 是一个使用于 [Grasscutter](https://github.com/Grasscutters/Grasscutter) 的插件,你可以使用这个插件在游戏内发送定时公告。
+
+[English](./README.md) | 简体中文
+
+## 开始
+### 安装插件
+1. [下载插件本体](https://github.com/Coooookies/MeaNotice/releases)
+2. 将插件本体放置在 `服务器根目录/plugins` 目录下
+3. 启动服务器,插件将会在你的服务器根目录下创建一个 `MeaNotice` 文件夹.
+```
+Root
+│ lib
+│ keys
+│ resources
+│ plugins
+│ ...
+└───MeaNotice
+ └───config.json
+```
+
+### 配置
+```json
+{
+ "interval": 30000,
+ "notices": [
+ "Welcome to this server! If you want to learn how to use commands, please type /help in chatroom.",
+ "Hey! Do you need help? Add UID1 as a friend, the admin will help you."
+ ]
+}
+```
+```
+说明:
+ interval: 必填项,用于设置定时公告的时间间隔,单位为毫秒,默认为 30000 毫秒(30 秒)。
+ notices: 用于设置定时公告的内容,格式为字符串数组,每条公告都会以上到下顺序发送给玩家。
+```
+
+### 命令 & 权限
+命令:
+```
+/meanotice reload
+重载插件配置
+```
+
+权限:
+```
+mea.notice | 控制插件功能
+```
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7dcd94d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,69 @@
+
+
+ 4.0.0
+
+ io.github
+ MeaMailPlus
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 17
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.4
+
+
+ package
+
+ shade
+
+
+ false
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+ emu.grasscutter
+ Grasscutter-api
+ 1.0
+ system
+ D:/Grasscutter/GrasscutterFork/grasscutter-1.0.3-dev.jar
+
+
+
+ com.google.code.gson
+ gson
+ 2.9.0
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/io/github/gmw/MeaMailPlusCore.java b/src/main/java/io/github/gmw/MeaMailPlusCore.java
new file mode 100644
index 0000000..7e4b349
--- /dev/null
+++ b/src/main/java/io/github/gmw/MeaMailPlusCore.java
@@ -0,0 +1,76 @@
+package io.github.gmw;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.command.CommandMap;
+import emu.grasscutter.plugin.Plugin;
+import emu.grasscutter.server.game.GameServerPacketHandler;
+import io.github.gmw.module.ConfigParser;
+import io.github.gmw.module.PluginCommand;
+import io.github.gmw.packetHandler.PlayerBornHook;
+import io.github.gmw.packetHandler.PlayerJoinHook;
+import io.github.gmw.module.TaskManager;
+
+public class MeaMailPlusCore extends Plugin {
+
+ private static MeaMailPlusCore instance;
+ public ConfigParser config;
+ public TaskManager task;
+
+
+ @Override
+ public void onLoad() {
+ this.logger("Loading...");
+ instance = this;
+
+ this.config = new ConfigParser();
+ this.config.loadConfig();
+ this.config.loadTemplates();
+ this.task = new TaskManager();
+
+ this.logger("Loaded!");
+ }
+
+ @Override
+ public void onEnable() {
+ CommandMap.getInstance().registerCommand("meamail", new PluginCommand());
+
+ // register packet handler
+ GameServerPacketHandler packetHandler = Grasscutter.getGameServer().getPacketHandler();
+ packetHandler.registerPacketHandler(PlayerJoinHook.class);
+ packetHandler.registerPacketHandler(PlayerBornHook.class);
+
+ this.task.enable();
+ }
+
+ @Override
+ public void onDisable() {
+ CommandMap.getInstance().unregisterCommand("meamail");
+ this.task.disable();
+ }
+
+ public static MeaMailPlusCore getInstance() {
+ return instance;
+ }
+
+ public void reloadConfig() {
+ this.task.disable();
+ this.config.loadConfig();
+ this.config.loadTemplates();
+ this.task.enable();
+ }
+
+ public void logger(String message) {
+ this.logger(message, "info");
+ }
+
+ public void logger(String message, String type) {
+ if (Grasscutter.getLogger() == null) return;
+
+ String msg = "[MeaMailPlusCore] " + message;
+ switch (type) {
+ case "info" -> Grasscutter.getLogger().info(msg);
+ case "warn" -> Grasscutter.getLogger().warn(msg);
+ case "error" -> Grasscutter.getLogger().error(msg);
+ }
+ }
+}
diff --git a/src/main/java/io/github/gmw/config/MeaMailConfig.java b/src/main/java/io/github/gmw/config/MeaMailConfig.java
new file mode 100644
index 0000000..97d265d
--- /dev/null
+++ b/src/main/java/io/github/gmw/config/MeaMailConfig.java
@@ -0,0 +1,42 @@
+package io.github.gmw.config;
+
+public final class MeaMailConfig {
+ public int[] updateTime = {4,0,0};
+ public int[] initialMail = {1001};
+ public int[] birthDayMail = {1004};
+
+ public DailySignInMailTask[] dailySignInMail = {
+ new DailySignInMailTask(1002,0)
+ };
+
+ public DailyRepetitionTask[] dailyRepetitionMail = {
+ new DailyRepetitionTask(1003,0,false, new int[]{12, 0, 0})
+ };
+
+ public static class DailyRepetitionTask extends MailTask {
+ public boolean onlineOnly;
+ public int[] triggerTime;
+
+ public DailyRepetitionTask(int templateId, int minLevel, boolean onlineOnly, int[] triggerTime) {
+ super(templateId, minLevel);
+ this.onlineOnly = onlineOnly;
+ this.triggerTime = triggerTime;
+ }
+ }
+
+ public static class DailySignInMailTask extends MailTask {
+ public DailySignInMailTask(int templateId, int minLevel) {
+ super(templateId, minLevel);
+ }
+ }
+
+ public static class MailTask {
+ public int templateId;
+ public int minLevel; // 0 - 60
+
+ public MailTask(int templateId, int minLevel) {
+ this.templateId = templateId;
+ this.minLevel = minLevel;
+ }
+ }
+}
diff --git a/src/main/java/io/github/gmw/config/MeaMailTemplate.java b/src/main/java/io/github/gmw/config/MeaMailTemplate.java
new file mode 100644
index 0000000..e939c2f
--- /dev/null
+++ b/src/main/java/io/github/gmw/config/MeaMailTemplate.java
@@ -0,0 +1,32 @@
+package io.github.gmw.config;
+
+public final class MeaMailTemplate {
+ public int templateId = 0;
+ public String title = "";
+ public String sender = "Server";
+ public long expireTime = 0;
+
+ public long remainTime = 2592000; // 31 days
+ public int importance = 0; // Starred mail, 0 = No star, 1 = Star.
+ public MailBody body = new MailBody();
+ public static class MailBody {
+ public String content = "";
+ public ItemInfo[] items = {};
+
+ public static class ItemInfo {
+ public int id;
+ public int count;
+ public int level;
+
+ public ItemInfo(int id, int count) {
+ this(id, count, 1);
+ }
+
+ public ItemInfo(int id, int count, int level) {
+ this.id = id;
+ this.count = count;
+ this.level = level;
+ }
+ }
+ }
+}
diff --git a/src/main/java/io/github/gmw/module/ConfigParser.java b/src/main/java/io/github/gmw/module/ConfigParser.java
new file mode 100644
index 0000000..5915a30
--- /dev/null
+++ b/src/main/java/io/github/gmw/module/ConfigParser.java
@@ -0,0 +1,191 @@
+package io.github.gmw.module;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import io.github.gmw.MeaMailPlusCore;
+import io.github.gmw.config.MeaMailConfig;
+import io.github.gmw.config.MeaMailTemplate;
+
+public class ConfigParser {
+ private MeaMailConfig config;
+ private ArrayList templates;
+ private final String configPath = "./plugins/MeaMailPlus";
+ private final String templatePath = this.configPath + "/template";
+ private final File configFile = new File( this.configPath + "/config.json");
+ private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ public void loadTemplates() {
+ switch (this.createConfigFolder(this.templatePath)){
+ case -1: return;
+ case 0: { // Without Template
+ this.createTemplate();
+ break;
+ }
+ case 1: {
+ break;
+ }
+ }
+
+
+ ArrayList templates = new ArrayList<>();
+ File[] files = new File(this.templatePath)
+ .listFiles();
+
+ if (files == null) return; // No Template
+ else {
+ for (File file : files) {
+ try (FileReader fileReader = new FileReader(file)) {
+ MeaMailTemplate template = gson.fromJson(fileReader, MeaMailTemplate.class);
+ templates.add(template); // 加入模板列表
+ MeaMailPlusCore.getInstance()
+ .logger("Loaded template: " + file.getName() + " (TemplateId: " + template.templateId + ")");
+ } catch (Exception e) {
+ MeaMailPlusCore.getInstance()
+ .logger("Unable to load template file: " + file.getName(),"error");
+ }
+ }
+ }
+
+ this.templates = templates;
+ MeaMailPlusCore.getInstance()
+ .logger("Loaded " + templates.size() + " templates. :D");
+
+ }
+
+ // get template by templateId
+ public MeaMailTemplate getTemplate(int templateId) {
+ for (MeaMailTemplate template : this.templates) {
+ if (template.templateId == templateId) return template;
+ }
+ return null;
+ }
+
+ public void createTemplate() {
+ MeaMailTemplate template_initialMail = new MeaMailTemplate();
+ MeaMailTemplate template_dailySignInMail = new MeaMailTemplate();
+ MeaMailTemplate template_dailyRepetitionMail = new MeaMailTemplate();
+ MeaMailTemplate template_birthDayMail = new MeaMailTemplate();
+
+ template_initialMail.templateId = 1001;
+ template_initialMail.remainTime = 2592000; // 一个月后过期
+ template_initialMail.title = "Thank you for using MeaMailPlus!";
+ template_initialMail.sender= "MeaKiritaniIwako";
+ template_initialMail.importance = 1;
+ template_initialMail.body.content =
+ "Hi!\n\n" +
+ "Thank you for using MeaMailPlus! You can use MeaMailPlus to send mail to your friends more conveniently.\r\n\r\n" +
+ "Github HomePage" +
+ "\r\n" +
+ "Grasscutter Discord" +
+ "";
+ template_initialMail.body.items = new MeaMailTemplate.MailBody.ItemInfo[]{
+ new MeaMailTemplate.MailBody.ItemInfo(80544, 1, 20),
+ new MeaMailTemplate.MailBody.ItemInfo(223, 150)
+ };
+
+ template_dailySignInMail.templateId = 1002;
+ template_dailySignInMail.remainTime = 604800; // 1 week
+ template_dailySignInMail.title = "Daily-Bonus";
+ template_dailySignInMail.body.content = "Have a nice day!";
+ template_dailySignInMail.body.items = new MeaMailTemplate.MailBody.ItemInfo[]{
+ new MeaMailTemplate.MailBody.ItemInfo(223, 20),
+ new MeaMailTemplate.MailBody.ItemInfo(224, 20)
+ };
+
+ template_dailyRepetitionMail.templateId = 1003;
+ template_dailyRepetitionMail.remainTime = 1209600; // 2 weeks later
+ template_dailyRepetitionMail.title = "Have a question?";
+ template_dailyRepetitionMail.sender= "TeamDarkshin";
+ template_dailyRepetitionMail.body.content =
+ "Join Grasscutter Discord server to ask questions and get answers:" +
+ "";
+
+ template_birthDayMail.templateId = 1004;
+ template_birthDayMail.remainTime = 2592000; // 1 month
+ template_birthDayMail.title = "Best Wishes on Your Birthday";
+ template_birthDayMail.sender = "Mailing System";
+ template_birthDayMail.importance = 1;
+ template_birthDayMail.body.content =
+ "Happy Birthday,Traveler! Please find your gift attached to this message.\r\n" +
+ "Thank you for all your support. We wish you a wonderful day, wherever in this world you may roam.";
+ template_birthDayMail.body.items = new MeaMailTemplate.MailBody.ItemInfo[]{
+ new MeaMailTemplate.MailBody.ItemInfo(118003, 1)
+ };
+
+
+ this.saveTemplate("InitialMail", template_initialMail);
+ this.saveTemplate("DailySignInMail", template_dailySignInMail);
+ this.saveTemplate("DailyRepetitionMail", template_dailyRepetitionMail);
+ this.saveTemplate("BirthDayMail", template_birthDayMail);
+ }
+
+ public boolean saveTemplate(String fileName, MeaMailTemplate templateClass) {
+ try (FileWriter file = new FileWriter(this.templatePath + "/" + fileName + ".json")) {
+ file.write(gson.toJson(templateClass));
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ // load config.json
+ public void loadConfig() {
+ try (FileReader file = new FileReader(this.configFile)) {
+ this.config = gson.fromJson(file, MeaMailConfig.class);
+ MeaMailPlusCore.getInstance().logger("Config Loaded!");
+ } catch (Exception e) {
+ this.config = new MeaMailConfig();
+ MeaMailPlusCore.getInstance().logger("Basic config creating...");
+ }
+
+ if (!saveConfig()) {
+ MeaMailPlusCore.getInstance().logger("Unable to save config file.","error");
+ }
+ }
+
+ // save config.json
+
+ public int createConfigFolder(String path) {
+ File dir = new File(path);
+
+ if (!dir.exists() || !dir.isDirectory()) {
+ if(new File(String.valueOf(dir)).mkdirs()) return 0;
+ else return -1;
+ }
+
+ return 1;
+ }
+
+ public boolean saveConfig() {
+ if (this.createConfigFolder(this.configPath) == -1) return false;
+
+ try (FileWriter file = new FileWriter(this.configFile)) {
+ file.write(gson.toJson(this.config));
+ } catch (Exception e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public MeaMailConfig getConfig() {
+ return this.config;
+ }
+
+ public long getTomorrowUpdateTime() {
+ // get tomorrow's update time
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, 1);
+ cal.set(Calendar.HOUR_OF_DAY, this.config.updateTime[0]);
+ cal.set(Calendar.MINUTE, this.config.updateTime[1]);
+ cal.set(Calendar.SECOND, this.config.updateTime[2]);
+ return cal.getTimeInMillis() / 1000;
+ }
+}
diff --git a/src/main/java/io/github/gmw/module/PluginCommand.java b/src/main/java/io/github/gmw/module/PluginCommand.java
new file mode 100644
index 0000000..d3ba11a
--- /dev/null
+++ b/src/main/java/io/github/gmw/module/PluginCommand.java
@@ -0,0 +1,118 @@
+package io.github.gmw.module;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.command.Command;
+import emu.grasscutter.command.CommandHandler;
+import emu.grasscutter.game.player.Player;
+import io.github.gmw.MeaMailPlusCore;
+import io.github.gmw.config.MeaMailConfig;
+import io.github.gmw.config.MeaMailTemplate;
+import io.github.gmw.utils.MailCore;
+
+import java.util.*;
+
+@Command(label = "meamail", usage = "meamail help",
+ description = "MeaMailPlusCore command", aliases = {"mmail"}, permission = "meo.mail")
+
+public class PluginCommand implements CommandHandler {
+ @Override
+ public void execute(Player sender, List args) {
+ switch (args.size()) {
+ default -> // *No args*
+ showHelpList(sender);
+ case 1 -> {
+ switch (args.get(0)) {
+ case "reload" -> {
+ MeaMailPlusCore.getInstance().reloadConfig();
+ if (sender == null)
+ Grasscutter.getLogger().info("[MeaNoticeCore] Reloaded!");
+ else
+ CommandHandler.sendMessage(sender, "MeaNoticeCore config reloaded");
+ }
+ case "help" -> showHelpList(sender);
+ default -> CommandHandler.sendMessage(sender, "Invalid args.");
+ }
+ }
+ case 2 -> {
+ int templateId = Integer.parseInt(args.get(1));
+ MeaMailTemplate template = MeaMailPlusCore.getInstance().config.getTemplate(templateId);
+
+
+ switch (args.get(0)) {
+ case "sendall" -> {
+ if (template == null) {
+ CommandHandler.sendMessage(sender, "Invalid template id.");
+ return;
+ }
+
+ MailCore.sendMailToAllPlayers(template);
+ CommandHandler.sendMessage(sender, "Mail Sended!");
+ }
+ default -> CommandHandler.sendMessage(sender, "Invalid args.");
+ }
+ }
+ case 3 -> {
+ int templateId = Integer.parseInt(args.get(1));
+ MeaMailTemplate template = MeaMailPlusCore.getInstance().config.getTemplate(templateId);
+
+ switch (args.get(0)) {
+ case "send" -> {
+ if (template == null) {
+ CommandHandler.sendMessage(sender, "Invalid template id.");
+ return;
+ }
+
+ int uid = Integer.parseInt(args.get(2));
+ CommandHandler.sendMessage(sender, "Mail Sending...");
+ Grasscutter.getGameServer().getPlayers().forEach((index, player) -> {
+ if (player.getUid() == uid) {
+ MailCore.sendMailToPlayer(player, template);
+ }
+ });
+ }
+ case "sendall" -> {
+ if (template == null) {
+ CommandHandler.sendMessage(sender, "Invalid template id.");
+ return;
+ }
+
+ int minLevel = Integer.parseInt(args.get(2));
+ MailCore.sendMailToAllPlayers(template, minLevel);
+ CommandHandler.sendMessage(sender, "Mail Sended!");
+ }
+ case "sendallonline" -> {
+ if (template == null) {
+ CommandHandler.sendMessage(sender, "Invalid template id.");
+ return;
+ }
+
+ int minLevel = Integer.parseInt(args.get(2));
+ MailCore.sendMailToAllPlayers(template, minLevel, true);
+ CommandHandler.sendMessage(sender, "Mail Sended!");
+ }
+ default -> CommandHandler.sendMessage(sender, "Invalid args.");
+ }
+ }
+ }
+ }
+
+ public void showHelpList(Player sender) {
+ String[] helpMap = new String[]{
+ "Send Mail:",
+ " /meamail send ",
+ " /meamail sendall ",
+ " /meamail sendallonline ",
+// " /meamail welcomemail ",
+// " /meamail dailymail ",
+// " /meamail initialmail ",
+ "Other:",
+ " /meamail reload",
+ " /meamail help",
+ };
+
+ for (String text : helpMap) {
+ CommandHandler.sendMessage(sender, text);
+ }
+ }
+}
+
diff --git a/src/main/java/io/github/gmw/module/TaskManager.java b/src/main/java/io/github/gmw/module/TaskManager.java
new file mode 100644
index 0000000..be5b34a
--- /dev/null
+++ b/src/main/java/io/github/gmw/module/TaskManager.java
@@ -0,0 +1,76 @@
+package io.github.gmw.module;
+
+import io.github.gmw.MeaMailPlusCore;
+import io.github.gmw.config.MeaMailConfig;
+import io.github.gmw.utils.MailCore;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import io.github.gmw.config.MeaMailConfig.DailyRepetitionTask;
+
+public class TaskManager {
+ private Timer timer;
+
+ private static class dailyBeginTask extends TimerTask {
+ @Override
+ public void run() {
+ // new day
+ MailCore.dailyUpdate();
+ }
+ }
+
+ private static class dailyRepetitionTask extends TimerTask {
+ private DailyRepetitionTask task;
+
+ public dailyRepetitionTask(DailyRepetitionTask task) {
+ this.task = task;
+ }
+
+ @Override
+ public void run() {
+ MailCore.repetitionUpdate(task);
+ }
+ }
+
+ public Date getTaskStartTime(int hour, int minute, int second) {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, hour);
+ cal.set(Calendar.MINUTE, minute);
+ cal.set(Calendar.SECOND, second);
+
+ if (cal.getTime().before(new Date())) {
+ cal.add(Calendar.DATE, 1);
+ }
+
+ return cal.getTime();
+ }
+
+ public void enable() {
+ DailyRepetitionTask[] tasks = MeaMailPlusCore.getInstance().config.getConfig().dailyRepetitionMail;
+ int[] updateTime = MeaMailPlusCore.getInstance().config.getConfig().updateTime;
+ int cycleTime = 1000 * 60 * 60 * 24;
+
+ // create timer and task
+ this.timer = new Timer();
+ this.timer.schedule(
+ new dailyBeginTask(),
+ getTaskStartTime(updateTime[0], updateTime[1], updateTime[2]),
+ cycleTime
+ );
+
+ for(DailyRepetitionTask task : tasks) {
+ this.timer.schedule(
+ new dailyRepetitionTask(task),
+ getTaskStartTime(task.triggerTime[0], task.triggerTime[1], task.triggerTime[2]),
+ cycleTime
+ );
+ }
+ }
+
+ public void disable() {
+ this.timer.cancel();
+ }
+}
diff --git a/src/main/java/io/github/gmw/packetHandler/PlayerBornHook.java b/src/main/java/io/github/gmw/packetHandler/PlayerBornHook.java
new file mode 100644
index 0000000..cab6961
--- /dev/null
+++ b/src/main/java/io/github/gmw/packetHandler/PlayerBornHook.java
@@ -0,0 +1,25 @@
+package io.github.gmw.packetHandler;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.server.game.GameSession;
+import emu.grasscutter.server.packet.recv.HandlerSetPlayerBornDataReq;
+import io.github.gmw.utils.MailCore;
+import io.github.gmw.utils.PlayerUtils;
+
+@Opcodes(PacketOpcodes.SetPlayerBornDataReq)
+public class PlayerBornHook extends HandlerSetPlayerBornDataReq {
+
+ @Override
+ public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+ super.handle(session, header, payload);
+ int playerUid = session.getAccount().getPlayerUid();
+
+ // 第一次加入
+ if(playerUid != 0) {
+ MailCore.sendInitialMailToPlayer(playerUid);
+ MailCore.sendDailySignInMailToPlayer(playerUid);
+ }
+ }
+}
diff --git a/src/main/java/io/github/gmw/packetHandler/PlayerJoinHook.java b/src/main/java/io/github/gmw/packetHandler/PlayerJoinHook.java
new file mode 100644
index 0000000..7219924
--- /dev/null
+++ b/src/main/java/io/github/gmw/packetHandler/PlayerJoinHook.java
@@ -0,0 +1,29 @@
+package io.github.gmw.packetHandler;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.server.packet.recv.HandlerPlayerLoginReq;
+import emu.grasscutter.server.game.GameSession;
+import io.github.gmw.utils.MailCore;
+import io.github.gmw.utils.PlayerUtils;
+
+@Opcodes(PacketOpcodes.PlayerLoginReq)
+public class PlayerJoinHook extends HandlerPlayerLoginReq {
+
+ @Override
+ public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+ super.handle(session, header, payload);
+ Player player = session.getPlayer();
+
+ if (player == null) return; // new player
+ if(PlayerUtils.isFirstLoginToday(player)) { // first login today
+ if (PlayerUtils.isPlayerBirthDay(player)) { // is player birthday
+ MailCore.sendBirthDayMailToPlayer(player);
+ }
+
+ // daily mail
+ MailCore.sendDailySignInMailToPlayer(player);
+ }
+ }
+}
diff --git a/src/main/java/io/github/gmw/utils/MailCore.java b/src/main/java/io/github/gmw/utils/MailCore.java
new file mode 100644
index 0000000..54ac103
--- /dev/null
+++ b/src/main/java/io/github/gmw/utils/MailCore.java
@@ -0,0 +1,177 @@
+package io.github.gmw.utils;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.database.DatabaseHelper;
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.game.mail.Mail;
+import io.github.gmw.MeaMailPlusCore;
+import io.github.gmw.config.MeaMailConfig;
+import io.github.gmw.config.MeaMailTemplate;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+public final class MailCore {
+ public static void sendMailToPlayer(Player player, MeaMailTemplate template) {
+ sendMailToPlayer(player, template, 0);
+ }
+
+ public static void sendMailToPlayer(Player player, int templateId) {
+ sendMailToPlayer(player, templateId, 0);
+ }
+
+ public static void sendMailToPlayer(Player player, int templateId, int minLevel) {
+ MeaMailTemplate template = getTemplateById(templateId);
+ if(template == null) {
+ MeaMailPlusCore.getInstance().logger("Template with id " + templateId + " not found");
+ return;
+ }
+ sendMailToPlayer(player, template, minLevel);
+ }
+
+ public static void sendMailToPlayer(Player player, MeaMailTemplate template, int minLevel) {
+ if (player.getLevel() >= minLevel) {
+ player.sendMail(templateToMail(template));
+ MeaMailPlusCore.getInstance().logger("Mail sent to " + player.getUid() + ": " + template.title);
+ } else {
+ MeaMailPlusCore.getInstance().logger("Player " + player.getUid() + " is not level " + minLevel + " and mail was not sent");
+ }
+ }
+
+ private static Mail templateToMail(MeaMailTemplate mailTemplate) {
+ Mail mail = new Mail();
+ mail.mailContent.content = mailTemplate.body.content;
+ mail.mailContent.title = mailTemplate.title;
+ mail.mailContent.sender = mailTemplate.sender;
+ mail.importance = mailTemplate.importance;
+
+ if (mailTemplate.remainTime == 0) {
+ mail.expireTime = mailTemplate.expireTime;
+ } else {
+ mail.expireTime = (int) Instant.now().getEpochSecond() + mailTemplate.remainTime;
+ }
+
+ mail.itemList = Arrays.stream(mailTemplate.body.items)
+ .map(item -> new Mail.MailItem(item.id, item.count, item.level))
+ .collect(Collectors.toList());
+ return mail;
+ }
+
+ private static MeaMailConfig getConfig() {
+ return MeaMailPlusCore.getInstance().config.getConfig();
+ }
+
+ private static MeaMailTemplate getTemplateById(int id) {
+ return MeaMailPlusCore.getInstance().config.getTemplate(id);
+ }
+
+ public static void sendMailToAllPlayers(int templateId) {
+ sendMailToAllPlayers(templateId, 0);
+ }
+
+ public static void sendMailToAllPlayers(MeaMailTemplate template) {
+ sendMailToAllPlayers(template, 0);
+ }
+
+ public static void sendMailToAllPlayers(int templateId, int minLevel) {
+ sendMailToAllPlayers(templateId, minLevel, false);
+ }
+
+ public static void sendMailToAllPlayers(MeaMailTemplate template, int minLevel) {
+ sendMailToAllPlayers(template, minLevel, false);
+ }
+
+ public static void sendMailToAllPlayers(int templateId, int minLevel, boolean onlineOnly) {
+ MeaMailTemplate template = getTemplateById(templateId);
+ if(template == null) {
+ MeaMailPlusCore.getInstance().logger("Template with id " + templateId + " not found");
+ return;
+ }
+ sendMailToAllPlayers(template, minLevel, onlineOnly);
+ }
+
+ public static void sendMailToAllPlayers(MeaMailTemplate template, int minLevel, boolean onlineOnly) {
+ if (template == null) return;
+
+ List onlinePlayers = new ArrayList<>();
+ List offlinePlayers = new ArrayList<>();
+ Grasscutter.getGameServer().getPlayers().forEach((index, player) -> onlinePlayers.add(player));
+
+ if (!onlineOnly) {
+ DatabaseHelper.getAllPlayers().forEach(player -> {
+ if (onlinePlayers.stream().noneMatch(onlinePlayer -> onlinePlayer.getUid() == player.getUid())) {
+ offlinePlayers.add(player);
+ }
+ });
+ }
+
+ // two methods to send mail
+ onlinePlayers.forEach(player -> sendMailToPlayer(player, template, minLevel));
+ offlinePlayers.forEach(player -> sendMailToPlayer(player, template, minLevel));
+ }
+
+ public static void sendDailySignInMailToPlayer(int uid) {
+ Grasscutter.getGameServer().getPlayers().forEach((index, player) -> {
+ if (player.getUid() == uid)
+ sendDailySignInMailToPlayer(player);
+ });
+ }
+
+ public static void sendDailySignInMailToPlayer(Player player) {
+ MeaMailConfig.DailySignInMailTask[] dailyTasks = getConfig().dailySignInMail;
+ for(MeaMailConfig.DailySignInMailTask task : dailyTasks) {
+ sendMailToPlayer(player, task.templateId, task.minLevel);
+ }
+ }
+
+ public static void sendBirthDayMailToPlayer(int uid) {
+ Grasscutter.getGameServer().getPlayers().forEach((index, player) -> {
+ if (player.getUid() == uid)
+ sendBirthDayMailToPlayer(player);
+ });
+ }
+
+ public static void sendBirthDayMailToPlayer(Player player) {
+ int[] BirthdayMailTemplateIds = getConfig().birthDayMail;
+ for(int templateId : BirthdayMailTemplateIds) {
+ sendMailToPlayer(player, templateId);
+ }
+ }
+
+ public static void sendInitialMailToPlayer(int uid) {
+ Grasscutter.getGameServer().getPlayers().forEach((index, player) -> {
+ if (player.getUid() == uid)
+ sendInitialMailToPlayer(player);
+ });
+ }
+
+ public static void sendInitialMailToPlayer(Player player) {
+ int[] InitialMailTemplateIds = getConfig().initialMail;
+ for(int templateId : InitialMailTemplateIds) {
+ sendMailToPlayer(player, templateId);
+ }
+ }
+
+ public static void dailyUpdate() {
+ // update daily
+ Grasscutter.getGameServer().getPlayers().forEach((index, player) -> {
+ // birthday mail
+ if (PlayerUtils.isPlayerBirthDay(player)) {
+ sendBirthDayMailToPlayer(player);
+ }
+
+ // daily sign in
+ sendDailySignInMailToPlayer(player);
+ });
+ }
+
+ public static void repetitionUpdate(MeaMailConfig.DailyRepetitionTask task) {
+ // update daily on time
+ sendMailToAllPlayers(task.templateId, task.minLevel, task.onlineOnly);
+ }
+}
diff --git a/src/main/java/io/github/gmw/utils/PlayerUtils.java b/src/main/java/io/github/gmw/utils/PlayerUtils.java
new file mode 100644
index 0000000..f00ffe5
--- /dev/null
+++ b/src/main/java/io/github/gmw/utils/PlayerUtils.java
@@ -0,0 +1,42 @@
+package io.github.gmw.utils;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.game.player.PlayerBirthday;
+import io.github.gmw.MeaMailPlusCore;
+
+import java.util.Calendar;
+
+public final class PlayerUtils {
+ public static long getPlayerBirthDayTime(Player player) {
+ int[] updateTime = MeaMailPlusCore.getInstance().config.getConfig().updateTime;
+ PlayerBirthday date = player.getBirthday();
+ Calendar cal = Calendar.getInstance();
+
+ cal.set(Calendar.DAY_OF_MONTH, date.getDay());
+ cal.set(Calendar.MONTH, date.getMonth() - 1);
+
+ cal.set(Calendar.HOUR_OF_DAY, updateTime[0]);
+ cal.set(Calendar.MINUTE, updateTime[1]);
+ cal.set(Calendar.SECOND, updateTime[2]);
+ return cal.getTimeInMillis() / 1000;
+ }
+
+ public static boolean isPlayerBirthDay(Player player) {
+ if (player.hasBirthday()) {
+ long playerBirthDayTime = getPlayerBirthDayTime(player);
+ long playerEndBirthDayTime = playerBirthDayTime + 24 * 60 * 60;
+ long currentTime = System.currentTimeMillis() / 1000;
+
+ return currentTime >= playerBirthDayTime && currentTime < playerEndBirthDayTime;
+ }
+ return false;
+ }
+
+ public static boolean isFirstLoginToday(Player player) {
+ long startOfDayTime = MeaMailPlusCore.getInstance().config.getTomorrowUpdateTime() - 24 * 60 * 60;
+ long playerLastActiveTime = player.getProfile().getLastActiveTime();
+ long currentTime = System.currentTimeMillis() / 1000;
+
+ return playerLastActiveTime < startOfDayTime && currentTime >= startOfDayTime;
+ }
+}
diff --git a/src/main/resources/plugin.json b/src/main/resources/plugin.json
new file mode 100644
index 0000000..bc3d472
--- /dev/null
+++ b/src/main/resources/plugin.json
@@ -0,0 +1,10 @@
+{
+ "name": "MeaMailPlus",
+ "description": "A plugin to send some notices to players",
+ "version": "1.0",
+ "mainClass": "io.github.gmw.MeaMailPlusCore",
+ "authors": [
+ "Hell",
+ "ButterCookies"
+ ]
+}
\ No newline at end of file