Compare commits

...
55 Commits
Author SHA1 Message Date
Elikill58 e8869dfcdf Edit default config and upgrade to 1.4. 2021-10-06 22:20:11 +02:00
Elikill58 b9cc5ad202 Upgrade log4j and maven-shade-plugin to prevent security isssue 2021-10-05 17:52:43 +02:00
Elikill58 4c9efd5d39 Merge branch 'master' of https://github.com/dadus33-plugins/ChatItem 2021-10-05 17:03:46 +02:00
Elikill58 b75325998d Remove unused code 2021-10-05 16:52:13 +02:00
Elikill58 c23de3969c End of ProtocolLib dependency ! (Which fix #18) 2021-10-05 16:45:29 +02:00
Elikill58 f1c9511c50 Use PL when there isn't ViaVersion or PS 2021-10-05 13:27:53 +02:00
Elikill58 bd2b4df822 Update version support 2021-10-05 13:06:10 +02:00
dadus33 f33e0c8ad4 Merge pull request #21 from Elikill58/master
Fix compilation, ProtocolLib support and other quick issues
2021-09-28 14:56:48 +03:00
Elikill58 eeb1e40c11 Fix https://github.com/dadus33/ChatItem/issues/20 2021-09-26 14:54:06 +02:00
Elikill58 7c84b3a85e Fix https://github.com/dadus33/ChatItem/issues/19 2021-09-26 14:45:54 +02:00
Elikill58 146fc3abe5 Remove few warnings 2021-09-25 23:34:27 +02:00
Elikill58 e9aa99ad82 Remove projects files 2021-09-25 23:27:42 +02:00
Elikill58 89d6a60235 Fix dependencies 2021-09-25 23:19:28 +02:00
dadus33 d69c0e02f3 Add proper spacing in authors array 2019-03-06 11:07:44 +02:00
dadus33 7f337c0f49 Merge pull request #11 from WOLFI3654/master
1.13 Update
2019-03-06 11:06:12 +02:00
Thomas Windt a403b35602 Merge branch '1.13' of github.com:WOLFI3654/ChatItem into 1.13 2019-02-13 20:15:27 +01:00
Thomas Windt 0cdb2eccda Add 1.13 protocols
Signed-off-by: Thomas Windt <thomas@architekt-windt.de>
2019-02-13 20:14:36 +01:00
Thomas Windt a5cfb6216a Update version 2019-02-13 20:01:29 +01:00
Thomas Windt a219e7c61e Dirty 1.13 port
Signed-off-by: Thomas Windt <thomas@architekt-windt.de>
2019-02-13 19:56:29 +01:00
dadus33 f932d56bf1 Fix color-if-already-colored config option bug 2018-01-06 18:56:21 +02:00
dadus33 d925b2d922 Update to 1.12.X (accepts any protocol number higher than the one for 1.12).
Implement minor performance improvements.
Fix permission and JSON styling related issues.
Add config setting for 'normal' and 'command' no permission message.
2018-01-01 19:52:12 +02:00
dadus33 4b4de6426c Fix support for situations when the server version is greater than the client version. 2017-09-15 19:46:07 +03:00
dadus33 0e75d355c6 Remove app.deveo.com support. 2017-08-26 12:51:59 +03:00
dadus33 523d13f9c5 Add API and Maven repository, update to Java 8, fix numerous bugs and add support for 1.12.1 2017-08-26 12:47:55 +03:00
dadus33 cd6b79d946 First commit of dev branch. 2017-07-25 23:47:55 +03:00
dadus33 5ecbffacbd Add 1.12 to the ProtocolVersion enum. Rewrite the remapping system. Use client version instead of server version when deciding on the NBTTagList format. 2017-06-25 21:26:52 +03:00
dadus33 2c84f0c60e Remove deubg messages. 2017-06-24 15:29:42 +03:00
dadus33 9a77c68685 Ignore null addresses in the HandShakeListener (player couldn't join the server for some reason or was kicked in the meantime) 2017-06-24 00:21:16 +03:00
dadus33 a597d86710 Only use the proper user (the one which is != null) 2017-06-18 22:25:23 +03:00
dadus33 8f3ed60b8c Properly load client addresses on servers connected to bungee by loading the address from an updated player (instead of temporary player) 2017-06-18 01:13:57 +03:00
dadus33 37e08ffb40 Add discord badge 2017-06-12 23:49:49 +03:00
dadus33 e400de8cc4 Remove ProtocolSupport as maven dependency (required manually installed file to local repo) and use reflection instead.
Fix OpenNBT imports.
2017-06-12 23:17:12 +03:00
dadus33 bfd78444e3 Use proper details for OpenNBT (change from spacehq repo) 2017-06-12 22:34:43 +03:00
dadus33 ed1934be36 Add 1.12 support. 2017-06-12 22:27:46 +03:00
dadus33 99a980476b Properly handle messages sent by different client versions. Update version to 1.3.5 2017-02-23 18:30:26 +02:00
dadus33 f581ccad1c Update version to 1.3.4. Remove "debug" from config.yml 2017-02-13 20:09:29 +02:00
dadus33 2c29af8a6a Use new method to replace NMS NBTTagCompound#toString(). Add a caching system for sent items. 2017-02-13 20:08:19 +02:00
dadus33 306cae8bcb Add alternative method (better than the default NMS one) to stringify items and NBTTagCompounds. 2017-02-12 20:53:57 +02:00
dadus33 781d0b3a70 Remove debug mode. 2017-02-12 18:22:04 +02:00
dadus33 355ff63318 Fix Translator class. 2017-01-24 00:25:01 +02:00
dadus33 243e117538 Properly use asynchronous tasks. Allow lists of strings in empty hand tooltips. 2017-01-23 13:11:00 +02:00
dadus33 f1a1f795d2 Add debug mode. 2017-01-13 22:49:32 +02:00
dadus33 bcb092f29f Fix bugs in the previous version. Add support for empty hand messages. 2017-01-08 21:42:01 +02:00
dadus33 4500161341 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/main/java/me/dadus33/chatitem/listeners/ChatPacketValidator.java
2017-01-08 20:00:44 +02:00
dadus33 79e1cd3951 Fix issues with order of simultaneous messages. 2017-01-08 20:00:15 +02:00
dadus33 77c8abf457 Fix issues with order of simultaneous messages. 2017-01-08 19:59:32 +02:00
dadus33 847b6ddc92 Add fltering for Shulker Boxes. 2017-01-08 14:30:13 +02:00
dadus33 d234858ec5 Fix permissions. Add cooldown placeholder. Reformat config. 2016-12-27 20:25:58 +02:00
dadus33 e7a3cf69fd Fix the integration with plugins that use Spigot's ChatComponent API. 2016-12-22 10:09:06 +02:00
dadus33 ee41588a91 Fix the "Invalid Item!" issue. 2016-12-17 15:33:17 +02:00
dadus33 1b160bdd28 Fix handling of empty arrays in JSON messages, fixing integration with some plugins like VentureChat. 2016-12-08 14:29:26 +02:00
dadus33 c33fe83030 Add cooldown system and new default config with translations. 2016-12-07 19:35:58 +02:00
dadus33 eaeba41c53 Fix null message handling in the Log4J filter and update it to match new separator system. 2016-12-04 13:35:53 +02:00
dadus33 34905cbfeb Revert travis changes. 2016-11-29 23:28:06 +02:00
dadus33 78e8b7f8a7 Add .travis.yml to .gitignore 2016-11-29 23:17:38 +02:00
47 changed files with 5354 additions and 513 deletions

No files matched your search

+4
View File
@@ -1,5 +1,9 @@
.idea/
src/test/
bin/
target/
*.iml
dependency-reduced-pom.xml
.classpath
.project
.settings/
+2 -1
View File
@@ -1 +1,2 @@
language: java
language: java
script: "mvn clean package -DskipTests"
+1 -1
View File
@@ -1,4 +1,4 @@
# ChatItem [![Build Status](https://travis-ci.org/dadus33/ChatItem.svg?branch=master)](https://travis-ci.org/dadus33/ChatItem)
# ChatItem [![Build Status](https://travis-ci.org/dadus33/ChatItem.svg?branch=master)](https://travis-ci.org/dadus33/ChatItem) [![Help Chat](https://img.shields.io/badge/chat-on_discord-7289da.svg)](https://discord.gg/eQJbVJt)
The official repository of the ChatItem Spigot/Bukkit plugin.
# Building
Building this is simple as we use Maven.
+73 -30
View File
@@ -5,44 +5,59 @@
<modelVersion>4.0.0</modelVersion>
<groupId>me.dadus33</groupId>
<artifactId>ChatItem</artifactId>
<version>1.2.5</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>
+92 -62
View File
@@ -1,52 +1,56 @@
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.ChatPacketValidator;
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 = 6;
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;
//public ProtocolManager pm;
public ChatPacketListener packetListener;
public ChatPacketValidator packetValidator;
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 +59,89 @@ 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
}
}
@@ -0,0 +1,182 @@
package me.dadus33.chatitem.api;
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.ChatPacketListener;
import me.dadus33.chatitem.utils.ProtocolVersion;
import me.dadus33.chatitem.utils.Storage;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
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, ChatPacketListener.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, ChatPacketListener.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 = ChatPacketListener.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 = ChatPacketListener.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);
}
@@ -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,137 @@
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.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 = Reflect.getOBCClass("inventory.CraftItemStack");
private static final Class<?> NBT_STRING = Reflect.getNMSClass("NBTTagString");
private static final Class<?> NBT_LIST = Reflect.getNMSClass("NBTTagList");
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 = Reflect.getNMSClass("ItemStack");
private static final Method AS_NMS_COPY = Reflect.getMethod(CRAFT_ITEM_STACK_CLASS, "asNMSCopy", ItemStack.class);
private static final Class<?> NBT_TAG_COMPOUND = Reflect.getNMSClass("NBTTagCompound");
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");
private static final Field LIST_FIELD = Reflect.getField(NBT_LIST, "list");
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(Reflect.getNMSClass("NBTTagByte"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagByteArray"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagDouble"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagFloat"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagInt"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagIntArray"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagLong"));
NBT_BASE_CLASSES.add(Reflect.getNMSClass("NBTTagShort"));
for (Class<?> NBT_BASE_CLASS : NBT_BASE_CLASSES) {
NBT_BASE_DATA_FIELD.add(Reflect.getField(NBT_BASE_CLASS, "data"));
}
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 +142,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 +155,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 +187,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 +373,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 +421,10 @@ 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,91 +1,135 @@
package me.dadus33.chatitem.json;
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();
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,22 +4,53 @@ 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;
private final static char SEPARATOR = ((char)0x0007);
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;
@@ -29,7 +60,7 @@ public class ChatEventListener implements Listener {
lastIndex = str.indexOf(findStr,lastIndex);
if(lastIndex != -1){
count ++;
count++;
lastIndex += findStr.length();
}
}
@@ -37,8 +68,8 @@ public class ChatEventListener implements Listener {
}
@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);
@@ -50,21 +81,51 @@ 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;
}
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){
@@ -86,25 +147,32 @@ public class ChatEventListener implements Listener {
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){
@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);
}
Command cmd = Bukkit.getPluginCommand(e.getMessage().split(" ")[0].substring(1));
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
return;
}
if(!c.ALLOWED_COMMANDS.contains(cmd)){
return;
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) {
@@ -118,7 +186,10 @@ public class ChatEventListener implements Listener {
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;
}
@@ -126,10 +197,33 @@ public class ChatEventListener implements Listener {
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;
}
}
return;
}
String s = e.getMessage();
@@ -150,6 +244,9 @@ public class ChatEventListener implements Listener {
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,22 +1,36 @@
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.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 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;
public class ChatPacketListener extends PacketAdapter {
@@ -24,12 +38,20 @@ 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,135 +76,160 @@ public class ChatPacketListener extends PacketAdapter {
}
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 onPacketSending(PacketEvent e) {
if(ChatItem.post17) //actionbar messages are only available after 1.7
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();
if(packet.getChatComponents().readSafely(0)==null){ // null check for some cases of messages from other plugins
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;
}
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
}
if(json.lastIndexOf("\\u0007")==-1){ //if the message doesn't contain the BEL separator, then it's certainly NOT a message we want to parse
return;
}
//here we find the last player name in the string
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
return;
}
//then we make sure the name is after the BEL separator.
if(json.lastIndexOf(name)!=json.lastIndexOf("\\u0007")+6){
return; //then it means somehow the message just contained a BEL character not inserted by our plugin
}
Player p = Bukkit.getPlayer(name);
StringBuilder buff = new StringBuilder(json);
buff.replace(topIndex-(name.length()+6), topIndex, ""); //we remove both the name and the separator from the json string
json = buff.toString();
//Item Styling
//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;
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;
}
} else {
if (inHand.hasItemMeta()) {
if (inHand.getItemMeta().hasDisplayName()) {
dname = true;
}
int index = json.lastIndexOf(pname)+pname.length();
if(index>topIndex){
topIndex = index;
name = pname.replace("\\u0007", "");
}
}
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(name==null){ //something went really bad, so we run away and hide (AKA the player left or is on another server)
return;
}
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);
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 {
replacer = replacer.replace(NAME, materialToName(inHand.getType()));
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();
}
//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(message!=null)
packet.getChatComponents().writeSafely(0, WrappedChatComponent.fromJson(message));
else
e.setCancelled(true);
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 {
@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,239 @@
package me.dadus33.chatitem.listeners;
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 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 final static List<Material> SHULKER_BOXES = new ArrayList<>();
private Storage c;
public ChatPacketListenerV2(Storage s) {
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;
}
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
return;
}
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)
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 (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;
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,100 @@
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
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,203 @@
package me.dadus33.chatitem.packets;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.NoSuchElementException;
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().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().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,26 @@
package me.dadus33.chatitem.packets;
import java.util.ArrayList;
import java.util.List;
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 List<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,21 @@
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);
}
@SuppressWarnings("unchecked")
public <T> T getMeta(String key, T def) {
return (T) metas.getOrDefault(key, def);
}
}
@@ -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,101 @@
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.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) {}
});
}
@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,108 @@
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.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;
@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));
} 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) {}
});
}
@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 {
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,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;
}
}
@@ -2,6 +2,7 @@ package me.dadus33.chatitem.utils;
import org.bukkit.Bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflect {
@@ -50,6 +51,20 @@ public class Reflect {
return clazz;
}
public static Class<?> getOBCEntityClass(String obcClassName){
String clazzName = "org.bukkit.craftbukkit." + getVersion() + "entity." + 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 {
@@ -60,4 +75,15 @@ public class Reflect {
}
}
public static Field getField(Class<?> clazz, String fieldName){
try{
Field f = clazz.getDeclaredField(fieldName);
f.setAccessible(true);
return f;
} catch (NoSuchFieldException e) {
e.printStackTrace();
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,21 +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 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_COMMANDS = new ArrayList<>();
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<>();
@@ -42,44 +59,68 @@ public class Storage {
TRANSLATIONS.put(key, entry);
}
CONFIG_VERSION = conf.getInt("config-version");
LIMIT = conf.getInt("limit");
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"));
LIMIT_MESSAGE = color(conf.getString("limit-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;
}
}
File diff suppressed because it is too large. Load diff
+9 -3
View File
@@ -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