mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-23 22:54:29 +00:00
Implement a plugin API for developers
Add a developer-friendly API to Grasscutter
This commit is contained in:
commit
0e55a0ce0a
@ -37,7 +37,7 @@ sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
group = 'tech.xigam'
|
||||
version = '1.0.0-dev'
|
||||
version = '1.0.2-dev'
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
|
@ -8,13 +8,14 @@ import java.util.List;
|
||||
|
||||
@Command(label = "coop", usage = "coop",
|
||||
description = "Forces someone to join the world of others", permission = "server.coop")
|
||||
public class CoopCommand implements CommandHandler {
|
||||
public final class CoopCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendMessage(sender, "Usage: coop <playerId> <target playerId>");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int tid = Integer.parseInt(args.get(0));
|
||||
int hostId = Integer.parseInt(args.get(1));
|
||||
|
@ -15,7 +15,7 @@ import java.util.*;
|
||||
|
||||
@Command(label = "giveall", usage = "giveall [player] [amount]",
|
||||
description = "Gives all items", aliases = {"givea"}, permission = "player.giveall", threading = true)
|
||||
public class GiveAllCommand implements CommandHandler {
|
||||
public final class GiveAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, List<String> args) {
|
||||
@ -142,16 +142,11 @@ public class GiveAllCommand implements CommandHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (testItemsList.contains(itemId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return testItemsList.contains(itemId);
|
||||
}
|
||||
|
||||
static class Range {
|
||||
private int min;
|
||||
private int max;
|
||||
private final int min, max;
|
||||
|
||||
public Range(int min, int max) {
|
||||
if(min > max){
|
||||
@ -159,6 +154,7 @@ public class GiveAllCommand implements CommandHandler {
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
@ -6,24 +6,20 @@ import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@Command(label = "sendmail", usage = "sendmail <userId|all|help> [templateId]",
|
||||
description = "Sends mail to the specified user. The usage of this command changes based on it's composition state.", permission = "server.sendmail")
|
||||
public class SendMailCommand implements CommandHandler {
|
||||
public final class SendMailCommand implements CommandHandler {
|
||||
|
||||
// TODO: You should be able to do /sendmail and then just send subsequent messages until you finish
|
||||
// However, due to the current nature of the command system, I don't think this is possible without rewriting
|
||||
// the command system (again). For now this will do
|
||||
|
||||
// Key = User that is constructing the mail.
|
||||
private static HashMap<Integer, MailBuilder> mailBeingConstructed = new HashMap<Integer, MailBuilder>();
|
||||
private static final HashMap<Integer, MailBuilder> mailBeingConstructed = new HashMap<Integer, MailBuilder>();
|
||||
|
||||
// Yes this is awful and I hate it.
|
||||
@Override
|
||||
@ -48,7 +44,6 @@ public class SendMailCommand implements CommandHandler {
|
||||
default -> {
|
||||
if (DatabaseHelper.getPlayerById(Integer.parseInt(args.get(0))) != null) {
|
||||
mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail());
|
||||
break;
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, "The user with an id of '" + args.get(0) + "' does not exist");
|
||||
return;
|
||||
@ -73,7 +68,7 @@ public class SendMailCommand implements CommandHandler {
|
||||
}
|
||||
case "finish" -> {
|
||||
if (mailBuilder.constructionStage == 3) {
|
||||
if (mailBuilder.sendToAll == false) {
|
||||
if (!mailBuilder.sendToAll) {
|
||||
Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail);
|
||||
CommandHandler.sendMessage(sender, "Message sent to user " + mailBuilder.recipient + "!");
|
||||
} else {
|
||||
|
@ -9,23 +9,25 @@ import java.util.List;
|
||||
|
||||
@Command(label = "tpall", usage = "tpall",
|
||||
description = "Teleports all players in your world to your position", permission = "player.tpall")
|
||||
public class TpallCommand implements CommandHandler {
|
||||
public final class TeleportAllCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sender.getWorld().isMultiplayer()) {
|
||||
CommandHandler.sendMessage(sender, "You only can use this command in MP mode.");
|
||||
return;
|
||||
}
|
||||
for (Player gp : sender.getWorld().getPlayers()) {
|
||||
if (gp.equals(sender))
|
||||
|
||||
for (Player player : sender.getWorld().getPlayers()) {
|
||||
if (player.equals(sender))
|
||||
continue;
|
||||
Position pos = sender.getPos();
|
||||
|
||||
gp.getWorld().transferPlayerToScene(gp, sender.getSceneId(), pos);
|
||||
player.getWorld().transferPlayerToScene(player, sender.getSceneId(), pos);
|
||||
}
|
||||
}
|
||||
}
|
@ -37,6 +37,9 @@ import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
|
||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||
import emu.grasscutter.net.proto.SocialShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.server.event.player.PlayerJoinEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerQuitEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerReceiveMailEvent;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
@ -725,9 +728,13 @@ public class Player {
|
||||
public List<Mail> getAllMail() { return this.mail; }
|
||||
|
||||
public void sendMail(Mail message) {
|
||||
// Call mail receive event.
|
||||
PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this, message); event.call();
|
||||
if(event.isCanceled()) return; message = event.getMessage();
|
||||
|
||||
this.mail.add(message);
|
||||
this.save();
|
||||
Grasscutter.getLogger().info("Mail sent to user [" + this.getUid() + ":" + this.getNickname() + "]!");
|
||||
Grasscutter.getLogger().debug("Mail sent to user [" + this.getUid() + ":" + this.getNickname() + "]!");
|
||||
if(this.isOnline()) {
|
||||
this.sendPacket(new PacketMailChangeNotify(this, message));
|
||||
} // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline
|
||||
@ -1037,6 +1044,11 @@ public class Player {
|
||||
|
||||
// First notify packets sent
|
||||
this.setHasSentAvatarDataNotify(true);
|
||||
|
||||
// Call join event.
|
||||
PlayerJoinEvent event = new PlayerJoinEvent(this); event.call();
|
||||
if(event.isCanceled()) // If event is not cancelled, continue.
|
||||
session.close();
|
||||
}
|
||||
|
||||
public void onLogout() {
|
||||
@ -1055,6 +1067,9 @@ public class Player {
|
||||
this.save();
|
||||
this.getTeamManager().saveAvatars();
|
||||
this.getFriendsList().save();
|
||||
|
||||
// Call quit event.
|
||||
PlayerQuitEvent event = new PlayerQuitEvent(this); event.call();
|
||||
}
|
||||
|
||||
public enum SceneLoadState {
|
||||
|
@ -1,13 +1,22 @@
|
||||
package emu.grasscutter.plugin;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.plugin.api.ServerHook;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
/**
|
||||
* The base class for all plugins to extend.
|
||||
*/
|
||||
public abstract class Plugin {
|
||||
private final ServerHook server = ServerHook.getInstance();
|
||||
|
||||
private PluginIdentifier identifier;
|
||||
private URLClassLoader classLoader;
|
||||
private File dataFolder;
|
||||
|
||||
/**
|
||||
* This method is reflected into.
|
||||
@ -15,10 +24,20 @@ public abstract class Plugin {
|
||||
* 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.");
|
||||
private void initializePlugin(PluginIdentifier identifier, URLClassLoader classLoader) {
|
||||
if(this.identifier != null) {
|
||||
Grasscutter.getLogger().warn(this.identifier.name + " had a reinitialization attempt.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.identifier = identifier;
|
||||
this.classLoader = classLoader;
|
||||
this.dataFolder = new File(Grasscutter.getConfig().PLUGINS_FOLDER, identifier.name);
|
||||
|
||||
if(!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
|
||||
Grasscutter.getLogger().warn("Failed to create plugin data folder for " + this.identifier.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +74,32 @@ public abstract class Plugin {
|
||||
* @return A server instance.
|
||||
*/
|
||||
public final GameServer getServer() {
|
||||
return Grasscutter.getGameServer();
|
||||
return this.server.getGameServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream for a resource in the JAR file.
|
||||
* @param resourceName The name of the resource.
|
||||
* @return An input stream.
|
||||
*/
|
||||
public final InputStream getResource(String resourceName) {
|
||||
return this.classLoader.getResourceAsStream(resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a directory where plugins can store data files.
|
||||
* @return A directory on the file system.
|
||||
*/
|
||||
public final File getDataFolder() {
|
||||
return this.dataFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server hook.
|
||||
* @return A server hook singleton.
|
||||
*/
|
||||
public final ServerHook getHandle() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
/* Called when the plugin is first loaded. */
|
||||
|
@ -3,9 +3,9 @@ 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.server.event.HandlerPriority;
|
||||
import emu.grasscutter.utils.EventConsumer;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
@ -21,7 +21,7 @@ import java.util.jar.JarFile;
|
||||
*/
|
||||
public final class PluginManager {
|
||||
private final Map<String, Plugin> plugins = new HashMap<>();
|
||||
private final Map<Plugin, List<Listener>> listeners = new HashMap<>();
|
||||
private final List<EventHandler<? extends Event>> listeners = new LinkedList<>();
|
||||
|
||||
public PluginManager() {
|
||||
this.loadPlugins(); // Load all plugins from the plugins directory.
|
||||
@ -68,12 +68,12 @@ public final class PluginManager {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if(entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().contains("module-info")) continue;
|
||||
String className = entry.getName().replace(".class", "").replace("/", ".");
|
||||
Class<?> clazz = loader.loadClass(className);
|
||||
loader.loadClass(className);
|
||||
}
|
||||
|
||||
Class<?> pluginClass = loader.loadClass(pluginConfig.mainClass);
|
||||
Plugin pluginInstance = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
|
||||
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig));
|
||||
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig), loader);
|
||||
|
||||
fileReader.close(); // Close the file reader.
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
@ -89,14 +89,14 @@ public final class PluginManager {
|
||||
* Load the specified plugin.
|
||||
* @param plugin The plugin instance.
|
||||
*/
|
||||
private void loadPlugin(Plugin plugin, PluginIdentifier identifier) {
|
||||
private void loadPlugin(Plugin plugin, PluginIdentifier identifier, URLClassLoader classLoader) {
|
||||
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);
|
||||
Method method = pluginClass.getDeclaredMethod("initializePlugin", PluginIdentifier.class, URLClassLoader.class);
|
||||
method.setAccessible(true); method.invoke(plugin, identifier, classLoader); method.setAccessible(false);
|
||||
} catch (Exception ignored) {
|
||||
Grasscutter.getLogger().warn("Failed to add plugin identifier: " + identifier.name);
|
||||
}
|
||||
@ -129,11 +129,10 @@ public final class PluginManager {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
public void registerListener(EventHandler<? extends Event> listener) {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,23 +140,31 @@ public final class PluginManager {
|
||||
* @param event The event to invoke.
|
||||
*/
|
||||
public void invokeEvent(Event event) {
|
||||
this.listeners.values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(listener -> this.invokeOnListener(listener, event));
|
||||
EnumSet.allOf(HandlerPriority.class)
|
||||
.forEach(priority -> this.checkAndFilter(event, priority));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to invoke the event on the provided listener.
|
||||
* Check an event to handlers for the priority.
|
||||
* @param event The event being called.
|
||||
* @param priority The priority to call for.
|
||||
*/
|
||||
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) { }
|
||||
private void checkAndFilter(Event event, HandlerPriority priority) {
|
||||
this.listeners.stream()
|
||||
.filter(handler -> handler.handles().isInstance(event))
|
||||
.filter(handler -> handler.getPriority() == priority)
|
||||
.toList().forEach(handler -> this.invokeHandler(event, handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs logic checks then invokes the provided event handler.
|
||||
* @param event The event passed through to the handler.
|
||||
* @param handler The handler to invoke.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Event> void invokeHandler(Event event, EventHandler<T> handler) {
|
||||
if(!event.isCanceled() ||
|
||||
(event.isCanceled() && handler.ignoresCanceled())
|
||||
) handler.getCallback().consume((T) event);
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package emu.grasscutter.plugin.api;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.dispatch.DispatchServer;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@ -11,7 +14,8 @@ import java.util.List;
|
||||
*/
|
||||
public final class ServerHook {
|
||||
private static ServerHook instance;
|
||||
private final GameServer server;
|
||||
private final GameServer gameServer;
|
||||
private final DispatchServer dispatchServer;
|
||||
|
||||
/**
|
||||
* Gets the server hook instance.
|
||||
@ -23,19 +27,47 @@ public final class ServerHook {
|
||||
|
||||
/**
|
||||
* Hooks into a server.
|
||||
* @param server The server to hook into.
|
||||
* @param gameServer The game server to hook into.
|
||||
* @param dispatchServer The dispatch server to hook into.
|
||||
*/
|
||||
public ServerHook(GameServer server) {
|
||||
this.server = server;
|
||||
|
||||
public ServerHook(GameServer gameServer, DispatchServer dispatchServer) {
|
||||
this.gameServer = gameServer;
|
||||
this.dispatchServer = dispatchServer;
|
||||
|
||||
instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The game server.
|
||||
*/
|
||||
public GameServer getGameServer() {
|
||||
return this.gameServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The dispatch server.
|
||||
*/
|
||||
public DispatchServer getDispatchServer() {
|
||||
return this.dispatchServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all online players.
|
||||
* @return Players connected to the server.
|
||||
*/
|
||||
public List<Player> getOnlinePlayers() {
|
||||
return new LinkedList<>(this.server.getPlayers().values());
|
||||
return new LinkedList<>(this.gameServer.getPlayers().values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command to the {@link emu.grasscutter.command.CommandMap}.
|
||||
* @param handler The command handler.
|
||||
*/
|
||||
public void registerCommand(CommandHandler handler) {
|
||||
Class<? extends CommandHandler> clazz = handler.getClass();
|
||||
if(!clazz.isAnnotationPresent(Command.class))
|
||||
throw new IllegalArgumentException("Command handler must be annotated with @Command.");
|
||||
Command commandData = clazz.getAnnotation(Command.class);
|
||||
this.gameServer.getCommandMap().registerCommand(commandData.label(), handler);
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ import java.net.BindException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyStore;
|
||||
import java.util.*;
|
||||
|
||||
@ -209,7 +210,7 @@ public final class DispatchServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private KeyManagerFactory createKeyManagerFactory(File keystore, String password) throws Exception {
|
||||
private KeyManagerFactory createKeyManagerFactory(File keystore, String password) {
|
||||
char[] pass = password.toCharArray();
|
||||
KeyManagerFactory kmf = null;
|
||||
|
||||
@ -220,8 +221,8 @@ public final class DispatchServer {
|
||||
|
||||
kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ks, pass);
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
} catch (Exception exception) {
|
||||
Grasscutter.getLogger().error("Unable to load keystore.", exception);
|
||||
}
|
||||
|
||||
return kmf;
|
||||
@ -243,10 +244,9 @@ public final class DispatchServer {
|
||||
try {
|
||||
kmf = createKeyManagerFactory(keystoreFile, "123456");
|
||||
Grasscutter.getLogger().warn(
|
||||
"[Dispatch] The default keystore password was loaded successfully. Please consider setting the password to 123456 in config.json.");
|
||||
"[Dispatch] The default keystore password was loaded successfully. Please consider setting the password to '123456' in config.json.");
|
||||
} catch (Exception e2) {
|
||||
Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!");
|
||||
e2.printStackTrace();
|
||||
Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!", e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -257,7 +257,7 @@ public final class DispatchServer {
|
||||
server = this.safelyCreateServer(this.getAddress());
|
||||
}
|
||||
|
||||
HttpsServer httpsServer = null;
|
||||
HttpsServer httpsServer;
|
||||
|
||||
try {
|
||||
httpsServer = HttpsServer.create(getAddress(), 0);
|
||||
@ -339,10 +339,6 @@ public final class DispatchServer {
|
||||
// added.
|
||||
account = DatabaseHelper.createAccountWithId(requestData.account, 0);
|
||||
|
||||
for (String permission : Grasscutter.getConfig().getDispatchOptions().defaultPermissions) {
|
||||
account.addPermission(permission);
|
||||
}
|
||||
|
||||
if (account != null) {
|
||||
responseData.message = "OK";
|
||||
responseData.data.account.uid = account.getId();
|
||||
@ -352,6 +348,9 @@ public final class DispatchServer {
|
||||
Grasscutter.getLogger()
|
||||
.info(String.format("[Dispatch] Client %s failed to log in: Account %s created",
|
||||
t.getRemoteAddress(), responseData.data.account.uid));
|
||||
for (String permission : Grasscutter.getConfig().getDispatchOptions().defaultPermissions) {
|
||||
account.addPermission(permission);
|
||||
}
|
||||
} else {
|
||||
responseData.retcode = -201;
|
||||
responseData.message = "Username not found, create failed.";
|
||||
@ -575,15 +574,11 @@ public final class DispatchServer {
|
||||
|
||||
if (next > last) {
|
||||
int eqPos = qs.indexOf('=', last);
|
||||
try {
|
||||
if (eqPos < 0 || eqPos > next) {
|
||||
result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), "");
|
||||
} else {
|
||||
result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"),
|
||||
URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java
|
||||
if (eqPos < 0 || eqPos > next) {
|
||||
result.put(URLDecoder.decode(qs.substring(last, next), StandardCharsets.UTF_8), "");
|
||||
} else {
|
||||
result.put(URLDecoder.decode(qs.substring(last, eqPos), StandardCharsets.UTF_8),
|
||||
URLDecoder.decode(qs.substring(eqPos + 1, next), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
last = next + 1;
|
||||
|
@ -1,11 +1,81 @@
|
||||
package emu.grasscutter.server.event;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.utils.EventConsumer;
|
||||
|
||||
/**
|
||||
* Declares a class as an event listener/handler.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EventHandler {
|
||||
public final class EventHandler<T extends Event> {
|
||||
private final Class<T> eventClass;
|
||||
private EventConsumer<T> listener;
|
||||
private HandlerPriority priority;
|
||||
private boolean handleCanceled;
|
||||
|
||||
public EventHandler(Class<T> eventClass) {
|
||||
this.eventClass = eventClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets which event this handler is handling.
|
||||
* @return An event class.
|
||||
*/
|
||||
public Class<T> handles() {
|
||||
return this.eventClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback for the handler.
|
||||
* @return A consumer callback.
|
||||
*/
|
||||
public EventConsumer<T> getCallback() {
|
||||
return this.listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handler's priority.
|
||||
* @return The priority of the handler.
|
||||
*/
|
||||
public HandlerPriority getPriority() {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the handler will ignore cancelled events.
|
||||
* @return The ignore cancelled state.
|
||||
*/
|
||||
public boolean ignoresCanceled() {
|
||||
return this.handleCanceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback method for when the event is invoked.
|
||||
* @param listener An event handler method.
|
||||
* @return Method chaining.
|
||||
*/
|
||||
public EventHandler<T> listener(EventConsumer<T> listener) {
|
||||
this.listener = listener; return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the handler's priority in handling events.
|
||||
* @param priority The priority of the handler.
|
||||
* @return Method chaining.
|
||||
*/
|
||||
public EventHandler<T> priority(HandlerPriority priority) {
|
||||
this.priority = priority; return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the handler will ignore cancelled events.
|
||||
* @param ignore If the handler should ignore cancelled events.
|
||||
* @return Method chaining.
|
||||
*/
|
||||
public EventHandler<T> ignore(boolean ignore) {
|
||||
this.handleCanceled = ignore; return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the handler into the PluginManager.
|
||||
*/
|
||||
public void register() {
|
||||
Grasscutter.getPluginManager().registerListener(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package emu.grasscutter.server.event;
|
||||
|
||||
public enum HandlerPriority {
|
||||
/**
|
||||
* The handler will be called before every other handler.
|
||||
*/
|
||||
HIGH,
|
||||
|
||||
/**
|
||||
* The handler will be called the same time as other handlers.
|
||||
*/
|
||||
NORMAL,
|
||||
|
||||
/**
|
||||
* The handler will be called after every other handler.
|
||||
*/
|
||||
LOW
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package emu.grasscutter.server.event;
|
||||
|
||||
/**
|
||||
* Implementing this interface declares a class as an event listener.
|
||||
*/
|
||||
public interface Listener {
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.server.event.dispatch;
|
||||
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
|
||||
public final class QueryAllRegionsEvent extends ServerEvent {
|
||||
private String regionList;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.server.event.dispatch;
|
||||
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
|
||||
public final class QueryCurrentRegionEvent extends ServerEvent {
|
||||
private String regionInfo;
|
||||
|
@ -0,0 +1,27 @@
|
||||
package emu.grasscutter.server.event.game;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.types.GameEvent;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
|
||||
public final class PlayerCreationEvent extends GameEvent {
|
||||
private final GameSession session;
|
||||
private Class<? extends Player> playerClass;
|
||||
|
||||
public PlayerCreationEvent(GameSession session, Class<? extends Player> playerClass) {
|
||||
this.session = session;
|
||||
this.playerClass = playerClass;
|
||||
}
|
||||
|
||||
public GameSession getSession() {
|
||||
return this.session;
|
||||
}
|
||||
|
||||
public void setPlayerClass(Class<? extends Player> playerClass) {
|
||||
this.playerClass = playerClass;
|
||||
}
|
||||
|
||||
public Class<? extends Player> getPlayerClass() {
|
||||
return this.playerClass;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.server.event.game;
|
||||
|
||||
import emu.grasscutter.server.event.Cancellable;
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
|
||||
public final class ReceivePacketEvent extends ServerEvent implements Cancellable {
|
||||
|
@ -2,7 +2,7 @@ package emu.grasscutter.server.event.game;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.server.event.Cancellable;
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
|
||||
public final class SendPacketEvent extends ServerEvent implements Cancellable {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.server.event.game;
|
||||
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
|
||||
public final class ServerTickEvent extends ServerEvent {
|
||||
public ServerTickEvent() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.server.event.internal;
|
||||
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.server.event.internal;
|
||||
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
package emu.grasscutter.server.event.player;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.Cancellable;
|
||||
import emu.grasscutter.server.event.types.PlayerEvent;
|
||||
|
||||
public final class PlayerJoinEvent extends PlayerEvent implements Cancellable {
|
||||
public PlayerJoinEvent(Player player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package emu.grasscutter.server.event.player;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.types.GameEvent;
|
||||
import emu.grasscutter.server.event.types.PlayerEvent;
|
||||
|
||||
public final class PlayerQuitEvent extends PlayerEvent {
|
||||
public PlayerQuitEvent(Player player) {
|
||||
super(player);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.server.event.player;
|
||||
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.Cancellable;
|
||||
import emu.grasscutter.server.event.types.PlayerEvent;
|
||||
|
||||
public final class PlayerReceiveMailEvent extends PlayerEvent implements Cancellable {
|
||||
private Mail message;
|
||||
|
||||
public PlayerReceiveMailEvent(Player player, Mail message) {
|
||||
super(player);
|
||||
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void setMessage(Mail message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Mail getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package emu.grasscutter.server.event.types;
|
||||
|
||||
import emu.grasscutter.server.event.Event;
|
||||
|
||||
/**
|
||||
* An event that is related to the game.
|
||||
*/
|
||||
public abstract class GameEvent extends Event {
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package emu.grasscutter.server.event.types;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.Event;
|
||||
|
||||
/**
|
||||
* An event that is related to player interactions.
|
||||
*/
|
||||
public abstract class PlayerEvent extends Event {
|
||||
protected final Player player;
|
||||
|
||||
public PlayerEvent(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
package emu.grasscutter.server.event;
|
||||
package emu.grasscutter.server.event.types;
|
||||
|
||||
import emu.grasscutter.server.event.Event;
|
||||
|
||||
/**
|
||||
* An event that is related to the internals of the server.
|
@ -17,7 +17,7 @@ import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||
import emu.grasscutter.netty.KcpServer;
|
||||
import emu.grasscutter.server.event.ServerEvent;
|
||||
import emu.grasscutter.server.event.types.ServerEvent;
|
||||
import emu.grasscutter.server.event.game.ServerTickEvent;
|
||||
import emu.grasscutter.server.event.internal.ServerStartEvent;
|
||||
import emu.grasscutter.server.event.internal.ServerStopEvent;
|
||||
|
@ -13,6 +13,7 @@ import emu.grasscutter.net.packet.Opcodes;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.SetPlayerBornDataReqOuterClass.SetPlayerBornDataReq;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.server.event.game.PlayerCreationEvent;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.game.GameSession.SessionState;
|
||||
|
||||
@ -27,7 +28,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler {
|
||||
|
||||
// Sanity checks
|
||||
int avatarId = req.getAvatarId();
|
||||
int startingSkillDepot = 0;
|
||||
int startingSkillDepot;
|
||||
if (avatarId == GameConstants.MAIN_CHARACTER_MALE) {
|
||||
startingSkillDepot = 504;
|
||||
} else if (avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
@ -41,8 +42,10 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler {
|
||||
nickname = "Traveler";
|
||||
}
|
||||
|
||||
// Create character
|
||||
Player player = new Player(session);
|
||||
// Call creation event.
|
||||
PlayerCreationEvent event = new PlayerCreationEvent(session, Player.class); event.call();
|
||||
// Create player instance from event.
|
||||
Player player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session);
|
||||
player.setNickname(nickname);
|
||||
|
||||
try {
|
||||
@ -92,5 +95,4 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,24 +3,12 @@ package emu.grasscutter.task;
|
||||
import org.quartz.*;
|
||||
|
||||
@PersistJobDataAfterExecution
|
||||
public class TaskHandler implements Job {
|
||||
|
||||
public abstract class TaskHandler implements Job {
|
||||
public void restartExecute() throws JobExecutionException {
|
||||
execute(null);
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
public abstract void onEnable();
|
||||
|
||||
public abstract void onDisable();
|
||||
}
|
||||
|
@ -4,22 +4,21 @@ import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.task.Task;
|
||||
import emu.grasscutter.task.TaskHandler;
|
||||
|
||||
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
@Task(taskName = "MoonCard", taskCronExpression = "0 0 0 * * ?", triggerName = "MoonCardTrigger")
|
||||
// taskCronExpression: Fixed time period: 0:0:0 every day (twenty-four hour system)
|
||||
public class MoonCard extends TaskHandler {
|
||||
public final class MoonCard extends TaskHandler {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Grasscutter.getLogger().info("[Task] MoonCard task enabled.");
|
||||
Grasscutter.getLogger().debug("[Task] MoonCard task enabled.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
Grasscutter.getLogger().info("[Task] MoonCard task disabled.");
|
||||
Grasscutter.getLogger().debug("[Task] MoonCard task disabled.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
7
src/main/java/emu/grasscutter/utils/EventConsumer.java
Normal file
7
src/main/java/emu/grasscutter/utils/EventConsumer.java
Normal file
@ -0,0 +1,7 @@
|
||||
package emu.grasscutter.utils;
|
||||
|
||||
import emu.grasscutter.server.event.Event;
|
||||
|
||||
public interface EventConsumer<T extends Event> {
|
||||
void consume(T event);
|
||||
}
|
Loading…
Reference in New Issue
Block a user