Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion FIXED_UPSTREAM_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
- Simple Difficulty(And any other similar mods) thirst is not getting reset on player respawn[(Luohuayu/CatServer#536)](https://github.com/Luohuayu/CatServer/issues/536)[(MohistMC/Mohist#2905)](https://github.com/MohistMC/Mohist/issues/2905)
- Ring dupe bug in The Betweenlands mod[(Luohuayu/CatServer#204)](https://github.com/Luohuayu/CatServer/issues/204)

**All fixes have been contributed to the upstream project.**
## More

- It is recommended to install [HybridFix](https://github.com/HaHaWTH/HybridFix), which aims to improve Forge+Bukkit compatibility and fix behaviour inconsistencies.
154 changes: 125 additions & 29 deletions src/main/java/catserver/server/command/internal/CommandCatserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,43 @@
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredListener;
import org.spigotmc.SneakyThrow;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;

public class CommandCatserver extends Command {
public CommandCatserver(String name) {
super(name);
this.description = "CatServer related commands";
this.usageMessage = "/catserver worlds|reload|reloadall|dumpitem"; // CatRoom - Dump item command
this.usageMessage = "/catserver worlds|reload|reloadall|dumpitem|dumplisteners";
setPermission("catserver.command.catserver");
}

private static final MethodHandle EVENT_TYPES_HANDLE;

static {
try {
final Field eventTypesField = HandlerList.class.getDeclaredField("EVENT_TYPES");
eventTypesField.setAccessible(true);
EVENT_TYPES_HANDLE = MethodHandles.lookup().unreflectGetter(eventTypesField);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (!testPermission(sender)) return true;
Expand All @@ -30,37 +58,52 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return false;
}

if (args[0].equals("worlds")) {
sender.sendMessage(formatStringLength("Dim", 8) + " " + formatStringLength("Name", 8) + " " + formatStringLength("Type", 8));
for (Integer dimension : DimensionManager.getStaticDimensionIDs()) {
World world = DimensionManager.getWorld(dimension, false);
String name = (world != null ? world.getWorld().getName() : "(Unload)");
String type = DimensionManager.getProviderType(dimension).toString();
sender.sendMessage(formatStringLength(String.valueOf(dimension), 8) + " " + formatStringLength(name, 8) + " " + formatStringLength(type, 8));
switch (args[0].toLowerCase(Locale.ROOT)) {
case "worlds" -> {
sender.sendMessage(formatStringLength("Dim", 8) + " " + formatStringLength("Name", 8) + " " + formatStringLength("Type", 8));
for (Integer dimension : DimensionManager.getStaticDimensionIDs()) {
World world = DimensionManager.getWorld(dimension, false);
String name = (world != null ? world.getWorld().getName() : "(Unload)");
String type = DimensionManager.getProviderType(dimension).toString();
sender.sendMessage(formatStringLength(String.valueOf(dimension), 8) + " " + formatStringLength(name, 8) + " " + formatStringLength(type, 8));
}
}
} else if (args[0].equals("reload")) {
CatServer.getConfig().loadConfig();
sender.sendMessage(ChatColor.GREEN + "Configuration reload complete.");
} else if (args[0].equals("reloadall")) {
CatServer.getConfig().loadConfig();
((CraftServer) Bukkit.getServer()).reloadConfig();
sender.sendMessage(ChatColor.GREEN + "Configuration reload complete.");
} else if (args[0].equals("dumpitem")) { // CatRoom start - Dump item command
if (!(sender instanceof Player player)) {
sender.sendMessage(ChatColor.RED + "Only players can use this command.");
return true;
case "reload" -> {
CatServer.getConfig().loadConfig();
sender.sendMessage(ChatColor.GREEN + "Configuration reload complete.");
}
var itemInHand = ((CraftPlayer) player).getHandle().getHeldItemMainhand();
if (itemInHand.isEmpty()) {
sender.sendMessage(ChatColor.RED + "You are not holding any item.");
return true;
case "reloadall" -> {
CatServer.getConfig().loadConfig();
((CraftServer) Bukkit.getServer()).reloadConfig();
sender.sendMessage(ChatColor.GREEN + "Configuration reload complete.");
}
case "dumpitem" -> {
if (!(sender instanceof Player player)) {
sender.sendMessage(ChatColor.RED + "Only players can use this command.");
return true;
}
var itemInHand = ((CraftPlayer) player).getHandle().getHeldItemMainhand();
if (itemInHand.isEmpty()) {
sender.sendMessage(ChatColor.RED + "You are not holding any item.");
return true;
}
sender.sendMessage(ItemStackUtils.formatItemStackToPrettyString(itemInHand));
TextComponent message = new TextComponent("[Click to insert give command]");
message.setColor(net.md_5.bungee.api.ChatColor.GREEN);
message.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ItemStackUtils.itemStackToGiveCommand(itemInHand)));
sender.spigot().sendMessage(message);
}
case "dumplisteners" -> {
if (args.length < 2) {
sender.sendMessage(ChatColor.RED + "Usage: /catserver dumplisteners tofile");
return true;
}
if (args[1].equals("tofile")) {
this.dumpToFile(sender);
} else {
sender.sendMessage(ChatColor.RED + "Usage: /catserver dumplisteners tofile");
}
}
sender.sendMessage(ItemStackUtils.formatItemStackToPrettyString(itemInHand));
TextComponent message = new TextComponent("[Click to insert give command]");
message.setColor(net.md_5.bungee.api.ChatColor.GREEN);
message.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ItemStackUtils.itemStackToGiveCommand(itemInHand)));
sender.spigot().sendMessage(message);
// CatRoom end - Dump item command
}

return true;
Expand All @@ -73,4 +116,57 @@ private static String formatStringLength(String str, int size) {
}
return str;
}

private void dumpToFile(final CommandSender sender) {
final File file = new File("debug/listeners-"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt");
file.getParentFile().mkdirs();
try (final PrintWriter writer = new PrintWriter(file)) {
for (final String eventClass : eventClassNames()) {
final HandlerList handlers;
try {
handlers = (HandlerList) findClass(eventClass).getMethod("getHandlerList").invoke(null);
} catch (final ReflectiveOperationException e) {
continue;
}
if (handlers.getRegisteredListeners().length != 0) {
writer.println(eventClass);
}
for (final RegisteredListener registeredListener : handlers.getRegisteredListeners()) {
writer.println(" - " + registeredListener);
}
}
} catch (final IOException ex) {
throw new RuntimeException(ex);
}
sender.sendMessage(ChatColor.GREEN + "Dumped listeners to " + file);
}

@SuppressWarnings("unchecked")
private static Set<String> eventClassNames() {
try {
return (Set<String>) EVENT_TYPES_HANDLE.invokeExact();
} catch (final Throwable e) {
SneakyThrow.sneaky(e);
return Collections.emptySet(); // Unreachable
}
}

private static Class<?> findClass(final String className) throws ClassNotFoundException {
try {
return Class.forName(className);
} catch (final ClassNotFoundException ignore) {
for (final Plugin plugin : Bukkit.getServer().getPluginManager().getPlugins()) {
if (!plugin.isEnabled()) {
continue;
}

try {
return Class.forName(className, false, plugin.getClass().getClassLoader());
} catch (final ClassNotFoundException ignore0) {
}
}
}
throw new ClassNotFoundException(className);
}
}
13 changes: 13 additions & 0 deletions src/main/java/org/bukkit/event/HandlerList.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ public class HandlerList {
*/
private static ArrayList<HandlerList> allLists = new ArrayList<HandlerList>();

// CatRoom start
/**
* Event types which have instantiated a {@link HandlerList}.
*/
private static final java.util.Set<String> EVENT_TYPES = java.util.concurrent.ConcurrentHashMap.newKeySet();
// CatRoom end

/**
* Bake all handler lists. Best used just after all normal event
* registration is complete, ie just after all plugins are loaded if
Expand Down Expand Up @@ -90,6 +97,12 @@ public static void unregisterAll(Listener listener) {
* The HandlerList is then added to meta-list for use in bakeAll()
*/
public HandlerList() {
// CatRoom start
java.lang.StackWalker.getInstance(java.util.EnumSet.of(java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE), 4)
.walk(s -> s.filter(f -> Event.class.isAssignableFrom(f.getDeclaringClass())).findFirst())
.map(f -> f.getDeclaringClass().getName())
.ifPresent(EVENT_TYPES::add);
// CatRoom end
handlerslots = new EnumMap<EventPriority, ArrayList<RegisteredListener>>(EventPriority.class);
for (EventPriority o : EventPriority.values()) {
handlerslots.put(o, new ArrayList<RegisteredListener>());
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/bukkit/plugin/RegisteredListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,17 @@ public void callEvent(final Event event) throws EventException {
public boolean isIgnoringCancelled() {
return ignoreCancelled;
}

// CatRoom start
@Override
public String toString() {
return "RegisteredListener{"
+ "plugin=\"" + this.plugin.getName()
+ "\", listener=\"" + this.listener
+ "\", executor=\"" + this.executor
+ "\", priority=\"" + this.priority.name() + " (" + this.priority.getSlot() + ")"
+ "\", ignoringCancelled=" + this.ignoreCancelled
+ "}";
}
// CatRoom end
}
4 changes: 2 additions & 2 deletions src/main/java/org/bukkit/plugin/SimplePluginManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ public void clearPlugins() {
*/
public void callEvent(Event event) {
if (event.getHandlers().getRegisteredListeners().length == 0) return; // CatRoom - Skip event if no listeners
if (CatServer.getConfig().fakePlayerEventPass && event instanceof PlayerEvent && ((PlayerEvent) event).getPlayer() instanceof CraftFakePlayer) return; // CatServer
if (CatServer.getConfig().fakePlayerEventPass && event instanceof PlayerEvent playerEvent && playerEvent.getPlayer() instanceof CraftFakePlayer) return; // CatServer
if (event.isAsynchronous() || !server.isPrimaryThread()) { // CatServer
if (Thread.holdsLock(this)) {
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.");
Expand Down Expand Up @@ -558,7 +558,7 @@ public void registerEvent(Class<? extends Event> event, Listener listener, Event
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
}

if (useTimings) { // Always false here
if (false) { // CatRoom - Disable Timings
getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
} else {
getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
Expand Down