mirror of
https://github.com/songoda/SongodaCore.git
synced 2024-12-26 02:17:40 +01:00
add popup notifications, add client version detection
This commit is contained in:
parent
ff3443043b
commit
efd48ede96
@ -6,6 +6,7 @@ import com.songoda.core.core.PluginInfoModule;
|
||||
import com.songoda.core.core.SongodaCoreCommand;
|
||||
import com.songoda.core.core.SongodaCoreDiagCommand;
|
||||
import com.songoda.core.commands.CommandManager;
|
||||
import com.songoda.core.compatibility.ClientVersion;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import java.io.IOException;
|
||||
@ -25,7 +26,9 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.json.simple.JSONObject;
|
||||
@ -44,7 +47,12 @@ public class SongodaCore {
|
||||
private JavaPlugin piggybackedPlugin;
|
||||
private final CommandManager commandManager;
|
||||
private final EventListener loginListener = new EventListener();
|
||||
private final HashMap<UUID, Long> lastCheck = new HashMap();
|
||||
private final ShadedEventListener shadingListener = new ShadedEventListener();
|
||||
|
||||
public static boolean hasShading() {
|
||||
// sneaky hack to check the package name since maven tries to re-shade all references to the package string
|
||||
return !SongodaCore.class.getPackage().getName().equals(new String(new char[]{'c','o','m','.','s','o','n','g','o','d','a','.','c','o','r','e'}));
|
||||
}
|
||||
|
||||
public static void registerPlugin(JavaPlugin plugin, int pluginID, LegacyMaterials icon) {
|
||||
registerPlugin(plugin, pluginID, icon == null ? "STONE" : icon.name());
|
||||
@ -58,9 +66,12 @@ public class SongodaCore {
|
||||
try {
|
||||
// use the active service
|
||||
clazz.getMethod("registerPlugin", JavaPlugin.class, int.class, String.class).invoke(null, plugin, pluginID, icon);
|
||||
|
||||
if(hasShading()) {
|
||||
Bukkit.getPluginManager().registerEvents(new ShadedEventListener(), plugin);
|
||||
}
|
||||
return;
|
||||
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -77,6 +88,7 @@ public class SongodaCore {
|
||||
commandManager.registerCommandDynamically(new SongodaCoreCommand(this))
|
||||
.addSubCommand(new SongodaCoreDiagCommand(this));
|
||||
Bukkit.getPluginManager().registerEvents(loginListener, javaPlugin);
|
||||
Bukkit.getPluginManager().registerEvents(shadingListener, javaPlugin);
|
||||
// we aggressevely want to own this command
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(javaPlugin, ()->{CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);}, 20 * 60 * 1);
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(javaPlugin, ()->{CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);}, 20 * 60 * 2);
|
||||
@ -153,11 +165,52 @@ public class SongodaCore {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private class EventListener implements Listener {
|
||||
private static class ShadedEventListener implements Listener {
|
||||
boolean via = false;
|
||||
boolean proto = false;
|
||||
|
||||
ShadedEventListener() {
|
||||
if ((via = Bukkit.getPluginManager().isPluginEnabled("ViaVersion"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginVia(p));
|
||||
} else if ((proto = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginProtocol(p));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onLogin(PlayerLoginEvent event) {
|
||||
if (via) {
|
||||
ClientVersion.onLoginVia(event.getPlayer());
|
||||
} else if (proto) {
|
||||
ClientVersion.onLoginProtocol(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onLogout(PlayerQuitEvent event) {
|
||||
if (via) {
|
||||
ClientVersion.onLogout(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onEnable(PluginEnableEvent event) {
|
||||
// technically shouldn't have online players here, but idk
|
||||
if (!via && (via = event.getPlugin().getName().equals("ViaVersion"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginVia(p));
|
||||
} else if (!proto && (proto = event.getPlugin().getName().equals("ProtocolSupport"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginProtocol(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EventListener implements Listener {
|
||||
final HashMap<UUID, Long> lastCheck = new HashMap();
|
||||
|
||||
@EventHandler
|
||||
void onLogin(PlayerLoginEvent event) {
|
||||
// don't spam players with update checks
|
||||
final Player player = event.getPlayer();
|
||||
// don't spam players with update checks
|
||||
long now = System.currentTimeMillis();
|
||||
Long last = lastCheck.get(player.getUniqueId());
|
||||
if(last != null && now - 10000 < last) return;
|
||||
@ -176,18 +229,19 @@ public class SongodaCore {
|
||||
void onDisable(PluginDisableEvent event) {
|
||||
// don't track disabled plugins
|
||||
PluginInfo pi = registeredPlugins.stream().filter(p -> event.getPlugin() == p.getJavaPlugin()).findFirst().orElse(null);
|
||||
if(pi != null) {
|
||||
if (pi != null) {
|
||||
registeredPlugins.remove(pi);
|
||||
}
|
||||
if(event.getPlugin() == piggybackedPlugin) {
|
||||
if (event.getPlugin() == piggybackedPlugin) {
|
||||
// uh-oh! Abandon ship!!
|
||||
Bukkit.getServicesManager().unregisterAll(piggybackedPlugin);
|
||||
// can we move somewhere else?
|
||||
if((pi = registeredPlugins.stream().findFirst().orElse(null)) != null) {
|
||||
if ((pi = registeredPlugins.stream().findFirst().orElse(null)) != null) {
|
||||
// move ourselves to this plugin
|
||||
piggybackedPlugin = pi.getJavaPlugin();
|
||||
Bukkit.getServicesManager().register(SongodaCore.class, INSTANCE, piggybackedPlugin, ServicePriority.Normal);
|
||||
Bukkit.getPluginManager().registerEvents(loginListener, piggybackedPlugin);
|
||||
Bukkit.getPluginManager().registerEvents(shadingListener, piggybackedPlugin);
|
||||
CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);
|
||||
}
|
||||
}
|
||||
|
103
src/main/java/com/songoda/core/compatibility/ClientVersion.java
Normal file
103
src/main/java/com/songoda/core/compatibility/ClientVersion.java
Normal file
@ -0,0 +1,103 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import com.songoda.core.SongodaCore;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Handy reference for checking a connected client's Minecraft version<br>
|
||||
* NOTE: this is automatically initialized through SongodaCore
|
||||
*/
|
||||
public class ClientVersion {
|
||||
|
||||
static HashMap<UUID, ServerVersion> players = new HashMap();
|
||||
|
||||
/**
|
||||
* Check to see what client version this player is connected to the server
|
||||
* with. Note that if a player is connecting with a newer client than the server,
|
||||
* this value will simply be the server version.
|
||||
*
|
||||
* @param player Player to check
|
||||
* @return ServerVersion that matches this player's Minecraft version
|
||||
*/
|
||||
public static ServerVersion getClientVersion(Player player) {
|
||||
if (player == null || !players.containsKey(player.getUniqueId())) {
|
||||
return ServerVersion.getServerVersion();
|
||||
}
|
||||
return players.get(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Not Use: This is handled by SongodaCore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void onLoginProtocol(Player p) {
|
||||
Bukkit.getScheduler().runTaskLater(SongodaCore.getHijackedPlugin(), ()-> {
|
||||
if(p.isOnline()) {
|
||||
final int version = protocolsupport.api.ProtocolSupportAPI.getProtocolVersion(p).getId();
|
||||
System.out.println("ProtoLogin" + version);
|
||||
players.put(p.getUniqueId(), protocolToVersion(version));
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Not Use: This is handled by SongodaCore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void onLoginVia(Player p) {
|
||||
Bukkit.getScheduler().runTaskLater(SongodaCore.getHijackedPlugin(), ()-> {
|
||||
if(p.isOnline()) {
|
||||
final int version = us.myles.ViaVersion.api.Via.getAPI().getPlayerVersion(p.getUniqueId());
|
||||
System.out.println("ViaLogin" + version);
|
||||
players.put(p.getUniqueId(), protocolToVersion(version));
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
private static ServerVersion protocolToVersion(int version) {
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/master/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolVersion.java
|
||||
// https://github.com/ProtocolSupport/ProtocolSupport/blob/master/src/protocolsupport/api/ProtocolVersion.java
|
||||
switch (version) {
|
||||
case 4:
|
||||
case 5:
|
||||
return ServerVersion.V1_7;
|
||||
case 47:
|
||||
return ServerVersion.V1_8;
|
||||
case 107:
|
||||
case 108:
|
||||
case 109:
|
||||
case 110:
|
||||
return ServerVersion.V1_9;
|
||||
case 210:
|
||||
return ServerVersion.V1_10;
|
||||
case 315:
|
||||
case 316:
|
||||
return ServerVersion.V1_11;
|
||||
case 335:
|
||||
case 338:
|
||||
case 340:
|
||||
return ServerVersion.V1_12;
|
||||
case 393:
|
||||
case 401:
|
||||
case 404:
|
||||
return ServerVersion.V1_13;
|
||||
case 477:
|
||||
case 485:
|
||||
case 490:
|
||||
case 498:
|
||||
return ServerVersion.V1_14;
|
||||
}
|
||||
return version > 498 ? ServerVersion.getServerVersion() : ServerVersion.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Not Use: This is handled by SongodaCore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void onLogout(Player p) {
|
||||
players.remove(p.getUniqueId());
|
||||
}
|
||||
}
|
@ -1133,6 +1133,15 @@ public enum LegacyMaterials {
|
||||
return data != null ? data : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current material requires a data value.
|
||||
*
|
||||
* @return true if server is legacy and this item requires data to be defined.
|
||||
*/
|
||||
public boolean usesData() {
|
||||
return data != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a Legacy Material by its modern id name. <br />
|
||||
* This also can grab materials by their legacy, but only if there is no
|
||||
|
14
src/main/java/com/songoda/core/gui/BackgroundType.java
Normal file
14
src/main/java/com/songoda/core/gui/BackgroundType.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
/**
|
||||
* Background images available for use in toast messages
|
||||
*/
|
||||
public enum BackgroundType {
|
||||
ADVENTURE, END, HUSBANDRY, NETHER, STONE;
|
||||
final String key;
|
||||
|
||||
private BackgroundType() {
|
||||
this.key = "minecraft:textures/gui/advancements/backgrounds/" + name().toLowerCase() + ".png";
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.songoda.core.compatibility.ClientVersion;
|
||||
import com.songoda.core.compatibility.CompatibleSounds;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@ -73,6 +76,30 @@ public class GuiManager {
|
||||
openInventories.put(player, inv);
|
||||
}
|
||||
|
||||
public void showPopup(Player player, String message) {
|
||||
showPopup(player, message, LegacyMaterials.NETHER_STAR, BackgroundType.ADVENTURE);
|
||||
}
|
||||
|
||||
public void showPopup(Player player, String message, LegacyMaterials icon) {
|
||||
showPopup(player, message, icon, BackgroundType.ADVENTURE);
|
||||
}
|
||||
|
||||
public void showPopup(Player player, String message, LegacyMaterials icon, BackgroundType background) {
|
||||
if (ClientVersion.getClientVersion(player).isServerVersionAtLeast(ServerVersion.V1_12)) {
|
||||
PopupMessage popup = new PopupMessage(plugin, icon, message, background);
|
||||
popup.add();
|
||||
popup.grant(player);
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> {
|
||||
popup.revoke(player);
|
||||
popup.remove();
|
||||
}, 70);
|
||||
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
||||
player.sendTitle("", message, 10, 70, 10);
|
||||
} else {
|
||||
player.sendTitle("", message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all active GUIs
|
||||
*/
|
||||
|
190
src/main/java/com/songoda/core/gui/PopupMessage.java
Normal file
190
src/main/java/com/songoda/core/gui/PopupMessage.java
Normal file
@ -0,0 +1,190 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.advancement.AdvancementProgress;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Instance of a popup message that can be sent to a player <br>
|
||||
* Popup toast messages only work on Minecraft 1.12+ <br>
|
||||
* Calling this class on anything below 1.12 will cause ClassLoader Exceptions!
|
||||
*/
|
||||
class PopupMessage {
|
||||
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final HashSet<UUID> registeredMessages = new HashSet();
|
||||
|
||||
final UUID id = UUID.randomUUID();
|
||||
private final NamespacedKey key;
|
||||
private final TextComponent title;
|
||||
LegacyMaterials icon;
|
||||
int iconAmount = 1; // experimental, untested
|
||||
TriggerType trigger = TriggerType.IMPOSSIBLE;
|
||||
FrameType frame = FrameType.GOAL;
|
||||
BackgroundType background = BackgroundType.ADVENTURE;
|
||||
|
||||
PopupMessage(Plugin source, LegacyMaterials icon, String title) {
|
||||
this.key = new NamespacedKey(source, "popup/" + id);
|
||||
this.title = new TextComponent(title.length() < 74 ? title : (title.substring(0, 72) + "..."));
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
PopupMessage(Plugin source, LegacyMaterials icon, String title, BackgroundType background) {
|
||||
this.key = new NamespacedKey(source, "popup/" + id);
|
||||
this.title = new TextComponent(title.length() < 74 ? title : (title.substring(0, 72) + "..."));
|
||||
this.icon = icon;
|
||||
this.background = background;
|
||||
}
|
||||
|
||||
private String getJSON() {
|
||||
JsonObject json = new JsonObject();
|
||||
JsonObject advDisplay = new JsonObject();
|
||||
if (this.icon != null) {
|
||||
JsonObject displayIcon = new JsonObject();
|
||||
displayIcon.addProperty("item", "minecraft:" + this.icon.getMaterial().name().toLowerCase());
|
||||
if (this.icon.usesData()) {
|
||||
displayIcon.addProperty("data", this.icon.getData());
|
||||
}
|
||||
if (this.iconAmount > 1) {
|
||||
displayIcon.addProperty("amount", this.iconAmount); // not entirely sure if this works
|
||||
}
|
||||
advDisplay.add("icon", displayIcon);
|
||||
}
|
||||
advDisplay.add("title", gson.fromJson(ComponentSerializer.toString(this.title), JsonElement.class));
|
||||
advDisplay.addProperty("background", background.key);
|
||||
advDisplay.addProperty("description", "");
|
||||
advDisplay.addProperty("frame", this.frame.id);
|
||||
advDisplay.addProperty("announce_to_chat", false);
|
||||
advDisplay.addProperty("show_toast", true);
|
||||
advDisplay.addProperty("hidden", true);
|
||||
json.add("display", advDisplay);
|
||||
|
||||
JsonObject advCriteria = new JsonObject();
|
||||
json.add("criteria", advCriteria);
|
||||
|
||||
JsonObject advTrigger = new JsonObject();
|
||||
advTrigger.addProperty("trigger", this.trigger.getKey());
|
||||
/*if() {
|
||||
JsonObject advConditions = new JsonObject();
|
||||
// can add items to this list with [item,amount,data]
|
||||
advTrigger.add("conditions", advConditions);
|
||||
}*/
|
||||
advCriteria.add("mentioned", advTrigger);
|
||||
|
||||
return gson.toJson(json);
|
||||
}
|
||||
|
||||
protected void grant(final Player pl) {
|
||||
final Advancement adv = getAdvancement();
|
||||
final AdvancementProgress progress = pl.getAdvancementProgress(adv);
|
||||
|
||||
if (!progress.isDone())
|
||||
progress.getRemainingCriteria().forEach((crit) -> progress.awardCriteria(crit));
|
||||
}
|
||||
|
||||
protected void revoke(final Player pl) {
|
||||
final Advancement adv = getAdvancement();
|
||||
final AdvancementProgress prog = pl.getAdvancementProgress(adv);
|
||||
|
||||
if (prog.isDone())
|
||||
prog.getAwardedCriteria().forEach((crit) -> prog.revokeCriteria(crit));
|
||||
}
|
||||
|
||||
protected void add() {
|
||||
if (!registeredMessages.contains(id)) {
|
||||
registeredMessages.add(id);
|
||||
try {
|
||||
Bukkit.getUnsafe().loadAdvancement(key, getJSON());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Bukkit.getLogger().warning("Failed to create popup advancement!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void remove() {
|
||||
if (registeredMessages.contains(id)) {
|
||||
registeredMessages.remove(id);
|
||||
Bukkit.getUnsafe().removeAdvancement(key);
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement getAdvancement() {
|
||||
return Bukkit.getAdvancement(key);
|
||||
}
|
||||
|
||||
public static enum FrameType {
|
||||
TASK,
|
||||
CHALLENGE,
|
||||
GOAL;
|
||||
final String id;
|
||||
|
||||
private FrameType() {
|
||||
id = name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static enum TriggerType {
|
||||
ARBITRARY_PLAYER_TICK(ServerVersion.V1_13, "TICK"),
|
||||
BRED_ANIMALS,
|
||||
BREWED_POTION,
|
||||
CHANGED_DIMENSION,
|
||||
CONSTRUCT_BEACON,
|
||||
CONSUME_ITEM,
|
||||
CURED_ZOMBIE_VILLAGER,
|
||||
EFFECTS_CHANGED,
|
||||
ENCHANTED_ITEM,
|
||||
ENTER_BLOCK,
|
||||
ENTITY_HURT_PLAYER,
|
||||
ENTITY_KILLED_PLAYER,
|
||||
IMPOSSIBLE,
|
||||
INVENTORY_CHANGED,
|
||||
ITEM_DURABILITY_CHANGED,
|
||||
LEVITATION,
|
||||
LOCATION,
|
||||
NETHER_TRAVEL,
|
||||
PLACED_BLOCK,
|
||||
PLAYER_HURT_ENTITY,
|
||||
PLAYER_KILL_ENTITY,
|
||||
RECIPE_UNLOCKED,
|
||||
SLEPT_IN_BED,
|
||||
SUMMONED_ENTITY,
|
||||
TAME_ANIMAL,
|
||||
TICK,
|
||||
USED_ENDER_EYE,
|
||||
USED_TOTEM,
|
||||
VILLAGER_TRADE;
|
||||
final ServerVersion minVersion;
|
||||
final String compatible;
|
||||
final String key;
|
||||
|
||||
private TriggerType() {
|
||||
this.minVersion = ServerVersion.UNKNOWN;
|
||||
this.compatible = "";
|
||||
this.key = "minecraft:" + name().toLowerCase();
|
||||
}
|
||||
|
||||
private TriggerType(ServerVersion minVersion, String compatible) {
|
||||
this.minVersion = minVersion;
|
||||
this.compatible = compatible;
|
||||
this.key = "minecraft:" + (ServerVersion.isServerVersionAtLeast(minVersion) ? name() : compatible).toLowerCase();
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user