Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a36c8ef09b | ||
|
|
45ee278b61 | ||
|
|
0470810eb8 | ||
|
|
2c01b03982 | ||
|
|
f285a7ceee | ||
|
|
b25e229e17 | ||
|
|
e8869dfcdf | ||
|
|
b9cc5ad202 | ||
|
|
4c9efd5d39 | ||
|
|
b75325998d | ||
|
|
c23de3969c | ||
|
|
f1c9511c50 | ||
|
|
bd2b4df822 | ||
|
|
f33e0c8ad4 | ||
|
|
eeb1e40c11 | ||
|
|
7c84b3a85e | ||
|
|
146fc3abe5 | ||
|
|
e9aa99ad82 | ||
|
|
89d6a60235 | ||
|
|
d69c0e02f3 | ||
|
|
7f337c0f49 | ||
|
|
a403b35602 | ||
|
|
0cdb2eccda | ||
|
|
a5cfb6216a | ||
|
|
a219e7c61e | ||
|
|
f932d56bf1 | ||
|
|
d925b2d922 | ||
|
|
4b4de6426c | ||
|
|
0e75d355c6 | ||
|
|
523d13f9c5 | ||
|
|
cd6b79d946 | ||
|
|
5ecbffacbd | ||
|
|
2c84f0c60e | ||
|
|
9a77c68685 | ||
|
|
a597d86710 | ||
|
|
8f3ed60b8c | ||
|
|
37e08ffb40 | ||
|
|
e400de8cc4 | ||
|
|
bfd78444e3 | ||
|
|
ed1934be36 | ||
|
|
99a980476b | ||
|
|
f581ccad1c | ||
|
|
2c29af8a6a | ||
|
|
306cae8bcb | ||
|
|
781d0b3a70 | ||
|
|
355ff63318 | ||
|
|
243e117538 | ||
|
|
f1a1f795d2 | ||
|
|
bcb092f29f | ||
|
|
4500161341 | ||
|
|
79e1cd3951 | ||
|
|
77c8abf457 | ||
|
|
847b6ddc92 | ||
|
|
d234858ec5 | ||
|
|
e7a3cf69fd | ||
|
|
ee41588a91 | ||
|
|
1b160bdd28 | ||
|
|
c33fe83030 | ||
|
|
eaeba41c53 | ||
|
|
34905cbfeb | ||
|
|
78e8b7f8a7 | ||
|
|
2d8256f2da | ||
|
|
8438e4c40e | ||
|
|
30e93d9c81 |
No files matched your search
@@ -1,4 +1,9 @@
|
||||
.idea/
|
||||
src/test/
|
||||
bin/
|
||||
target/
|
||||
*.iml
|
||||
dependency-reduced-pom.xml
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
language: java
|
||||
language: java
|
||||
script: "mvn clean package -DskipTests"
|
||||
@@ -1,4 +1,4 @@
|
||||
# ChatItem [](https://travis-ci.org/dadus33/ChatItem)
|
||||
# ChatItem [](https://travis-ci.org/dadus33/ChatItem) [](https://discord.gg/eQJbVJt)
|
||||
The official repository of the ChatItem Spigot/Bukkit plugin.
|
||||
# Building
|
||||
Building this is simple as we use Maven.
|
||||
|
||||
@@ -5,44 +5,59 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>me.dadus33</groupId>
|
||||
<artifactId>ChatItem</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<artifactId>chatitem</artifactId>
|
||||
<version>1.4</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>Plugin Metrics</id>
|
||||
<url>http://repo.mcstats.org/content/repositories/public</url>
|
||||
<id>Bungeecord</id>
|
||||
<url>https://repo.md-5.net/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>viaversion-repo</id>
|
||||
<url>https://repo.viaversion.com</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.10-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.10-R0.1-SNAPSHOT</version>
|
||||
<version>1.17-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>minecraft-server</artifactId>
|
||||
<version>1.7.10</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>3.6.5</version>
|
||||
<version>4.7.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -51,26 +66,45 @@
|
||||
<version>2.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mcstats.bukkit</groupId>
|
||||
<artifactId>metrics-lite</artifactId>
|
||||
<version>R8-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.14.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.14.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ProtocolSupport</groupId>
|
||||
<artifactId>ProtocolSupport</artifactId>
|
||||
<version>f34959c331</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.GeyserMC</groupId>
|
||||
<artifactId>opennbt</artifactId>
|
||||
<version>1.4</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.53.Final</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>ChatItem-${project.version}</finalName>
|
||||
<directory>./target</directory>
|
||||
<resources>
|
||||
<resource>
|
||||
@@ -90,8 +124,8 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -105,7 +139,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -113,10 +147,19 @@
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>me.dadus33.libs.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.google.gson</pattern>
|
||||
<shadedPattern>me.dadus33.gson</shadedPattern>
|
||||
<shadedPattern>me.dadus33.libs.gson</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.github.GeyserMC</pattern>
|
||||
<shadedPattern>me.dadus33.libs.opennbt</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
@@ -126,4 +169,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
@@ -1,52 +1,51 @@
|
||||
package me.dadus33.chatitem;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.async.AsyncListenerHandler;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.dadus33.chatitem.api.APIImplementation;
|
||||
import me.dadus33.chatitem.api.ChatItemAPI;
|
||||
import me.dadus33.chatitem.commands.CIReload;
|
||||
import me.dadus33.chatitem.filters.Log4jFilter;
|
||||
import me.dadus33.chatitem.json.JSONManipulator;
|
||||
import me.dadus33.chatitem.json.JSONManipulatorCurrent;
|
||||
import me.dadus33.chatitem.listeners.ChatEventListener;
|
||||
import me.dadus33.chatitem.listeners.ChatPacketListener;
|
||||
import me.dadus33.chatitem.utils.General;
|
||||
import me.dadus33.chatitem.listeners.ChatPacketListenerV2;
|
||||
import me.dadus33.chatitem.listeners.ChatPacketValidatorV2;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacketManager;
|
||||
import me.dadus33.chatitem.playerversion.IPlayerVersion;
|
||||
import me.dadus33.chatitem.playerversion.hooks.DefaultVersionHook;
|
||||
import me.dadus33.chatitem.playerversion.hooks.ProtocolSupportHook;
|
||||
import me.dadus33.chatitem.playerversion.hooks.ViaVersionHook;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.mcstats.MetricsLite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ChatItem extends JavaPlugin {
|
||||
|
||||
public final static int CFG_VER = 4;
|
||||
public final static int CFG_VER = 12;
|
||||
private static ChatItem instance;
|
||||
private ChatEventListener chatEventListener;
|
||||
private Log4jFilter filter;
|
||||
private Storage storage;
|
||||
private ProtocolManager pm;
|
||||
private ChatPacketListener listener;
|
||||
private static JSONManipulator manip;
|
||||
public static boolean post17 = false;
|
||||
private static boolean baseComponentAvailable = true;
|
||||
private IPlayerVersion playerVersionAdapter;
|
||||
private ChatItemPacketManager packetManager;
|
||||
|
||||
public static void reload(CommandSender sender) {
|
||||
ChatItem obj = getInstance();
|
||||
obj.pm = ProtocolLibrary.getProtocolManager();
|
||||
//obj.pm = ProtocolLibrary.getProtocolManager();
|
||||
obj.saveDefaultConfig();
|
||||
obj.reloadConfig();
|
||||
obj.storage = new Storage(obj.getConfig());
|
||||
General.init(obj.storage);
|
||||
General.checkConfigVersion();
|
||||
obj.listener.setStorage(obj.storage);
|
||||
//obj.packetListener.setStorage(obj.storage);
|
||||
//obj.packetValidator.setStorage(obj.storage);
|
||||
obj.chatEventListener.setStorage(obj.storage);
|
||||
obj.filter.setStorage(obj.storage);
|
||||
APIImplementation api = (APIImplementation) Bukkit.getServicesManager().getRegistration(ChatItemAPI.class).getProvider();
|
||||
api.setStorage(obj.storage);
|
||||
api.updateLogger();
|
||||
if (!obj.storage.RELOAD_MESSAGE.isEmpty())
|
||||
sender.sendMessage(obj.storage.RELOAD_MESSAGE);
|
||||
}
|
||||
@@ -55,63 +54,94 @@ public class ChatItem extends JavaPlugin {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
//Save the instance (we're basically a singleton)
|
||||
instance = this;
|
||||
pm = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
//Load ProtocolManager
|
||||
//pm = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
//Load config
|
||||
saveDefaultConfig();
|
||||
storage = new Storage(getConfig());
|
||||
General.init(storage);
|
||||
General.checkConfigVersion();
|
||||
listener = new ChatPacketListener(this, ListenerPriority.HIGHEST, storage, PacketType.Play.Server.CHAT);
|
||||
AsynchronousManager am = pm.getAsynchronousManager();
|
||||
AsyncListenerHandler packetListenerAsyncThread = am.registerAsyncHandler(listener);
|
||||
packetListenerAsyncThread.start();
|
||||
|
||||
//Load API
|
||||
APIImplementation api = new APIImplementation(storage);
|
||||
Bukkit.getServicesManager().register(ChatItemAPI.class, api, this, ServicePriority.Highest);
|
||||
|
||||
//Packet listeners
|
||||
packetManager = new ChatItemPacketManager(this);
|
||||
packetManager.getPacketManager().addHandler(new ChatPacketValidatorV2(storage));
|
||||
packetManager.getPacketManager().addHandler(new ChatPacketListenerV2(storage));
|
||||
/*packetListener = new ChatPacketListener(this, ListenerPriority.LOW, storage, PacketType.Play.Server.CHAT);
|
||||
packetValidator = new ChatPacketValidator(this, ListenerPriority.LOWEST, storage, PacketType.Play.Server.CHAT);
|
||||
pm.addPacketListener(packetValidator);
|
||||
pm.addPacketListener(packetListener);*/
|
||||
|
||||
if(Bukkit.getPluginManager().getPlugin("ViaVersion") != null){
|
||||
playerVersionAdapter = new ViaVersionHook();
|
||||
} else if(Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null){
|
||||
playerVersionAdapter = new ProtocolSupportHook();
|
||||
} else {
|
||||
playerVersionAdapter = new DefaultVersionHook();
|
||||
}
|
||||
|
||||
//We halt the use of this system for now, until we can solve the infamous getProtocolVersion issue
|
||||
//Till then, users of both ViaVersion and ProtocolSupport should do just fine
|
||||
/*if(!protocolSupport && !viaVersion) {
|
||||
//We only implement our own way of getting protocol versions if we have no other choice
|
||||
pm.addPacketListener(new HandshakeListener(this, ListenerPriority.MONITOR, PacketType.Handshake.Client.SET_PROTOCOL));
|
||||
}*/
|
||||
|
||||
//Commands
|
||||
CIReload rld = new CIReload();
|
||||
Bukkit.getPluginCommand("cireload").setExecutor(rld);
|
||||
|
||||
//Bukkit API listeners
|
||||
chatEventListener = new ChatEventListener(storage);
|
||||
Bukkit.getPluginManager().registerEvents(chatEventListener, this);
|
||||
if(isMc18OrLater()) {
|
||||
post17 = true;
|
||||
}
|
||||
manip = new JSONManipulatorCurrent();
|
||||
|
||||
//Check for existence of BaseComponent class (only on spigot)
|
||||
try {
|
||||
MetricsLite metrics = new MetricsLite(this);
|
||||
metrics.start();
|
||||
} catch (IOException e) {
|
||||
getLogger().log(Level.WARNING, ChatColor.RED + "Couldn't start metrics!");
|
||||
Class.forName("net.md_5.bungee.api.chat.BaseComponent");
|
||||
} catch (ClassNotFoundException e) {
|
||||
baseComponentAvailable = false;
|
||||
}
|
||||
|
||||
//Initialize Log4J filter (remove ugly console messages)
|
||||
filter = new Log4jFilter(storage);
|
||||
}
|
||||
|
||||
public IPlayerVersion getPlayerVersionAdapter() {
|
||||
return playerVersionAdapter;
|
||||
}
|
||||
|
||||
public ChatItemPacketManager getPacketManager() {
|
||||
return packetManager;
|
||||
}
|
||||
|
||||
|
||||
public void onDisable() {
|
||||
instance = null;
|
||||
post17 = false;
|
||||
}
|
||||
|
||||
private boolean isMc18OrLater(){
|
||||
switch(getVersion(Bukkit.getServer())){
|
||||
case "v1_8_R1": return true;
|
||||
case "v1_8_R2": return true;
|
||||
case "v1_8_R3": return true;
|
||||
case "v1_9_R1": return true;
|
||||
case "v1_9_R2": return true;
|
||||
case "v1_10_R1": return true;
|
||||
case "v1_10_R2": return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String getVersion(Server server) {
|
||||
public static String getVersion(Server server) {
|
||||
final String packageName = server.getClass().getPackage().getName();
|
||||
|
||||
return packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
public static boolean supportsChatComponentApi(){
|
||||
return baseComponentAvailable;
|
||||
}
|
||||
|
||||
public static JSONManipulator getManipulator(){
|
||||
return manip;
|
||||
/*
|
||||
We used to have 2 kinds of JSONManipulators because of my bad understanding of the 1.7 way of parsing JSON chat
|
||||
The interface should however stay as there might be great changes in future versions in JSON parsing (most likely 1.13)
|
||||
*/
|
||||
return new JSONManipulatorCurrent();
|
||||
//We just return a new one whenever requested for the moment, should implement a cache of some sort some time though
|
||||
}
|
||||
|
||||
public static void debug(String msg) {
|
||||
if(getInstance().getConfig().getBoolean("debug", false))
|
||||
getInstance().getLogger().info(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package me.dadus33.chatitem.api;
|
||||
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.json.JSONManipulator;
|
||||
import me.dadus33.chatitem.json.Translator;
|
||||
import me.dadus33.chatitem.listeners.ChatPacketListenerV2;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
|
||||
public class APIImplementation implements ChatItemAPI {
|
||||
|
||||
private final static String DEFAULT_JSON = "{\"extra\":[{\"text\":\"i\"}]}";
|
||||
private final static String BASE_JSON = "{\"text\":\"\", \"extra\":[]}";
|
||||
private final static List<String> DEFAULT_REPLACEMENT = Collections.singletonList("i");
|
||||
|
||||
private Storage c;
|
||||
|
||||
private Logger logger;
|
||||
|
||||
public APIImplementation(Storage st){
|
||||
this.c = st;
|
||||
this.logger = ChatItem.getInstance().getLogger();
|
||||
}
|
||||
|
||||
public void updateLogger(){
|
||||
this.logger = ChatItem.getInstance().getLogger();
|
||||
}
|
||||
|
||||
public void setStorage(Storage newStorage){
|
||||
this.c = newStorage;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getJSONFromItem(ItemStack item) {
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
try {
|
||||
return manipulator.parse(DEFAULT_JSON, DEFAULT_REPLACEMENT, item, ChatPacketListenerV2.styleItem(item, c), ProtocolVersion.getServerVersion().MAX_VER);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromItem(ItemStack item, String customReplacement) {
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
try {
|
||||
return manipulator.parse(DEFAULT_JSON, DEFAULT_REPLACEMENT, item, customReplacement, ProtocolVersion.getServerVersion().MAX_VER);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromItem(ItemStack item, Player client) {
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
try {
|
||||
return manipulator.parse(DEFAULT_JSON, DEFAULT_REPLACEMENT, item, ChatPacketListenerV2.styleItem(item, c), ProtocolVersion.getClientVersion(client));
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromItem(ItemStack item, String customReplacement, Player client) {
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
try {
|
||||
return manipulator.parse(DEFAULT_JSON, DEFAULT_REPLACEMENT, item, customReplacement, ProtocolVersion.getClientVersion(client));
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromInlineItem(String text, ItemStack... items) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = parser.parse(BASE_JSON).getAsJsonObject();
|
||||
obj.add("extra", Translator.toJson(text));
|
||||
String main = obj.toString();
|
||||
int version = ProtocolVersion.getServerVersion().MAX_VER;
|
||||
for(int i = 0; i < items.length; ++i){
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
ItemStack item = items[i];
|
||||
String style = ChatPacketListenerV2.styleItem(item, c);
|
||||
List<String> replacements = Collections.singletonList("%_"+i);
|
||||
try {
|
||||
main = manipulator.parse(main, replacements, item, style, version);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return main;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromInlineItem(String text, String[] customReplacements, ItemStack... items) {
|
||||
JsonObject wrapper = new JsonObject();
|
||||
wrapper.add("extra", Translator.toJson(text));
|
||||
String main = wrapper.toString();
|
||||
int version = ProtocolVersion.getServerVersion().MAX_VER;
|
||||
for(int i = 0; i < items.length; ++i){
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
ItemStack item = items[i];
|
||||
String style = Translator.toJson(customReplacements[i]).toString();
|
||||
List<String> replacements = Collections.singletonList("%_"+i);
|
||||
try {
|
||||
main = manipulator.parse(main, replacements, item, style, version);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return main;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromInlineItem(String text, Player client, ItemStack... items) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = parser.parse(BASE_JSON).getAsJsonObject();
|
||||
obj.add("extra", Translator.toJson(text));
|
||||
String main = obj.toString();
|
||||
int version = ProtocolVersion.getClientVersion(client);
|
||||
for(int i = 0; i < items.length; ++i){
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
ItemStack item = items[i];
|
||||
String style = ChatPacketListenerV2.styleItem(item, c);
|
||||
List<String> replacements = Collections.singletonList("%_"+i);
|
||||
try {
|
||||
main = manipulator.parse(main, replacements, item, style, version);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return main;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJSONFromInlineItem(String text, Player client, String[] customReplacements, ItemStack... items) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = parser.parse(BASE_JSON).getAsJsonObject();
|
||||
obj.add("extra", Translator.toJson(text));
|
||||
String main = obj.toString();
|
||||
int version = ProtocolVersion.getClientVersion(client);
|
||||
for(int i = 0; i < items.length; ++i){
|
||||
JSONManipulator manipulator = ChatItem.getManipulator();
|
||||
ItemStack item = items[i];
|
||||
String style = Translator.toJson(customReplacements[i]).toString();
|
||||
List<String> replacements = Collections.singletonList("%_"+i);
|
||||
try {
|
||||
main = manipulator.parse(main, replacements, item, style, version);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "An unexpected exception was caught while running an API method from ChatItem. Please contact the developer immediately, providing him with the following stack-trace:");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return main;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.dadus33.chatitem.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface ChatItemAPI {
|
||||
String getJSONFromItem(ItemStack item);
|
||||
|
||||
String getJSONFromItem(ItemStack item, String customReplacement);
|
||||
|
||||
String getJSONFromItem(ItemStack item, Player client);
|
||||
|
||||
String getJSONFromItem(ItemStack item, String customReplacement, Player client);
|
||||
|
||||
String getJSONFromInlineItem(String text, ItemStack... items);
|
||||
|
||||
String getJSONFromInlineItem(String text, String[] customReplacements, ItemStack... items);
|
||||
|
||||
String getJSONFromInlineItem(String text, Player client, ItemStack... items);
|
||||
|
||||
String getJSONFromInlineItem(String text, Player client, String[] customReplacements, ItemStack... items);
|
||||
}
|
||||
@@ -5,9 +5,7 @@ import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* Created by Vlad on 30.09.2016.
|
||||
*/
|
||||
|
||||
public class CIReload implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.dadus33.chatitem.filters;
|
||||
|
||||
import me.dadus33.chatitem.listeners.ChatEventListener;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -11,16 +12,12 @@ import org.apache.logging.log4j.message.Message;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
|
||||
|
||||
|
||||
public class Log4jFilter implements Filter {
|
||||
|
||||
private boolean stopped;
|
||||
private final static CopyOnWriteArrayList<String> ls = new CopyOnWriteArrayList<>();
|
||||
public Storage c;
|
||||
private Storage c;
|
||||
|
||||
public Log4jFilter(Storage st){
|
||||
c = st;
|
||||
@@ -58,42 +55,21 @@ public class Log4jFilter implements Filter {
|
||||
return checkMessage(logEvent.getMessage().getFormattedMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
stopped = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
return !this.stopped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStopped() {
|
||||
return this.stopped;
|
||||
}
|
||||
|
||||
private Result checkMessage(String msg){
|
||||
if(msg==null){
|
||||
return Result.NEUTRAL;
|
||||
}
|
||||
for(String placeholder : c.PLACEHOLDERS){
|
||||
if(msg.contains(placeholder)){
|
||||
ls.add(msg);
|
||||
for(Player p : Bukkit.getOnlinePlayers()){
|
||||
msg = msg.replaceAll("\\p{C}", "");
|
||||
if(msg.endsWith(p.getName()) || msg.lastIndexOf(p.getName())+2+p.getName().length()>=msg.length()){
|
||||
if(msg.contains(ChatEventListener.SEPARATOR +p.getName())){
|
||||
return Result.DENY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return Result.NEUTRAL;
|
||||
|
||||
}
|
||||
|
||||
public void setStorage(Storage nst){
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package me.dadus33.chatitem.json;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface JSONManipulator {
|
||||
String parse(String json, String[] replacements, ItemStack item, String repl) throws InvocationTargetException, IllegalAccessException, InstantiationException;
|
||||
String parse(String json, List<String> replacements, ItemStack item, String repl, int protocol) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException;
|
||||
String parseEmpty(String json, List<String> replacements, String repl, List<String> tooltip, Player sender);
|
||||
}
|
||||
@@ -1,71 +1,138 @@
|
||||
package me.dadus33.chatitem.json;
|
||||
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import com.google.gson.*;
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.utils.Item;
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Reflect;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
|
||||
private static Class<?> craftItemStackClass = Reflect.getOBCClass("inventory.CraftItemStack");
|
||||
private static Class<?> nmsItemStackClass = Reflect.getNMSClass("ItemStack");
|
||||
private static Method asNMSCopy = Reflect.getMethod(craftItemStackClass, "asNMSCopy", ItemStack.class);
|
||||
private static Class<?> nbtTagCompoundClass = Reflect.getNMSClass("NBTTagCompound");
|
||||
private static Method saveNmsItemStackMethod = Reflect.getMethod(nmsItemStackClass, "save", nbtTagCompoundClass);
|
||||
private static final Class<?> CRAFT_ITEM_STACK_CLASS = PacketUtils.getObcClass("inventory.CraftItemStack");
|
||||
private static final Class<?> NBT_STRING = PacketUtils.getNmsClass("NBTTagString", "nbt.");
|
||||
private static final Class<?> NBT_LIST = PacketUtils.getNmsClass("NBTTagList", "nbt.");
|
||||
private static final Map<Type, Tag> TYPES_TO_OPEN_NBT_TAGS = new HashMap<>();
|
||||
private static final List<Class<?>> NBT_BASE_CLASSES = new ArrayList<>();
|
||||
private static final List<Field> NBT_BASE_DATA_FIELD = new ArrayList<>();
|
||||
private static final Class<?> NMS_ITEM_STACK_CLASS = PacketUtils.getNmsClass("ItemStack", "world.item.");
|
||||
private static final Method AS_NMS_COPY = Reflect.getMethod(CRAFT_ITEM_STACK_CLASS, "asNMSCopy", ItemStack.class);
|
||||
private static final Class<?> NBT_TAG_COMPOUND = PacketUtils.getNmsClass("NBTTagCompound", "nbt.");
|
||||
private static final Method SAVE_NMS_ITEM_STACK_METHOD = Reflect.getMethod(NMS_ITEM_STACK_CLASS, "save", NBT_TAG_COMPOUND);
|
||||
private static final Field MAP = Reflect.getField(NBT_TAG_COMPOUND, "map", "x");
|
||||
private static final Field LIST_FIELD = Reflect.getField(NBT_LIST, "list", "c");
|
||||
|
||||
private static String[] replaces;
|
||||
private static String rgx;
|
||||
private static JsonArray toUse;
|
||||
private static JsonParser parser = new JsonParser();
|
||||
//Tags to be ignored. Currently it only contains tags from PortableHorses, but feel free to submit a pull request to add tags from your plugins
|
||||
private static final List<String> IGNORED = Arrays.asList("horsetag", "phorse", "iscnameviz", "cname");
|
||||
|
||||
private static final ConcurrentHashMap<Map.Entry<ProtocolVersion, ItemStack>, JsonObject> STACKS = new ConcurrentHashMap<>();
|
||||
|
||||
static{
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagByte", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagByteArray", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagDouble", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagFloat", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagInt", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagIntArray", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagLong", "nbt."));
|
||||
NBT_BASE_CLASSES.add(PacketUtils.getNmsClass("NBTTagShort", "nbt."));
|
||||
|
||||
for (Class<?> NBT_BASE_CLASS : NBT_BASE_CLASSES) {
|
||||
NBT_BASE_DATA_FIELD.add(Reflect.getField(NBT_BASE_CLASS, "data", "c", "x"));
|
||||
}
|
||||
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Byte.class, new ByteTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Byte[].class, new ByteArrayTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Double.class, new DoubleTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Float.class, new FloatTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Integer.class, new IntTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Integer[].class, new IntArrayTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Long.class, new LongTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(Short.class, new ShortTag(""));
|
||||
//Add the primitive types too, just in case
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(byte.class, new ByteTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(byte[].class, new ByteArrayTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(double.class, new DoubleTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(float.class, new FloatTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(int.class, new IntTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(int[].class, new IntArrayTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(long.class, new LongTag(""));
|
||||
TYPES_TO_OPEN_NBT_TAGS.put(short.class, new ShortTag(""));
|
||||
|
||||
}
|
||||
|
||||
private List<String> replaces;
|
||||
private String rgx;
|
||||
private ProtocolVersion protocolVersion;
|
||||
private JsonObject itemTooltip;
|
||||
private JsonArray classicTooltip;
|
||||
private final JsonParser PARSER = new JsonParser();
|
||||
|
||||
|
||||
public String parse(String json, String[] replacements, ItemStack item, String repl) throws InvocationTargetException, IllegalAccessException, InstantiationException {
|
||||
JsonObject obj = parser.parse(json).getAsJsonObject();
|
||||
public String parse(String json, List<String> replacements, ItemStack item, String replacement, int protocol) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException {
|
||||
JsonObject obj = PARSER.parse(json).getAsJsonObject();
|
||||
JsonArray array = obj.getAsJsonArray("extra");
|
||||
replaces = replacements;
|
||||
String regex = "";
|
||||
for (int i = 0; i < replacements.length; ++i) {
|
||||
if (replacements.length == 1) {
|
||||
regex = Pattern.quote(replacements[0]);
|
||||
for (int i = 0; i < replacements.size(); ++i) {
|
||||
if (replacements.size() == 1) {
|
||||
regex = Pattern.quote(replacements.get(0));
|
||||
break;
|
||||
}
|
||||
if (i == 0 || i + 1 == replacements.length) {
|
||||
if (i == 0 || i + 1 == replacements.size()) {
|
||||
if (i == 0) {
|
||||
regex = "(" + Pattern.quote(replacements[i]);
|
||||
regex = "(" + Pattern.quote(replacements.get(i));
|
||||
} else {
|
||||
regex = regex.concat("|").concat(Pattern.quote(replacements[i])).concat(")");
|
||||
regex = regex.concat("|").concat(Pattern.quote(replacements.get(i))).concat(")");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
regex = regex.concat("|").concat(Pattern.quote(replacements[i]));
|
||||
regex = regex.concat("|").concat(Pattern.quote(replacements.get(i)));
|
||||
}
|
||||
rgx = regex;
|
||||
JsonArray rep = new JsonArray();
|
||||
repl = escapeBackslash(repl);
|
||||
JsonArray use = parser.parse(Translator.toJSON(repl)).getAsJsonArray();
|
||||
final AbstractMap.SimpleEntry<ProtocolVersion, ItemStack> p = new AbstractMap.SimpleEntry<>(protocolVersion = ProtocolVersion.getVersion(protocol), item);
|
||||
|
||||
JsonObject hover = parser.parse("{\"action\":\"show_item\", \"value\": \"\"}").getAsJsonObject();
|
||||
Object nmsStack = asNMSCopy.invoke(null, item);
|
||||
Object tag = nbtTagCompoundClass.newInstance();
|
||||
tag = saveNmsItemStackMethod.invoke(nmsStack, tag);
|
||||
String jsonRep = escapeBackslash(tag.toString());
|
||||
hover.addProperty("value", jsonRep);
|
||||
for (JsonElement ob : use)
|
||||
ob.getAsJsonObject().add("hoverEvent", hover);
|
||||
if ((itemTooltip = STACKS.get(p)) == null) {
|
||||
JsonArray use = Translator.toJson(replacement); //We get the json representation of the old color formatting method
|
||||
|
||||
toUse = use;
|
||||
JsonObject hover = PARSER.parse("{\"action\":\"show_item\", \"value\": \"\"}").getAsJsonObject(); //There's no public clone method for JSONObjects so we need to parse them every time
|
||||
|
||||
String jsonRep = stringifyItem(item); //Get the JSON representation of the item (well, not really JSON, but rather a string representation of NBT data)
|
||||
hover.addProperty("value", jsonRep);
|
||||
|
||||
JsonObject wrapper = new JsonObject(); //Create a wrapper object for the whole array
|
||||
wrapper.addProperty("text", ""); //The text field is compulsory, even if it's empty
|
||||
wrapper.add("extra", use);
|
||||
wrapper.add("hoverEvent", hover);
|
||||
|
||||
itemTooltip = wrapper; //Save the tooltip for later use when we encounter a placeholder
|
||||
STACKS.put(p, itemTooltip); //Save it in the cache too so when parsing other packets with the same item (and client version) we no longer have to create it again
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(ChatItem.getInstance(), () ->
|
||||
STACKS.remove(p)
|
||||
, 100L); //We remove it later when no longer needed to save memory
|
||||
}
|
||||
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
if (array.get(i).isJsonObject()){
|
||||
JsonObject o = array.get(i).getAsJsonObject();
|
||||
boolean inside = false;
|
||||
for (String replacement : replacements)
|
||||
if (o.toString().contains(replacement)) {
|
||||
for (String replace : replacements)
|
||||
if (o.toString().contains(replace)) {
|
||||
if (inside) {
|
||||
break;
|
||||
}
|
||||
@@ -76,8 +143,12 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
JsonElement el = o.get("extra");
|
||||
if (el != null) {
|
||||
JsonArray jar = el.getAsJsonArray();
|
||||
jar = parseArray(jar);
|
||||
o.add("extra", jar);
|
||||
if(jar.size()!=0) {
|
||||
jar = parseArray(jar);
|
||||
o.add("extra", jar);
|
||||
}else{
|
||||
o.remove("extra");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
@@ -85,8 +156,12 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
JsonElement el = o.get("extra");
|
||||
if (el != null) {
|
||||
JsonArray jar = el.getAsJsonArray();
|
||||
jar = parseArray(jar);
|
||||
o.add("extra", jar);
|
||||
if(jar.size()!=0) {
|
||||
jar = parseArray(jar);
|
||||
o.add("extra", jar);
|
||||
}else{
|
||||
o.remove("extra");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +188,177 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
boolean endDot = (j == splits.length - 1) && isLast;
|
||||
if (!splits[j].isEmpty() && !endDot) {
|
||||
String st = o.toString();
|
||||
JsonObject fix = parser.parse(st).getAsJsonObject();
|
||||
JsonObject fix = PARSER.parse(st).getAsJsonObject();
|
||||
fix.addProperty("text", splits[j]);
|
||||
rep.add(fix);
|
||||
}
|
||||
if (j != splits.length - 1) {
|
||||
rep.add(itemTooltip);
|
||||
}
|
||||
}
|
||||
if (!fnd) {
|
||||
rep.add(o);
|
||||
}
|
||||
}else{
|
||||
if(array.get(i).isJsonNull()){
|
||||
continue;
|
||||
}else{
|
||||
if(array.get(i).isJsonArray()){
|
||||
JsonArray jar = array.get(i).getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseArray(array.get(i).getAsJsonArray());
|
||||
rep.set(i, jar);
|
||||
}
|
||||
}else{
|
||||
|
||||
|
||||
String msg = array.get(i).getAsString();
|
||||
boolean isLast = false;
|
||||
boolean done = false;
|
||||
boolean fnd;
|
||||
String[] splits;
|
||||
for (String repls : replacements) {
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
isLast = msg.endsWith(repls);
|
||||
if (isLast) {
|
||||
done = true;
|
||||
msg = msg.concat(".");
|
||||
}
|
||||
}
|
||||
splits = msg.split(regex);
|
||||
fnd = splits.length != 1;
|
||||
if (fnd)
|
||||
for (int j = 0; j < splits.length; ++j) {
|
||||
boolean endDot = (j == splits.length - 1) && isLast;
|
||||
if (!splits[j].isEmpty() && !endDot) {
|
||||
JsonElement fix = new JsonPrimitive(splits[j]);
|
||||
rep.add(fix);
|
||||
}
|
||||
if (j != splits.length - 1) {
|
||||
rep.add(itemTooltip);
|
||||
}
|
||||
}
|
||||
if (!fnd) {
|
||||
rep.add(array.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
obj.add("extra", rep);
|
||||
if(!obj.has("text")){
|
||||
obj.addProperty("text", "");
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseEmpty(String json, List<String> replacements, String repl, List<String> tooltip, Player sender) {
|
||||
JsonObject obj = PARSER.parse(json).getAsJsonObject();
|
||||
JsonArray array = obj.getAsJsonArray("extra");
|
||||
replaces = replacements;
|
||||
String regex = "";
|
||||
for (int i = 0; i < replacements.size(); ++i) {
|
||||
if (replacements.size() == 1) {
|
||||
regex = Pattern.quote(replacements.get(0));
|
||||
break;
|
||||
}
|
||||
if (i == 0 || i + 1 == replacements.size()) {
|
||||
if (i == 0) {
|
||||
regex = "(" + Pattern.quote(replacements.get(i));
|
||||
} else {
|
||||
regex = regex.concat("|").concat(Pattern.quote(replacements.get(i))).concat(")");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
regex = regex.concat("|").concat(Pattern.quote(replacements.get(i)));
|
||||
}
|
||||
rgx = regex;
|
||||
JsonArray rep = new JsonArray();
|
||||
JsonArray use = Translator.toJson(repl.replace("{name}", sender.getName()).replace("{display-name}", sender.getDisplayName()));
|
||||
JsonObject hover = PARSER.parse("{\"action\":\"show_text\", \"value\": \"\"}").getAsJsonObject();
|
||||
|
||||
StringBuilder oneLineTooltip = new StringBuilder("");
|
||||
int index = 0;
|
||||
for(String m : tooltip){
|
||||
oneLineTooltip.append(m.replace("{name}", sender.getName()).replace("{display-name}", sender.getDisplayName()));
|
||||
++index;
|
||||
if(index!=tooltip.size()){
|
||||
oneLineTooltip.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
hover.add("value", new JsonPrimitive(oneLineTooltip.toString()));
|
||||
for (JsonElement ob : use)
|
||||
ob.getAsJsonObject().add("hoverEvent", hover);
|
||||
|
||||
classicTooltip = use;
|
||||
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
if (array.get(i).isJsonObject()){
|
||||
JsonObject o = array.get(i).getAsJsonObject();
|
||||
boolean inside = false;
|
||||
for (String replace : replacements)
|
||||
if (o.toString().contains(replace)) {
|
||||
if (inside) {
|
||||
break;
|
||||
}
|
||||
inside = true;
|
||||
}
|
||||
JsonElement text = o.get("text");
|
||||
if (text == null) {
|
||||
JsonElement el = o.get("extra");
|
||||
if (el != null) {
|
||||
JsonArray jar = el.getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseNoItemArray(jar);
|
||||
o.add("extra", jar);
|
||||
}else{
|
||||
o.remove("extra");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
if (text.getAsString().isEmpty()) {
|
||||
JsonElement el = o.get("extra");
|
||||
if (el != null) {
|
||||
JsonArray jar = el.getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseNoItemArray(jar);
|
||||
o.add("extra", jar);
|
||||
}else{
|
||||
o.remove("extra");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String msg = text.getAsString();
|
||||
boolean isLast = false;
|
||||
boolean done = false;
|
||||
boolean fnd;
|
||||
String[] splits;
|
||||
for (String repls : replacements) {
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
isLast = msg.endsWith(repls);
|
||||
if (isLast) {
|
||||
done = true;
|
||||
msg = msg.concat(".");
|
||||
}
|
||||
}
|
||||
splits = msg.split(regex);
|
||||
fnd = splits.length != 1;
|
||||
if (fnd)
|
||||
for (int j = 0; j < splits.length; ++j) {
|
||||
boolean endDot = (j == splits.length - 1) && isLast;
|
||||
if (!splits[j].isEmpty() && !endDot) {
|
||||
String st = o.toString();
|
||||
JsonObject fix = PARSER.parse(st).getAsJsonObject();
|
||||
fix.addProperty("text", splits[j]);
|
||||
rep.add(fix);
|
||||
}
|
||||
@@ -129,8 +374,11 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
continue;
|
||||
}else{
|
||||
if(array.get(i).isJsonArray()){
|
||||
JsonArray jar = parseArray(array.get(i).getAsJsonArray());
|
||||
rep.set(i, jar);
|
||||
JsonArray jar = array.get(i).getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseNoItemArray(array.get(i).getAsJsonArray());
|
||||
rep.set(i, jar);
|
||||
}
|
||||
}else{
|
||||
|
||||
|
||||
@@ -174,11 +422,9 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
}
|
||||
obj.add("extra", rep);
|
||||
return obj.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static JsonArray parseArray(JsonArray arr) {
|
||||
private JsonArray parseNoItemArray(JsonArray arr) {
|
||||
JsonArray replacer = new JsonArray();
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
if (arr.get(i).isJsonObject()){
|
||||
@@ -192,6 +438,7 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
inside = true;
|
||||
}
|
||||
if (!inside) { //the placeholder we're looking for is not inside this element, so we continue searching
|
||||
replacer.add(o);
|
||||
continue;
|
||||
}
|
||||
JsonElement text = o.get("text");
|
||||
@@ -204,8 +451,12 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
continue;
|
||||
}
|
||||
JsonArray jar = el.getAsJsonArray();
|
||||
jar = parseArray(jar);
|
||||
o.add("extra", jar);
|
||||
if(jar.size()!=0) {
|
||||
jar = parseNoItemArray(jar);
|
||||
o.add("extra", jar);
|
||||
}else{
|
||||
o.remove("extra");
|
||||
}
|
||||
}
|
||||
|
||||
String msg = text.getAsString();
|
||||
@@ -230,12 +481,12 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
boolean endDot = (j == splits.length - 1) && isLast;
|
||||
if (!splits[j].isEmpty() && !endDot) {
|
||||
String st = o.toString();
|
||||
JsonObject fix = parser.parse(st).getAsJsonObject();
|
||||
JsonObject fix = PARSER.parse(st).getAsJsonObject();
|
||||
fix.addProperty("text", splits[j]);
|
||||
replacer.add(fix);
|
||||
}
|
||||
if (j != splits.length - 1) {
|
||||
replacer.addAll(toUse);
|
||||
replacer.addAll(classicTooltip);
|
||||
}
|
||||
}
|
||||
if (!fnd) {
|
||||
@@ -246,8 +497,11 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
continue;
|
||||
}else{
|
||||
if(arr.get(i).isJsonArray()){
|
||||
JsonArray jar = parseArray(arr.get(i).getAsJsonArray());
|
||||
replacer.set(i, jar);
|
||||
JsonArray jar = arr.get(i).getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseNoItemArray(arr.get(i).getAsJsonArray());
|
||||
replacer.set(i, jar);
|
||||
}
|
||||
}else{
|
||||
String msg = arr.get(i).getAsString();
|
||||
boolean isLast = false;
|
||||
@@ -274,7 +528,7 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
replacer.add(fix);
|
||||
}
|
||||
if (j != splits.length - 1) {
|
||||
replacer.addAll(toUse);
|
||||
replacer.addAll(classicTooltip);
|
||||
}
|
||||
}
|
||||
if (!fnd) {
|
||||
@@ -288,10 +542,361 @@ public class JSONManipulatorCurrent implements JSONManipulator{
|
||||
return replacer;
|
||||
}
|
||||
|
||||
private JsonArray parseArray(JsonArray arr) {
|
||||
JsonArray replacer = new JsonArray();
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
if (arr.get(i).isJsonObject()){
|
||||
JsonObject o = arr.get(i).getAsJsonObject();
|
||||
boolean inside = false;
|
||||
for (String replacement : replaces)
|
||||
if (o.toString().contains(replacement)) {
|
||||
if (inside) {
|
||||
break;
|
||||
}
|
||||
inside = true;
|
||||
}
|
||||
if (!inside) { //the placeholder we're looking for is not inside this element, so we continue searching
|
||||
replacer.add(o);
|
||||
continue;
|
||||
}
|
||||
JsonElement text = o.get("text");
|
||||
if (text == null) {
|
||||
continue;
|
||||
}
|
||||
if (text.getAsString().isEmpty()) {
|
||||
JsonElement el = o.get("extra");
|
||||
if (el == null) {
|
||||
continue;
|
||||
}
|
||||
JsonArray jar = el.getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseArray(jar);
|
||||
o.add("extra", jar);
|
||||
}else{
|
||||
o.remove("extra");
|
||||
}
|
||||
}
|
||||
|
||||
private static String escapeBackslash(String json){
|
||||
json = json.replace("\\", "\\\\");
|
||||
return json;
|
||||
String msg = text.getAsString();
|
||||
boolean isLast = false;
|
||||
boolean done = false;
|
||||
boolean fnd;
|
||||
String[] splits;
|
||||
for (String repls : replaces) {
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
isLast = msg.endsWith(repls);
|
||||
if (isLast) {
|
||||
done = true;
|
||||
msg = msg.concat(".");
|
||||
}
|
||||
}
|
||||
splits = msg.split(rgx);
|
||||
fnd = splits.length != 1;
|
||||
if (fnd)
|
||||
for (int j = 0; j < splits.length; ++j) {
|
||||
boolean endDot = (j == splits.length - 1) && isLast;
|
||||
if (!splits[j].isEmpty() && !endDot) {
|
||||
String st = o.toString();
|
||||
JsonObject fix = PARSER.parse(st).getAsJsonObject();
|
||||
fix.addProperty("text", splits[j]);
|
||||
replacer.add(fix);
|
||||
}
|
||||
if (j != splits.length - 1) {
|
||||
replacer.add(itemTooltip);
|
||||
}
|
||||
}
|
||||
if (!fnd) {
|
||||
replacer.add(o);
|
||||
}
|
||||
}else{
|
||||
if(arr.get(i).isJsonNull()){
|
||||
continue;
|
||||
}else{
|
||||
if(arr.get(i).isJsonArray()){
|
||||
JsonArray jar = arr.get(i).getAsJsonArray();
|
||||
if(jar.size()!=0) {
|
||||
jar = parseArray(arr.get(i).getAsJsonArray());
|
||||
replacer.set(i, jar);
|
||||
}
|
||||
}else{
|
||||
String msg = arr.get(i).getAsString();
|
||||
boolean isLast = false;
|
||||
boolean done = false;
|
||||
boolean fnd;
|
||||
String[] splits;
|
||||
for (String repls : replaces) {
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
isLast = msg.endsWith(repls);
|
||||
if (isLast) {
|
||||
done = true;
|
||||
msg = msg.concat(".");
|
||||
}
|
||||
}
|
||||
splits = msg.split(rgx);
|
||||
fnd = splits.length != 1;
|
||||
if (fnd)
|
||||
for (int j = 0; j < splits.length; ++j) {
|
||||
boolean endDot = (j == splits.length - 1) && isLast;
|
||||
if (!splits[j].isEmpty() && !endDot) {
|
||||
JsonElement fix = new JsonPrimitive(splits[j]);
|
||||
replacer.add(fix);
|
||||
}
|
||||
if (j != splits.length - 1) {
|
||||
replacer.add(itemTooltip);
|
||||
}
|
||||
}
|
||||
if (!fnd) {
|
||||
replacer.add(arr.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return replacer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private Item toItem(ItemStack is) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
|
||||
CompoundTag tag = new CompoundTag("tag");
|
||||
|
||||
Object nmsStack = AS_NMS_COPY.invoke(null, is);
|
||||
Object nmsTag = NBT_TAG_COMPOUND.newInstance();
|
||||
SAVE_NMS_ITEM_STACK_METHOD.invoke(nmsStack, nmsTag);
|
||||
Map<String, Object> nmsMap = (Map<String, Object>) MAP.get(nmsTag);
|
||||
String id = nmsMap.get("id").toString().replace("\"", "");
|
||||
Object realTag = nmsMap.get("tag");
|
||||
if(NBT_TAG_COMPOUND.isInstance(realTag)) { //We need to make sure this is indeed an NBTTagCompound
|
||||
Map<String, Object> realMap = (Map<String, Object>) MAP.get(realTag);
|
||||
Set<Map.Entry<String, Object>> entrySet = realMap.entrySet();
|
||||
Map<String, Tag> map = tag.getValue();
|
||||
for (Map.Entry<String, Object> entry : entrySet) {
|
||||
map.put(entry.getKey(), toOpenTag(entry.getValue(), entry.getKey()));
|
||||
}
|
||||
tag.setValue(map);
|
||||
}
|
||||
|
||||
Item item = new Item();
|
||||
item.setAmount((byte)is.getAmount());
|
||||
item.setData(is.getDurability());
|
||||
item.setId(id);
|
||||
item.setTag(tag);
|
||||
return item;
|
||||
}
|
||||
|
||||
private Tag toOpenTag(Object nmsTag, String name) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
|
||||
if(NBT_TAG_COMPOUND.isInstance(nmsTag)) {
|
||||
CompoundTag tag = new CompoundTag(name);
|
||||
Map<String, Tag> tagMap = tag.getValue();
|
||||
|
||||
Map<String, Object> nmsMap = (Map<String, Object>) MAP.get(nmsTag);
|
||||
Set<Map.Entry<String, Object>> entrySet = nmsMap.entrySet();
|
||||
for (Map.Entry<String, Object> entry : entrySet) {
|
||||
Tag value = toOpenTag(entry.getValue(), entry.getKey());
|
||||
tagMap.put(entry.getKey(), value);
|
||||
}
|
||||
tag.setValue(tagMap);
|
||||
return tag;
|
||||
} else {
|
||||
//Strings are a special case as they need proper escaping
|
||||
if(NBT_STRING.isInstance(nmsTag)) {
|
||||
String toString = nmsTag.toString();
|
||||
if (toString.startsWith("\"") && toString.endsWith("\"")) {
|
||||
toString = toString.substring(1, toString.length() - 1);
|
||||
//toString = escapeSpecials(toString);
|
||||
return new StringTag(name, toString);
|
||||
}
|
||||
}
|
||||
|
||||
//NBTTag Lists are also special, as they are a sort of compound themselves and need to be parsed recursively
|
||||
if(NBT_LIST.isInstance(nmsTag)){
|
||||
List<Object> nmsNBTBaseList = (List<Object>) LIST_FIELD.get(nmsTag);
|
||||
List<Tag> list = new ArrayList<>();
|
||||
int i = 0;
|
||||
for(Object baseTag : nmsNBTBaseList){
|
||||
list.add(toOpenTag(baseTag, String.valueOf(i)));
|
||||
++i;
|
||||
}
|
||||
return new ListTag(name, list);
|
||||
}
|
||||
|
||||
for(int i = 0; i < NBT_BASE_CLASSES.size(); ++i){
|
||||
Class<?> c = NBT_BASE_CLASSES.get(i);
|
||||
if(c.isInstance(nmsTag)){
|
||||
Object value = NBT_BASE_DATA_FIELD.get(i).get(nmsTag);
|
||||
|
||||
Tag t = TYPES_TO_OPEN_NBT_TAGS.get(value.getClass()).getClass().getConstructor(String.class).newInstance(name);
|
||||
|
||||
if(t instanceof ByteTag){
|
||||
((ByteTag)t).setValue((byte)value);
|
||||
((ByteTag)t).setValue((byte)value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof ByteArrayTag){
|
||||
((ByteArrayTag)t).setValue((byte[])value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof DoubleTag){
|
||||
((DoubleTag)t).setValue((double)value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof FloatTag){
|
||||
((FloatTag)t).setValue((float)value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof IntTag){
|
||||
((IntTag)t).setValue((int)value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof IntArrayTag){
|
||||
((IntArrayTag)t).setValue((int[])value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof LongTag){
|
||||
((LongTag)t).setValue((long)value);
|
||||
return t;
|
||||
}
|
||||
if(t instanceof ShortTag){
|
||||
((ShortTag)t).setValue((short)value);
|
||||
return t;
|
||||
}
|
||||
return null; //Should never happen
|
||||
}
|
||||
}
|
||||
return null; //Should never happen
|
||||
}
|
||||
}
|
||||
|
||||
private String stringifyItem(ItemStack stack) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException {
|
||||
Item item = toItem(stack);
|
||||
ProtocolVersion.remapIds(ProtocolVersion.getServerVersion().MAX_VER, protocolVersion.MAX_VER, item);
|
||||
StringBuilder sb = new StringBuilder("{id:");
|
||||
sb.append("\"").append(item.getId()).append("\"").append(","); //Append the id
|
||||
sb.append("Count:").append(item.getAmount()).append("b,"); //Append the amount
|
||||
sb.append("Damage:").append(item.getData()).append("s"); //Append the durability data
|
||||
|
||||
Map<String, Tag> tagMap = item.getTag().getValue();
|
||||
if(tagMap.isEmpty()){
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
Set<Map.Entry<String, Tag>> entrySet = tagMap.entrySet();
|
||||
boolean first = true;
|
||||
sb.append(",tag:{"); //Start of the tag
|
||||
for(Map.Entry<String, Tag> entry : entrySet){
|
||||
String key = entry.getKey();
|
||||
if(IGNORED.contains(key)){
|
||||
continue;
|
||||
}
|
||||
Pattern pattern = Pattern.compile("[{}\\[\\],\":\\\\/]");
|
||||
Matcher matcher = pattern.matcher(key);
|
||||
if(matcher.find()){
|
||||
continue; //Skip invalid keys, as they can cause exceptions client-side
|
||||
}
|
||||
String value = stringifyTag(entry.getValue());
|
||||
if(!first){
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(key).append(":").append(value);
|
||||
first = false;
|
||||
}
|
||||
sb.append("}}"); //End of tag and end of item
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String stringifyTag(Tag normalTag){
|
||||
if(normalTag instanceof CompoundTag) {
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
CompoundTag tagCompound = (CompoundTag) normalTag;
|
||||
Map<String, Tag> tagMap = tagCompound.getValue();
|
||||
Set<Map.Entry<String, Tag>> entrySet = tagMap.entrySet();
|
||||
for(Map.Entry<String, Tag> entry : entrySet){
|
||||
String value = stringifyTag(entry.getValue());
|
||||
if(value == null){
|
||||
continue;
|
||||
}
|
||||
if(sb.length()>1){
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(entry.getKey()).append(":").append(value);
|
||||
}
|
||||
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
} else {
|
||||
if(normalTag instanceof StringTag) {
|
||||
return "\""+((StringTag)normalTag).getValue()+"\""; //Should be already escaped
|
||||
}
|
||||
|
||||
if(normalTag instanceof ListTag){
|
||||
List<Tag> list = ((ListTag)normalTag).getValue();
|
||||
StringBuilder sb = new StringBuilder("[");
|
||||
boolean first = true;
|
||||
for(Tag tag : list){
|
||||
String index = tag.getName();
|
||||
String value = stringifyTag(tag);
|
||||
if(value == null){
|
||||
continue;
|
||||
}
|
||||
if(!first){
|
||||
sb.append(",");
|
||||
}
|
||||
if(protocolVersion.MAX_VER <= ProtocolVersion.V1_11.MAX_VER){ //it's before 1.12
|
||||
sb.append(index).append(":").append(value);
|
||||
}else{
|
||||
sb.append(value);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if(normalTag instanceof ByteTag){
|
||||
return normalTag.getValue()+"b";
|
||||
}
|
||||
if(normalTag instanceof ByteArrayTag){
|
||||
return "["+((byte[])normalTag.getValue()).length+" bytes]";
|
||||
}
|
||||
if(normalTag instanceof DoubleTag){
|
||||
return (double)normalTag.getValue()+"d";
|
||||
}
|
||||
if(normalTag instanceof FloatTag){
|
||||
return (float)normalTag.getValue()+"f";
|
||||
}
|
||||
if(normalTag instanceof IntTag){
|
||||
return String.valueOf((int)normalTag.getValue());
|
||||
}
|
||||
if(normalTag instanceof IntArrayTag){
|
||||
int[] array = (int[])normalTag.getValue();
|
||||
if(array.length == 0){
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("[");
|
||||
boolean first = true;
|
||||
for(int i : array){
|
||||
if(!first){
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(i);
|
||||
first = false;
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
if(normalTag instanceof LongTag){
|
||||
return (long)normalTag.getValue()+"L";
|
||||
}
|
||||
if(normalTag instanceof ShortTag){
|
||||
return (short)normalTag.getValue()+"s";
|
||||
}
|
||||
return null; //Should never happen
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,93 +1,135 @@
|
||||
package me.dadus33.chatitem.json;
|
||||
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
/*
|
||||
Many thanks to @DarkSeraphim for this!
|
||||
I only modified his work to make it compatible with what I was building.
|
||||
*/
|
||||
//Based on DarkSeraphim's system, but using Gson and supporting some more edge cases
|
||||
public class Translator {
|
||||
private static final StringBuilder BUILDER = new StringBuilder("");
|
||||
private static final StringBuilder STYLE = new StringBuilder();
|
||||
private static final JsonParser JSON_PARSER = new JsonParser();
|
||||
|
||||
static String toJSON(String message) {
|
||||
if (message == null || message.isEmpty())
|
||||
return null;
|
||||
String[] parts = message.split(Character.toString(ChatColor.COLOR_CHAR));
|
||||
boolean first = true;
|
||||
String colour = null;
|
||||
String format = null;
|
||||
BUILDER.setLength(0);
|
||||
BUILDER.append("[");
|
||||
boolean ignoreFirst = !parts[0].isEmpty() && ChatColor.getByChar(parts[0].charAt(0)) != null;
|
||||
for (String part : parts) {
|
||||
if (part.isEmpty()) {
|
||||
private static final String STYLES = "klmnor"; //All style codes
|
||||
|
||||
public static JsonArray toJson(String old){
|
||||
if(old.lastIndexOf(ChatColor.COLOR_CHAR) == -1){
|
||||
JsonArray arr = new JsonArray();
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("text", old);
|
||||
arr.add(obj);
|
||||
return arr;
|
||||
}
|
||||
boolean startsWithCode = old.startsWith(Character.toString(ChatColor.COLOR_CHAR));
|
||||
JsonArray message = new JsonArray();
|
||||
String[] parts = old.split(Character.toString(ChatColor.COLOR_CHAR));
|
||||
JsonObject next = null; //refers to the object we created before (in time) but next in the message, as we're going from end to start
|
||||
for(int i = parts.length-1; i >= 0; --i){ //We go in reverse order
|
||||
String part = parts[i];
|
||||
|
||||
if(part.isEmpty()){
|
||||
continue;
|
||||
}
|
||||
|
||||
String newStyle = null;
|
||||
if (!ignoreFirst) {
|
||||
newStyle = getStyle(part.charAt(0));
|
||||
} else {
|
||||
ignoreFirst = false;
|
||||
if(i == 0 && !startsWithCode){
|
||||
JsonObject toAdd = new JsonObject();
|
||||
toAdd.addProperty("text", part);
|
||||
message.add(toAdd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (newStyle != null) {
|
||||
part = part.substring(1);
|
||||
if (newStyle.startsWith("\"c"))
|
||||
colour = newStyle;
|
||||
else
|
||||
format = newStyle;
|
||||
char code = Character.toLowerCase(part.charAt(0));
|
||||
|
||||
if(!isColorOrStyle(code)){
|
||||
if(next != null){
|
||||
String text = next.get("text").getAsString();
|
||||
text = ChatColor.COLOR_CHAR + part + text;
|
||||
next.addProperty("text", text);
|
||||
}else{
|
||||
JsonObject added = new JsonObject();
|
||||
added.addProperty("text", ChatColor.COLOR_CHAR + part);
|
||||
message.add(added);
|
||||
next = added;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!part.isEmpty()) {
|
||||
if (first)
|
||||
first = false;
|
||||
else {
|
||||
BUILDER.append(", ");
|
||||
|
||||
if(part.length() == 1){ //If it's just format and no text, we try to format the next element, if it wasn't formatted already (and it's not null)
|
||||
if(next == null){
|
||||
continue;
|
||||
}
|
||||
BUILDER.append("{");
|
||||
if (colour != null) {
|
||||
BUILDER.append(colour);
|
||||
colour = null;
|
||||
|
||||
if(isStyle(code)){
|
||||
next.addProperty(getStyleName(code), true);
|
||||
}else{ //it's a color
|
||||
if(isAlreadyColored(next)){
|
||||
continue;
|
||||
}
|
||||
next.addProperty("color", getColorName(code));
|
||||
}
|
||||
if (format != null) {
|
||||
BUILDER.append(format);
|
||||
format = null;
|
||||
}
|
||||
BUILDER.append(String.format("\"text\":\"%s\"", part));
|
||||
BUILDER.append("}");
|
||||
continue;
|
||||
}
|
||||
//Last possibility that remains is that we have a normal color/format + text situation
|
||||
JsonObject added = new JsonObject();
|
||||
added.addProperty("text", part.substring(1));
|
||||
if(isStyle(code)){
|
||||
added.addProperty(getStyleName(code), true);
|
||||
message.add(added);
|
||||
next = added;
|
||||
continue;
|
||||
}
|
||||
//else it can only be a color
|
||||
added.addProperty("color", getColorName(code));
|
||||
//also try to color the next element if not colored already
|
||||
if(next != null){
|
||||
if(!isAlreadyColored(next)){
|
||||
next.addProperty("color", getColorName(code));
|
||||
}
|
||||
}
|
||||
message.add(added);
|
||||
next = added;
|
||||
}
|
||||
BUILDER.append("]");
|
||||
return BUILDER.toString();
|
||||
|
||||
int i = message.size()-1;
|
||||
JsonArray orderedMessage = new JsonArray();//This is where we'll store the reverted message (in proper order for being sent to the client)
|
||||
//First we have to copy all elements
|
||||
for(JsonElement el : message){
|
||||
orderedMessage.add(el);
|
||||
}
|
||||
for(JsonElement element : message){
|
||||
JsonObject obj = (JsonObject) element;
|
||||
orderedMessage.set(i, obj); //And then we add the object to the properly ordered array
|
||||
--i;
|
||||
}
|
||||
|
||||
return orderedMessage;
|
||||
}
|
||||
|
||||
private static String getStyle(char colour) {
|
||||
if (STYLE.length() > 0)
|
||||
STYLE.delete(0, STYLE.length());
|
||||
switch (colour) {
|
||||
case 'k':
|
||||
return "\"obfuscated\": true,";
|
||||
case 'l':
|
||||
return "\"bold\": true,";
|
||||
case 'm':
|
||||
return "\"strikethrough\": true,";
|
||||
case 'n':
|
||||
return "\"underlined\": true,";
|
||||
case 'o':
|
||||
return "\"italic\": true,";
|
||||
case 'r':
|
||||
return "\"reset\": true,";
|
||||
default:
|
||||
break;
|
||||
private static String getColorName(char code){
|
||||
return ChatColor.getByChar(code).name().toLowerCase();
|
||||
}
|
||||
|
||||
private static String getStyleName(char code){
|
||||
switch(code){
|
||||
case 'k': return "obfuscated";
|
||||
case 'l': return "bold";
|
||||
case 'm': return "strikethrough";
|
||||
case 'n': return "underlined";
|
||||
case 'o': return "italic";
|
||||
case 'r': return "reset";
|
||||
default: return null; //Should never happen. Made it return null to throw errors if a new format pops up and it really happens
|
||||
}
|
||||
ChatColor cc = ChatColor.getByChar(colour);
|
||||
if (cc == null)
|
||||
return null;
|
||||
return STYLE.append("\"color\":\"").append(cc.name().toLowerCase()).append("\", ").toString();
|
||||
}
|
||||
|
||||
private static boolean isColorOrStyle(char code){
|
||||
return ChatColor.getByChar(code) != null;
|
||||
}
|
||||
|
||||
private static boolean isStyle(char c){
|
||||
return STYLES.indexOf(c) != -1;
|
||||
}
|
||||
|
||||
private static boolean isAlreadyColored(JsonObject obj){
|
||||
return obj.has("color");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,25 +4,76 @@ import me.dadus33.chatitem.utils.Storage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ChatEventListener implements Listener {
|
||||
|
||||
private Storage c;
|
||||
public final static char SEPARATOR = ((char)0x0007);
|
||||
private final static String LEFT = "{remaining}";
|
||||
private final HashMap<String, Long> COOLDOWNS = new HashMap<>();
|
||||
|
||||
public ChatEventListener(Storage storage) {
|
||||
c = storage;
|
||||
}
|
||||
|
||||
private String calculateTime(long seconds){
|
||||
if(seconds < 60){
|
||||
return seconds+c.SECONDS;
|
||||
}
|
||||
if(seconds < 3600){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int minutes = (int) seconds / 60;
|
||||
builder.append(minutes).append(c.MINUTES);
|
||||
int secs = (int) seconds - minutes*60;
|
||||
if(secs != 0){
|
||||
builder.append(" ").append(secs).append(c.SECONDS);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int hours = (int) seconds / 3600;
|
||||
builder.append(hours).append(c.HOURS);
|
||||
int minutes = (int) (seconds/60) - (hours*60);
|
||||
if(minutes != 0){
|
||||
builder.append(" ").append(minutes).append(c.MINUTES);
|
||||
}
|
||||
int secs = (int) (seconds - ((seconds/60)*60));
|
||||
if(secs != 0){
|
||||
builder.append(" ").append(secs).append(c.SECONDS);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private int countOccurrences(String findStr, String str){
|
||||
int lastIndex = 0;
|
||||
int count = 0;
|
||||
while(lastIndex != -1){
|
||||
|
||||
lastIndex = str.indexOf(findStr,lastIndex);
|
||||
|
||||
if(lastIndex != -1){
|
||||
count++;
|
||||
lastIndex += findStr.length();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onChat(AsyncPlayerChatEvent e) {
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) //We need to have lowest priority in order to get to the event before DeluxeChat or other plugins do
|
||||
public void onChat(final AsyncPlayerChatEvent e) {
|
||||
if(e.getMessage().indexOf(SEPARATOR)!=-1){ //If the BELL character is found, we have to remove it
|
||||
String msg = e.getMessage().replace(Character.toString(SEPARATOR), "");
|
||||
e.setMessage(msg);
|
||||
}
|
||||
boolean found = false;
|
||||
|
||||
for (String rep : c.PLACEHOLDERS)
|
||||
@@ -30,60 +81,172 @@ public class ChatEventListener implements Listener {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
if (!e.getPlayer().hasPermission("chatitem.use")) {
|
||||
e.setCancelled(true);
|
||||
|
||||
Player p = e.getPlayer();
|
||||
|
||||
if (!p.hasPermission("chatitem.use")) {
|
||||
if(!c.LET_MESSAGE_THROUGH) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
if(!c.NO_PERMISSION_MESSAGE.isEmpty() && c.SHOW_NO_PERM_NORMAL){
|
||||
p.sendMessage(c.NO_PERMISSION_MESSAGE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (e.getPlayer().getItemInHand().getType().equals(Material.AIR)) {
|
||||
if (p.getItemInHand().getType().equals(Material.AIR)) {
|
||||
if (c.DENY_IF_NO_ITEM) {
|
||||
e.setCancelled(true);
|
||||
if (!c.DENY_MESSAGE.isEmpty())
|
||||
e.getPlayer().sendMessage(c.DENY_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if(c.HAND_DISABLED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(c.COOLDOWN > 0 && !p.hasPermission("chatitem.ignore-cooldown")){
|
||||
if(COOLDOWNS.containsKey(p.getName())){
|
||||
long start = COOLDOWNS.get(p.getName());
|
||||
long current = System.currentTimeMillis()/1000;
|
||||
long elapsed = current - start;
|
||||
if(elapsed >= c.COOLDOWN){
|
||||
COOLDOWNS.remove(p.getName());
|
||||
}else{
|
||||
if(!c.LET_MESSAGE_THROUGH) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
if(!c.COOLDOWN_MESSAGE.isEmpty()){
|
||||
long left = (start + c.COOLDOWN) - current;
|
||||
p.sendMessage(c.COOLDOWN_MESSAGE.replace(LEFT, calculateTime(left)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
String s = e.getMessage();
|
||||
for(String placeholder : c.PLACEHOLDERS){
|
||||
s = s.replace(placeholder, c.PLACEHOLDERS.get(0));
|
||||
}
|
||||
int occurrences = countOccurrences(c.PLACEHOLDERS.get(0), s);
|
||||
|
||||
if(occurrences>c.LIMIT){
|
||||
e.setCancelled(true);
|
||||
if(c.LIMIT_MESSAGE.isEmpty()){
|
||||
return;
|
||||
}
|
||||
e.getPlayer().sendMessage(c.LIMIT_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String oldmsg = e.getMessage();
|
||||
e.setMessage(e.getMessage().concat(e.getPlayer().getName()));
|
||||
StringBuilder sb = new StringBuilder(oldmsg);
|
||||
sb.append(SEPARATOR).append(e.getPlayer().getName());
|
||||
e.setMessage(sb.toString());
|
||||
Bukkit.getConsoleSender().sendMessage(String.format(e.getFormat(), e.getPlayer().getDisplayName(), oldmsg));
|
||||
if(!p.hasPermission("chatitem.ignore-cooldown")) {
|
||||
COOLDOWNS.put(p.getName(), System.currentTimeMillis() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onCommand(PlayerCommandPreprocessEvent e){
|
||||
Command cmd = Bukkit.getPluginCommand(e.getMessage().split(" ")[0].substring(1));
|
||||
if(!c.ALLOWED_COMMANDS.contains(cmd)){
|
||||
return;
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onCommand(final PlayerCommandPreprocessEvent e){
|
||||
if(e.getMessage().indexOf(SEPARATOR)!=-1){ //If the BELL character is found, we have to remove it
|
||||
String msg = e.getMessage().replace(Character.toString(SEPARATOR), "");
|
||||
e.setMessage(msg);
|
||||
}
|
||||
String commandString = e.getMessage().split(" ")[0].replaceAll("^/+", ""); //First part of the command, without leading slashes and without arguments
|
||||
Command cmd = Bukkit.getPluginCommand(commandString);
|
||||
if(cmd==null){ //not a plugin command
|
||||
if(!c.ALLOWED_DEFAULT_COMMANDS.contains(commandString)){
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
if(!c.ALLOWED_PLUGIN_COMMANDS.contains(cmd)){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Player p = e.getPlayer();
|
||||
boolean found = false;
|
||||
|
||||
for (String rep : c.PLACEHOLDERS)
|
||||
for (String rep : c.PLACEHOLDERS) {
|
||||
if (e.getMessage().contains(rep)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
if (!e.getPlayer().hasPermission("chatitem.use")) {
|
||||
|
||||
if (!p.hasPermission("chatitem.use")) {
|
||||
if(!c.NO_PERMISSION_MESSAGE.isEmpty() && c.SHOW_NO_PERM_COMMAND){
|
||||
p.sendMessage(c.NO_PERMISSION_MESSAGE);
|
||||
}
|
||||
e.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.getPlayer().getItemInHand().getType().equals(Material.AIR)) {
|
||||
if (c.DENY_IF_NO_ITEM) {
|
||||
e.setCancelled(true);
|
||||
if (!c.DENY_MESSAGE.isEmpty())
|
||||
if (!c.DENY_MESSAGE.isEmpty()) {
|
||||
e.getPlayer().sendMessage(c.DENY_MESSAGE);
|
||||
}
|
||||
}
|
||||
if(c.HAND_DISABLED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(c.COOLDOWN > 0 && !p.hasPermission("chatitem.ignore-cooldown")){
|
||||
if(COOLDOWNS.containsKey(p.getName())){
|
||||
long start = COOLDOWNS.get(p.getName());
|
||||
long current = System.currentTimeMillis()/1000;
|
||||
long elapsed = current - start;
|
||||
if(elapsed >= c.COOLDOWN){
|
||||
COOLDOWNS.remove(p.getName());
|
||||
}else{
|
||||
if(!c.LET_MESSAGE_THROUGH) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
if(!c.COOLDOWN_MESSAGE.isEmpty()){
|
||||
long left = (start + c.COOLDOWN) - current;
|
||||
p.sendMessage(c.COOLDOWN_MESSAGE.replace(LEFT, calculateTime(left)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String s = e.getMessage();
|
||||
for(String placeholder : c.PLACEHOLDERS){
|
||||
s = s.replace(placeholder, c.PLACEHOLDERS.get(0));
|
||||
}
|
||||
int occurrences = countOccurrences(c.PLACEHOLDERS.get(0), s);
|
||||
|
||||
if(occurrences>c.LIMIT){
|
||||
e.setCancelled(true);
|
||||
if(c.LIMIT_MESSAGE.isEmpty()){
|
||||
return;
|
||||
}
|
||||
e.getPlayer().sendMessage(c.LIMIT_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
e.setMessage(e.getMessage().concat(e.getPlayer().getName()));
|
||||
|
||||
StringBuilder sb = new StringBuilder(e.getMessage());
|
||||
sb.append(SEPARATOR).append(e.getPlayer().getName());
|
||||
e.setMessage(sb.toString());
|
||||
if(!p.hasPermission("chatitem.ignore-cooldown")) {
|
||||
COOLDOWNS.put(p.getName(), System.currentTimeMillis() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,35 +1,57 @@
|
||||
package me.dadus33.chatitem.listeners;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BlockStateMeta;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
@Deprecated
|
||||
public class ChatPacketListener extends PacketAdapter {
|
||||
|
||||
private final static String NAME = "{name}";
|
||||
private final static String AMOUNT = "{amount}";
|
||||
private final static String TIMES = "{times}";
|
||||
private ChatItem instance;
|
||||
private final static List<Material> SHULKER_BOXES = new ArrayList<>();
|
||||
|
||||
private Storage c;
|
||||
|
||||
|
||||
public ChatPacketListener(Plugin plugin, ListenerPriority listenerPriority, Storage s, PacketType... types) {
|
||||
super(plugin, listenerPriority, types);
|
||||
this.instance = (ChatItem) plugin;
|
||||
if(ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_11)){
|
||||
SHULKER_BOXES.addAll(Arrays.asList(Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
|
||||
Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX,
|
||||
Material.LIGHT_BLUE_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
|
||||
Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX,
|
||||
Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX));
|
||||
}
|
||||
c = s;
|
||||
}
|
||||
|
||||
@@ -54,121 +76,158 @@ public class ChatPacketListener extends PacketAdapter {
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent e) {
|
||||
if(ChatItem.post17)
|
||||
if (e.getPacket().getBytes().readSafely(0) == (byte) 2) {
|
||||
return; //It means it's an actionbar message, and we ain't intercepting those
|
||||
}
|
||||
|
||||
PacketContainer packet = e.getPacket();
|
||||
String json = packet.getChatComponents().readSafely(0).getJson();
|
||||
|
||||
boolean found = false;
|
||||
for (String rep : c.PLACEHOLDERS)
|
||||
if (json.contains(rep)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
return; //then it's just a normal message without placeholders, so we leave it alone
|
||||
}
|
||||
int topIndex = -1;
|
||||
String name = null;
|
||||
for(Player p : Bukkit.getOnlinePlayers()){
|
||||
String pname = p.getName();
|
||||
if(!json.contains(pname)){
|
||||
continue;
|
||||
}
|
||||
int index = json.lastIndexOf(pname)+pname.length();
|
||||
if(index>topIndex){
|
||||
topIndex = index;
|
||||
name = pname;
|
||||
}
|
||||
}
|
||||
if(name==null){ //something went really bad, so we run away and hide
|
||||
private void stripData(ItemStack i){
|
||||
if(i == null){
|
||||
return;
|
||||
}
|
||||
Player p = Bukkit.getPlayer(name);
|
||||
StringBuilder buff = new StringBuilder(json);
|
||||
buff.replace(topIndex-name.length(), topIndex, "");
|
||||
json = buff.toString();
|
||||
|
||||
|
||||
//STYLE BEGIN
|
||||
|
||||
ItemStack inHand = p.getItemInHand();
|
||||
String replacer = c.NAME_FORMAT;
|
||||
String amount = c.AMOUNT_FORMAT;
|
||||
boolean dname = false;
|
||||
if (!c.COLOR_IF_ALREADY_COLORED && inHand.hasItemMeta()) {
|
||||
if (inHand.getItemMeta().hasDisplayName()) {
|
||||
replacer = ChatColor.stripColor(replacer);
|
||||
dname = true;
|
||||
}
|
||||
} else {
|
||||
if (inHand.hasItemMeta()) {
|
||||
if (inHand.getItemMeta().hasDisplayName()) {
|
||||
dname = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inHand.getAmount() == 1) {
|
||||
if (c.FORCE_ADD_AMOUNT) {
|
||||
amount = amount.replace(TIMES, "1");
|
||||
replacer = replacer.replace(AMOUNT, amount);
|
||||
} else {
|
||||
replacer = replacer.replace(AMOUNT, "");
|
||||
}
|
||||
} else {
|
||||
amount = amount.replace(TIMES, String.valueOf(inHand.getAmount()));
|
||||
replacer = replacer.replace(AMOUNT, amount);
|
||||
}
|
||||
if (dname) {
|
||||
String trp = inHand.getItemMeta().getDisplayName();
|
||||
replacer = replacer.replace(NAME, trp);
|
||||
} else {
|
||||
HashMap<Short, String> translationSection = c.TRANSLATIONS.get(inHand.getType().name());
|
||||
if(translationSection==null){
|
||||
String trp = materialToName(inHand.getType());
|
||||
replacer = replacer.replace(NAME, trp);
|
||||
}else {
|
||||
String translated = translationSection.get(inHand.getDurability());
|
||||
if (translated != null) {
|
||||
replacer = replacer.replace(NAME, translated);
|
||||
} else {
|
||||
replacer = replacer.replace(NAME, materialToName(inHand.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//STYLE END
|
||||
|
||||
String[] reps = new String[c.PLACEHOLDERS.size()];
|
||||
c.PLACEHOLDERS.toArray(reps);
|
||||
String message = null;
|
||||
try {
|
||||
if(!p.getItemInHand().getType().equals(Material.AIR))
|
||||
message = ChatItem.getManipulator().parse(json, reps, p.getItemInHand(), replacer);
|
||||
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e1) {
|
||||
e1.printStackTrace();
|
||||
if(i.getType().equals(Material.AIR)){
|
||||
return;
|
||||
}
|
||||
if(message!=null)
|
||||
packet.getChatComponents().writeSafely(0, WrappedChatComponent.fromJson(message));
|
||||
else
|
||||
e.setCancelled(true);
|
||||
if(!i.hasItemMeta()){
|
||||
return;
|
||||
}
|
||||
ItemMeta im = Bukkit.getItemFactory().getItemMeta(i.getType());
|
||||
ItemMeta original = i.getItemMeta();
|
||||
if(original.hasDisplayName()){
|
||||
im.setDisplayName(original.getDisplayName());
|
||||
}
|
||||
i.setItemMeta(im);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(final PacketEvent e) {
|
||||
if(e.isPlayerTemporary())
|
||||
return;
|
||||
final PacketContainer packet = e.getPacket();
|
||||
if(!packet.getMeta("parse").isPresent()){ //First we check if the packet validator has validated this packet to be parsed by us
|
||||
return;
|
||||
}
|
||||
final boolean usesBaseComponents = (boolean)packet.getMeta("base-component").orElse(false); //The packet validator should also tell if this packet uses base components
|
||||
e.setCancelled(true); //We cancel the packet as we're going to resend it anyways (ignoring listeners this time)
|
||||
Bukkit.getScheduler().runTaskAsynchronously(ChatItem.getInstance(), () -> {
|
||||
String json = (String)packet.getMeta("json").orElse(""); //The packet validator got the json for us, so no need to get it again
|
||||
int topIndex = -1;
|
||||
String name = null;
|
||||
for(Player p : Bukkit.getOnlinePlayers()){
|
||||
String pname = "\\u0007"+p.getName();
|
||||
if(!json.contains(pname)){
|
||||
continue;
|
||||
}
|
||||
int index = json.lastIndexOf(pname)+pname.length();
|
||||
if(index>topIndex){
|
||||
topIndex = index;
|
||||
name = pname.replace("\\u0007", "");
|
||||
}
|
||||
}
|
||||
if(name==null){ //something went really bad, so we run away and hide (AKA the player left or is on another server)
|
||||
return;
|
||||
}
|
||||
|
||||
Player p = Bukkit.getPlayer(name);
|
||||
StringBuilder builder = new StringBuilder(json);
|
||||
builder.replace(topIndex-(name.length()+6), topIndex, ""); //we remove both the name and the separator from the json string
|
||||
json = builder.toString();
|
||||
|
||||
String message = null;
|
||||
try {
|
||||
if(!p.getItemInHand().getType().equals(Material.AIR)) {
|
||||
ItemStack copy = p.getItemInHand().clone();
|
||||
if(copy.getType().name().contains("_BOOK")){ //filtering written books
|
||||
BookMeta bm = (BookMeta)copy.getItemMeta();
|
||||
bm.setPages(Collections.emptyList());
|
||||
copy.setItemMeta(bm);
|
||||
} else {
|
||||
if (ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_11)) { //filtering shulker boxes
|
||||
if (SHULKER_BOXES.contains(copy.getType())) {
|
||||
if (copy.hasItemMeta()) {
|
||||
BlockStateMeta bsm = (BlockStateMeta) copy.getItemMeta();
|
||||
if (bsm.hasBlockState()) {
|
||||
ShulkerBox sb = (ShulkerBox) bsm.getBlockState();
|
||||
for (ItemStack item : sb.getInventory()) {
|
||||
stripData(item);
|
||||
}
|
||||
bsm.setBlockState(sb);
|
||||
}
|
||||
copy.setItemMeta(bsm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message = ChatItem.getManipulator().parse(json, c.PLACEHOLDERS, copy, styleItem(copy, c), ProtocolVersion.getClientVersion(e.getPlayer()));
|
||||
} else {
|
||||
if(!c.HAND_DISABLED){
|
||||
message = ChatItem.getManipulator().parseEmpty(json, c.PLACEHOLDERS, c.HAND_NAME, c.HAND_TOOLTIP, p);
|
||||
}
|
||||
}
|
||||
} catch (InvocationTargetException | IllegalAccessException | InstantiationException | NoSuchFieldException | NoSuchMethodException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
if(message!=null) {
|
||||
if(!usesBaseComponents) {
|
||||
packet.getChatComponents().writeSafely(0, WrappedChatComponent.fromJson(message));
|
||||
}else{
|
||||
packet.getSpecificModifier(BaseComponent[].class).writeSafely(0, ComponentSerializer.parse(message));
|
||||
}
|
||||
try {
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(e.getPlayer(), packet, false);
|
||||
} catch (InvocationTargetException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(e.getPlayer(), packet, false);
|
||||
} catch (InvocationTargetException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setStorage(Storage st) {
|
||||
c = st;
|
||||
}
|
||||
|
||||
public static String styleItem(ItemStack item, Storage c){
|
||||
String replacer = c.NAME_FORMAT;
|
||||
String amount = c.AMOUNT_FORMAT;
|
||||
boolean dname = item.hasItemMeta() ? item.getItemMeta().hasDisplayName() : false;
|
||||
|
||||
if (item.getAmount() == 1) {
|
||||
if (c.FORCE_ADD_AMOUNT) {
|
||||
amount = amount.replace(TIMES, "1");
|
||||
replacer = replacer.replace(AMOUNT, amount);
|
||||
} else {
|
||||
replacer = replacer.replace(AMOUNT, "");
|
||||
}
|
||||
} else {
|
||||
amount = amount.replace(TIMES, String.valueOf(item.getAmount()));
|
||||
replacer = replacer.replace(AMOUNT, amount);
|
||||
}
|
||||
if (dname) {
|
||||
String trp = item.getItemMeta().getDisplayName();
|
||||
if(c.COLOR_IF_ALREADY_COLORED){
|
||||
replacer = replacer.replace(NAME, ChatColor.stripColor(trp));
|
||||
} else {
|
||||
replacer = replacer.replace(NAME, trp);
|
||||
}
|
||||
} else {
|
||||
HashMap<Short, String> translationSection = c.TRANSLATIONS.get(item.getType().name());
|
||||
if(translationSection==null){
|
||||
String trp = materialToName(item.getType());
|
||||
replacer = replacer.replace(NAME, trp);
|
||||
}else {
|
||||
String translated = translationSection.get(item.getDurability());
|
||||
if (translated != null) {
|
||||
replacer = replacer.replace(NAME, translated);
|
||||
} else {
|
||||
replacer = replacer.replace(NAME, materialToName(item.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return replacer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package me.dadus33.chatitem.listeners;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BlockStateMeta;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketContent;
|
||||
import me.dadus33.chatitem.packets.PacketHandler;
|
||||
import me.dadus33.chatitem.packets.PacketMetadata;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
public class ChatPacketListenerV2 extends PacketHandler {
|
||||
|
||||
private final static String NAME = "{name}";
|
||||
private final static String AMOUNT = "{amount}";
|
||||
private final static String TIMES = "{times}";
|
||||
|
||||
private Storage c;
|
||||
|
||||
public ChatPacketListenerV2(Storage s) {
|
||||
c = s;
|
||||
}
|
||||
|
||||
private static String materialToName(Material m) {
|
||||
if (m.equals(Material.TNT)) {
|
||||
return "TNT";
|
||||
}
|
||||
String orig = m.toString().toLowerCase();
|
||||
String[] splits = orig.split("_");
|
||||
StringBuilder sb = new StringBuilder(orig.length());
|
||||
int pos = 0;
|
||||
for (String split : splits) {
|
||||
sb.append(split);
|
||||
int loc = sb.lastIndexOf(split);
|
||||
char charLoc = sb.charAt(loc);
|
||||
if (!(split.equalsIgnoreCase("of") || split.equalsIgnoreCase("and") || split.equalsIgnoreCase("with")
|
||||
|| split.equalsIgnoreCase("on")))
|
||||
sb.setCharAt(loc, Character.toUpperCase(charLoc));
|
||||
if (pos != splits.length - 1)
|
||||
sb.append(' ');
|
||||
++pos;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void stripData(ItemStack i) {
|
||||
if (i == null) {
|
||||
return;
|
||||
}
|
||||
if (i.getType().equals(Material.AIR)) {
|
||||
return;
|
||||
}
|
||||
if (!i.hasItemMeta()) {
|
||||
return;
|
||||
}
|
||||
ItemMeta im = Bukkit.getItemFactory().getItemMeta(i.getType());
|
||||
ItemMeta original = i.getItemMeta();
|
||||
if (original.hasDisplayName()) {
|
||||
im.setDisplayName(original.getDisplayName());
|
||||
}
|
||||
i.setItemMeta(im);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onSend(ChatItemPacket e) {
|
||||
if(!e.hasPlayer() || !e.getPacketType().equals(PacketType.Server.CHAT)) {
|
||||
return;
|
||||
}
|
||||
final PacketContent packet = e.getContent();
|
||||
final PacketMetadata meta = e.getMetaData();
|
||||
if (!meta.hasMeta("parse")) { // First we check if the packet validator has validated this packet to be parsed
|
||||
// by us
|
||||
ChatItem.debug("Can't parse : " + e.getPacket());
|
||||
meta.getMetas().forEach((s, o) -> ChatItem.debug("metaData: " + s + " > " + o));
|
||||
return;
|
||||
}
|
||||
meta.removeMeta("parse");
|
||||
boolean usesBaseComponents = meta.getMeta("base-component", false); // The packet validator should also tell if this
|
||||
// packet uses base
|
||||
// components
|
||||
e.setCancelled(true); // We cancel the packet as we're going to resend it anyways (ignoring listeners
|
||||
// this time)
|
||||
Bukkit.getScheduler().runTaskAsynchronously(ChatItem.getInstance(), () -> {
|
||||
String json = (String) meta.getMeta("json", ""); // The packet validator got the json for us, so no need to
|
||||
// get it again
|
||||
|
||||
int topIndex = -1;
|
||||
String name = null;
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
String pname = "\\u0007" + p.getName();
|
||||
if (!json.contains(pname)) {
|
||||
continue;
|
||||
}
|
||||
int index = json.lastIndexOf(pname) + pname.length();
|
||||
if (index > topIndex) {
|
||||
topIndex = index;
|
||||
name = pname.replace("\\u0007", "");
|
||||
}
|
||||
}
|
||||
if (name == null) { // something went really bad, so we run away and hide (AKA the player left or is
|
||||
// on another server)
|
||||
ChatItem.debug("Name null for " + json);
|
||||
return;
|
||||
}
|
||||
|
||||
Player p = Bukkit.getPlayer(name);
|
||||
StringBuilder builder = new StringBuilder(json);
|
||||
builder.replace(topIndex - (name.length() + 6), topIndex, ""); // we remove both the name and the separator
|
||||
// from the json string
|
||||
json = builder.toString();
|
||||
|
||||
String message = null;
|
||||
try {
|
||||
if (!p.getItemInHand().getType().equals(Material.AIR)) {
|
||||
ItemStack copy = p.getItemInHand().clone();
|
||||
if (copy.getType().name().contains("_BOOK")) { // filtering written books
|
||||
BookMeta bm = (BookMeta) copy.getItemMeta();
|
||||
bm.setPages(Collections.emptyList());
|
||||
copy.setItemMeta(bm);
|
||||
} else {
|
||||
if (copy.getType().name().contains("SHULKER_BOX")) { // if it's shulker
|
||||
if (copy.hasItemMeta()) {
|
||||
BlockStateMeta bsm = (BlockStateMeta) copy.getItemMeta();
|
||||
if (bsm.hasBlockState()) {
|
||||
ShulkerBox sb = (ShulkerBox) bsm.getBlockState();
|
||||
for (ItemStack item : sb.getInventory()) {
|
||||
stripData(item);
|
||||
}
|
||||
bsm.setBlockState(sb);
|
||||
}
|
||||
copy.setItemMeta(bsm);
|
||||
}
|
||||
}
|
||||
}
|
||||
message = ChatItem.getManipulator().parse(json, c.PLACEHOLDERS, copy, styleItem(copy, c),
|
||||
ProtocolVersion.getClientVersion(e.getPlayer()));
|
||||
} else {
|
||||
if (!c.HAND_DISABLED) {
|
||||
message = ChatItem.getManipulator().parseEmpty(json, c.PLACEHOLDERS, c.HAND_NAME,
|
||||
c.HAND_TOOLTIP, p);
|
||||
}
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
if (message != null) {
|
||||
if (!usesBaseComponents) {
|
||||
packet.getChatComponents().write(0, jsonToChatComponent(message));
|
||||
} else {
|
||||
packet.getSpecificModifier(BaseComponent[].class).write(0, ComponentSerializer.parse(message));
|
||||
}
|
||||
}
|
||||
PacketUtils.sendPacket(p, e.getPacket());
|
||||
});
|
||||
}
|
||||
|
||||
private Object jsonToChatComponent(String json) {
|
||||
try {
|
||||
return PacketUtils.CHAT_SERIALIZER.getMethod("a", String.class).invoke(null, json);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setStorage(Storage st) {
|
||||
c = st;
|
||||
}
|
||||
|
||||
public static String styleItem(ItemStack item, Storage c) {
|
||||
String replacer = c.NAME_FORMAT;
|
||||
String amount = c.AMOUNT_FORMAT;
|
||||
boolean dname = item.hasItemMeta() ? item.getItemMeta().hasDisplayName() : false;
|
||||
|
||||
if (item.getAmount() == 1) {
|
||||
if (c.FORCE_ADD_AMOUNT) {
|
||||
amount = amount.replace(TIMES, "1");
|
||||
replacer = replacer.replace(AMOUNT, amount);
|
||||
} else {
|
||||
replacer = replacer.replace(AMOUNT, "");
|
||||
}
|
||||
} else {
|
||||
amount = amount.replace(TIMES, String.valueOf(item.getAmount()));
|
||||
replacer = replacer.replace(AMOUNT, amount);
|
||||
}
|
||||
if (dname) {
|
||||
String trp = item.getItemMeta().getDisplayName();
|
||||
if (c.COLOR_IF_ALREADY_COLORED) {
|
||||
replacer = replacer.replace(NAME, ChatColor.stripColor(trp));
|
||||
} else {
|
||||
replacer = replacer.replace(NAME, trp);
|
||||
}
|
||||
} else {
|
||||
HashMap<Short, String> translationSection = c.TRANSLATIONS.get(item.getType().name());
|
||||
if (translationSection == null) {
|
||||
String trp = materialToName(item.getType());
|
||||
replacer = replacer.replace(NAME, trp);
|
||||
} else {
|
||||
@SuppressWarnings("deprecation")
|
||||
String translated = translationSection.get(item.getDurability());
|
||||
if (translated != null) {
|
||||
replacer = replacer.replace(NAME, translated);
|
||||
} else {
|
||||
replacer = replacer.replace(NAME, materialToName(item.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return replacer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package me.dadus33.chatitem.listeners;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
@Deprecated
|
||||
public class ChatPacketValidator extends PacketAdapter {
|
||||
|
||||
private Storage c;
|
||||
|
||||
public ChatPacketValidator(Plugin plugin, ListenerPriority listenerPriority, Storage s, PacketType... types) {
|
||||
super(plugin, listenerPriority, types);
|
||||
c = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent e){
|
||||
if(e.isPlayerTemporary())
|
||||
return;
|
||||
ProtocolVersion version = ProtocolVersion.getServerVersion();
|
||||
if(version.isNewerOrEquals(ProtocolVersion.V1_8)) { //only if action bar messages are supported in this version of minecraft
|
||||
if(version.isNewerOrEquals(ProtocolVersion.V1_12)){
|
||||
try {
|
||||
if(((Enum<?>)e.getPacket().getSpecificModifier(Class.forName("net.minecraft.server." + ProtocolVersion.BUKKIT_VERSION + ".ChatMessageType")).read(0)).name().equals("GAME_INFO")){
|
||||
return; //It means it's an actionbar message, and we ain't intercepting those
|
||||
}
|
||||
} catch (Exception exc) {
|
||||
exc.printStackTrace();
|
||||
}
|
||||
} else if (e.getPacket().getBytes().readSafely(0) == (byte) 2) {
|
||||
return; //It means it's an actionbar message, and we ain't intercepting those
|
||||
}
|
||||
}
|
||||
boolean usesBaseComponents = false;
|
||||
PacketContainer packet = e.getPacket();
|
||||
String json;
|
||||
if(packet.getChatComponents().readSafely(0)==null){ //null check for some cases of messages sent using spigot's Chat Component API or other means
|
||||
if(ChatItem.supportsChatComponentApi()){ //only if the API is supported in this server distribution
|
||||
BaseComponent[] components = packet.getSpecificModifier(BaseComponent[].class).readSafely(0);
|
||||
if(components == null){
|
||||
return;
|
||||
}
|
||||
json = ComponentSerializer.toString(components);
|
||||
usesBaseComponents = true;
|
||||
}else{
|
||||
return; //We don't know how to deal with anything else. Most probably some mod message we shouldn't mess with anyways
|
||||
}
|
||||
}else{
|
||||
json = packet.getChatComponents().readSafely(0).getJson();
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
for (String rep : c.PLACEHOLDERS)
|
||||
if (json.contains(rep)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
return; //then it's just a normal message without placeholders, so we leave it alone
|
||||
}
|
||||
if(json.lastIndexOf("\\u0007")==-1){ //if the message doesn't contain the BELL separator, then it's certainly NOT a message we want to parse
|
||||
return;
|
||||
}
|
||||
|
||||
packet.setMeta("parse", true); //We mark this packet to be parsed by the packet listener
|
||||
packet.setMeta("base-component", usesBaseComponents); //We also tell it whether this packet uses the base component API
|
||||
packet.setMeta("json", json); //And we finally provide it with the json we already got from the packet
|
||||
}
|
||||
|
||||
public void setStorage(Storage st){
|
||||
c = st;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package me.dadus33.chatitem.listeners;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketContent;
|
||||
import me.dadus33.chatitem.packets.PacketHandler;
|
||||
import me.dadus33.chatitem.packets.PacketMetadata;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Storage;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
public class ChatPacketValidatorV2 extends PacketHandler {
|
||||
|
||||
private Storage c;
|
||||
private Method serializerGetJson;
|
||||
|
||||
public ChatPacketValidatorV2(Storage s) {
|
||||
c = s;
|
||||
try {
|
||||
for(Method m : PacketUtils.CHAT_SERIALIZER.getDeclaredMethods()) {
|
||||
if(m.getParameterCount() == 1 && m.getParameterTypes()[0].equals(PacketUtils.COMPONENT_CLASS) && m.getReturnType().equals(String.class)) {
|
||||
serializerGetJson = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(serializerGetJson == null)
|
||||
ChatItem.getInstance().getLogger().warning("Failed to find JSON serializer in class: " + PacketUtils.CHAT_SERIALIZER.getCanonicalName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSend(ChatItemPacket e) {
|
||||
if(!e.hasPlayer() || !e.getPacketType().equals(PacketType.Server.CHAT))
|
||||
return;
|
||||
ProtocolVersion version = ProtocolVersion.getServerVersion();
|
||||
if(version.isNewerOrEquals(ProtocolVersion.V1_8)) { //only if action bar messages are supported in this version of minecraft
|
||||
if(version.isNewerOrEquals(ProtocolVersion.V1_12)){
|
||||
try {
|
||||
if(((Enum<?>)e.getContent().getSpecificModifier(PacketUtils.getNmsClass("ChatMessageType", "network.chat.")).read(0)).name().equals("GAME_INFO")){
|
||||
return; //It means it's an actionbar message, and we ain't intercepting those
|
||||
}
|
||||
} catch (Exception exc) {
|
||||
exc.printStackTrace();
|
||||
}
|
||||
} else if (e.getContent().getBytes().readSafely(0) == (byte) 2) {
|
||||
return; //It means it's an actionbar message, and we ain't intercepting those
|
||||
}
|
||||
}
|
||||
boolean usesBaseComponents = false;
|
||||
PacketContent packet = e.getContent();
|
||||
String json;
|
||||
if(packet.getChatComponents().readSafely(0) == null){ //null check for some cases of messages sent using spigot's Chat Component API or other means
|
||||
if(!ChatItem.supportsChatComponentApi()) //only if the API is supported in this server distribution
|
||||
return;//We don't know how to deal with anything else. Most probably some mod message we shouldn't mess with anyways
|
||||
|
||||
BaseComponent[] components = packet.getSpecificModifier(BaseComponent[].class).readSafely(0);
|
||||
if(components == null){
|
||||
return;
|
||||
}
|
||||
json = ComponentSerializer.toString(components);
|
||||
usesBaseComponents = true;
|
||||
} else {
|
||||
try {
|
||||
json = (String) serializerGetJson.invoke(null, packet.getChatComponents().readSafely(0));// (String) ReflectionUtils.callMethod(packet.getChatComponents().readSafely(0), "getJson");
|
||||
} catch (Exception exc) {
|
||||
exc.printStackTrace();
|
||||
json = "{}";
|
||||
}
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
for (String rep : c.PLACEHOLDERS) {
|
||||
if (json.contains(rep)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return; //then it's just a normal message without placeholders, so we leave it alone
|
||||
}
|
||||
if(json.lastIndexOf("\\u0007") == -1){ //if the message doesn't contain the BELL separator, then it's certainly NOT a message we want to parse
|
||||
ChatItem.debug("Not containb bell " + json);
|
||||
return;
|
||||
}
|
||||
PacketMetadata meta = e.getMetaData();
|
||||
meta.setMeta("parse", true); //We mark this packet to be parsed by the packet listener
|
||||
meta.setMeta("base-component", usesBaseComponents); //We also tell it whether this packet uses the base component API
|
||||
meta.setMeta("json", json); //And we finally provide it with the json we already got from the packet
|
||||
}
|
||||
|
||||
public void setStorage(Storage st){
|
||||
c = st;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package me.dadus33.chatitem.listeners;
|
||||
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.playerversion.hooks.DefaultVersionHook;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
|
||||
public class HandshakeListener extends PacketAdapter{
|
||||
|
||||
public HandshakeListener() {
|
||||
super(ChatItem.getInstance(), ListenerPriority.MONITOR, PacketType.Handshake.Client.SET_PROTOCOL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(final PacketEvent e){
|
||||
PacketType.Protocol p = e.getPacket().getProtocols().read(0);
|
||||
if(p == PacketType.Protocol.STATUS || p == PacketType.Protocol.LEGACY){
|
||||
return;
|
||||
}
|
||||
final int version = e.getPacket().getIntegers().readSafely(0);
|
||||
//Delay the mapping to make sure the true address of the player was received when using bungeecord or other types of proxies
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), () ->
|
||||
DefaultVersionHook.PROTOCOL_PER_ADDRESS.put(ProtocolVersion.stringifyAdress(e.getPlayer().getAddress()), version), 10L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ChatItemPacket {
|
||||
|
||||
protected Player player;
|
||||
protected Object packet;
|
||||
protected PacketType type;
|
||||
protected boolean cancel = false;
|
||||
protected PacketMetadata metaData = new PacketMetadata();
|
||||
|
||||
public ChatItemPacket(PacketType type, Object packet, Player player) {
|
||||
this.player = player;
|
||||
this.packet = packet;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public boolean hasPlayer() {
|
||||
return player != null;
|
||||
}
|
||||
|
||||
public String getPlayername() {
|
||||
return getPlayer().getName();
|
||||
}
|
||||
|
||||
public Object getPacket() {
|
||||
return packet;
|
||||
}
|
||||
|
||||
public String getPacketName() {
|
||||
return packet.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
public PacketType getPacketType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
public PacketContent getContent() {
|
||||
return new PacketContent(this);
|
||||
}
|
||||
|
||||
public PacketMetadata getMetaData() {
|
||||
return metaData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.packets.custom.CustomPacketManager;
|
||||
import me.dadus33.chatitem.packets.protocollib.ProtocollibPacketManager;
|
||||
|
||||
public class ChatItemPacketManager {
|
||||
|
||||
private PacketManager packetManager;
|
||||
|
||||
public ChatItemPacketManager(ChatItem pl) {
|
||||
Plugin protocolLibPlugin = Bukkit.getPluginManager().getPlugin("ProtocolLib");
|
||||
if (protocolLibPlugin != null) {
|
||||
if(checkProtocollibConditions()) {
|
||||
pl.getLogger().info("The plugin ProtocolLib has been detected. Loading Protocollib support ...");
|
||||
packetManager = new ProtocollibPacketManager(pl);
|
||||
} else {
|
||||
pl.getLogger().warning("The plugin ProtocolLib has been detected but you have an OLD version, so we cannot use it.");
|
||||
pl.getLogger().warning("Fallback to default Packet system ...");
|
||||
packetManager = new CustomPacketManager(pl);
|
||||
}
|
||||
} else
|
||||
packetManager = new CustomPacketManager(pl);
|
||||
}
|
||||
|
||||
public PacketManager getPacketManager() {
|
||||
return packetManager;
|
||||
}
|
||||
|
||||
private boolean checkProtocollibConditions() {
|
||||
try {
|
||||
Class.forName("com.comphenix.protocol.injector.server.TemporaryPlayer");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
|
||||
public class PacketContent {
|
||||
|
||||
|
||||
private final ChatItemPacket packet;
|
||||
private final Object obj;
|
||||
|
||||
/**
|
||||
* Create a PacketContent to easily read and write value in it
|
||||
*
|
||||
* @param packet the Negativity's packet
|
||||
*/
|
||||
public PacketContent(ChatItemPacket packet) {
|
||||
this.packet = packet;
|
||||
this.obj = packet.getPacket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PacketContent to easily read and write to the specified object
|
||||
*
|
||||
* @param obj the object that will be read or edit
|
||||
*/
|
||||
public PacketContent(Object obj) {
|
||||
this.packet = null;
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
public ChatItemPacket getPacket() {
|
||||
return packet;
|
||||
}
|
||||
|
||||
public <T> ContentModifier<T> getSpecificModifier(T type){
|
||||
return new ContentModifier<T>(obj, type.getClass());
|
||||
}
|
||||
|
||||
public <T> ContentModifier<T> getSpecificModifier(Class<T> clazz){
|
||||
return new ContentModifier<T>(obj, clazz);
|
||||
}
|
||||
|
||||
public ContentModifier<String> getStrings(){
|
||||
return new ContentModifier<String>(obj, String.class);
|
||||
}
|
||||
|
||||
public ContentModifier<Byte> getBytes(){
|
||||
return new ContentModifier<Byte>(obj, byte.class);
|
||||
}
|
||||
|
||||
public ContentModifier<Boolean> getBooleans(){
|
||||
return new ContentModifier<Boolean>(obj, boolean.class);
|
||||
}
|
||||
|
||||
public ContentModifier<Integer> getIntegers(){
|
||||
return new ContentModifier<Integer>(obj, int.class);
|
||||
}
|
||||
|
||||
public ContentModifier<byte[]> getByteArrays(){
|
||||
return new ContentModifier<byte[]>(obj, byte[].class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ContentModifier<Object> getChatComponents() {
|
||||
return (ContentModifier<Object>) getSpecificModifier(PacketUtils.COMPONENT_CLASS);
|
||||
}
|
||||
|
||||
public static class ContentModifier<T> {
|
||||
|
||||
private Object obj;
|
||||
private HashMap<Field, T> content = new HashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ContentModifier(Object obj, Class<?> clazz) {
|
||||
this.obj = obj;
|
||||
|
||||
for(Field f : obj.getClass().getDeclaredFields()) {
|
||||
try {
|
||||
if(f.getType().isAssignableFrom(clazz)) {
|
||||
f.setAccessible(true);
|
||||
content.put(f, (T) f.get(obj));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if(!obj.getClass().getSuperclass().equals(Object.class)) {
|
||||
for(Field f : obj.getClass().getSuperclass().getDeclaredFields()) {
|
||||
try {
|
||||
if(f.getType().isAssignableFrom(clazz)) {
|
||||
f.setAccessible(true);
|
||||
content.put(f, (T) f.get(obj));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the Map of content
|
||||
*
|
||||
* @return the number of content
|
||||
*/
|
||||
public int size() {
|
||||
return content.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the field which is named by the given one
|
||||
*
|
||||
* @param fieldName the name of the needed field
|
||||
* @return the value of the field
|
||||
*/
|
||||
public T read(String fieldName) {
|
||||
for(Field field : content.keySet())
|
||||
if(field.getName().equalsIgnoreCase(fieldName))
|
||||
return content.get(field);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the field which is named by the given one
|
||||
*
|
||||
* @param fieldName the name of the needed field
|
||||
* @param defaultValue the returned value if there is no value
|
||||
* @return the value of the field
|
||||
*/
|
||||
public T read(String fieldName, T defaultValue) {
|
||||
for(Field field : content.keySet())
|
||||
if(field.getName().equalsIgnoreCase(fieldName))
|
||||
return content.get(field);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the specified value.
|
||||
*
|
||||
* @param i the number of the value to get getted. Start at 0.
|
||||
* @return the requested value.
|
||||
*
|
||||
* @throws NoSuchElementException if the value doesn't exist
|
||||
*/
|
||||
public T read(int i) {
|
||||
return content.values().stream().filter(Objects::nonNull).findFirst().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the specified value.
|
||||
*
|
||||
* @param i the number of the value to get getted. Start at 0.
|
||||
* @return the requested value, or null if it doesn't exist.
|
||||
*/
|
||||
public T readSafely(int i) {
|
||||
return readSafely(i, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the specified value.
|
||||
*
|
||||
* @param i the number of the value to get getted. Start at 0.
|
||||
* @param defaultValue the value which is return if the key doesn't exist
|
||||
* @return the requested value.
|
||||
*/
|
||||
public T readSafely(int i, T defaultValue) {
|
||||
return content.values().stream().filter(Objects::nonNull).findFirst().orElse(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value in the specified object.
|
||||
*
|
||||
* @param i the key of the value to set
|
||||
* @param value the new value which will be set
|
||||
*/
|
||||
public void write(int i, T value) {
|
||||
Field key = content.keySet().toArray(new Field[] {})[i];
|
||||
if(key == null) {
|
||||
new NoSuchFieldException("Not enough value in " + obj.getClass() + ".").printStackTrace();
|
||||
} else {
|
||||
content.put(key, value);
|
||||
try {
|
||||
key.set(obj, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field which her value
|
||||
*
|
||||
* @return all content
|
||||
*/
|
||||
public HashMap<Field, T> getContent(){
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
public abstract class PacketHandler {
|
||||
|
||||
public void onReceive(ChatItemPacket packet) {}
|
||||
public void onSend(ChatItemPacket packet) {}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class PacketManager {
|
||||
|
||||
public abstract void addPlayer(Player p);
|
||||
public abstract void removePlayer(Player p);
|
||||
public abstract void clear();
|
||||
|
||||
private final ArrayList<PacketHandler> handlers = new ArrayList<>();
|
||||
public boolean addHandler(PacketHandler handler) {
|
||||
return !handlers.add(handler);
|
||||
}
|
||||
|
||||
public boolean removeHandler(PacketHandler handler) {
|
||||
return handlers.remove(handler);
|
||||
}
|
||||
|
||||
public void notifyHandlersSent(ChatItemPacket packet) {
|
||||
handlers.forEach((handler) -> handler.onSend(packet));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PacketMetadata {
|
||||
|
||||
private final HashMap<String, Object> metas = new HashMap<>();
|
||||
|
||||
public void setMeta(String key, Object obj) {
|
||||
this.metas.put(key, obj);
|
||||
}
|
||||
|
||||
public boolean hasMeta(String key) {
|
||||
return metas.containsKey(key);
|
||||
}
|
||||
|
||||
public void removeMeta(String key) {
|
||||
metas.remove(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getMeta(String key, T def) {
|
||||
return (T) metas.getOrDefault(key, def);
|
||||
}
|
||||
|
||||
public HashMap<String, Object> getMetas() {
|
||||
return metas;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package me.dadus33.chatitem.packets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface PacketType {
|
||||
|
||||
String name();
|
||||
String getPacketName();
|
||||
String getFullName();
|
||||
List<String> getAlias();
|
||||
|
||||
static final String SERVER_PREFIX = "PacketPlayOut", HANDSHAKE_PREFIX = "PacketHandshaking";
|
||||
|
||||
public static PacketType getType(String packetName) {
|
||||
if(packetName.startsWith(SERVER_PREFIX)) {
|
||||
return getPacketTypeFor(packetName, Server.values(), Server.UNSET);
|
||||
} else if(packetName.startsWith(HANDSHAKE_PREFIX)) {
|
||||
return getPacketTypeFor(packetName, Handshake.values(), Handshake.UNSET);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static PacketType getPacketTypeFor(String packetName, PacketType[] types, PacketType unset) {
|
||||
for(PacketType packet : types)
|
||||
if(packet.getFullName().equalsIgnoreCase(packetName) || packet.getPacketName().equalsIgnoreCase(packetName) || packet.getAlias().contains(packetName))
|
||||
return packet;
|
||||
return unset;
|
||||
}
|
||||
|
||||
public static enum Server implements PacketType {
|
||||
|
||||
CHAT("Chat"),
|
||||
UNSET("Unset");
|
||||
|
||||
private final String packetName, fullName;
|
||||
private List<String> alias = new ArrayList<>();
|
||||
|
||||
private Server(String packetName, String... alias) {
|
||||
this.packetName = packetName;
|
||||
this.fullName = SERVER_PREFIX + packetName;
|
||||
for(String al : alias)
|
||||
this.alias.add(SERVER_PREFIX + al);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPacketName() {
|
||||
return packetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAlias() {
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Handshake implements PacketType {
|
||||
|
||||
IS_SET_PROTOCOL("InSetProtocol"),
|
||||
UNSET("Unset");
|
||||
|
||||
private final String packetName, fullName;
|
||||
private List<String> alias = new ArrayList<>();
|
||||
|
||||
private Handshake(String packetName, String... alias) {
|
||||
this.packetName = packetName;
|
||||
for(String al : alias)
|
||||
this.alias.add(HANDSHAKE_PREFIX + al);
|
||||
this.fullName = HANDSHAKE_PREFIX + packetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPacketName() {
|
||||
return packetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package me.dadus33.chatitem.packets.custom;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketManager;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.packets.custom.channel.ChannelAbstract;
|
||||
import me.dadus33.chatitem.packets.custom.channel.INC2Channel;
|
||||
import me.dadus33.chatitem.packets.custom.channel.INCChannel;
|
||||
import me.dadus33.chatitem.packets.custom.channel.NMUChannel;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.Utils;
|
||||
|
||||
public class CustomPacketManager extends PacketManager implements Listener {
|
||||
|
||||
private ChannelAbstract channel;
|
||||
private Plugin pl;
|
||||
public HashMap<Object, Integer> protocolVersionPerChannel = new HashMap<>();
|
||||
private boolean isStarted = false;
|
||||
|
||||
public CustomPacketManager(Plugin pl) {
|
||||
this.pl = pl;
|
||||
ProtocolVersion version = ProtocolVersion.getServerVersion();
|
||||
if (version.isNewerOrEquals(ProtocolVersion.V1_17))
|
||||
channel = new INC2Channel(this);
|
||||
else if (version.equals(ProtocolVersion.V1_7))
|
||||
channel = new NMUChannel(this);
|
||||
else
|
||||
channel = new INCChannel(this);
|
||||
pl.getServer().getPluginManager().registerEvents(this, pl);
|
||||
|
||||
// we wait the start server
|
||||
Bukkit.getScheduler().runTaskLater(pl, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
isStarted = true;
|
||||
for(Player p : Utils.getOnlinePlayers())
|
||||
addPlayer(p);
|
||||
}
|
||||
}, 40);
|
||||
}
|
||||
|
||||
public Plugin getPlugin() {
|
||||
return pl;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
addPlayer(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent e) {
|
||||
removePlayer(e.getPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlayer(Player p) {
|
||||
if(isStarted)
|
||||
channel.addPlayer(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePlayer(Player p) {
|
||||
channel.removePlayer(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for(Player player : Utils.getOnlinePlayers())
|
||||
removePlayer(player);
|
||||
if(channel.getAddChannelExecutor() != null)
|
||||
channel.getAddChannelExecutor().shutdownNow();
|
||||
if(channel.getRemoveChannelExecutor() != null)
|
||||
channel.getRemoveChannelExecutor().shutdownNow();
|
||||
}
|
||||
|
||||
public ChatItemPacket onPacketSent(PacketType type, Player sender, Object packet) {
|
||||
if(type == null)
|
||||
return null;
|
||||
ChatItemPacket customPacket = new ChatItemPacket(type, packet, sender);
|
||||
notifyHandlersSent(customPacket);
|
||||
return customPacket;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package me.dadus33.chatitem.packets.custom.channel;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.packets.custom.CustomPacketManager;
|
||||
import me.dadus33.chatitem.playerversion.hooks.DefaultVersionHook;
|
||||
|
||||
public abstract class ChannelAbstract {
|
||||
|
||||
private ExecutorService addChannelExecutor, removeChannelExecutor;
|
||||
static final String KEY_HANDSHAKE = "packet_handshake_negativity", KEY_HANDLER_SERVER = "packet_handler", KEY_SERVER = "packet_server_negativity";
|
||||
|
||||
private CustomPacketManager customPacketManager;
|
||||
private HashSet<UUID> players = new HashSet<>();
|
||||
|
||||
protected ChannelAbstract(CustomPacketManager customPacketManager) {
|
||||
this.customPacketManager = customPacketManager;
|
||||
}
|
||||
|
||||
public CustomPacketManager getPacketManager() {
|
||||
return customPacketManager;
|
||||
}
|
||||
|
||||
public ExecutorService getAddChannelExecutor() {
|
||||
return addChannelExecutor;
|
||||
}
|
||||
|
||||
public ExecutorService getOrCreateAddChannelExecutor() {
|
||||
if(addChannelExecutor == null)
|
||||
addChannelExecutor = Executors.newSingleThreadExecutor();
|
||||
return addChannelExecutor;
|
||||
}
|
||||
|
||||
public ExecutorService getRemoveChannelExecutor() {
|
||||
return removeChannelExecutor;
|
||||
}
|
||||
|
||||
public ExecutorService getOrCreateRemoveChannelExecutor() {
|
||||
if(removeChannelExecutor == null)
|
||||
removeChannelExecutor = Executors.newSingleThreadExecutor();
|
||||
return removeChannelExecutor;
|
||||
}
|
||||
|
||||
public void addPlayer(Player p) {
|
||||
if(players.add(p.getUniqueId())) {
|
||||
addChannel(p, p.getUniqueId().toString());
|
||||
try {
|
||||
Integer protocol = customPacketManager.protocolVersionPerChannel.remove(getChannel(p));
|
||||
if(protocol != null)
|
||||
DefaultVersionHook.PROTOCOL_PER_UUID.put(p.getUniqueId(), protocol);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(Player p) {
|
||||
if(players.remove(p.getUniqueId()))
|
||||
removeChannel(p, p.getUniqueId().toString());
|
||||
}
|
||||
|
||||
public abstract void addChannel(Player player, String endChannelName);
|
||||
|
||||
public abstract void removeChannel(Player player, String endChannelName);
|
||||
|
||||
public abstract Object getChannel(Player p) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package me.dadus33.chatitem.packets.custom.channel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketContent;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.packets.custom.CustomPacketManager;
|
||||
|
||||
public class ChannelInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
public static final HashMap<Channel, ChatItemPacket> TMP = new HashMap<>();
|
||||
private CustomPacketManager packetManager;
|
||||
|
||||
public ChannelInboundHandler(CustomPacketManager packetManager) {
|
||||
this.packetManager = packetManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
ctx.fireChannelRead(msg);
|
||||
((Channel) msg).pipeline().addFirst(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
try {
|
||||
channel.eventLoop().submit(() -> {
|
||||
try {
|
||||
ChannelHandler interceptor = channel.pipeline().get(ChannelAbstract.KEY_HANDSHAKE);
|
||||
// Inject our packet interceptor
|
||||
if (interceptor == null) {
|
||||
interceptor = new ChannelHandlerHandshakeReceive(channel);
|
||||
channel.pipeline().addBefore("packet_handler", ChannelAbstract.KEY_HANDSHAKE, interceptor);
|
||||
}
|
||||
return interceptor;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Try again
|
||||
return channel.pipeline().get(ChannelAbstract.KEY_HANDSHAKE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
packetManager.getPlugin().getLogger().log(Level.SEVERE, "Cannot inject incoming channel " + channel, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class ChannelHandlerHandshakeReceive extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private Channel channel;
|
||||
|
||||
public ChannelHandlerHandshakeReceive(Channel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object packet) {
|
||||
try {
|
||||
PacketType packetType = PacketType.getType(packet.getClass().getSimpleName());
|
||||
if(packetType != null && packetType == PacketType.Handshake.IS_SET_PROTOCOL) {
|
||||
packetManager.protocolVersionPerChannel.put(channel, new PacketContent(packet).getIntegers().readSafely(0, 0));
|
||||
}
|
||||
super.channelRead(ctx, packet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package me.dadus33.chatitem.packets.custom.channel;
|
||||
|
||||
import static me.dadus33.chatitem.utils.PacketUtils.getPlayerConnection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.packets.custom.CustomPacketManager;
|
||||
import me.dadus33.chatitem.playerversion.hooks.DefaultVersionHook;
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.ReflectionUtils;
|
||||
|
||||
public class INC2Channel extends ChannelAbstract {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public INC2Channel(CustomPacketManager customPacketManager) {
|
||||
super(customPacketManager);
|
||||
try {
|
||||
Object mcServer = ReflectionUtils.callMethod(PacketUtils.getCraftServer(), "getServer");
|
||||
Object co = ReflectionUtils.getFirstWith(mcServer, PacketUtils.getNmsClass("MinecraftServer", "server."), PacketUtils.getNmsClass("ServerConnection", "server.network."));
|
||||
((List<ChannelFuture>) ReflectionUtils.getField(co, "f")).forEach((channelFuture) -> {
|
||||
channelFuture.channel().pipeline().addFirst(new ChannelInboundHandler(customPacketManager));
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChannel(final Player player, String endChannelName) {
|
||||
getOrCreateAddChannelExecutor().execute(() -> {
|
||||
if(!player.isOnline())
|
||||
return;
|
||||
try {
|
||||
Channel channel = getChannel(player);
|
||||
// Managing outgoing packet (to the player)
|
||||
channel.pipeline().addAfter(KEY_HANDLER_SERVER, KEY_SERVER + endChannelName, new ChannelHandlerSent(player));
|
||||
|
||||
ChatItemPacket pa = ChannelInboundHandler.TMP.remove(channel);
|
||||
if(pa != null)
|
||||
DefaultVersionHook.PROTOCOL_PER_UUID.put(player.getUniqueId(), pa.getContent().getIntegers().readSafely(0, ProtocolVersion.getServerVersion().MAX_VER));
|
||||
} catch (NoSuchElementException e) {
|
||||
// appear when the player's channel isn't accessible because of reload.
|
||||
getPacketManager().getPlugin().getLogger().warning("Please, don't use reload, this can produce some problem. Currently, " + player.getName() + " isn't fully checked because of that. More details: " + e.getMessage() + " (NoSuchElementException)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
if(e.getMessage().contains("Duplicate handler")) {
|
||||
removeChannel(player, endChannelName);
|
||||
addChannel(player, endChannelName);
|
||||
} else
|
||||
getPacketManager().getPlugin().getLogger().severe("Error while loading Packet channel. " + e.getMessage() + ". Please, prefer restart than reload.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChannel(Player player, String endChannelName) {
|
||||
getOrCreateRemoveChannelExecutor().execute(() -> {
|
||||
try {
|
||||
final Channel channel = getChannel(player);
|
||||
if(channel.pipeline().get(KEY_SERVER + endChannelName) != null)
|
||||
channel.pipeline().remove(KEY_SERVER + endChannelName);
|
||||
} catch (Exception e) {
|
||||
ChatItem.getInstance().getLogger().warning("Failed to remove channel for " + player.getName() + ". Reason: " + e.getMessage() + " (" + e.getStackTrace()[0].toString() + ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player p) throws Exception {
|
||||
Object playerConnection = getPlayerConnection(p);
|
||||
Object networkManager = playerConnection.getClass().getField("a").get(playerConnection);
|
||||
return (Channel) networkManager.getClass().getDeclaredField("k").get(networkManager);//(Channel) networkManager.getClass().getDeclaredField("channel").get(networkManager);
|
||||
}
|
||||
|
||||
private class ChannelHandlerSent extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
private final Player owner;
|
||||
|
||||
public ChannelHandlerSent(Player player) {
|
||||
this.owner = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||
ChatItemPacket nextPacket = getPacketManager().onPacketSent(PacketType.getType(packet.getClass().getSimpleName()), owner, packet);
|
||||
if(nextPacket != null && nextPacket.isCancelled())
|
||||
return;
|
||||
super.write(ctx, packet, promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package me.dadus33.chatitem.packets.custom.channel;
|
||||
|
||||
import static me.dadus33.chatitem.utils.PacketUtils.getPlayerConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketContent;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.packets.custom.CustomPacketManager;
|
||||
import me.dadus33.chatitem.playerversion.hooks.DefaultVersionHook;
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
import me.dadus33.chatitem.utils.ReflectionUtils;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class INCChannel extends ChannelAbstract {
|
||||
|
||||
public INCChannel(CustomPacketManager customPacketManager) {
|
||||
super(customPacketManager);
|
||||
getFuturChannel().forEach((channelFuture) -> {
|
||||
channelFuture.channel().pipeline().addFirst(new ChannelInboundHandler(customPacketManager));
|
||||
});
|
||||
}
|
||||
|
||||
public List<ChannelFuture> getFuturChannel() {
|
||||
try {
|
||||
Object dedicatedSrv = PacketUtils.getCraftServer();
|
||||
Object mcServer = dedicatedSrv.getClass().getMethod("getServer").invoke(dedicatedSrv);
|
||||
Object co = ReflectionUtils.getFirstWith(mcServer, PacketUtils.getNmsClass("MinecraftServer", "server."), PacketUtils.getNmsClass("ServerConnection", "network."));
|
||||
try {
|
||||
return (List<ChannelFuture>) ReflectionUtils.getPrivateField(co, "g");
|
||||
} catch (NoSuchFieldException e) {
|
||||
return (List<ChannelFuture>) ReflectionUtils.getPrivateField(co, "listeningChannels");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChannel(final Player player, String endChannelName) {
|
||||
getOrCreateAddChannelExecutor().execute(() -> {
|
||||
if(!player.isOnline())
|
||||
return;
|
||||
try {
|
||||
Channel channel = getChannel(player);
|
||||
// Managing outgoing packet (to the player)
|
||||
channel.pipeline().addAfter(KEY_HANDLER_SERVER, KEY_SERVER + endChannelName, new ChannelHandlerSent(player));
|
||||
|
||||
ChatItemPacket pa = ChannelInboundHandler.TMP.remove(channel);
|
||||
if(pa != null)
|
||||
DefaultVersionHook.PROTOCOL_PER_UUID.put(player.getUniqueId(), pa.getContent().getIntegers().readSafely(0, ProtocolVersion.getServerVersion().MAX_VER));
|
||||
} catch (NoSuchElementException e) {
|
||||
// appear when the player's channel isn't accessible because of reload.
|
||||
getPacketManager().getPlugin().getLogger().warning("Please, don't use reload, this can produce some problem. Currently, " + player.getName() + " isn't fully checked because of that. More details: " + e.getMessage() + " (NoSuchElementException)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
if(e.getMessage().contains("Duplicate handler")) {
|
||||
removeChannel(player, endChannelName);
|
||||
addChannel(player, endChannelName);
|
||||
} else
|
||||
getPacketManager().getPlugin().getLogger().severe("Error while loading Packet channel. " + e.getMessage() + ". Please, prefer restart than reload.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChannel(Player player, String endChannelName) {
|
||||
getOrCreateRemoveChannelExecutor().execute(() -> {
|
||||
try {
|
||||
final Channel channel = getChannel(player);
|
||||
|
||||
if(channel.pipeline().get(KEY_SERVER + endChannelName) != null)
|
||||
channel.pipeline().remove(KEY_SERVER + endChannelName);
|
||||
} catch (Exception e) {
|
||||
ChatItem.getInstance().getLogger().warning("Failed to remove channel for " + player.getName() + ". Reason: " + e.getMessage() + " (" + e.getStackTrace()[0].toString() + ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player p) throws Exception {
|
||||
Object playerConnection = getPlayerConnection(p);
|
||||
Object networkManager = playerConnection.getClass().getField("networkManager").get(playerConnection);
|
||||
return new PacketContent(networkManager).getSpecificModifier(Channel.class).readSafely(0);//(Channel) networkManager.getClass().getDeclaredField("channel").get(networkManager);
|
||||
}
|
||||
|
||||
private class ChannelHandlerSent extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
private final Player owner;
|
||||
|
||||
public ChannelHandlerSent(Player player) {
|
||||
this.owner = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||
try {
|
||||
ChatItemPacket nextPacket = getPacketManager().onPacketSent(PacketType.getType(packet.getClass().getSimpleName()), owner, packet);
|
||||
if(nextPacket != null && nextPacket.isCancelled())
|
||||
return;
|
||||
super.write(ctx, packet, promise);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
super.write(ctx, packet, promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package me.dadus33.chatitem.packets.custom.channel;
|
||||
|
||||
import static me.dadus33.chatitem.utils.PacketUtils.getPlayerConnection;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketContent;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
import me.dadus33.chatitem.packets.custom.CustomPacketManager;
|
||||
import me.dadus33.chatitem.utils.PacketUtils;
|
||||
import me.dadus33.chatitem.utils.ReflectionUtils;
|
||||
import net.minecraft.util.io.netty.channel.Channel;
|
||||
import net.minecraft.util.io.netty.channel.ChannelFuture;
|
||||
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
||||
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import net.minecraft.util.io.netty.channel.ChannelInitializer;
|
||||
import net.minecraft.util.io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import net.minecraft.util.io.netty.channel.ChannelPromise;
|
||||
|
||||
public class NMUChannel extends ChannelAbstract {
|
||||
|
||||
public NMUChannel(CustomPacketManager customPacketManager) {
|
||||
super(customPacketManager);
|
||||
try {
|
||||
Object dedicatedSrv = PacketUtils.getCraftServer();
|
||||
Object mcServer = dedicatedSrv.getClass().getMethod("getServer").invoke(dedicatedSrv);
|
||||
Object co = ReflectionUtils.getFirstWith(mcServer, PacketUtils.getNmsClass("MinecraftServer", "server."), PacketUtils.getNmsClass("ServerConnection", "network."));
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ChannelFuture> g = (List<ChannelFuture>) ReflectionUtils.getField(co, "g");
|
||||
g.forEach((channelFuture) -> {
|
||||
channelFuture.channel().pipeline().addFirst(new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
((Channel) msg).pipeline().addFirst(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
try {
|
||||
channel.eventLoop().submit(() -> {
|
||||
try {
|
||||
ChannelHandler interceptor = channel.pipeline().get(KEY_HANDSHAKE);
|
||||
// Inject our packet interceptor
|
||||
if (interceptor == null) {
|
||||
interceptor = new ChannelHandlerHandshakeReceive(channel);
|
||||
channel.pipeline().addBefore("packet_handler", KEY_HANDSHAKE, interceptor);
|
||||
}
|
||||
return interceptor;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Try again
|
||||
return channel.pipeline().get(KEY_HANDSHAKE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
getPacketManager().getPlugin().getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChannel(final Player player, String endChannelName) {
|
||||
getOrCreateAddChannelExecutor().execute(() -> {
|
||||
try {
|
||||
Channel channel = getChannel(player);
|
||||
// Managing outgoing packet (to the player)
|
||||
channel.pipeline().addAfter(KEY_HANDLER_SERVER, KEY_SERVER + endChannelName, new ChannelHandlerSent(player));
|
||||
} catch (NoSuchElementException e) {
|
||||
// appear when the player's channel isn't accessible because of reload.
|
||||
getPacketManager().getPlugin().getLogger().warning("Please, don't use reload, this can produce some problem. Currently, " + player.getName() + " isn't fully checked because of that. More details: " + e.getMessage() + " (NoSuchElementException)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
if(e.getMessage().contains("Duplicate")) {
|
||||
removeChannel(player, endChannelName);
|
||||
addChannel(player, endChannelName);
|
||||
} else
|
||||
getPacketManager().getPlugin().getLogger().severe("Error while loading Packet channel. " + e.getMessage() + ". Please, prefer restart than reload.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChannel(Player player, String endChannelName) {
|
||||
getOrCreateRemoveChannelExecutor().execute(() -> {
|
||||
try {
|
||||
final Channel channel = getChannel(player);
|
||||
if(channel.pipeline().get(KEY_SERVER + endChannelName) != null)
|
||||
channel.pipeline().remove(KEY_SERVER + endChannelName);
|
||||
} catch (Exception e) {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player p) throws Exception {
|
||||
Object playerConnection = getPlayerConnection(p);
|
||||
Object networkManager = playerConnection.getClass().getField("networkManager").get(playerConnection);
|
||||
|
||||
for (Field field : networkManager.getClass().getDeclaredFields())
|
||||
if (field.getType().equals(Channel.class)) {
|
||||
field.setAccessible(true);
|
||||
return (Channel) field.get(networkManager);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ChannelHandlerSent extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
private final Player owner;
|
||||
|
||||
public ChannelHandlerSent(Player player) {
|
||||
this.owner = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||
ChatItemPacket nextPacket = getPacketManager().onPacketSent(PacketType.getType(packet.getClass().getSimpleName()), owner, packet);
|
||||
if(nextPacket != null && nextPacket.isCancelled())
|
||||
return;
|
||||
super.write(ctx, packet, promise);
|
||||
}
|
||||
}
|
||||
|
||||
private class ChannelHandlerHandshakeReceive extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final Channel channel;
|
||||
|
||||
public ChannelHandlerHandshakeReceive(Channel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object packet) {
|
||||
try {
|
||||
PacketType packetType = PacketType.getType(packet.getClass().getSimpleName());
|
||||
if(packetType != null && packetType == PacketType.Handshake.IS_SET_PROTOCOL) {
|
||||
getPacketManager().protocolVersionPerChannel.put(channel, new PacketContent(packet).getIntegers().readSafely(0, 0));
|
||||
}
|
||||
super.channelRead(ctx, packet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package me.dadus33.chatitem.packets.protocollib;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Play;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
import me.dadus33.chatitem.listeners.HandshakeListener;
|
||||
import me.dadus33.chatitem.packets.ChatItemPacket;
|
||||
import me.dadus33.chatitem.packets.PacketManager;
|
||||
import me.dadus33.chatitem.packets.PacketType;
|
||||
|
||||
public class ProtocollibPacketManager extends PacketManager {
|
||||
|
||||
private final ProtocolManager protocolManager;
|
||||
|
||||
public ProtocollibPacketManager(Plugin pl) {
|
||||
protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
protocolManager.addPacketListener(new PacketAdapter(pl, ListenerPriority.LOWEST, Arrays.asList(Play.Server.CHAT)) {
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
if (p == null || e.isPlayerTemporary())
|
||||
return;
|
||||
ChatItemPacket packet = onPacketSent(PacketType.getType(e.getPacket().getHandle().getClass().getSimpleName()),
|
||||
p, e.getPacket().getHandle());
|
||||
if(packet == null)
|
||||
return;
|
||||
if(!e.isCancelled())
|
||||
e.setCancelled(packet.isCancelled());
|
||||
}
|
||||
});
|
||||
protocolManager.addPacketListener(new HandshakeListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlayer(Player p) {}
|
||||
|
||||
@Override
|
||||
public void removePlayer(Player p) {}
|
||||
|
||||
@Override
|
||||
public void clear() {}
|
||||
|
||||
public ChatItemPacket onPacketSent(PacketType type, Player sender, Object packet) {
|
||||
if(type == null)
|
||||
return null;
|
||||
ChatItemPacket customPacket = new ChatItemPacket(type, packet, sender);
|
||||
notifyHandlersSent(customPacket);
|
||||
return customPacket;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package me.dadus33.chatitem.playerversion;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
|
||||
public interface IPlayerVersion {
|
||||
|
||||
public int getProtocolVersion(Player p);
|
||||
|
||||
default ProtocolVersion getPlayerVersion(Player p) {
|
||||
return ProtocolVersion.getVersion(getProtocolVersion(p));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.dadus33.chatitem.playerversion.hooks;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.playerversion.IPlayerVersion;
|
||||
import me.dadus33.chatitem.utils.ProtocolVersion;
|
||||
|
||||
public class DefaultVersionHook implements IPlayerVersion {
|
||||
|
||||
public static final HashMap<String, Integer> PROTOCOL_PER_ADDRESS = new HashMap<>();
|
||||
public static final HashMap<UUID, Integer> PROTOCOL_PER_UUID = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player p) {
|
||||
if(PROTOCOL_PER_UUID.containsKey(p.getUniqueId()))
|
||||
return PROTOCOL_PER_UUID.get(p.getUniqueId());
|
||||
return PROTOCOL_PER_ADDRESS.getOrDefault(ProtocolVersion.stringifyAdress(p.getAddress()), ProtocolVersion.getServerVersion().MAX_VER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.dadus33.chatitem.playerversion.hooks;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.playerversion.IPlayerVersion;
|
||||
import protocolsupport.api.ProtocolSupportAPI;
|
||||
|
||||
public class ProtocolSupportHook implements IPlayerVersion {
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player p) {
|
||||
return ProtocolSupportAPI.getProtocolVersion(p).getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package me.dadus33.chatitem.playerversion.hooks;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.playerversion.IPlayerVersion;
|
||||
import us.myles.ViaVersion.api.Via;
|
||||
|
||||
public class ViaVersionHook implements IPlayerVersion {
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player p) {
|
||||
return Via.getAPI().getPlayerVersion(p.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class General {
|
||||
|
||||
private static Storage c;
|
||||
|
||||
public static void checkConfigVersion() {
|
||||
int latestVersion = ChatItem.CFG_VER;
|
||||
if (latestVersion != c.CONFIG_VERSION) {
|
||||
Bukkit.getLogger().log(Level.WARNING, ChatColor.RED + "ChatItem detected an older or invalid configuration file. Replacing it with the default config...");
|
||||
c.performOverwrite();
|
||||
Bukkit.getLogger().log(Level.WARNING, ChatColor.RED + "Replacement complete!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void init(Storage st) {
|
||||
c = st;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
|
||||
public class Item {
|
||||
private String id;
|
||||
private byte amount;
|
||||
private short data;
|
||||
private CompoundTag tag;
|
||||
|
||||
public CompoundTag getTag(){
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTag(CompoundTag newTag){
|
||||
this.tag = newTag;
|
||||
}
|
||||
|
||||
public void setId(String newId){
|
||||
this.id = newId;
|
||||
}
|
||||
|
||||
public void setData(short newData){
|
||||
data = newData;
|
||||
}
|
||||
|
||||
public String getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public short getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setAmount(byte newAmount){
|
||||
this.amount = newAmount;
|
||||
}
|
||||
|
||||
public byte getAmount(){
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
|
||||
|
||||
/**
|
||||
* Thanks to the developers of ViaVersion for this!
|
||||
*/
|
||||
public class ItemRewriter_1_11_TO_1_10 {
|
||||
private static BiMap<String, String> oldToNewNames = HashBiMap.create();
|
||||
|
||||
static {
|
||||
oldToNewNames.put("AreaEffectCloud", "minecraft:area_effect_cloud");
|
||||
oldToNewNames.put("ArmorStand", "minecraft:armor_stand");
|
||||
oldToNewNames.put("Arrow", "minecraft:arrow");
|
||||
oldToNewNames.put("Bat", "minecraft:bat");
|
||||
oldToNewNames.put("Blaze", "minecraft:blaze");
|
||||
oldToNewNames.put("Boat", "minecraft:boat");
|
||||
oldToNewNames.put("CaveSpider", "minecraft:cave_spider");
|
||||
oldToNewNames.put("Chicken", "minecraft:chicken");
|
||||
oldToNewNames.put("Cow", "minecraft:cow");
|
||||
oldToNewNames.put("Creeper", "minecraft:creeper");
|
||||
oldToNewNames.put("Donkey", "minecraft:donkey");
|
||||
oldToNewNames.put("DragonFireball", "minecraft:dragon_fireball");
|
||||
oldToNewNames.put("ElderGuardian", "minecraft:elder_guardian");
|
||||
oldToNewNames.put("EnderCrystal", "minecraft:ender_crystal");
|
||||
oldToNewNames.put("EnderDragon", "minecraft:ender_dragon");
|
||||
oldToNewNames.put("Enderman", "minecraft:enderman");
|
||||
oldToNewNames.put("Endermite", "minecraft:endermite");
|
||||
oldToNewNames.put("EntityHorse", "minecraft:horse");
|
||||
oldToNewNames.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal");
|
||||
oldToNewNames.put("FallingSand", "minecraft:falling_block");
|
||||
oldToNewNames.put("Fireball", "minecraft:fireball");
|
||||
oldToNewNames.put("FireworksRocketEntity", "minecraft:fireworks_rocket");
|
||||
oldToNewNames.put("Ghast", "minecraft:ghast");
|
||||
oldToNewNames.put("Giant", "minecraft:giant");
|
||||
oldToNewNames.put("Guardian", "minecraft:guardian");
|
||||
oldToNewNames.put("Husk", "minecraft:husk");
|
||||
oldToNewNames.put("Item", "minecraft:item");
|
||||
oldToNewNames.put("ItemFrame", "minecraft:item_frame");
|
||||
oldToNewNames.put("LavaSlime", "minecraft:magma_cube");
|
||||
oldToNewNames.put("LeashKnot", "minecraft:leash_knot");
|
||||
oldToNewNames.put("MinecartChest", "minecraft:chest_minecart");
|
||||
oldToNewNames.put("MinecartCommandBlock", "minecraft:commandblock_minecart");
|
||||
oldToNewNames.put("MinecartFurnace", "minecraft:furnace_minecart");
|
||||
oldToNewNames.put("MinecartHopper", "minecraft:hopper_minecart");
|
||||
oldToNewNames.put("MinecartRideable", "minecraft:minecart");
|
||||
oldToNewNames.put("MinecartSpawner", "minecraft:spawner_minecart");
|
||||
oldToNewNames.put("MinecartTNT", "minecraft:tnt_minecart");
|
||||
oldToNewNames.put("Mule", "minecraft:mule");
|
||||
oldToNewNames.put("MushroomCow", "minecraft:mooshroom");
|
||||
oldToNewNames.put("Ozelot", "minecraft:ocelot");
|
||||
oldToNewNames.put("Painting", "minecraft:painting");
|
||||
oldToNewNames.put("Pig", "minecraft:pig");
|
||||
oldToNewNames.put("PigZombie", "minecraft:zombie_pigman");
|
||||
oldToNewNames.put("PolarBear", "minecraft:polar_bear");
|
||||
oldToNewNames.put("PrimedTnt", "minecraft:tnt");
|
||||
oldToNewNames.put("Rabbit", "minecraft:rabbit");
|
||||
oldToNewNames.put("Sheep", "minecraft:sheep");
|
||||
oldToNewNames.put("Shulker", "minecraft:shulker");
|
||||
oldToNewNames.put("ShulkerBullet", "minecraft:shulker_bullet");
|
||||
oldToNewNames.put("Silverfish", "minecraft:silverfish");
|
||||
oldToNewNames.put("Skeleton", "minecraft:skeleton");
|
||||
oldToNewNames.put("SkeletonHorse", "minecraft:skeleton_horse");
|
||||
oldToNewNames.put("Slime", "minecraft:slime");
|
||||
oldToNewNames.put("SmallFireball", "minecraft:small_fireball");
|
||||
oldToNewNames.put("Snowball", "minecraft:snowball");
|
||||
oldToNewNames.put("SnowMan", "minecraft:snowman");
|
||||
oldToNewNames.put("SpectralArrow", "minecraft:spectral_arrow");
|
||||
oldToNewNames.put("Spider", "minecraft:spider");
|
||||
oldToNewNames.put("Squid", "minecraft:squid");
|
||||
oldToNewNames.put("Stray", "minecraft:stray");
|
||||
oldToNewNames.put("ThrownEgg", "minecraft:egg");
|
||||
oldToNewNames.put("ThrownEnderpearl", "minecraft:ender_pearl");
|
||||
oldToNewNames.put("ThrownExpBottle", "minecraft:xp_bottle");
|
||||
oldToNewNames.put("ThrownPotion", "minecraft:potion");
|
||||
oldToNewNames.put("Villager", "minecraft:villager");
|
||||
oldToNewNames.put("VillagerGolem", "minecraft:villager_golem");
|
||||
oldToNewNames.put("Witch", "minecraft:witch");
|
||||
oldToNewNames.put("WitherBoss", "minecraft:wither");
|
||||
oldToNewNames.put("WitherSkeleton", "minecraft:wither_skeleton");
|
||||
oldToNewNames.put("WitherSkull", "minecraft:wither_skull");
|
||||
oldToNewNames.put("Wolf", "minecraft:wolf");
|
||||
oldToNewNames.put("XPOrb", "minecraft:xp_orb");
|
||||
oldToNewNames.put("Zombie", "minecraft:zombie");
|
||||
oldToNewNames.put("ZombieHorse", "minecraft:zombie_horse");
|
||||
oldToNewNames.put("ZombieVillager", "minecraft:zombie_villager");
|
||||
}
|
||||
|
||||
static void toClient(Item item) {
|
||||
if (hasEntityTag(item)) {
|
||||
CompoundTag entityTag = item.getTag().get("EntityTag");
|
||||
if (entityTag.get("id") instanceof StringTag) {
|
||||
StringTag id = entityTag.get("id");
|
||||
if (oldToNewNames.containsKey(id.getValue())) {
|
||||
id.setValue(oldToNewNames.get(id.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void reverseToClient(Item item) {
|
||||
if (hasEntityTag(item)) {
|
||||
CompoundTag entityTag = item.getTag().get("EntityTag");
|
||||
if (entityTag.get("id") instanceof StringTag) {
|
||||
StringTag id = entityTag.get("id");
|
||||
if (oldToNewNames.containsKey(id.getValue())) {
|
||||
id.setValue(oldToNewNames.get(id.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean hasEntityTag(Item item) {
|
||||
if (item != null && item.getId().equals("minecraft:spawn_egg")) { // Monster Egg
|
||||
CompoundTag tag = item.getTag();
|
||||
if (tag != null && tag.contains("EntityTag") && tag.get("EntityTag") instanceof CompoundTag) {
|
||||
if (((CompoundTag) tag.get("EntityTag")).get("id") instanceof StringTag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* Thanks to the developers of ViaVersion for this!
|
||||
*/
|
||||
public class ItemRewriter_1_9_TO_1_8 {
|
||||
|
||||
private static final Map<String, Integer> ENTTIY_NAME_TO_ID = new HashMap<>();
|
||||
private static final Map<Integer, String> ENTTIY_ID_TO_NAME = new HashMap<>();
|
||||
private static final Map<String, Integer> POTION_NAME_TO_ID = new HashMap<>();
|
||||
private static final Map<Integer, String> POTION_ID_TO_NAME = new HashMap<>();
|
||||
|
||||
private static final Map<Integer, Integer> POTION_INDEX = new HashMap<>();
|
||||
|
||||
static {
|
||||
/* Entities */
|
||||
registerEntity(1, "Item");
|
||||
registerEntity(2, "XPOrb");
|
||||
registerEntity(7, "ThrownEgg");
|
||||
registerEntity(8, "LeashKnot");
|
||||
registerEntity(9, "Painting");
|
||||
registerEntity(10, "Arrow");
|
||||
registerEntity(11, "Snowball");
|
||||
registerEntity(12, "Fireball");
|
||||
registerEntity(13, "SmallFireball");
|
||||
registerEntity(14, "ThrownEnderpearl");
|
||||
registerEntity(15, "EyeOfEnderSignal");
|
||||
registerEntity(16, "ThrownPotion");
|
||||
registerEntity(17, "ThrownExpBottle");
|
||||
registerEntity(18, "ItemFrame");
|
||||
registerEntity(19, "WitherSkull");
|
||||
registerEntity(20, "PrimedTnt");
|
||||
registerEntity(21, "FallingSand");
|
||||
registerEntity(22, "FireworksRocketEntity");
|
||||
registerEntity(30, "ArmorStand");
|
||||
registerEntity(40, "MinecartCommandBlock");
|
||||
registerEntity(41, "Boat");
|
||||
registerEntity(42, "MinecartRideable");
|
||||
registerEntity(43, "MinecartChest");
|
||||
registerEntity(44, "MinecartFurnace");
|
||||
registerEntity(45, "MinecartTNT");
|
||||
registerEntity(46, "MinecartHopper");
|
||||
registerEntity(47, "MinecartSpawner");
|
||||
registerEntity(48, "Mob");
|
||||
registerEntity(49, "Monster");
|
||||
registerEntity(50, "Creeper");
|
||||
registerEntity(51, "Skeleton");
|
||||
registerEntity(52, "Spider");
|
||||
registerEntity(53, "Giant");
|
||||
registerEntity(54, "Zombie");
|
||||
registerEntity(55, "Slime");
|
||||
registerEntity(56, "Ghast");
|
||||
registerEntity(57, "PigZombie");
|
||||
registerEntity(58, "Enderman");
|
||||
registerEntity(59, "CaveSpider");
|
||||
registerEntity(60, "Silverfish");
|
||||
registerEntity(61, "Blaze");
|
||||
registerEntity(62, "LavaSlime");
|
||||
registerEntity(63, "EnderDragon");
|
||||
registerEntity(64, "WitherBoss");
|
||||
registerEntity(65, "Bat");
|
||||
registerEntity(66, "Witch");
|
||||
registerEntity(67, "Endermite");
|
||||
registerEntity(68, "Guardian");
|
||||
registerEntity(90, "Pig");
|
||||
registerEntity(91, "Sheep");
|
||||
registerEntity(92, "Cow");
|
||||
registerEntity(93, "Chicken");
|
||||
registerEntity(94, "Squid");
|
||||
registerEntity(95, "Wolf");
|
||||
registerEntity(96, "MushroomCow");
|
||||
registerEntity(97, "SnowMan");
|
||||
registerEntity(98, "Ozelot");
|
||||
registerEntity(99, "VillagerGolem");
|
||||
registerEntity(100, "EntityHorse");
|
||||
registerEntity(101, "Rabbit");
|
||||
registerEntity(120, "Villager");
|
||||
registerEntity(200, "EnderCrystal");
|
||||
|
||||
/* Potions */
|
||||
registerPotion(-1, "empty");
|
||||
registerPotion(0, "water");
|
||||
registerPotion(64, "mundane");
|
||||
registerPotion(32, "thick");
|
||||
registerPotion(16, "awkward");
|
||||
|
||||
registerPotion(8198, "night_vision");
|
||||
registerPotion(8262, "long_night_vision");
|
||||
|
||||
registerPotion(8206, "invisibility");
|
||||
registerPotion(8270, "long_invisibility");
|
||||
|
||||
registerPotion(8203, "leaping");
|
||||
registerPotion(8267, "long_leaping");
|
||||
registerPotion(8235, "strong_leaping");
|
||||
|
||||
registerPotion(8195, "fire_resistance");
|
||||
registerPotion(8259, "long_fire_resistance");
|
||||
|
||||
registerPotion(8194, "swiftness");
|
||||
registerPotion(8258, "long_swiftness");
|
||||
registerPotion(8226, "strong_swiftness");
|
||||
|
||||
registerPotion(8202, "slowness");
|
||||
registerPotion(8266, "long_slowness");
|
||||
|
||||
registerPotion(8205, "water_breathing");
|
||||
registerPotion(8269, "long_water_breathing");
|
||||
|
||||
registerPotion(8261, "healing");
|
||||
registerPotion(8229, "strong_healing");
|
||||
|
||||
registerPotion(8204, "harming");
|
||||
registerPotion(8236, "strong_harming");
|
||||
|
||||
registerPotion(8196, "poison");
|
||||
registerPotion(8260, "long_poison");
|
||||
registerPotion(8228, "strong_poison");
|
||||
|
||||
registerPotion(8193, "regeneration");
|
||||
registerPotion(8257, "long_regeneration");
|
||||
registerPotion(8225, "strong_regeneration");
|
||||
|
||||
registerPotion(8201, "strength");
|
||||
registerPotion(8265, "long_strength");
|
||||
registerPotion(8233, "strong_strength");
|
||||
|
||||
registerPotion(8200, "weakness");
|
||||
registerPotion(8264, "long_weakness");
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void toClient(Item item) {
|
||||
if (item != null) {
|
||||
if (item.getId().equals("minecraft:spawn_egg") && item.getData() != 0) { // Monster Egg
|
||||
CompoundTag tag = item.getTag();
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
CompoundTag entityTag = new CompoundTag("EntityTag");
|
||||
if (ENTTIY_ID_TO_NAME.containsKey((int) item.getData())) {
|
||||
StringTag id = new StringTag("id", ENTTIY_ID_TO_NAME.get((int) item.getData()));
|
||||
entityTag.put(id);
|
||||
tag.put(entityTag);
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) 0);
|
||||
}
|
||||
if (item.getId().equals("minecraft:potion")) { // Potion
|
||||
CompoundTag tag = item.getTag();
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag("tag");
|
||||
}
|
||||
if (item.getData() >= 16384) {
|
||||
item.setId("minecraft:splash_potion"); // splash id
|
||||
item.setData((short) (item.getData() - 8192));
|
||||
}
|
||||
String name = potionNameFromDamage(item.getData());
|
||||
StringTag potion = new StringTag("Potion", "minecraft:" + name);
|
||||
tag.put(potion);
|
||||
item.setTag(tag);
|
||||
item.setData((short) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void reversedToClient(Item item) {
|
||||
if (item != null) {
|
||||
if (item.getId().equals("minecraft:spawn_egg") && item.getData() == 0) { // Monster Egg
|
||||
CompoundTag tag = item.getTag();
|
||||
int data = 0;
|
||||
if(tag != null) {
|
||||
if (tag.get("EntityTag") instanceof CompoundTag) {
|
||||
CompoundTag entityTag = tag.get("EntityTag");
|
||||
if (entityTag.get("id") instanceof StringTag) {
|
||||
StringTag id = entityTag.get("id");
|
||||
if (ENTTIY_NAME_TO_ID.containsKey(id.getValue()))
|
||||
data = ENTTIY_NAME_TO_ID.get(id.getValue());
|
||||
}
|
||||
tag.remove("EntityTag");
|
||||
}
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) data);
|
||||
}
|
||||
if (item.getId().equals("minecraft:potion")) { // Potion
|
||||
CompoundTag tag = item.getTag();
|
||||
int data = 0;
|
||||
if(tag != null) {
|
||||
if (tag.get("Potion") instanceof StringTag) {
|
||||
StringTag potion = tag.get("Potion");
|
||||
String potionName = potion.getValue().replace("minecraft:", "");
|
||||
if (POTION_NAME_TO_ID.containsKey(potionName)) {
|
||||
data = POTION_NAME_TO_ID.get(potionName);
|
||||
}
|
||||
tag.remove("Potion");
|
||||
}
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) data);
|
||||
}
|
||||
// Splash potion
|
||||
if (item.getId().equals("minecraft:splash_potion")) {
|
||||
CompoundTag tag = item.getTag();
|
||||
int data = 0;
|
||||
item.setId("minecraft:potion"); // Potion
|
||||
if(tag != null) {
|
||||
if (tag.get("Potion") instanceof StringTag) {
|
||||
StringTag potion = tag.get("Potion");
|
||||
String potionName = potion.getValue().replace("minecraft:", "");
|
||||
if (POTION_NAME_TO_ID.containsKey(potionName)) {
|
||||
data = POTION_NAME_TO_ID.get(potionName) + 8192;
|
||||
}
|
||||
tag.remove("Potion");
|
||||
}
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String potionNameFromDamage(short damage) {
|
||||
String cached = POTION_ID_TO_NAME.get((int) damage);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
if (damage == 0) {
|
||||
return "water";
|
||||
}
|
||||
|
||||
int effect = damage & 0xF;
|
||||
int name = damage & 0x3F;
|
||||
boolean enhanced = (damage & 0x20) > 0;
|
||||
boolean extended = (damage & 0x40) > 0;
|
||||
|
||||
boolean canEnhance = true;
|
||||
boolean canExtend = true;
|
||||
|
||||
String id;
|
||||
switch (effect) {
|
||||
case 1:
|
||||
id = "regeneration";
|
||||
break;
|
||||
case 2:
|
||||
id = "swiftness";
|
||||
break;
|
||||
case 3:
|
||||
id = "fire_resistance";
|
||||
canEnhance = false;
|
||||
break;
|
||||
case 4:
|
||||
id = "poison";
|
||||
break;
|
||||
case 5:
|
||||
id = "healing";
|
||||
canExtend = false;
|
||||
break;
|
||||
case 6:
|
||||
id = "night_vision";
|
||||
canEnhance = false;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
id = "weakness";
|
||||
canEnhance = false;
|
||||
break;
|
||||
case 9:
|
||||
id = "strength";
|
||||
break;
|
||||
case 10:
|
||||
id = "slowness";
|
||||
canEnhance = false;
|
||||
break;
|
||||
case 11:
|
||||
id = "leaping";
|
||||
break;
|
||||
case 12:
|
||||
id = "harming";
|
||||
canExtend = false;
|
||||
break;
|
||||
case 13:
|
||||
id = "water_breathing";
|
||||
canEnhance = false;
|
||||
break;
|
||||
case 14:
|
||||
id = "invisibility";
|
||||
canEnhance = false;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
canEnhance = false;
|
||||
canExtend = false;
|
||||
switch (name) {
|
||||
case 0:
|
||||
id = "mundane";
|
||||
break;
|
||||
case 16:
|
||||
id = "awkward";
|
||||
break;
|
||||
case 32:
|
||||
id = "thick";
|
||||
break;
|
||||
default:
|
||||
id = "empty";
|
||||
}
|
||||
}
|
||||
|
||||
if (effect > 0) {
|
||||
if (canEnhance && enhanced) {
|
||||
id = "strong_" + id;
|
||||
} else if (canExtend && extended) {
|
||||
id = "long_" + id;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void registerEntity(Integer id, String name) {
|
||||
ENTTIY_ID_TO_NAME.put(id, name);
|
||||
ENTTIY_NAME_TO_ID.put(name, id);
|
||||
}
|
||||
|
||||
private static void registerPotion(Integer id, String name) {
|
||||
POTION_INDEX.put(id, POTION_ID_TO_NAME.size());
|
||||
POTION_ID_TO_NAME.put(id, name);
|
||||
POTION_NAME_TO_ID.put(name, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PacketUtils {
|
||||
|
||||
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",")
|
||||
.split(",")[3];
|
||||
public static final String NMS_PREFIX;
|
||||
public static final String OBC_PREFIX;
|
||||
|
||||
public static final Class<?> CRAFT_PLAYER_CLASS, CRAFT_SERVER_CLASS, CHAT_SERIALIZER, COMPONENT_CLASS;
|
||||
|
||||
/**
|
||||
* This Map is to reduce Reflection action which take more ressources than just RAM action
|
||||
*/
|
||||
private static final HashMap<String, Class<?>> ALL_CLASS;
|
||||
|
||||
static {
|
||||
ALL_CLASS = new HashMap<>();
|
||||
NMS_PREFIX = ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_17) ? "net.minecraft." : "net.minecraft.server." + VERSION + ".";
|
||||
OBC_PREFIX = "org.bukkit.craftbukkit." + VERSION + ".";
|
||||
CHAT_SERIALIZER = getNmsClass("IChatBaseComponent$ChatSerializer", "network.chat.", "ChatSerializer");
|
||||
COMPONENT_CLASS = getNmsClass("IChatBaseComponent", "network.chat.");
|
||||
CRAFT_PLAYER_CLASS = getObcClass("entity.CraftPlayer");
|
||||
CRAFT_SERVER_CLASS = getObcClass("CraftServer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Class in NMS, with a processing reducer
|
||||
*
|
||||
* @param name of the NMS class (in net.minecraft.server package ONLY, because it's NMS)
|
||||
* @return clazz the searched class
|
||||
*/
|
||||
public static Class<?> getNmsClass(String name, String packagePrefix, String... alias){
|
||||
return ALL_CLASS.computeIfAbsent(name, (a) -> {
|
||||
String fullPrefix = NMS_PREFIX + (ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_17) ? packagePrefix : "");
|
||||
try {
|
||||
Class<?> clazz = Class.forName(fullPrefix + name);
|
||||
if(clazz != null)
|
||||
return clazz;
|
||||
} catch (ClassNotFoundException e) {
|
||||
if(alias.length == 0)
|
||||
e.printStackTrace(); // no alias, print error
|
||||
// else ignore and go check for alias
|
||||
}
|
||||
|
||||
for(String className : alias) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(fullPrefix + className);
|
||||
if(clazz != null)
|
||||
return clazz;
|
||||
} catch (ClassNotFoundException e) {
|
||||
if(className == alias[alias.length - 1]) // if it's last alias, print error
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Class in NMS, with a processing reducer
|
||||
*
|
||||
* @param name of the NMS class (in net.minecraft.server package ONLY, because it's NMS)
|
||||
* @return clazz the searched class
|
||||
*/
|
||||
public static Class<?> getObcClass(String name, String... alias){
|
||||
return ALL_CLASS.computeIfAbsent(name, (a) -> {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(OBC_PREFIX + name);
|
||||
if(clazz != null)
|
||||
return clazz;
|
||||
} catch (ClassNotFoundException e) {
|
||||
if(alias.length == 0)
|
||||
e.printStackTrace(); // no alias, print error
|
||||
// else ignore and go check for alias
|
||||
}
|
||||
|
||||
for(String className : alias) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(OBC_PREFIX + className);
|
||||
if(clazz != null)
|
||||
return clazz;
|
||||
} catch (ClassNotFoundException e) {
|
||||
if(className == alias[alias.length - 1]) // if it's last alias, print error
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current player ping
|
||||
*
|
||||
* @param p the player
|
||||
* @return the player ping
|
||||
*/
|
||||
public static int getPing(Player p) {
|
||||
try {
|
||||
Object entityPlayer = getEntityPlayer(p);
|
||||
return entityPlayer.getClass().getField("ping").getInt(entityPlayer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get NMS player connection of specified player
|
||||
*
|
||||
* @param p Player of which we want to get the player connection
|
||||
* @return the NMS player connection
|
||||
*/
|
||||
public static Object getPlayerConnection(Player p) {
|
||||
try {
|
||||
Object entityPlayer = getEntityPlayer(p);
|
||||
if(ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_17))
|
||||
return entityPlayer.getClass().getField("b").get(entityPlayer);
|
||||
else
|
||||
return entityPlayer.getClass().getField("playerConnection").get(entityPlayer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get NMS entity player of specified one
|
||||
*
|
||||
* @param p the player that we want the NMS entity player
|
||||
* @return the entity player
|
||||
*/
|
||||
public static Object getEntityPlayer(Player p) {
|
||||
try {
|
||||
Object craftPlayer = CRAFT_PLAYER_CLASS.cast(p);
|
||||
return craftPlayer.getClass().getMethod("getHandle").invoke(craftPlayer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getNmsEntityName(Object nmsEntity) {
|
||||
try {
|
||||
if(ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_13)) {
|
||||
Object chatBaseComponent = getNmsClass("Entity", "world.entity.").getDeclaredMethod("getDisplayName").invoke(nmsEntity);
|
||||
return (String) getNmsClass("IChatBaseComponent", "network.chat.").getDeclaredMethod("getString").invoke(chatBaseComponent);
|
||||
} else {
|
||||
return (String) getNmsClass("Entity", "world.entity.").getDeclaredMethod("getName").invoke(nmsEntity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the packet to the specified player
|
||||
*
|
||||
* @param p which will receive the packet
|
||||
* @param packet the packet to sent
|
||||
*/
|
||||
public static void sendPacket(Player p, Object packet) {
|
||||
try {
|
||||
Object playerConnection = getPlayerConnection(p);
|
||||
playerConnection.getClass().getMethod("sendPacket", getNmsClass("Packet", "network.protocol.")).invoke(playerConnection, packet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get NMS entity player of specified one
|
||||
*
|
||||
* @param p the player that we want the NMS entity player
|
||||
* @return the entity player
|
||||
*/
|
||||
public static Object getCraftServer() {
|
||||
try {
|
||||
Object craftServer = CRAFT_SERVER_CLASS.cast(Bukkit.getServer());
|
||||
return craftServer.getClass().getMethod("getHandle").invoke(craftServer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
|
||||
public enum ProtocolVersion {
|
||||
|
||||
// http://wiki.vg/Protocol_version_numbers
|
||||
V1_7(0, 5, 0),
|
||||
V1_8(6, 47, 1),
|
||||
V1_9(49, 110, 2), // 1.9.X - Starts with 49 as 48 was an april fools update
|
||||
V1_10(201, 210, 3), // 1.10.X - Starts with 201 because why not.
|
||||
V1_11(301, 316, 4),
|
||||
V1_12(317, 340, 5),
|
||||
V1_13(341, 440, 6),
|
||||
V1_14(100, 441, 500),
|
||||
V1_15(100, 550, 578),
|
||||
V1_16(100, 700, 754),
|
||||
V1_17(100, 755, 756),
|
||||
V1_18(100, 757, 800),
|
||||
V1_19(100, 801, 1000),
|
||||
HIGHER(Integer.MAX_VALUE, -1, Integer.MAX_VALUE);
|
||||
|
||||
// Latest version should always have the upper limit set to Integer.MAX_VALUE so
|
||||
// I don't have to update the plugin for every minor protocol change
|
||||
|
||||
public final int MIN_VER;
|
||||
public final int MAX_VER;
|
||||
public final int index; // Represents how new the version is (0 - extremely old)
|
||||
|
||||
private static final ProtocolVersion SERVER_VERSION;
|
||||
public static final String BUKKIT_VERSION;
|
||||
|
||||
static {
|
||||
BUKKIT_VERSION = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
|
||||
SERVER_VERSION = getVersionByName(BUKKIT_VERSION);
|
||||
}
|
||||
|
||||
ProtocolVersion(int min, int max, int index) {
|
||||
this.MIN_VER = min;
|
||||
this.MAX_VER = max;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public boolean isNewerThan(ProtocolVersion other) {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
public boolean isNewerOrEquals(ProtocolVersion other) {
|
||||
return index >= other.index;
|
||||
}
|
||||
|
||||
public static ProtocolVersion getVersionByName(String name) {
|
||||
for (ProtocolVersion v : ProtocolVersion.values())
|
||||
if (name.toLowerCase().startsWith(v.name().toLowerCase()))
|
||||
return v;
|
||||
return HIGHER;
|
||||
}
|
||||
|
||||
public static ProtocolVersion getVersion(int protocolVersion) {
|
||||
for (ProtocolVersion ver : ProtocolVersion.values()) {
|
||||
if (protocolVersion >= ver.MIN_VER && protocolVersion <= ver.MAX_VER) {
|
||||
return ver;
|
||||
}
|
||||
}
|
||||
return HIGHER;
|
||||
}
|
||||
|
||||
public static ProtocolVersion getServerVersion() {
|
||||
return SERVER_VERSION;
|
||||
}
|
||||
|
||||
public static void remapIds(int server, int player, Item item) {
|
||||
if (areIdsCompatible(server, player)) {
|
||||
return;
|
||||
}
|
||||
if ((server >= V1_9.MIN_VER && player <= V1_8.MAX_VER) || (player >= V1_9.MIN_VER && server <= V1_8.MAX_VER)) {
|
||||
if ((server >= V1_9.MIN_VER && player <= V1_8.MAX_VER)) {
|
||||
ItemRewriter_1_9_TO_1_8.reversedToClient(item);
|
||||
return;
|
||||
}
|
||||
ItemRewriter_1_9_TO_1_8.toClient(item);
|
||||
return;
|
||||
}
|
||||
if ((server <= V1_10.MAX_VER && player >= V1_11.MIN_VER)
|
||||
|| (player <= V1_10.MAX_VER && server >= V1_11.MIN_VER)) {
|
||||
if (server <= V1_10.MAX_VER && player >= V1_11.MIN_VER) {
|
||||
ItemRewriter_1_11_TO_1_10.toClient(item);
|
||||
} else {
|
||||
ItemRewriter_1_11_TO_1_10.reverseToClient(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean areIdsCompatible(int version1, int version2) {
|
||||
if ((version1 >= V1_9.MIN_VER && version2 <= V1_8.MAX_VER)
|
||||
|| (version2 >= V1_9.MIN_VER && version1 <= V1_8.MAX_VER)) {
|
||||
return false;
|
||||
}
|
||||
if ((version1 <= V1_10.MAX_VER && version2 >= V1_11.MIN_VER)
|
||||
|| (version1 <= V1_10.MAX_VER && version2 >= V1_11.MIN_VER)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getClientVersion(Player p) {
|
||||
return ChatItem.getInstance().getPlayerVersionAdapter().getProtocolVersion(p);
|
||||
}
|
||||
|
||||
public static String stringifyAdress(InetSocketAddress address) {
|
||||
String port = String.valueOf(address.getPort());
|
||||
String ip = address.getAddress().getHostAddress();
|
||||
return ip + ":" + port;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +1,13 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import me.dadus33.chatitem.ChatItem;
|
||||
|
||||
public class Reflect {
|
||||
|
||||
|
||||
private static String versionString;
|
||||
|
||||
|
||||
private static String getVersion() {
|
||||
if (versionString == null) {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
|
||||
}
|
||||
|
||||
return versionString;
|
||||
}
|
||||
|
||||
public static Class<?> getNMSClass(String nmsClassName) {
|
||||
|
||||
String clazzName = "net.minecraft.server." + getVersion() + nmsClassName;
|
||||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = Class.forName(clazzName);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
|
||||
public static Class<?> getOBCClass(String obcClassName) {
|
||||
|
||||
String clazzName = "org.bukkit.craftbukkit." + getVersion() + obcClassName;
|
||||
Class<?> clazz;
|
||||
|
||||
try {
|
||||
clazz = Class.forName(clazzName);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
|
||||
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... params) {
|
||||
try {
|
||||
return clazz.getMethod(methodName, params);
|
||||
@@ -60,4 +17,18 @@ public class Reflect {
|
||||
}
|
||||
}
|
||||
|
||||
public static Field getField(Class<?> clazz, String... fieldNames){
|
||||
for(String field : fieldNames) {
|
||||
try{
|
||||
Field f = clazz.getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (NoSuchFieldException e) {
|
||||
// go to next
|
||||
}
|
||||
}
|
||||
ChatItem.getInstance().getLogger().severe("Failed to find field: " + Arrays.asList(fieldNames) + " in class: " + clazz.getCanonicalName());
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
public static Object getPrivateField(Object object, String field)
|
||||
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
|
||||
Field objectField = object.getClass().getDeclaredField(field);
|
||||
objectField.setAccessible(true);
|
||||
return objectField.get(object);
|
||||
}
|
||||
|
||||
public static void setField(Object src, String fieldName, Object value) {
|
||||
try {
|
||||
Field field = src.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(src, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified field name in the object source
|
||||
*
|
||||
* @param source where we will find the field
|
||||
* @param field the name of the field
|
||||
* @return the requested field
|
||||
*/
|
||||
public static Object getField(Object source, String field) {
|
||||
try {
|
||||
Field f = source.getClass().getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
return f.get(source);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call method. Return the method return.
|
||||
*
|
||||
* @param source the object where we want to run the method
|
||||
* @param method the name of the method to call
|
||||
* @return the return of the method called
|
||||
*/
|
||||
public static Object callMethod(Object source, String method) {
|
||||
try {
|
||||
return source.getClass().getDeclaredMethod(method).invoke(source);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first field which have the searching for class type
|
||||
*
|
||||
* @param from the object where we will try to find the field
|
||||
* @param clazz the class that have to define the field
|
||||
* @param searchingFor the class of the required field
|
||||
* @return the field (or null if not found)
|
||||
* @throws Exception if something gone wrong
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getFirstWith(Object from, Class<?> clazz, Class<T> searchingFor) throws Exception {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getType().equals(searchingFor)) {
|
||||
f.setAccessible(true);
|
||||
return (T) f.get(from);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,19 +16,38 @@ import java.util.logging.Level;
|
||||
|
||||
public class Storage {
|
||||
|
||||
private FileConfiguration conf;
|
||||
public final HashMap<String, HashMap<Short, String>> TRANSLATIONS = new HashMap<>();
|
||||
public final List<String> PLACEHOLDERS;
|
||||
public final String NAME_FORMAT;
|
||||
public final String AMOUNT_FORMAT;
|
||||
public final Boolean COLOR_IF_ALREADY_COLORED;
|
||||
public final Boolean FORCE_ADD_AMOUNT;
|
||||
public final Boolean LET_MESSAGE_THROUGH;
|
||||
public final Boolean DENY_IF_NO_ITEM;
|
||||
public final Boolean HAND_DISABLED;
|
||||
public final Boolean SHOW_NO_PERM_NORMAL;
|
||||
public final Boolean SHOW_NO_PERM_COMMAND;
|
||||
public final String HAND_NAME;
|
||||
public final String NAME_FORMAT;
|
||||
public final String AMOUNT_FORMAT;
|
||||
public final String NO_PERMISSION_MESSAGE;
|
||||
public final String DENY_MESSAGE;
|
||||
public final String LIMIT_MESSAGE;
|
||||
public final String RELOAD_MESSAGE;
|
||||
final Integer CONFIG_VERSION;
|
||||
public final List<Command> ALLOWED_COMMANDS = new ArrayList<>();
|
||||
public final String COOLDOWN_MESSAGE;
|
||||
public final String SECONDS;
|
||||
public final String MINUTES;
|
||||
public final String HOURS;
|
||||
private final Integer CONFIG_VERSION;
|
||||
public final Long COOLDOWN;
|
||||
public final Integer LIMIT;
|
||||
public final List<Command> ALLOWED_PLUGIN_COMMANDS = new ArrayList<>();
|
||||
public final List<String> ALLOWED_DEFAULT_COMMANDS = new ArrayList<>();
|
||||
public final List<String> PLACEHOLDERS;
|
||||
public final List<String> HAND_TOOLTIP;
|
||||
|
||||
public Storage(FileConfiguration conf) {
|
||||
public Storage(FileConfiguration cnf) {
|
||||
this.conf = cnf;
|
||||
CONFIG_VERSION = conf.getInt("config-version");
|
||||
checkConfigVersion();
|
||||
Set<String> keys = conf.getConfigurationSection("Translations").getKeys(false);
|
||||
for (String key : keys) {
|
||||
HashMap<Short, String> entry = new HashMap<>();
|
||||
@@ -40,42 +59,68 @@ public class Storage {
|
||||
|
||||
TRANSLATIONS.put(key, entry);
|
||||
}
|
||||
CONFIG_VERSION = conf.getInt("config-version");
|
||||
List<String> added = conf.getStringList("placeholders");
|
||||
COOLDOWN = conf.getLong("General.cooldown");
|
||||
LIMIT = conf.getInt("General.limit");
|
||||
List<String> added = conf.getStringList("General.placeholders");
|
||||
PLACEHOLDERS = ImmutableList.copyOf(added);
|
||||
NAME_FORMAT = color(conf.getString("name-format"));
|
||||
AMOUNT_FORMAT = color(conf.getString("amount-format"));
|
||||
COLOR_IF_ALREADY_COLORED = conf.getBoolean("color-if-already-colored");
|
||||
FORCE_ADD_AMOUNT = conf.getBoolean("force-add-amount");
|
||||
DENY_IF_NO_ITEM = conf.getBoolean("deny-if-no-item");
|
||||
DENY_MESSAGE = color(conf.getString("deny-message"));
|
||||
RELOAD_MESSAGE = color(conf.getString("reload-success"));
|
||||
final List<String> cmds = conf.getStringList("commands");
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(ChatItem.getInstance(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for(String s : cmds){
|
||||
Command c = Bukkit.getPluginCommand(s);
|
||||
if(c!=null) {
|
||||
ALLOWED_COMMANDS.add(Bukkit.getPluginCommand(s));
|
||||
}
|
||||
else
|
||||
ChatItem.getInstance().getLogger().log(Level.WARNING, ChatColor.RED.toString().concat("Unknown command ")
|
||||
.concat(s).concat(" found in config! Ignoring occurrence..."));
|
||||
NAME_FORMAT = color(conf.getString("General.name-format"));
|
||||
AMOUNT_FORMAT = color(conf.getString("General.amount-format"));
|
||||
COLOR_IF_ALREADY_COLORED = conf.getBoolean("General.color-if-already-colored");
|
||||
LET_MESSAGE_THROUGH = conf.getBoolean("General.let-message-through");
|
||||
FORCE_ADD_AMOUNT = conf.getBoolean("General.force-add-amount");
|
||||
DENY_IF_NO_ITEM = conf.getBoolean("General.deny-if-no-item");
|
||||
HAND_DISABLED = conf.getBoolean("General.hand.disabled");
|
||||
SHOW_NO_PERM_NORMAL = conf.getBoolean("General.show-no-permission-message.normal");
|
||||
SHOW_NO_PERM_COMMAND = conf.getBoolean("General.show-no-permission-message.command");
|
||||
DENY_MESSAGE = color(conf.getString("Messages.deny-message"));
|
||||
HAND_NAME = color(conf.getString("General.hand.name"));
|
||||
LIMIT_MESSAGE = color(conf.getString("Messages.limit-message"));
|
||||
RELOAD_MESSAGE = color(conf.getString("Messages.reload-success"));
|
||||
NO_PERMISSION_MESSAGE = color(conf.getString("Messages.no-permission"));
|
||||
COOLDOWN_MESSAGE = color(conf.getString("Messages.cooldown-message"));
|
||||
SECONDS = color(conf.getString("Messages.seconds"));
|
||||
MINUTES = color(conf.getString("Messages.minutes"));
|
||||
HOURS = color(conf.getString("Messages.hours"));
|
||||
HAND_TOOLTIP = conf.getStringList("General.hand.tooltip");
|
||||
colorStringList(HAND_TOOLTIP);
|
||||
final List<String> cmds = conf.getStringList("General.commands");
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(ChatItem.getInstance(), () -> {
|
||||
for(String s : cmds){
|
||||
Command c = Bukkit.getPluginCommand(s);
|
||||
if(c!=null) {
|
||||
ALLOWED_PLUGIN_COMMANDS.add(c);
|
||||
}
|
||||
else {
|
||||
ALLOWED_DEFAULT_COMMANDS.add(s);
|
||||
}
|
||||
}
|
||||
}, 100L);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static String color(String s) {
|
||||
return ChatColor.translateAlternateColorCodes('&', s);
|
||||
}
|
||||
|
||||
private void checkConfigVersion() {
|
||||
int latestVersion = ChatItem.CFG_VER;
|
||||
if (latestVersion != CONFIG_VERSION) {
|
||||
Bukkit.getLogger().log(Level.WARNING, ChatColor.RED + "ChatItem detected an older or invalid configuration file. Replacing it with the default config...");
|
||||
performOverwrite();
|
||||
conf = ChatItem.getInstance().getConfig();
|
||||
Bukkit.getLogger().log(Level.WARNING, ChatColor.RED + "Replacement complete!");
|
||||
}
|
||||
}
|
||||
|
||||
void performOverwrite() {
|
||||
|
||||
private void performOverwrite() {
|
||||
ChatItem.getInstance().saveResource("config.yml", true);
|
||||
}
|
||||
|
||||
private static void colorStringList(List<String> input){
|
||||
for(int i=0; i<input.size(); ++i){
|
||||
input.set(i, color(input.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package me.dadus33.chatitem.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",")
|
||||
.split(",")[3];
|
||||
|
||||
public static String coloredMessage(String msg) {
|
||||
return ChatColor.translateAlternateColorCodes('&', msg);
|
||||
}
|
||||
|
||||
public static List<String> coloredMessage(String... messages) {
|
||||
List<String> ret = new ArrayList<>();
|
||||
for (String message : messages) {
|
||||
ret.add(coloredMessage(message));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static List<Player> getOnlinePlayers() {
|
||||
List<Player> list = new ArrayList<>();
|
||||
try {
|
||||
Class<?> mcServer = PacketUtils.getNmsClass("MinecraftServer", "server.");
|
||||
Object server = mcServer.getMethod("getServer").invoke(mcServer);
|
||||
Object craftServer = server.getClass().getField("server").get(server);
|
||||
Object getted = craftServer.getClass().getMethod("getOnlinePlayers").invoke(craftServer);
|
||||
if (getted instanceof Player[])
|
||||
for (Player obj : (Player[]) getted)
|
||||
list.add(obj);
|
||||
else if (getted instanceof List)
|
||||
for (Object obj : (List<?>) getted)
|
||||
list.add((Player) obj);
|
||||
else
|
||||
System.out.println("Unknow getOnlinePlayers");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current player ping
|
||||
*
|
||||
* @param p the player
|
||||
* @return the player ping
|
||||
*/
|
||||
public static int getPing(Player p) {
|
||||
try {
|
||||
Object entityPlayer = PacketUtils.getEntityPlayer(p);
|
||||
if(ProtocolVersion.getServerVersion().isNewerOrEquals(ProtocolVersion.V1_17))
|
||||
return entityPlayer.getClass().getField("e").getInt(entityPlayer);
|
||||
else
|
||||
return entityPlayer.getClass().getField("ping").getInt(entityPlayer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+1201
-62
File diff suppressed because it is too large.
Load diff
@@ -1,8 +1,11 @@
|
||||
name: ChatItem
|
||||
main: me.dadus33.chatitem.ChatItem
|
||||
version: ${project.version}
|
||||
depend: [ProtocolLib]
|
||||
author: dadus33
|
||||
|
||||
softdepend: [ViaVersion, ProtocolSupport, ProtocolLib]
|
||||
authors: [dadus33, WOLFI3654, Elikill58]
|
||||
api-version: 1.13
|
||||
|
||||
commands:
|
||||
cireload:
|
||||
description: Reload the ChatItem config.
|
||||
@@ -14,4 +17,7 @@ permissions:
|
||||
default: op
|
||||
chatitem.use:
|
||||
description: Allows your messages to be parsed by the plugin
|
||||
default: true
|
||||
default: op
|
||||
chatitem.ignore-cooldown:
|
||||
description: Allows you to ignore cooldowns (if enabled)
|
||||
default: op
|
||||
Reference in new issue
Block a user