mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-25 18:09:27 +00:00
@@ -0,0 +1,93 @@
|
||||
package net.momirealms.craftengine.bukkit.font;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent;
|
||||
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.font.AbstractImageManager;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import net.momirealms.craftengine.core.util.CharacterUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class BukkitImageManager extends AbstractImageManager implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final Object serializer;
|
||||
|
||||
public BukkitImageManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
try {
|
||||
Object builder = Reflections.method$GsonComponentSerializer$builder.invoke(null);
|
||||
this.serializer = Reflections.method$GsonComponentSerializer$Builder$build.invoke(builder);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public void onChat(AsyncChatDecorateEvent event) {
|
||||
if (event.player() == null) return;
|
||||
this.processChatMessages(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public void onChatCommand(AsyncChatCommandDecorateEvent event) {
|
||||
if (event.player() == null) return;
|
||||
this.processChatMessages(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent event) {
|
||||
event.setMessage(processIllegalString(event.getMessage()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
private void processChatMessages(AsyncChatDecorateEvent event) {
|
||||
try {
|
||||
Object originalMessage = Reflections.clazz$AdventureComponent.cast(Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event));
|
||||
JsonElement json = (JsonElement) Reflections.method$GsonComponentSerializer$serializeToTree.invoke(serializer, originalMessage);
|
||||
String jsonMessage = AdventureHelper.jsonElementToStringJson(json);
|
||||
if (!this.isDefaultFontInUse()) return;
|
||||
String str = processIllegalString(jsonMessage);
|
||||
Object component = Reflections.method$ComponentSerializer$deserialize.invoke(serializer, str);
|
||||
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String processIllegalString(String string) {
|
||||
char[] chars = string.toCharArray();
|
||||
int[] codepoints = CharacterUtils.charsToCodePoints(chars);
|
||||
int[] newCodepoints = new int[codepoints.length];
|
||||
for (int i = 0; i < codepoints.length; i++) {
|
||||
int codepoint = codepoints[i];
|
||||
if (!this.isIllegalCharacter(codepoint)) {
|
||||
newCodepoints[i] = codepoint;
|
||||
} else {
|
||||
newCodepoints[i] = '*';
|
||||
}
|
||||
}
|
||||
return new String(newCodepoints, 0, newCodepoints.length);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitImageManager;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors;
|
||||
import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
|
||||
@@ -49,6 +50,7 @@ import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BukkitCraftEngine extends CraftEngine {
|
||||
@@ -56,6 +58,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
private final JavaPlugin bootstrap;
|
||||
private SchedulerTask tickTask;
|
||||
private boolean successfullyLoaded = false;
|
||||
private boolean successfullyEnabled = false;
|
||||
private boolean requiresRestart = false;
|
||||
private boolean hasMod = false;
|
||||
private AntiGriefLib antiGrief;
|
||||
@@ -103,6 +106,18 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
if (successfullyEnabled) {
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe("Please do not restart plugins at runtime.");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
Bukkit.getPluginManager().disablePlugin(this.bootstrap);
|
||||
return;
|
||||
}
|
||||
this.successfullyEnabled = true;
|
||||
if (this.hasMod && this.requiresRestart) {
|
||||
logger().warn(" ");
|
||||
logger().warn(" ");
|
||||
@@ -138,6 +153,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
super.worldManager = new BukkitWorldManager(this);
|
||||
super.soundManager = new BukkitSoundManager(this);
|
||||
super.vanillaLootManager = new BukkitVanillaLootManager(this);
|
||||
this.imageManager = new BukkitImageManager(this);
|
||||
super.enable();
|
||||
// tick task
|
||||
if (VersionHelper.isFolia()) {
|
||||
@@ -175,6 +191,16 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
public void disable() {
|
||||
super.disable();
|
||||
if (this.tickTask != null) this.tickTask.cancel();
|
||||
if (!Bukkit.getServer().isStopping()) {
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe("Please do not disable plugins at runtime.");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
logger().severe(" ");
|
||||
Bukkit.getServer().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -125,7 +125,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
|
||||
registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos);
|
||||
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SOUND, Reflections.clazz$ClientboundSoundPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
|
||||
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
|
||||
|
||||
@@ -663,34 +663,6 @@ public class PacketConsumers {
|
||||
}
|
||||
};
|
||||
|
||||
// we handle it on packet level to prevent it from being captured by plugins (most are chat plugins)
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CHAT = (user, event, packet) -> {
|
||||
try {
|
||||
String message = (String) Reflections.field$ServerboundChatPacket$message.get(packet);
|
||||
if (message != null && !message.isEmpty()) {
|
||||
ImageManager manager = CraftEngine.instance().imageManager();
|
||||
if (!manager.isDefaultFontInUse()) return;
|
||||
runIfContainsIllegalCharacter(message, manager, (s) -> {
|
||||
event.setCancelled(true);
|
||||
try {
|
||||
Object newPacket = Reflections.constructor$ServerboundChatPacket.newInstance(
|
||||
s,
|
||||
Reflections.field$ServerboundChatPacket$timeStamp.get(packet),
|
||||
Reflections.field$ServerboundChatPacket$salt.get(packet),
|
||||
Reflections.field$ServerboundChatPacket$signature.get(packet),
|
||||
Reflections.field$ServerboundChatPacket$lastSeenMessages.get(packet)
|
||||
);
|
||||
user.receivePacket(newPacket);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to create replaced chat packet", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ServerboundChatPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
// we handle it on packet level to prevent it from being captured by plugins
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RENAME_ITEM = (user, event, packet) -> {
|
||||
try {
|
||||
@@ -733,6 +705,7 @@ public class PacketConsumers {
|
||||
};
|
||||
|
||||
private static void runIfContainsIllegalCharacter(String string, ImageManager manager, Consumer<String> callback) {
|
||||
//noinspection DuplicatedCode
|
||||
char[] chars = string.toCharArray();
|
||||
int[] codepoints = CharacterUtils.charsToCodePoints(chars);
|
||||
int[] newCodepoints = new int[codepoints.length];
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.JsonElement;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
@@ -5201,4 +5203,64 @@ public class Reflections {
|
||||
clazz$ServerboundSignUpdatePacket, String[].class, 0
|
||||
)
|
||||
);
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public static final Field field$AsyncChatDecorateEvent$originalMessage = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(AsyncChatDecorateEvent.class, "originalMessage")
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ComponentSerializer = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
"net{}kyori{}adventure{}text{}serializer{}ComponentSerializer".replace("{}", ".")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$GsonComponentSerializer = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
"net{}kyori{}adventure{}text{}serializer{}gson{}GsonComponentSerializer".replace("{}", ".")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$GsonComponentSerializer$Builder = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
"net{}kyori{}adventure{}text{}serializer{}gson{}GsonComponentSerializer$Builder".replace("{}", ".")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$AdventureComponent = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
"net{}kyori{}adventure{}text{}Component".replace("{}", ".")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$GsonComponentSerializer$builder = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$GsonComponentSerializer, clazz$GsonComponentSerializer$Builder
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$GsonComponentSerializer$Builder$build = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$GsonComponentSerializer$Builder, clazz$GsonComponentSerializer
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$GsonComponentSerializer$serializeToTree = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$GsonComponentSerializer, JsonElement.class, clazz$AdventureComponent
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$ComponentSerializer$deserialize = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ComponentSerializer, Object.class, Object.class
|
||||
)
|
||||
);
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public static final Method method$AsyncChatDecorateEvent$result = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
AsyncChatDecorateEvent.class, void.class, clazz$AdventureComponent
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class ImageManagerImpl implements ImageManager {
|
||||
public abstract class AbstractImageManager implements ImageManager {
|
||||
private final CraftEngine plugin;
|
||||
// namespace:font font
|
||||
private final HashMap<Key, Font> fonts = new HashMap<>();
|
||||
@@ -22,7 +22,7 @@ public class ImageManagerImpl implements ImageManager {
|
||||
|
||||
private OffsetFont offsetFont;
|
||||
|
||||
public ImageManagerImpl(CraftEngine plugin) {
|
||||
public AbstractImageManager(CraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@@ -68,4 +68,6 @@ public interface ImageManager extends Reloadable, ConfigSectionParser {
|
||||
default int loadingSequence() {
|
||||
return LoadingSequence.FONT;
|
||||
}
|
||||
|
||||
void delayedInit();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin;
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
|
||||
import net.momirealms.craftengine.core.font.ImageManager;
|
||||
import net.momirealms.craftengine.core.font.ImageManagerImpl;
|
||||
import net.momirealms.craftengine.core.font.AbstractImageManager;
|
||||
import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeManager;
|
||||
import net.momirealms.craftengine.core.loot.VanillaLootManager;
|
||||
@@ -120,7 +120,6 @@ public abstract class CraftEngine implements Plugin {
|
||||
@Override
|
||||
public void enable() {
|
||||
this.networkManager.enable();
|
||||
this.imageManager = new ImageManagerImpl(this);
|
||||
this.templateManager = new TemplateManagerImpl(this);
|
||||
this.itemBrowserManager = new ItemBrowserManagerImpl(this);
|
||||
this.commandManager.registerDefaultFeatures();
|
||||
@@ -135,6 +134,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
this.worldManager.delayedInit();
|
||||
this.packManager.delayedInit();
|
||||
this.furnitureManager.delayedInit();
|
||||
this.imageManager.delayedInit();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.util;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
@@ -182,6 +183,24 @@ public class AdventureHelper {
|
||||
return getGson().serialize(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JsonElement to a Component.
|
||||
* @param gson the JsonElement to convert
|
||||
* @return the resulting Component
|
||||
*/
|
||||
public static Component jsonElementToComponent(JsonElement gson) {
|
||||
return GsonComponentSerializer.gson().deserializeFromTree(gson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JsonElement to a JSON string.
|
||||
* @param jsonElement the JsonElement to convert
|
||||
* @return the JSON string representation
|
||||
*/
|
||||
public static String jsonElementToStringJson(JsonElement jsonElement) {
|
||||
return componentToJson(jsonElementToComponent(jsonElement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a character is a legacy color code.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user