mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-02-05 03:44:20 +08:00
Merge branch 'api' into development
This commit is contained in:
commit
30d4fec9d4
3
.gitignore
vendored
3
.gitignore
vendored
@ -30,6 +30,9 @@ hs_err_pid*
|
|||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
# Ignore Gradle properties
|
||||||
|
gradle.properties
|
||||||
|
|
||||||
# Eclipse
|
# Eclipse
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
|
76
build.gradle
76
build.gradle
@ -12,11 +12,22 @@ plugins {
|
|||||||
|
|
||||||
// Apply the application plugin to add support for building a CLI application
|
// Apply the application plugin to add support for building a CLI application
|
||||||
id 'application'
|
id 'application'
|
||||||
|
|
||||||
|
id 'maven-publish'
|
||||||
|
id 'signing'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group = 'tech.xigam'
|
||||||
|
version = '1.0.0-dev'
|
||||||
|
|
||||||
sourceCompatibility = 17
|
sourceCompatibility = 17
|
||||||
targetCompatibility = 17
|
targetCompatibility = 17
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
@ -65,3 +76,68 @@ jar {
|
|||||||
destinationDir = file(".")
|
destinationDir = file(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
artifactId = 'grasscutter'
|
||||||
|
from components.java
|
||||||
|
versionMapping {
|
||||||
|
usage('java-api') {
|
||||||
|
fromResolutionOf('runtimeClasspath')
|
||||||
|
}
|
||||||
|
usage('java-runtime') {
|
||||||
|
fromResolutionResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
name = 'Grasscutter'
|
||||||
|
description = 'A server software reimplementation for an anime game.'
|
||||||
|
url = 'https://github.com/Grasscutters/Grasscutter'
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = 'The Apache License, Version 2.0'
|
||||||
|
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = 'melledy'
|
||||||
|
name = 'Melledy'
|
||||||
|
email = 'melledy@xigam.tech' // not a real email kek
|
||||||
|
}
|
||||||
|
developer {
|
||||||
|
id = 'magix'
|
||||||
|
name = 'Magix'
|
||||||
|
email = 'magix@xigam.tech'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = 'scm:git:git@github.com:Grasscutters/Grasscutter.git'
|
||||||
|
developerConnection = 'scm:git:ssh://github.com:Grasscutters/Grasscutter.git'
|
||||||
|
url = 'https://github.com/Grasscutters/Grasscutter'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
// change URLs to point to your repos, e.g. http://my.org/repo
|
||||||
|
def releasesRepoUrl = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/'
|
||||||
|
def snapshotsRepoUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
|
||||||
|
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||||
|
|
||||||
|
name = 'sonatype'
|
||||||
|
credentials(PasswordCredentials)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
sign publishing.publications.mavenJava
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
if(JavaVersion.current().isJava9Compatible()) {
|
||||||
|
options.addBooleanOption('html5', true)
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,7 @@ public final class Grasscutter {
|
|||||||
private static GameServer gameServer;
|
private static GameServer gameServer;
|
||||||
private static PluginManager pluginManager;
|
private static PluginManager pluginManager;
|
||||||
|
|
||||||
public static final Reflections reflector = new Reflections();
|
public static final Reflections reflector = new Reflections("emu.grasscutter");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Declare logback configuration.
|
// Declare logback configuration.
|
||||||
@ -70,13 +70,13 @@ public final class Grasscutter {
|
|||||||
// Database
|
// Database
|
||||||
DatabaseManager.initialize();
|
DatabaseManager.initialize();
|
||||||
|
|
||||||
|
// Create plugin manager instance.
|
||||||
|
pluginManager = new PluginManager();
|
||||||
|
|
||||||
// Create server instances.
|
// Create server instances.
|
||||||
dispatchServer = new DispatchServer();
|
dispatchServer = new DispatchServer();
|
||||||
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
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.start();
|
dispatchServer.start();
|
||||||
|
@ -3,15 +3,10 @@ package emu.grasscutter.command.commands;
|
|||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.command.Command;
|
import emu.grasscutter.command.Command;
|
||||||
import emu.grasscutter.command.CommandHandler;
|
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.GenshinPlayer;
|
||||||
import emu.grasscutter.game.inventory.GenshinItem;
|
|
||||||
import emu.grasscutter.game.inventory.Inventory;
|
import emu.grasscutter.game.inventory.Inventory;
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
import emu.grasscutter.game.inventory.ItemType;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Command(label = "clear", usage = "clear <all|wp|art|mat>", //Merged /clearartifacts and /clearweapons to /clear <args> [uid]
|
@Command(label = "clear", usage = "clear <all|wp|art|mat>", //Merged /clearartifacts and /clearweapons to /clear <args> [uid]
|
||||||
@ -33,62 +28,62 @@ public final class ClearCommand implements CommandHandler {
|
|||||||
try {
|
try {
|
||||||
target = Integer.parseInt(args.get(0));
|
target = Integer.parseInt(args.get(0));
|
||||||
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||||
if (targetPlayer == null && sender != null) {
|
if (targetPlayer == null) {
|
||||||
target = sender.getUid();
|
target = sender.getUid();
|
||||||
} else {
|
} else {
|
||||||
switch (cmdSwitch){
|
switch (cmdSwitch) {
|
||||||
case "wp":
|
case "wp" -> {
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
|
.filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
|
||||||
.filter(item -> !item.isLocked() && !item.isEquipped())
|
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||||
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
||||||
sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " .");
|
sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " .");
|
||||||
break;
|
}
|
||||||
case "art":
|
case "art" -> {
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY)
|
.filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY)
|
||||||
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
||||||
.filter(item -> !item.isLocked() && !item.isEquipped())
|
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||||
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
||||||
sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
|
sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
|
||||||
break;
|
}
|
||||||
case "mat":
|
case "mat" -> {
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item -> item.getItemType() == ItemType.ITEM_MATERIAL)
|
.filter(item -> item.getItemType() == ItemType.ITEM_MATERIAL)
|
||||||
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
.filter(item -> item.getLevel() == 1 && item.getExp() == 0)
|
||||||
.filter(item -> !item.isLocked() && !item.isEquipped())
|
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||||
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
.forEach(item -> playerInventory.removeItem(item, item.getCount()));
|
||||||
sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
|
sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
|
||||||
break;
|
}
|
||||||
case "all":
|
case "all" -> {
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item1 -> item1.getItemType() == ItemType.ITEM_RELIQUARY)
|
.filter(item1 -> item1.getItemType() == ItemType.ITEM_RELIQUARY)
|
||||||
.filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0)
|
.filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0)
|
||||||
.filter(item1 -> !item1.isLocked() && !item1.isEquipped())
|
.filter(item1 -> !item1.isLocked() && !item1.isEquipped())
|
||||||
.forEach(item1 -> playerInventory.removeItem(item1, item1.getCount()));
|
.forEach(item1 -> playerInventory.removeItem(item1, item1.getCount()));
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL)
|
.filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL)
|
||||||
.filter(item2 -> !item2.isLocked() && !item2.isEquipped())
|
.filter(item2 -> !item2.isLocked() && !item2.isEquipped())
|
||||||
.forEach(item2 -> playerInventory.removeItem(item2, item2.getCount()));
|
.forEach(item2 -> playerInventory.removeItem(item2, item2.getCount()));
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON)
|
.filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON)
|
||||||
.filter(item3 -> item3.getLevel() == 1 && item3.getExp() == 0)
|
.filter(item3 -> item3.getLevel() == 1 && item3.getExp() == 0)
|
||||||
.filter(item3 -> !item3.isLocked() && !item3.isEquipped())
|
.filter(item3 -> !item3.isLocked() && !item3.isEquipped())
|
||||||
.forEach(item3 -> playerInventory.removeItem(item3, item3.getCount()));
|
.forEach(item3 -> playerInventory.removeItem(item3, item3.getCount()));
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE)
|
.filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE)
|
||||||
.filter(item4 -> !item4.isLocked() && !item4.isEquipped())
|
.filter(item4 -> !item4.isLocked() && !item4.isEquipped())
|
||||||
.forEach(item4 -> playerInventory.removeItem(item4, item4.getCount()));
|
.forEach(item4 -> playerInventory.removeItem(item4, item4.getCount()));
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY)
|
.filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY)
|
||||||
.filter(item5 -> !item5.isLocked() && !item5.isEquipped())
|
.filter(item5 -> !item5.isLocked() && !item5.isEquipped())
|
||||||
.forEach(item5 -> playerInventory.removeItem(item5, item5.getCount()));
|
.forEach(item5 -> playerInventory.removeItem(item5, item5.getCount()));
|
||||||
playerInventory.getItems().values().stream()
|
playerInventory.getItems().values().stream()
|
||||||
.filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL)
|
.filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL)
|
||||||
.filter(item6 -> !item6.isLocked() && !item6.isEquipped())
|
.filter(item6 -> !item6.isLocked() && !item6.isEquipped())
|
||||||
.forEach(item6 -> playerInventory.removeItem(item6, item6.getCount()));
|
.forEach(item6 -> playerInventory.removeItem(item6, item6.getCount()));
|
||||||
sender.dropMessage("Cleared everything for " + targetPlayer.getNickname() + " .");
|
sender.dropMessage("Cleared everything for " + targetPlayer.getNickname() + " .");
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
|
@ -6,20 +6,19 @@ import emu.grasscutter.game.GenshinPlayer;
|
|||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Command(label = "heal", usage = "heal|h",
|
@Command(label = "heal", usage = "heal|h", aliases = {"h"},
|
||||||
description = "Heal all characters in your current team.", aliases = {"h"}, permission = "player.heal")
|
description = "Heal all characters in your current team.", permission = "player.heal")
|
||||||
public class HealCommand implements CommandHandler {
|
public final class HealCommand implements CommandHandler {
|
||||||
@Override
|
@Override
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
if (sender == null) {
|
if (sender == null) {
|
||||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender.getTeamManager().getActiveTeam().forEach(entity -> {
|
sender.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||||
boolean isAlive = entity.isAlive();
|
boolean isAlive = entity.isAlive();
|
||||||
entity.setFightProperty(
|
entity.setFightProperty(
|
||||||
@ -31,6 +30,6 @@ public class HealCommand implements CommandHandler {
|
|||||||
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
CommandHandler.sendMessage(sender, "All characters are healed.");
|
CommandHandler.sendMessage(sender, "All characters have been healed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Command(label = "list", description = "List online players")
|
@Command(label = "list", description = "List online players")
|
||||||
public class ListCommand implements CommandHandler {
|
public final class ListCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
@ -19,14 +19,10 @@ public class ListCommand implements CommandHandler {
|
|||||||
|
|
||||||
if (playersMap.size() != 0) {
|
if (playersMap.size() != 0) {
|
||||||
StringBuilder playerSet = new StringBuilder();
|
StringBuilder playerSet = new StringBuilder();
|
||||||
|
playersMap.values().forEach(player ->
|
||||||
for (Map.Entry<Integer, GenshinPlayer> entry : playersMap.entrySet()) {
|
playerSet.append(player.getNickname()).append(", "));
|
||||||
playerSet.append(entry.getValue().getNickname());
|
|
||||||
playerSet.append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
String players = playerSet.toString();
|
String players = playerSet.toString();
|
||||||
|
|
||||||
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
|
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ public final class SetFetterLevelCommand implements CommandHandler {
|
|||||||
sender.sendPacket(new PacketAvatarFetterDataNotify(avatar));
|
sender.sendPacket(new PacketAvatarFetterDataNotify(avatar));
|
||||||
CommandHandler.sendMessage(sender, "Fetter level set to " + fetterLevel);
|
CommandHandler.sendMessage(sender, "Fetter level set to " + fetterLevel);
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
CommandHandler.sendMessage(null, "Invalid fetter level.");
|
CommandHandler.sendMessage(sender, "Invalid fetter level.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Command(label = "talent", usage = "talent <talentID> <value>",
|
@Command(label = "talent", usage = "talent <talentID> <value>",
|
||||||
description = "Set talent level for your current active character", permission = "player.settalent")
|
description = "Set talent level for your current active character", permission = "player.settalent")
|
||||||
public class TalentCommand implements CommandHandler {
|
public final class TalentCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Command(label = "teleport", usage = "teleport <x> <y> <z>", aliases = {"tp"},
|
@Command(label = "teleport", usage = "teleport <x> <y> <z>", aliases = {"tp"},
|
||||||
description = "Change the player's position.", permission = "player.teleport")
|
description = "Change the player's position.", permission = "player.teleport")
|
||||||
public class TelePortCommand implements CommandHandler {
|
public final class TeleportCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
public void execute(GenshinPlayer sender, List<String> args) {
|
@ -286,8 +286,6 @@ public class GachaManager {
|
|||||||
this.watchService = FileSystems.getDefault().newWatchService();
|
this.watchService = FileSystems.getDefault().newWatchService();
|
||||||
Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath();
|
Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath();
|
||||||
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
|
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
|
||||||
|
|
||||||
server.OnGameServerTick.register(this);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
|
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
5
src/main/java/emu/grasscutter/plugin/api/Item.java
Normal file
5
src/main/java/emu/grasscutter/plugin/api/Item.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package emu.grasscutter.plugin.api;
|
||||||
|
|
||||||
|
public enum Item {
|
||||||
|
/* TODO: Use handbook to generate an Item enum. */
|
||||||
|
}
|
113
src/main/java/emu/grasscutter/plugin/api/PlayerHook.java
Normal file
113
src/main/java/emu/grasscutter/plugin/api/PlayerHook.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package emu.grasscutter.plugin.api;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.game.avatar.GenshinAvatar;
|
||||||
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
|
import emu.grasscutter.game.props.EnterReason;
|
||||||
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
|
import emu.grasscutter.net.packet.GenshinPacket;
|
||||||
|
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks into the {@link GenshinPlayer} class, adding convenient ways to do certain things.
|
||||||
|
*/
|
||||||
|
public final class PlayerHook {
|
||||||
|
private final GenshinPlayer player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks into the player.
|
||||||
|
* @param player The player to hook into.
|
||||||
|
*/
|
||||||
|
public PlayerHook(GenshinPlayer player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kicks a player from the server.
|
||||||
|
*/
|
||||||
|
public void kick() {
|
||||||
|
this.player.getSession().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a player to another scene.
|
||||||
|
* @param sceneId The scene to send the player to.
|
||||||
|
*/
|
||||||
|
public void changeScenes(int sceneId) {
|
||||||
|
this.player.getWorld().transferPlayerToScene(this.player, sceneId, this.player.getPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts an avatar property notify to all world players.
|
||||||
|
* @param property The property that was updated.
|
||||||
|
*/
|
||||||
|
public void updateFightProperty(FightProperty property) {
|
||||||
|
this.broadcastPacketToWorld(new PacketAvatarFightPropUpdateNotify(this.getCurrentAvatar(), property));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts the packet sent to all world players.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void broadcastPacketToWorld(GenshinPacket packet) {
|
||||||
|
this.player.getWorld().broadcastPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the currently equipped avatar's health.
|
||||||
|
* @param health The health to set the avatar to.
|
||||||
|
*/
|
||||||
|
public void setHealth(float health) {
|
||||||
|
this.getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health);
|
||||||
|
this.updateFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revives the specified avatar.
|
||||||
|
* @param avatar The avatar to revive.
|
||||||
|
*/
|
||||||
|
public void reviveAvatar(GenshinAvatar avatar) {
|
||||||
|
this.broadcastPacketToWorld(new PacketAvatarLifeStateChangeNotify(avatar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleports a player to a position.
|
||||||
|
* This will **not** transfer the player to another scene.
|
||||||
|
* @param position The position to teleport the player to.
|
||||||
|
*/
|
||||||
|
public void teleport(Position position) {
|
||||||
|
this.player.getPos().set(position);
|
||||||
|
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
|
||||||
|
EnterType.EnterJump, EnterReason.TransPoint,
|
||||||
|
this.player.getSceneId(), position
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently selected avatar's max health.
|
||||||
|
* @return The max health as a float.
|
||||||
|
*/
|
||||||
|
public float getMaxHealth() {
|
||||||
|
return this.getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently selected avatar in entity form.
|
||||||
|
* @return The avatar as an {@link EntityAvatar}.
|
||||||
|
*/
|
||||||
|
public EntityAvatar getCurrentAvatarEntity() {
|
||||||
|
return this.player.getTeamManager().getCurrentAvatarEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently selected avatar.
|
||||||
|
* @return The avatar as an {@link GenshinAvatar}.
|
||||||
|
*/
|
||||||
|
public GenshinAvatar getCurrentAvatar() {
|
||||||
|
return this.getCurrentAvatarEntity().getAvatar();
|
||||||
|
}
|
||||||
|
}
|
2
src/main/java/emu/grasscutter/plugin/api/README.md
Normal file
2
src/main/java/emu/grasscutter/plugin/api/README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Grasscutter Plugin API
|
||||||
|
**Warning!** As of now, this is a work in progress and isn't completely documented.
|
41
src/main/java/emu/grasscutter/plugin/api/ServerHook.java
Normal file
41
src/main/java/emu/grasscutter/plugin/api/ServerHook.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package emu.grasscutter.plugin.api;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
|
import emu.grasscutter.server.game.GameServer;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks into the {@link GameServer} class, adding convenient ways to do certain things.
|
||||||
|
*/
|
||||||
|
public final class ServerHook {
|
||||||
|
private static ServerHook instance;
|
||||||
|
private final GameServer server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the server hook instance.
|
||||||
|
* @return A {@link ServerHook} singleton.
|
||||||
|
*/
|
||||||
|
public static ServerHook getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks into a server.
|
||||||
|
* @param server The server to hook into.
|
||||||
|
*/
|
||||||
|
public ServerHook(GameServer server) {
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all online players.
|
||||||
|
* @return Players connected to the server.
|
||||||
|
*/
|
||||||
|
public List<GenshinPlayer> getOnlinePlayers() {
|
||||||
|
return new LinkedList<>(this.server.getPlayers().values());
|
||||||
|
}
|
||||||
|
}
|
@ -103,8 +103,8 @@ public final class DispatchServer {
|
|||||||
byte[] decoded2 = Base64.getDecoder().decode(query_cur_region);
|
byte[] decoded2 = Base64.getDecoder().decode(query_cur_region);
|
||||||
QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2);
|
QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2);
|
||||||
|
|
||||||
List<RegionSimpleInfo> servers = new ArrayList<RegionSimpleInfo>();
|
List<RegionSimpleInfo> servers = new ArrayList<>();
|
||||||
List<String> usedNames = new ArrayList<String>(); // List to check for potential naming conflicts
|
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts
|
||||||
if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in
|
if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in
|
||||||
// hybrid mode
|
// hybrid mode
|
||||||
RegionSimpleInfo server = RegionSimpleInfo.newBuilder()
|
RegionSimpleInfo server = RegionSimpleInfo.newBuilder()
|
||||||
@ -268,7 +268,10 @@ public final class DispatchServer {
|
|||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress()));
|
.info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress()));
|
||||||
|
|
||||||
responseHTML(t, regionListBase64);
|
// Invoke event.
|
||||||
|
QueryAllRegionsEvent event = new QueryAllRegionsEvent(regionListBase64); event.call();
|
||||||
|
// Respond with event result.
|
||||||
|
responseHTML(t, event.getRegionList());
|
||||||
});
|
});
|
||||||
|
|
||||||
for (String regionName : regions.keySet()) {
|
for (String regionName : regions.keySet()) {
|
||||||
|
@ -10,6 +10,10 @@ public abstract class ServerEvent extends Event {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Type getServerType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
DISPATCH,
|
DISPATCH,
|
||||||
GAME
|
GAME
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package emu.grasscutter.server.event.game;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
|
||||||
|
public final class ServerTickEvent extends ServerEvent {
|
||||||
|
public ServerTickEvent() {
|
||||||
|
super(Type.GAME);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.server.event.internal;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
public final class ServerStartEvent extends ServerEvent {
|
||||||
|
private final OffsetDateTime startTime;
|
||||||
|
|
||||||
|
public ServerStartEvent(Type type, OffsetDateTime startTime) {
|
||||||
|
super(type);
|
||||||
|
|
||||||
|
this.startTime = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getStartTime() {
|
||||||
|
return this.startTime;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.server.event.internal;
|
||||||
|
|
||||||
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
public final class ServerStopEvent extends ServerEvent {
|
||||||
|
private final OffsetDateTime stopTime;
|
||||||
|
|
||||||
|
public ServerStopEvent(Type type, OffsetDateTime stopTime) {
|
||||||
|
super(type);
|
||||||
|
|
||||||
|
this.stopTime = stopTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getStopTime() {
|
||||||
|
return this.stopTime;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.server.game;
|
package emu.grasscutter.server.game;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -20,7 +21,10 @@ import emu.grasscutter.game.shop.ShopManager;
|
|||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||||
import emu.grasscutter.netty.MihoyoKcpServer;
|
import emu.grasscutter.netty.MihoyoKcpServer;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import emu.grasscutter.server.event.ServerEvent;
|
||||||
|
import emu.grasscutter.server.event.game.ServerTickEvent;
|
||||||
|
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
||||||
|
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
||||||
|
|
||||||
public final class GameServer extends MihoyoKcpServer {
|
public final class GameServer extends MihoyoKcpServer {
|
||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
@ -36,18 +40,10 @@ public final class GameServer extends MihoyoKcpServer {
|
|||||||
private final MultiplayerManager multiplayerManager;
|
private final MultiplayerManager multiplayerManager;
|
||||||
private final DungeonManager dungeonManager;
|
private final DungeonManager dungeonManager;
|
||||||
private final CommandMap commandMap;
|
private final CommandMap commandMap;
|
||||||
|
|
||||||
public EventBus OnGameServerStartFinish;
|
|
||||||
public EventBus OnGameServerTick;
|
|
||||||
public EventBus OnGameServerStop;
|
|
||||||
|
|
||||||
public GameServer(InetSocketAddress address) {
|
public GameServer(InetSocketAddress address) {
|
||||||
super(address);
|
super(address);
|
||||||
|
|
||||||
OnGameServerStartFinish = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build();
|
|
||||||
OnGameServerTick = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build();
|
|
||||||
OnGameServerStop = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build();
|
|
||||||
|
|
||||||
this.setServerInitializer(new GameServerInitializer(this));
|
this.setServerInitializer(new GameServerInitializer(this));
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
||||||
@ -178,12 +174,8 @@ public final class GameServer extends MihoyoKcpServer {
|
|||||||
|
|
||||||
world.onTick();
|
world.onTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GenshinPlayer player : this.getPlayers().values()) {
|
ServerTickEvent event = new ServerTickEvent(); event.call();
|
||||||
player.onTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
OnGameServerTick.post(new GameServerTickEvent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerWorld(World world) {
|
public void registerWorld(World world) {
|
||||||
@ -198,12 +190,11 @@ public final class GameServer extends MihoyoKcpServer {
|
|||||||
@Override
|
@Override
|
||||||
public void onStartFinish() {
|
public void onStartFinish() {
|
||||||
Grasscutter.getLogger().info("Game Server started on port " + address.getPort());
|
Grasscutter.getLogger().info("Game Server started on port " + address.getPort());
|
||||||
|
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
||||||
OnGameServerStartFinish.post(new GameServerStartFinishEvent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServerShutdown() {
|
public void onServerShutdown() {
|
||||||
OnGameServerStop.post(new GameServerStopEvent());
|
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
||||||
|
|
||||||
// Kick and save all players
|
// Kick and save all players
|
||||||
List<GenshinPlayer> list = new ArrayList<>(this.getPlayers().size());
|
List<GenshinPlayer> list = new ArrayList<>(this.getPlayers().size());
|
||||||
|
Loading…
Reference in New Issue
Block a user