mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-30 20:56:55 +00:00
Merge remote-tracking branch 'upstream/development' into dev-mail
This commit is contained in:
commit
028c7e5604
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: '16'
|
java-version: '17'
|
||||||
- name: Run Gradle
|
- name: Run Gradle
|
||||||
run: ./gradlew && ./gradlew jar
|
run: ./gradlew && ./gradlew jar
|
||||||
- name: Upload build
|
- name: Upload build
|
||||||
|
@ -24,7 +24,7 @@ EN | [中文](README_zh-CN.md)
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
* Java SE - 16 ([mirror link](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) since Oracle required an account to download old builds)
|
* Java SE - 17 ([mirror link](https://github.com/adoptium/temurin17-binaries/releases/tag/jdk-17.0.3+7) since Oracle required an account to download old builds)
|
||||||
|
|
||||||
**Note:** If you just want to **run it**, then **jre** is fine
|
**Note:** If you just want to **run it**, then **jre** is fine
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ character falling from a very high destination, exact location that you marked.
|
|||||||
|
|
||||||
# Quick Troubleshooting
|
# Quick Troubleshooting
|
||||||
|
|
||||||
* If compiling wasn't successful, please check your JDK installation (JDK 16 and validated JDK's bin PATH variable)
|
* If compiling wasn't successful, please check your JDK installation (JDK 17 and validated JDK's bin PATH variable)
|
||||||
* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using
|
* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using
|
||||||
Fiddler make sure it running on another port except 8888
|
Fiddler make sure it running on another port except 8888
|
||||||
* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client
|
* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
### 环境需求
|
### 环境需求
|
||||||
|
|
||||||
* Java SE - 16 (当您没有Oracle账户,可以使用[镜像](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7))
|
* Java SE - 17 (当您没有Oracle账户,可以使用[镜像](https://mirrors.tuna.tsinghua.edu.cn/Adoptium/17/jdk/))
|
||||||
|
|
||||||
**注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了
|
**注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了
|
||||||
|
|
||||||
@ -142,6 +142,6 @@ chmod +x gradlew
|
|||||||
|
|
||||||
# 快速排除问题
|
# 快速排除问题
|
||||||
|
|
||||||
* 如果编译未能成功,请检查您的jdk安装 (JDK 16并确认jdk处于环境变量`PATH`中
|
* 如果编译未能成功,请检查您的jdk安装 (JDK 17并确认jdk处于环境变量`PATH`中
|
||||||
* 我的客户端无法登录/连接, 4206, 其它... - 大部分情况下这是因为您的代理存在问题.如果使用Fiddler请确认Fiddler监听端口不是`8888`
|
* 我的客户端无法登录/连接, 4206, 其它... - 大部分情况下这是因为您的代理存在问题.如果使用Fiddler请确认Fiddler监听端口不是`8888`
|
||||||
* 启动顺序: MongoDB > Grasscutter > 代理程序 (mitmdump, fiddler等.) > 客户端
|
* 启动顺序: MongoDB > Grasscutter > 代理程序 (mitmdump, fiddler等.) > 客户端
|
||||||
|
@ -14,8 +14,8 @@ plugins {
|
|||||||
id 'application'
|
id 'application'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = 16
|
sourceCompatibility = 17
|
||||||
targetCompatibility = 16
|
targetCompatibility = 17
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package emu.grasscutter;
|
package emu.grasscutter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public final class Config {
|
public final class Config {
|
||||||
|
|
||||||
public String DatabaseUrl = "mongodb://localhost:27017";
|
public String DatabaseUrl = "mongodb://localhost:27017";
|
||||||
@ -12,6 +10,7 @@ public final class Config {
|
|||||||
public String PACKETS_FOLDER = "./packets/";
|
public String PACKETS_FOLDER = "./packets/";
|
||||||
public String DUMPS_FOLDER = "./dumps/";
|
public String DUMPS_FOLDER = "./dumps/";
|
||||||
public String KEY_FOLDER = "./keys/";
|
public String KEY_FOLDER = "./keys/";
|
||||||
|
public String PLUGINS_FOLDER = "./plugins/";
|
||||||
|
|
||||||
public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY
|
public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY
|
||||||
public GameServerOptions GameServer = new GameServerOptions();
|
public GameServerOptions GameServer = new GameServerOptions();
|
||||||
|
@ -8,6 +8,7 @@ import java.io.InputStreamReader;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
import emu.grasscutter.command.CommandMap;
|
import emu.grasscutter.command.CommandMap;
|
||||||
|
import emu.grasscutter.plugin.PluginManager;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -33,8 +34,9 @@ public final class Grasscutter {
|
|||||||
public static RunMode MODE = RunMode.BOTH;
|
public static RunMode MODE = RunMode.BOTH;
|
||||||
private static DispatchServer dispatchServer;
|
private static DispatchServer dispatchServer;
|
||||||
private static GameServer gameServer;
|
private static GameServer gameServer;
|
||||||
|
private static PluginManager pluginManager;
|
||||||
|
|
||||||
public static final Reflections reflector = new Reflections("emu.grasscutter");
|
public static final Reflections reflector = new Reflections();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Declare logback configuration.
|
// Declare logback configuration.
|
||||||
@ -52,15 +54,11 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
switch (arg.toLowerCase()) {
|
switch (arg.toLowerCase()) {
|
||||||
case "-auth":
|
case "-auth" -> MODE = RunMode.AUTH;
|
||||||
MODE = RunMode.AUTH;
|
case "-game" -> MODE = RunMode.GAME;
|
||||||
break;
|
case "-handbook" -> {
|
||||||
case "-game":
|
Tools.createGmHandbook(); return;
|
||||||
MODE = RunMode.GAME;
|
}
|
||||||
break;
|
|
||||||
case "-handbook":
|
|
||||||
Tools.createGmHandbook();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,18 +70,20 @@ public final class Grasscutter {
|
|||||||
// Database
|
// Database
|
||||||
DatabaseManager.initialize();
|
DatabaseManager.initialize();
|
||||||
|
|
||||||
|
// Create server instances.
|
||||||
|
dispatchServer = new DispatchServer();
|
||||||
|
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
||||||
|
|
||||||
|
// Create plugin manager instance.
|
||||||
|
pluginManager = new PluginManager();
|
||||||
|
|
||||||
// Start servers.
|
// Start servers.
|
||||||
if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
|
if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
|
||||||
dispatchServer = new DispatchServer();
|
|
||||||
dispatchServer.start();
|
dispatchServer.start();
|
||||||
|
|
||||||
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
|
||||||
gameServer.start();
|
gameServer.start();
|
||||||
} else if (getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
} else if (getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
||||||
dispatchServer = new DispatchServer();
|
|
||||||
dispatchServer.start();
|
dispatchServer.start();
|
||||||
} else if (getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
} else if (getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
||||||
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
|
||||||
gameServer.start();
|
gameServer.start();
|
||||||
} else {
|
} else {
|
||||||
getLogger().error("Invalid server run mode. " + getConfig().RunMode);
|
getLogger().error("Invalid server run mode. " + getConfig().RunMode);
|
||||||
@ -92,10 +92,21 @@ public final class Grasscutter {
|
|||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable all plugins.
|
||||||
|
pluginManager.enablePlugins();
|
||||||
|
|
||||||
// Open console.
|
// Open console.
|
||||||
startConsole();
|
startConsole();
|
||||||
|
// Hook into shutdown event.
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server shutdown event.
|
||||||
|
*/
|
||||||
|
private static void onShutdown() {
|
||||||
|
// Disable all plugins.
|
||||||
|
pluginManager.disablePlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadConfig() {
|
public static void loadConfig() {
|
||||||
@ -112,7 +123,7 @@ public final class Grasscutter {
|
|||||||
try (FileWriter file = new FileWriter(configFile)) {
|
try (FileWriter file = new FileWriter(configFile)) {
|
||||||
file.write(gson.toJson(config));
|
file.write(gson.toJson(config));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Grasscutter.getLogger().error("Config save error");
|
Grasscutter.getLogger().error("Unable to save config file.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,13 +134,13 @@ public final class Grasscutter {
|
|||||||
while ((input = br.readLine()) != null) {
|
while ((input = br.readLine()) != null) {
|
||||||
try {
|
try {
|
||||||
if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
||||||
getLogger().error("Commands are not supported in dispatch only mode");
|
getLogger().error("Commands are not supported in dispatch only mode.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandMap.getInstance().invoke(null, input);
|
CommandMap.getInstance().invoke(null, input);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Grasscutter.getLogger().error("Command error: ");
|
Grasscutter.getLogger().error("Command error:", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -162,4 +173,8 @@ public final class Grasscutter {
|
|||||||
public static GameServer getGameServer() {
|
public static GameServer getGameServer() {
|
||||||
return gameServer;
|
return gameServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PluginManager getPluginManager() {
|
||||||
|
return pluginManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package emu.grasscutter.command.commands;
|
|
||||||
|
|
||||||
import emu.grasscutter.command.Command;
|
|
||||||
import emu.grasscutter.command.CommandHandler;
|
|
||||||
import emu.grasscutter.game.GenshinPlayer;
|
|
||||||
import emu.grasscutter.game.inventory.Inventory;
|
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Command(label = "clearartifacts", usage = "clearartifacts",
|
|
||||||
description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory",
|
|
||||||
aliases = {"clearart"}, permission = "player.clearartifacts")
|
|
||||||
public final class ClearArtifactsCommand implements CommandHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
|
||||||
if (sender == null) {
|
|
||||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
|
||||||
return; // TODO: clear player's artifacts from console or other players
|
|
||||||
}
|
|
||||||
|
|
||||||
Inventory playerInventory = sender.getInventory();
|
|
||||||
playerInventory.getItems().values().stream()
|
|
||||||
.filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY)
|
|
||||||
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
|
||||||
.filter(item -> !item.isLocked() && !item.isEquipped())
|
|
||||||
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
|
||||||
}
|
|
||||||
}
|
|
106
src/main/java/emu/grasscutter/command/commands/ClearCommand.java
Normal file
106
src/main/java/emu/grasscutter/command/commands/ClearCommand.java
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.data.GenshinData;
|
||||||
|
import emu.grasscutter.data.def.ItemData;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.inventory.GenshinItem;
|
||||||
|
import emu.grasscutter.game.inventory.Inventory;
|
||||||
|
import emu.grasscutter.game.inventory.ItemType;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Command(label = "clear", usage = "clear <all|wp|art|mat>", //Merged /clearartifacts and /clearweapons to /clear <args> [uid]
|
||||||
|
description = "Deletes unequipped unlocked items, including yellow rarity ones from your inventory",
|
||||||
|
aliases = {"clear"}, permission = "player.clearinv")
|
||||||
|
|
||||||
|
public final class ClearCommand implements CommandHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
|
int target;
|
||||||
|
if (sender == null) {
|
||||||
|
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String cmdSwitch = args.get(1);
|
||||||
|
|
||||||
|
Inventory playerInventory = sender.getInventory();
|
||||||
|
try {
|
||||||
|
target = Integer.parseInt(args.get(0));
|
||||||
|
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||||
|
if (targetPlayer == null && sender != null) {
|
||||||
|
target = sender.getUid();
|
||||||
|
} else {
|
||||||
|
switch (cmdSwitch){
|
||||||
|
case "wp":
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
|
||||||
|
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||||
|
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
||||||
|
sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " .");
|
||||||
|
break;
|
||||||
|
case "art":
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY)
|
||||||
|
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
||||||
|
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||||
|
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
||||||
|
sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
|
||||||
|
break;
|
||||||
|
case "mat":
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item -> item.getItemType() == ItemType.ITEM_MATERIAL)
|
||||||
|
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
||||||
|
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||||
|
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
||||||
|
sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
|
||||||
|
break;
|
||||||
|
case "all":
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item1 -> item1.getItemType() == ItemType.ITEM_RELIQUARY)
|
||||||
|
.filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0)
|
||||||
|
.filter(item1 -> !item1.isLocked() && !item1.isEquipped())
|
||||||
|
.forEach(item1 -> playerInventory.removeItem(item1, item1.getCount()));
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL)
|
||||||
|
.filter(item2 -> !item2.isLocked() && !item2.isEquipped())
|
||||||
|
.forEach(item2 -> playerInventory.removeItem(item2, item2.getCount()));
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON)
|
||||||
|
.filter(item3 -> item3.getLevel() == 1 && item3.getExp() == 0)
|
||||||
|
.filter(item3 -> !item3.isLocked() && !item3.isEquipped())
|
||||||
|
.forEach(item3 -> playerInventory.removeItem(item3, item3.getCount()));
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE)
|
||||||
|
.filter(item4 -> !item4.isLocked() && !item4.isEquipped())
|
||||||
|
.forEach(item4 -> playerInventory.removeItem(item4, item4.getCount()));
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY)
|
||||||
|
.filter(item5 -> !item5.isLocked() && !item5.isEquipped())
|
||||||
|
.forEach(item5 -> playerInventory.removeItem(item5, item5.getCount()));
|
||||||
|
playerInventory.getItems().values().stream()
|
||||||
|
.filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL)
|
||||||
|
.filter(item6 -> !item6.isLocked() && !item6.isEquipped())
|
||||||
|
.forEach(item6 -> playerInventory.removeItem(item6, item6.getCount()));
|
||||||
|
sender.dropMessage("Cleared everything for " + targetPlayer.getNickname() + " .");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
// TODO: Parse from item name using GM Handbook.
|
||||||
|
CommandHandler.sendMessage(sender, "Invalid playerId.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||||
|
if (targetPlayer == null) {
|
||||||
|
CommandHandler.sendMessage(sender, "Player not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,51 +0,0 @@
|
|||||||
package emu.grasscutter.command.commands;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.command.Command;
|
|
||||||
import emu.grasscutter.command.CommandHandler;
|
|
||||||
import emu.grasscutter.game.GenshinPlayer;
|
|
||||||
import emu.grasscutter.game.inventory.Inventory;
|
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Command(label = "clearweapons", usage = "clearweapons",
|
|
||||||
description = "Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory",
|
|
||||||
aliases = {"clearwp"}, permission = "player.clearweapons")
|
|
||||||
public final class ClearWeaponsCommand implements CommandHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
|
||||||
if (sender == null) {
|
|
||||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int target;
|
|
||||||
if (args.size() == 1) {
|
|
||||||
try {
|
|
||||||
target = Integer.parseInt(args.get(0));
|
|
||||||
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
|
|
||||||
target = sender.getUid();
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
CommandHandler.sendMessage(sender, "Invalid player id.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target = sender.getUid();
|
|
||||||
}
|
|
||||||
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
|
||||||
if (targetPlayer == null) {
|
|
||||||
CommandHandler.sendMessage(sender, "Player not found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Inventory playerInventory = targetPlayer.getInventory();
|
|
||||||
playerInventory.getItems().values().stream()
|
|
||||||
.filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
|
|
||||||
.filter(item -> !item.isLocked() && !item.isEquipped())
|
|
||||||
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
|
||||||
sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " .");
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,68 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
|
import emu.grasscutter.game.props.LifeState;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Command(label = "killcharacter", usage = "killcharacter [playerId]", aliases = {"suicide", "kill"},
|
||||||
|
description = "Kills the players current character", permission = "player.killcharacter")
|
||||||
|
public final class KillCharacterCommand implements CommandHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
|
int target;
|
||||||
|
if (sender == null) {
|
||||||
|
// from console
|
||||||
|
if (args.size() == 1) {
|
||||||
|
try {
|
||||||
|
target = Integer.parseInt(args.get(0));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
CommandHandler.sendMessage(null, "Invalid player id.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CommandHandler.sendMessage(null, "Usage: /killcharacter [playerId]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (args.size() == 1) {
|
||||||
|
try {
|
||||||
|
target = Integer.parseInt(args.get(0));
|
||||||
|
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
|
||||||
|
target = sender.getUid();
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
CommandHandler.sendMessage(sender, "Invalid player id.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = sender.getUid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||||
|
if (targetPlayer == null) {
|
||||||
|
CommandHandler.sendMessage(sender, "Player not found or offline.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||||
|
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||||
|
// Packets
|
||||||
|
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
||||||
|
// remove
|
||||||
|
targetPlayer.getScene().removeEntity(entity);
|
||||||
|
entity.onDeath(0);
|
||||||
|
|
||||||
|
CommandHandler.sendMessage(sender, "Killed " + targetPlayer.getNickname() + " current character.");
|
||||||
|
}
|
||||||
|
}
|
67
src/main/java/emu/grasscutter/plugin/Plugin.java
Normal file
67
src/main/java/emu/grasscutter/plugin/Plugin.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package emu.grasscutter.plugin;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.server.game.GameServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for all plugins to extend.
|
||||||
|
*/
|
||||||
|
public abstract class Plugin {
|
||||||
|
private PluginIdentifier identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is reflected into.
|
||||||
|
*
|
||||||
|
* Set plugin variables.
|
||||||
|
* @param identifier The plugin's identifier.
|
||||||
|
*/
|
||||||
|
private void initializePlugin(PluginIdentifier identifier) {
|
||||||
|
if(this.identifier == null)
|
||||||
|
this.identifier = identifier;
|
||||||
|
else Grasscutter.getLogger().warn(this.identifier.name + " had a reinitialization attempt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The plugin's identifier instance.
|
||||||
|
* @return An instance of {@link PluginIdentifier}.
|
||||||
|
*/
|
||||||
|
public final PluginIdentifier getIdentifier(){
|
||||||
|
return this.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the plugin's name.
|
||||||
|
*/
|
||||||
|
public final String getName() {
|
||||||
|
return this.identifier.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the plugin's description.
|
||||||
|
*/
|
||||||
|
public final String getDescription() {
|
||||||
|
return this.identifier.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the plugin's version.
|
||||||
|
*/
|
||||||
|
public final String getVersion() {
|
||||||
|
return this.identifier.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server that initialized the plugin.
|
||||||
|
* @return A server instance.
|
||||||
|
*/
|
||||||
|
public final GameServer getServer() {
|
||||||
|
return Grasscutter.getGameServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called when the plugin is first loaded. */
|
||||||
|
public void onLoad() { }
|
||||||
|
/* Called after (most of) the server enables. */
|
||||||
|
public void onEnable() { }
|
||||||
|
/* Called before the server disables. */
|
||||||
|
public void onDisable() { }
|
||||||
|
}
|
18
src/main/java/emu/grasscutter/plugin/PluginConfig.java
Normal file
18
src/main/java/emu/grasscutter/plugin/PluginConfig.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data contained in the plugin's `plugin.json` file.
|
||||||
|
*/
|
||||||
|
public final class PluginConfig {
|
||||||
|
public String name, description, version;
|
||||||
|
public String mainClass;
|
||||||
|
public String[] authors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to validate this config instance.
|
||||||
|
* @return True if the config is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean validate() {
|
||||||
|
return name != null && description != null && mainClass != null;
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/emu/grasscutter/plugin/PluginIdentifier.java
Normal file
29
src/main/java/emu/grasscutter/plugin/PluginIdentifier.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package emu.grasscutter.plugin;
|
||||||
|
|
||||||
|
// TODO: Potentially replace with Lombok?
|
||||||
|
public final class PluginIdentifier {
|
||||||
|
public final String name, description, version;
|
||||||
|
public final String[] authors;
|
||||||
|
|
||||||
|
public PluginIdentifier(
|
||||||
|
String name, String description, String version,
|
||||||
|
String[] authors
|
||||||
|
) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.version = version;
|
||||||
|
this.authors = authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a {@link PluginConfig} into a {@link PluginIdentifier}.
|
||||||
|
*/
|
||||||
|
public static PluginIdentifier fromPluginConfig(PluginConfig config) {
|
||||||
|
if(!config.validate())
|
||||||
|
throw new IllegalArgumentException("A valid plugin config is required to convert into a plugin identifier.");
|
||||||
|
return new PluginIdentifier(
|
||||||
|
config.name, config.description, config.version,
|
||||||
|
config.authors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
152
src/main/java/emu/grasscutter/plugin/PluginManager.java
Normal file
152
src/main/java/emu/grasscutter/plugin/PluginManager.java
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package emu.grasscutter.plugin;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.server.event.Event;
|
||||||
|
import emu.grasscutter.server.event.EventHandler;
|
||||||
|
import emu.grasscutter.server.event.Listener;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the server's plugins & the event system.
|
||||||
|
*/
|
||||||
|
public final class PluginManager {
|
||||||
|
private final Map<String, Plugin> plugins = new HashMap<>();
|
||||||
|
private final Map<Plugin, List<Listener>> listeners = new HashMap<>();
|
||||||
|
|
||||||
|
public PluginManager() {
|
||||||
|
this.loadPlugins(); // Load all plugins from the plugins directory.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads plugins from the config-specified directory.
|
||||||
|
*/
|
||||||
|
private void loadPlugins() {
|
||||||
|
String directory = Grasscutter.getConfig().PLUGINS_FOLDER;
|
||||||
|
File pluginsDir = new File(Utils.toFilePath(directory));
|
||||||
|
if(!pluginsDir.exists() && !pluginsDir.mkdirs()) {
|
||||||
|
Grasscutter.getLogger().error("Failed to create plugins directory: " + pluginsDir.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] files = pluginsDir.listFiles();
|
||||||
|
if(files == null) {
|
||||||
|
// The directory is empty, there aren't any plugins to load.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<File> plugins = Arrays.stream(files)
|
||||||
|
.filter(file -> file.getName().endsWith(".jar"))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
plugins.forEach(plugin -> {
|
||||||
|
try {
|
||||||
|
URL url = plugin.toURI().toURL();
|
||||||
|
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
|
||||||
|
URL configFile = loader.findResource("plugin.json");
|
||||||
|
InputStreamReader fileReader = new InputStreamReader(configFile.openStream());
|
||||||
|
|
||||||
|
PluginConfig pluginConfig = Grasscutter.getGsonFactory().fromJson(fileReader, PluginConfig.class);
|
||||||
|
if(!pluginConfig.validate()) {
|
||||||
|
Utils.logObject(pluginConfig);
|
||||||
|
Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid config file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> pluginClass = loader.loadClass(pluginConfig.mainClass);
|
||||||
|
Plugin pluginInstance = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
|
||||||
|
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig));
|
||||||
|
|
||||||
|
fileReader.close(); // Close the file reader.
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid main class.");
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Grasscutter.getLogger().error("Failed to load plugin: " + plugin.getName(), exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the specified plugin.
|
||||||
|
* @param plugin The plugin instance.
|
||||||
|
*/
|
||||||
|
private void loadPlugin(Plugin plugin, PluginIdentifier identifier) {
|
||||||
|
Grasscutter.getLogger().info("Loading plugin: " + identifier.name);
|
||||||
|
|
||||||
|
// Add the plugin's identifier.
|
||||||
|
try {
|
||||||
|
Class<Plugin> pluginClass = Plugin.class;
|
||||||
|
Method method = pluginClass.getDeclaredMethod("initializePlugin", PluginIdentifier.class);
|
||||||
|
method.setAccessible(true); method.invoke(plugin, identifier); method.setAccessible(false);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
Grasscutter.getLogger().warn("Failed to add plugin identifier: " + identifier.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the plugin to the list of loaded plugins.
|
||||||
|
this.plugins.put(identifier.name, plugin);
|
||||||
|
// Call the plugin's onLoad method.
|
||||||
|
plugin.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables all registered plugins.
|
||||||
|
*/
|
||||||
|
public void enablePlugins() {
|
||||||
|
this.plugins.forEach((name, plugin) -> {
|
||||||
|
Grasscutter.getLogger().info("Enabling plugin: " + name);
|
||||||
|
plugin.onEnable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables all registered plugins.
|
||||||
|
*/
|
||||||
|
public void disablePlugins() {
|
||||||
|
this.plugins.forEach((name, plugin) -> {
|
||||||
|
Grasscutter.getLogger().info("Disabling plugin: " + name);
|
||||||
|
plugin.onDisable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a plugin's event listener.
|
||||||
|
* @param plugin The plugin instance.
|
||||||
|
* @param listener The event listener.
|
||||||
|
*/
|
||||||
|
public void registerListener(Plugin plugin, Listener listener) {
|
||||||
|
this.listeners.computeIfAbsent(plugin, k -> new ArrayList<>()).add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the provided event on all registered event listeners.
|
||||||
|
* @param event The event to invoke.
|
||||||
|
*/
|
||||||
|
public void invokeEvent(Event event) {
|
||||||
|
this.listeners.values().stream()
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.forEach(listener -> this.invokeOnListener(listener, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to invoke the event on the provided listener.
|
||||||
|
*/
|
||||||
|
private void invokeOnListener(Listener listener, Event event) {
|
||||||
|
try {
|
||||||
|
Class<?> listenerClass = listener.getClass();
|
||||||
|
Method[] methods = listenerClass.getMethods();
|
||||||
|
for (Method method : methods) {
|
||||||
|
if(!method.isAnnotationPresent(EventHandler.class)) return;
|
||||||
|
if(!method.getParameterTypes()[0].isAssignableFrom(event.getClass())) return;
|
||||||
|
method.invoke(listener, event);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) { }
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,8 @@ import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
|
|||||||
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
|
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
|
||||||
import emu.grasscutter.server.dispatch.json.*;
|
import emu.grasscutter.server.dispatch.json.*;
|
||||||
import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
|
import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
|
||||||
|
import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent;
|
||||||
|
import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent;
|
||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
@ -277,7 +279,11 @@ public final class DispatchServer {
|
|||||||
if (uri.getQuery() != null && uri.getQuery().length() > 0) {
|
if (uri.getQuery() != null && uri.getQuery().length() > 0) {
|
||||||
response = regionCurrentBase64;
|
response = regionCurrentBase64;
|
||||||
}
|
}
|
||||||
responseHTML(t, response);
|
|
||||||
|
// Invoke event.
|
||||||
|
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(response); event.call();
|
||||||
|
// Respond with event result.
|
||||||
|
responseHTML(t, event.getRegionInfo());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package emu.grasscutter.server.event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementing this interface marks an event as cancellable.
|
||||||
|
*/
|
||||||
|
public interface Cancellable {
|
||||||
|
void cancel();
|
||||||
|
}
|
32
src/main/java/emu/grasscutter/server/event/Event.java
Normal file
32
src/main/java/emu/grasscutter/server/event/Event.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package emu.grasscutter.server.event;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic server event.
|
||||||
|
*/
|
||||||
|
public abstract class Event {
|
||||||
|
private boolean cancelled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the cancelled state of the event.
|
||||||
|
*/
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return this.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the event if possible.
|
||||||
|
*/
|
||||||
|
public void cancel() {
|
||||||
|
if(this instanceof Cancellable)
|
||||||
|
this.cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes this event to all listeners.
|
||||||
|
*/
|
||||||
|
public void call() {
|
||||||
|
Grasscutter.getPluginManager().invokeEvent(this);
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/emu/grasscutter/server/event/EventHandler.java
Normal file
11
src/main/java/emu/grasscutter/server/event/EventHandler.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package emu.grasscutter.server.event;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares a class as an event listener/handler.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface EventHandler {
|
||||||
|
}
|
7
src/main/java/emu/grasscutter/server/event/Listener.java
Normal file
7
src/main/java/emu/grasscutter/server/event/Listener.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.server.event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementing this interface declares a class as an event listener.
|
||||||
|
*/
|
||||||
|
public interface Listener {
|
||||||
|
}
|
17
src/main/java/emu/grasscutter/server/event/ServerEvent.java
Normal file
17
src/main/java/emu/grasscutter/server/event/ServerEvent.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.server.event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is related to the internals of the server.
|
||||||
|
*/
|
||||||
|
public abstract class ServerEvent extends Event {
|
||||||
|
protected final Type type;
|
||||||
|
|
||||||
|
public ServerEvent(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
DISPATCH,
|
||||||
|
GAME
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.server.event.dispatch;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
|
||||||
|
public final class QueryAllRegionsEvent extends ServerEvent {
|
||||||
|
private String regionList;
|
||||||
|
|
||||||
|
public QueryAllRegionsEvent(String regionList) {
|
||||||
|
super(Type.DISPATCH);
|
||||||
|
|
||||||
|
this.regionList = regionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegionList(String regionList) {
|
||||||
|
this.regionList = regionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegionList() {
|
||||||
|
return this.regionList;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.server.event.dispatch;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
|
||||||
|
public final class QueryCurrentRegionEvent extends ServerEvent {
|
||||||
|
private String regionInfo;
|
||||||
|
|
||||||
|
public QueryCurrentRegionEvent(String regionInfo) {
|
||||||
|
super(Type.DISPATCH);
|
||||||
|
|
||||||
|
this.regionInfo = regionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegionInfo(String regionInfo) {
|
||||||
|
this.regionInfo = regionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegionInfo() {
|
||||||
|
return this.regionInfo;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package emu.grasscutter.server.event.game;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.Cancellable;
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
|
||||||
|
public final class ReceivePacketEvent extends ServerEvent implements Cancellable {
|
||||||
|
private final GameSession gameSession;
|
||||||
|
private final int packetId;
|
||||||
|
private byte[] packetData;
|
||||||
|
|
||||||
|
public ReceivePacketEvent(GameSession gameSession, int packetId, byte[] packetData) {
|
||||||
|
super(Type.GAME);
|
||||||
|
|
||||||
|
this.gameSession = gameSession;
|
||||||
|
this.packetId = packetId;
|
||||||
|
this.packetData = packetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameSession getGameSession() {
|
||||||
|
return this.gameSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPacketId() {
|
||||||
|
return this.packetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPacketData(byte[] packetData) {
|
||||||
|
this.packetData = packetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPacketData() {
|
||||||
|
return this.packetData;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package emu.grasscutter.server.event.game;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
|
import emu.grasscutter.server.event.Cancellable;
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
|
||||||
|
public final class SendPacketEvent extends ServerEvent implements Cancellable {
|
||||||
|
private final GameSession gameSession;
|
||||||
|
private GenshinPacket packet;
|
||||||
|
|
||||||
|
public SendPacketEvent(GameSession gameSession, GenshinPacket packet) {
|
||||||
|
super(Type.GAME);
|
||||||
|
|
||||||
|
this.gameSession = gameSession;
|
||||||
|
this.packet = packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameSession getGameSession() {
|
||||||
|
return this.gameSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPacket(GenshinPacket packet) {
|
||||||
|
this.packet = packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenshinPacket getPacket() {
|
||||||
|
return this.packet;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package emu.grasscutter.server.game;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.game.ReceivePacketEvent;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
@ -48,9 +49,7 @@ public class GameServerPacketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
|
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
|
||||||
PacketHandler handler = null;
|
PacketHandler handler = this.handlers.get(opcode);
|
||||||
|
|
||||||
handler = this.handlers.get(opcode);
|
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
try {
|
try {
|
||||||
@ -77,8 +76,10 @@ public class GameServerPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle
|
// Invoke event.
|
||||||
handler.handle(session, header, payload);
|
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
|
||||||
|
if(!event.isCanceled()) // If event is not canceled, continue.
|
||||||
|
handler.handle(session, header, event.getPacketData());
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// TODO Remove this when no more needed
|
// TODO Remove this when no more needed
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
@ -10,6 +10,7 @@ import emu.grasscutter.game.GenshinPlayer;
|
|||||||
import emu.grasscutter.net.packet.GenshinPacket;
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodesUtil;
|
import emu.grasscutter.net.packet.PacketOpcodesUtil;
|
||||||
import emu.grasscutter.netty.MihoyoKcpChannel;
|
import emu.grasscutter.netty.MihoyoKcpChannel;
|
||||||
|
import emu.grasscutter.server.event.game.SendPacketEvent;
|
||||||
import emu.grasscutter.utils.Crypto;
|
import emu.grasscutter.utils.Crypto;
|
||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
@ -161,16 +162,15 @@ public class GameSession extends MihoyoKcpChannel {
|
|||||||
genshinPacket.buildHeader(this.getNextClientSequence());
|
genshinPacket.buildHeader(this.getNextClientSequence());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build packet
|
|
||||||
byte[] data = genshinPacket.build();
|
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) {
|
if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) {
|
||||||
logPacket(genshinPacket);
|
logPacket(genshinPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send
|
// Invoke event.
|
||||||
send(data);
|
SendPacketEvent event = new SendPacketEvent(this, genshinPacket); event.call();
|
||||||
|
if(!event.isCanceled()) // If event is not cancelled, continue.
|
||||||
|
this.send(event.getPacket().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logPacket(int opcode) {
|
private void logPacket(int opcode) {
|
||||||
|
@ -137,6 +137,15 @@ public final class Utils {
|
|||||||
return nonNull != null ? nonNull : fallback;
|
return nonNull != null ? nonNull : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an object to the console.
|
||||||
|
* @param object The object to log.
|
||||||
|
*/
|
||||||
|
public static void logObject(Object object) {
|
||||||
|
String asJson = Grasscutter.getGsonFactory().toJson(object);
|
||||||
|
Grasscutter.getLogger().info(asJson);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for required files and folders before startup.
|
* Checks for required files and folders before startup.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user