Compare commits

..

41 Commits

Author SHA1 Message Date
Auxilor
264c591020 Fixed readme again 2023-02-15 13:49:01 +00:00
Auxilor
6bd3ca0f5c Updated README 2023-02-15 13:48:07 +00:00
Auxilor
98df14a23a Fixed javadoc 2023-02-15 13:43:46 +00:00
Auxilor
ccb6e38064 Cleaned up entity / target goal code 2023-02-15 13:07:16 +00:00
Auxilor
50f1ba6a19 Fix 2023-02-15 12:55:16 +00:00
Auxilor
501e7b05a6 Fucksake, reverted my bugfixes 2023-02-14 20:21:12 +00:00
Auxilor
ebf8abf764 Oops 2023-02-14 20:09:56 +00:00
Auxilor
aff8fa8e88 Javadoc 2023-02-14 20:07:12 +00:00
Auxilor
1accad88fe Added GUI Detection system with DeluxeMenus support 2023-02-14 20:00:39 +00:00
Auxilor
a3a5e4df38 Fixed packet injector 2023-02-14 17:42:52 +00:00
Auxilor
6e8dc1d729 Packet refactor 2023-02-14 17:26:01 +00:00
Auxilor
a8556008f9 Fixed EcoPlugin compiler warnings 2023-02-14 16:53:45 +00:00
Auxilor
ab18a8bd29 Updated to 6.51.0 2023-02-14 16:49:16 +00:00
Auxilor
a053f512f8 Moved away from ProtocolLib 2023-02-14 16:49:04 +00:00
Auxilor
add5390787 Removed ProtocolLib dependency 2023-02-14 12:29:55 +00:00
Auxilor
49687f9a91 Updated to 6.50.3 2023-02-05 16:32:56 +00:00
Auxilor
5480c70f8c Fixed dynamic commands 2023-02-05 16:32:48 +00:00
Auxilor
ef922f6d3f Fixed dynamic commands 2023-01-31 18:58:05 +00:00
Auxilor
026bc55ffb Updated to 6.50.2 2023-01-31 18:35:17 +00:00
Will FP
270fdbb18c Merge pull request #238
Update oraxen dependency
2023-01-31 18:35:02 +00:00
casper
fbf5967d17 Update oraxen dependency 2023-01-31 12:21:41 -06:00
Auxilor
4102be1201 Updated to 6.50.1 2023-01-27 17:47:21 +00:00
Will FP
f6bdb9cc65 Merge pull request #236 from 0ft3n/develop
Fixed fix
2023-01-27 17:46:19 +00:00
Will FP
c8282d1acf Merge branch 'develop' into develop 2023-01-27 17:46:13 +00:00
_OfTeN_
b056b537ef Fixed fix x2 2023-01-27 20:31:00 +03:00
_OfTeN_
f69b458731 Fixed fix 2023-01-27 19:48:28 +03:00
Auxilor
b035fa8940 Changed deprecations 2023-01-27 12:52:55 +00:00
Auxilor
25c087592d Cached placeholder lookups 2023-01-27 12:45:57 +00:00
Auxilor
083cb39771 Cleanups, fixes 2023-01-27 12:20:51 +00:00
Auxilor
eb3e0f5c09 Updated to 6.50.0 2023-01-27 12:18:01 +00:00
Auxilor
08f43ddafd Added dynamic placeholders 2023-01-27 12:17:51 +00:00
Auxilor
9d3efb5e83 Imports, often. Imports 2023-01-27 11:50:50 +00:00
Auxilor
8a5d1a604a Updated to 6.49.2 2023-01-27 11:46:15 +00:00
Auxilor
ef67c6d6ae Merge branch 'develop' 2023-01-27 11:45:42 +00:00
Auxilor
5b2654db15 Merge remote-tracking branch 'origin/master' 2023-01-27 11:45:18 +00:00
Will FP
eccd793317 Merge pull request #235 from Baterka/master
Fix Prices evaluation if they include player placeholders
2023-01-27 11:43:56 +00:00
Will FP
1bc44755a0 Merge pull request #234 from 0ft3n/develop
EconomyShopGUI integration fixes
2023-01-27 11:35:39 +00:00
Baterka
ec606d9ebe Fix Prices evaluation if they include player placeholders 2023-01-27 03:04:48 +01:00
_OfTeN_
c5556f15ab Attempted to fix Lands integration 2023-01-27 04:54:09 +03:00
_OfTeN_
399cce21f5 EconomyShopGUI integration fixes 2023-01-27 04:48:18 +03:00
Auxilor
b25feffdfa Codestyle 2023-01-24 13:54:59 +00:00
126 changed files with 1680 additions and 1403 deletions

View File

@@ -31,6 +31,7 @@ eco comes packed with all the tools you need in your plugins:
- Yaml/JSON/TOML config system
- Persistent data storage API with Yaml/MySQL/MongoDB support
- Packet item display system
- Lightweight event loop based packet API
- Entity AI API with near-1:1 NMS mappings
- More events
- Extension API, essentially plugins for plugins
@@ -52,7 +53,6 @@ eco comes packed with all the tools you need in your plugins:
- And much more
# For server owners
- Requires ProtocolLib to be installed: get the latest version [here](https://www.spigotmc.org/resources/protocollib.1997/)
- Supports 1.17+
## Downloads

View File

@@ -13,27 +13,30 @@ import java.util.Collections;
/**
* Wrapper class for ProtocolLib packets.
*
* @deprecated ProtocolLib is no longer used by eco. Use {@link com.willfp.eco.core.packet.PacketListener} instead.
*/
@Deprecated(since = "6.51.0")
public abstract class AbstractPacketAdapter extends PacketAdapter {
/**
* The packet type to listen for.
* The handle type to listen for.
*/
private final PacketType type;
/**
* Whether the packet adapter should be registered after the server has loaded.
* Whether the handle adapter should be registered after the server has loaded.
* <p>
* Useful for monitor priority adapters that <b>must</b> be ran last.
*/
private final boolean postLoad;
/**
* Create a new packet adapter for a specified plugin and type.
* Create a new handle adapter for a specified plugin and type.
*
* @param plugin The plugin that ProtocolLib should mark as the owner.
* @param type The {@link PacketType} to listen for.
* @param priority The priority at which the adapter should be ran on packet send/receive.
* @param postLoad If the packet adapter should be registered after the server has loaded.
* @param priority The priority at which the adapter should be ran on handle send/receive.
* @param postLoad If the handle adapter should be registered after the server has loaded.
*/
protected AbstractPacketAdapter(@NotNull final EcoPlugin plugin,
@NotNull final PacketType type,
@@ -45,11 +48,11 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* Create a new packet adapter for a specified plugin and type.
* Create a new handle adapter for a specified plugin and type.
*
* @param plugin The plugin that ProtocolLib should mark as the owner.
* @param type The {@link PacketType} to listen for.
* @param postLoad If the packet adapter should be registered after the server has loaded.
* @param postLoad If the handle adapter should be registered after the server has loaded.
*/
protected AbstractPacketAdapter(@NotNull final EcoPlugin plugin,
@NotNull final PacketType type,
@@ -58,9 +61,9 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* The code that should be executed once the packet has been received.
* The code that should be executed once the handle has been received.
*
* @param packet The packet.
* @param packet The handle.
* @param player The player.
* @param event The event.
*/
@@ -71,9 +74,9 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* THe code that should be executed once the packet has been sent.
* THe code that should be executed once the handle has been sent.
*
* @param packet The packet.
* @param packet The handle.
* @param player The player.
* @param event The event.
*/
@@ -84,7 +87,7 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* Boilerplate to assert that the packet is of the specified type.
* Boilerplate to assert that the handle is of the specified type.
*
* @param event The ProtocolLib event.
*/
@@ -102,7 +105,7 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* Boilerplate to assert that the packet is of the specified type.
* Boilerplate to assert that the handle is of the specified type.
*
* @param event The ProtocolLib event.
*/
@@ -125,7 +128,7 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* Register the packet adapter with ProtocolLib.
* Register the handle adapter with ProtocolLib.
*/
public final void register() {
if (!ProtocolLibrary.getProtocolManager().getPacketListeners().contains(this)) {
@@ -134,7 +137,7 @@ public abstract class AbstractPacketAdapter extends PacketAdapter {
}
/**
* Get if the packet adapter should be loaded last.
* Get if the handle adapter should be loaded last.
*
* @return If post load.
*/

View File

@@ -26,13 +26,13 @@ import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.packet.Packet;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
@@ -547,19 +547,21 @@ public interface Eco {
*/
void syncCommands();
/**
* Get the command map.
*
* @return The command map.
*/
@NotNull CommandMap getCommandMap();
/**
* Unregister a command.
*
* @param command The command.
*/
void unregisterCommand(@NotNull final PluginCommand command);
void unregisterCommand(@NotNull PluginCommandBase command);
/**
* Send a packet.
*
* @param player The player.
* @param packet The packet.
*/
void sendPacket(@NotNull Player player,
@NotNull Packet packet);
/**
* Get the instance of eco; the bridge between the api frontend and the implementation backend.

View File

@@ -13,6 +13,7 @@ import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.integrations.IntegrationLoader;
import com.willfp.eco.core.packet.PacketListener;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.web.UpdateChecker;
@@ -52,7 +53,7 @@ import java.util.stream.Collectors;
* <b>IMPORTANT: When reloading a plugin, all runnables / tasks will
* be cancelled.</b>
*/
@SuppressWarnings("unused")
@SuppressWarnings({"unused", "DeprecatedIsStillUsed", "deprecation", "RedundantSuppression"})
public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
/**
* The polymart resource ID of the plugin.
@@ -422,13 +423,16 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
Prerequisite.update();
this.loadPacketAdapters().forEach(abstractPacketAdapter -> {
if (!abstractPacketAdapter.isPostLoad()) {
abstractPacketAdapter.register();
}
});
if (Prerequisite.HAS_PROTOCOLLIB.isMet()) {
this.loadPacketAdapters().forEach(abstractPacketAdapter -> {
if (!abstractPacketAdapter.isPostLoad()) {
abstractPacketAdapter.register();
}
});
}
this.loadListeners().forEach(listener -> this.getEventManager().registerListener(listener));
this.loadPacketListeners().forEach(listener -> this.getEventManager().registerPacketListener(listener));
this.loadPluginCommands().forEach(PluginCommand::register);
@@ -520,11 +524,13 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
Display.registerDisplayModule(this.getDisplayModule());
}
this.loadPacketAdapters().forEach(abstractPacketAdapter -> {
if (abstractPacketAdapter.isPostLoad()) {
abstractPacketAdapter.register();
}
});
if (Prerequisite.HAS_PROTOCOLLIB.isMet()) {
this.loadPacketAdapters().forEach(abstractPacketAdapter -> {
if (abstractPacketAdapter.isPostLoad()) {
abstractPacketAdapter.register();
}
});
}
if (!Prerequisite.HAS_PAPER.isMet()) {
this.getLogger().severe("");
@@ -681,16 +687,27 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
}
/**
* ProtocolLib packet adapters to be registered.
* ProtocolLib handle adapters to be registered.
* <p>
* If the plugin does not require ProtocolLib this can be left empty.
*
* @return A list of packet adapters.
* @return A list of handle adapters.
* @deprecated Use {@link #loadPacketListeners()} instead.
*/
@Deprecated(since = "6.51.0")
protected List<AbstractPacketAdapter> loadPacketAdapters() {
return new ArrayList<>();
}
/**
* Packet Listeners to be registered.
*
* @return A list of handle listeners.
*/
protected List<PacketListener> loadPacketListeners() {
return new ArrayList<>();
}
/**
* All listeners to be registered.
*

View File

@@ -30,6 +30,14 @@ public class Prerequisite {
"Requires server to be running paper (or a fork)"
);
/**
* Requires the server to be running an implementation of paper.
*/
public static final Prerequisite HAS_PROTOCOLLIB = new Prerequisite(
() -> ClassUtils.exists("com.comphenix.protocol.events.PacketAdapter"),
"Requires server to have ProtocolLib"
);
/**
* Requires the server to have vault installed.
*

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core.command;
import org.jetbrains.annotations.NotNull;
/**
* A notification exception is thrown when {@link org.bukkit.command.CommandSender}s don't
* specify valid arguments in commands.
@@ -18,7 +20,7 @@ public class NotificationException extends Exception {
*
* @param key The lang key of the notification.
*/
public NotificationException(String key) {
public NotificationException(@NotNull final String key) {
super(key);
this.key = key;
}

View File

@@ -1,6 +1,7 @@
package com.willfp.eco.core.display;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.integrations.guidetection.GUIDetectionManager;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
@@ -18,6 +19,9 @@ import java.util.TreeMap;
/**
* Utility class to manage client-side item display.
* <p>
* Packet display is not done on the main thread, so make sure
* all your modules are thread-safe.
*/
public final class Display {
/**
@@ -71,7 +75,7 @@ public final class Display {
ItemStack original = itemStack.clone();
Inventory inventory = player == null ? null : player.getOpenInventory().getTopInventory();
boolean inInventory = inventory != null && inventory.contains(original);
boolean inGui = inventory != null && inventory.getHolder() == null;
boolean inGui = player != null && GUIDetectionManager.hasGUIOpen(player);
DisplayProperties properties = new DisplayProperties(
inInventory,

View File

@@ -8,6 +8,8 @@ import org.jetbrains.annotations.Nullable;
/**
* Class for all plugin-specific client-side item display modules.
* <p>
* Display modules are called in the netty thread, so make sure they are thread-safe.
*/
public abstract class DisplayModule {
/**

View File

@@ -13,10 +13,10 @@ import org.jetbrains.annotations.Nullable;
/**
* Avoid entities.
*
* @param entity The entity type to avoid.
* @param distance The distance to flee to.
* @param slowSpeed The slow movement speed.
* @param fastSpeed The fast movement speed.
* @param entity The entity type to avoid.
* @param distance The distance to flee to.
* @param slowSpeed The slow movement speed.
* @param fastSpeed The fast movement speed.
*/
public record EntityGoalAvoidEntity(
@NotNull TestableEntity entity,
@@ -45,23 +45,14 @@ public record EntityGoalAvoidEntity(
return null;
}
try {
TestableEntity entity = Entities.lookup(config.getString("entity"));
TestableEntity entity = Entities.lookup(config.getString("entity"));
return new EntityGoalAvoidEntity(
entity,
config.getDouble("distance"),
config.getDouble("slowSpeed"),
config.getDouble("fastSpeed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalAvoidEntity(
entity,
config.getDouble("distance"),
config.getDouble("slowSpeed"),
config.getDouble("fastSpeed")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalBreakDoors(
return null;
}
try {
return new EntityGoalBreakDoors(
config.getInt("ticks")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalBreakDoors(
config.getInt("ticks")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalBreed(
return null;
}
try {
return new EntityGoalBreed(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalBreed(
config.getDouble("speed")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalCatLieOnBed(
return null;
}
try {
return new EntityGoalCatLieOnBed(
config.getDouble("speed"),
config.getInt("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalCatLieOnBed(
config.getDouble("speed"),
config.getInt("range")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalCatSitOnBed(
return null;
}
try {
return new EntityGoalCatSitOnBed(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalCatSitOnBed(
config.getDouble("speed")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalFleeSun(
return null;
}
try {
return new EntityGoalFleeSun(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalFleeSun(
config.getDouble("speed")
);
}
@NotNull

View File

@@ -40,20 +40,11 @@ public record EntityGoalFollowMobs(
return null;
}
try {
return new EntityGoalFollowMobs(
config.getDouble("speed"),
config.getDouble("minDistance"),
config.getDouble("maxDistance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalFollowMobs(
config.getDouble("speed"),
config.getDouble("minDistance"),
config.getDouble("maxDistance")
);
}
@NotNull

View File

@@ -42,20 +42,11 @@ public record EntityGoalInteract(
return null;
}
try {
return new EntityGoalInteract(
Entities.lookup(config.getString("target")),
config.getDouble("range"),
config.getDouble("chance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalInteract(
Entities.lookup(config.getString("target")),
config.getDouble("range"),
config.getDouble("chance")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalLeapAtTarget(
return null;
}
try {
return new EntityGoalLeapAtTarget(
config.getDouble("velocity")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalLeapAtTarget(
config.getDouble("velocity")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalLookAtPlayer(
return null;
}
try {
return new EntityGoalLookAtPlayer(
config.getDouble("range"),
config.getDouble("chance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalLookAtPlayer(
config.getDouble("range"),
config.getDouble("chance")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalMeleeAttack(
return null;
}
try {
return new EntityGoalMeleeAttack(
config.getDouble("speed"),
config.getBool("pauseWhenMobIdle")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalMeleeAttack(
config.getDouble("speed"),
config.getBool("pauseWhenMobIdle")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalMoveBackToVillage(
return null;
}
try {
return new EntityGoalMoveBackToVillage(
config.getDouble("speed"),
config.getBool("canDespawn")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalMoveBackToVillage(
config.getDouble("speed"),
config.getBool("canDespawn")
);
}
@NotNull

View File

@@ -43,21 +43,12 @@ public record EntityGoalMoveThroughVillage(
return null;
}
try {
return new EntityGoalMoveThroughVillage(
config.getDouble("speed"),
config.getBool("onlyAtNight"),
config.getInt("distance"),
config.getBool("canPassThroughDoors")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalMoveThroughVillage(
config.getDouble("speed"),
config.getBool("onlyAtNight"),
config.getInt("distance"),
config.getBool("canPassThroughDoors")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalMoveTowardsRestriction(
return null;
}
try {
return new EntityGoalMoveTowardsRestriction(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalMoveTowardsRestriction(
config.getDouble("speed")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalMoveTowardsTarget(
return null;
}
try {
return new EntityGoalMoveTowardsTarget(
config.getDouble("speed"),
config.getDouble("maxDistance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalMoveTowardsTarget(
config.getDouble("speed"),
config.getDouble("maxDistance")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalOpenDoors(
return null;
}
try {
return new EntityGoalOpenDoors(
config.getBool("delayClosing")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalOpenDoors(
config.getBool("delayClosing")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalPanic(
return null;
}
try {
return new EntityGoalPanic(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalPanic(
config.getDouble("speed")
);
}
@NotNull

View File

@@ -40,20 +40,11 @@ public record EntityGoalRandomStroll(
return null;
}
try {
return new EntityGoalRandomStroll(
config.getDouble("speed"),
config.getInt("interval"),
config.getBool("canDespawn")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalRandomStroll(
config.getDouble("speed"),
config.getInt("interval"),
config.getBool("canDespawn")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalRandomSwimming(
return null;
}
try {
return new EntityGoalRandomSwimming(
config.getDouble("speed"),
config.getInt("interval")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalRandomSwimming(
config.getDouble("speed"),
config.getInt("interval")
);
}
@NotNull

View File

@@ -45,21 +45,12 @@ public record EntityGoalRangedAttack(
return null;
}
try {
return new EntityGoalRangedAttack(
config.getDouble("speed"),
config.getInt("minInterval"),
config.getInt("maxInterval"),
config.getDouble("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalRangedAttack(
config.getDouble("speed"),
config.getInt("minInterval"),
config.getInt("maxInterval"),
config.getDouble("range")
);
}
@NotNull

View File

@@ -13,9 +13,9 @@ import org.jetbrains.annotations.Nullable;
* <p>
* Only supports monsters that have bow attacks.
*
* @param speed The speed.
* @param speed The speed.
* @param interval The interval between attacks (in ticks).
* @param range The max range at which to attack.
* @param range The max range at which to attack.
*/
public record EntityGoalRangedBowAttack(
double speed,
@@ -42,20 +42,11 @@ public record EntityGoalRangedBowAttack(
return null;
}
try {
return new EntityGoalRangedBowAttack(
config.getDouble("speed"),
config.getInt("interval"),
config.getDouble("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalRangedBowAttack(
config.getDouble("speed"),
config.getInt("interval"),
config.getDouble("range")
);
}
@NotNull

View File

@@ -39,19 +39,10 @@ public record EntityGoalRangedCrossbowAttack(
return null;
}
try {
return new EntityGoalRangedCrossbowAttack(
config.getDouble("speed"),
config.getDouble("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalRangedCrossbowAttack(
config.getDouble("speed"),
config.getDouble("range")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalStrollThroughVillage(
return null;
}
try {
return new EntityGoalStrollThroughVillage(
config.getInt("searchRange")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalStrollThroughVillage(
config.getInt("searchRange")
);
}
@NotNull

View File

@@ -58,25 +58,16 @@ public record EntityGoalTempt(
return null;
}
try {
Collection<TestableItem> items = config.getStrings("items").stream()
.map(Items::lookup)
.filter(it -> !(it instanceof EmptyTestableItem))
.collect(Collectors.toList());
Collection<TestableItem> items = config.getStrings("items").stream()
.map(Items::lookup)
.filter(it -> !(it instanceof EmptyTestableItem))
.collect(Collectors.toList());
return new EntityGoalTempt(
config.getDouble("speed"),
items,
config.getBool("canBeScared")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalTempt(
config.getDouble("speed"),
items,
config.getBool("canBeScared")
);
}
@NotNull

View File

@@ -48,22 +48,13 @@ public record EntityGoalUseItem(
return null;
}
try {
TestableEntity filter = Entities.lookup(config.getString("condition"));
TestableEntity filter = Entities.lookup(config.getString("condition"));
return new EntityGoalUseItem(
Items.lookup(config.getString("item")).getItem(),
Sound.valueOf(config.getString("sound").toUpperCase()),
filter::matches
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalUseItem(
Items.lookup(config.getString("item")).getItem(),
Sound.valueOf(config.getString("sound").toUpperCase()),
filter::matches
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalWaterAvoidingRandomFlying(
return null;
}
try {
return new EntityGoalWaterAvoidingRandomFlying(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalWaterAvoidingRandomFlying(
config.getDouble("speed")
);
}
@NotNull

View File

@@ -37,19 +37,10 @@ public record EntityGoalWaterAvoidingRandomStroll(
return null;
}
try {
return new EntityGoalWaterAvoidingRandomStroll(
config.getDouble("speed"),
config.getDouble("chance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalWaterAvoidingRandomStroll(
config.getDouble("speed"),
config.getDouble("chance")
);
}
@NotNull

View File

@@ -34,18 +34,9 @@ public record EntityGoalWolfBeg(
return null;
}
try {
return new EntityGoalWolfBeg(
config.getDouble("distance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new EntityGoalWolfBeg(
config.getDouble("distance")
);
}
@NotNull

View File

@@ -37,18 +37,9 @@ public record TargetGoalHurtBy(
return null;
}
try {
return new TargetGoalHurtBy(
Entities.lookup(config.getString("blacklist"))
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new TargetGoalHurtBy(
Entities.lookup(config.getString("blacklist"))
);
}
@NotNull

View File

@@ -63,32 +63,23 @@ public record TargetGoalNearestAttackable(
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"),
filter::matches
);
} else {
return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"),
filter::matches
);
} else {
return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance")
);
}
}

View File

@@ -63,32 +63,23 @@ public record TargetGoalNearestAttackableWitch(
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestAttackableWitch(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"),
filter::matches
);
} else {
return new TargetGoalNearestAttackableWitch(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
return new TargetGoalNearestAttackableWitch(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"),
filter::matches
);
} else {
return new TargetGoalNearestAttackableWitch(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance")
);
}
}

View File

@@ -53,28 +53,19 @@ public record TargetGoalNearestHealableRaider(
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestHealableRaider(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
filter::matches
);
} else {
return new TargetGoalNearestHealableRaider(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
return new TargetGoalNearestHealableRaider(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
filter::matches
);
} else {
return new TargetGoalNearestHealableRaider(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility")
);
}
}

View File

@@ -53,28 +53,19 @@ public record TargetGoalNonTameRandom(
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNonTameRandom(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
filter::matches
);
} else {
return new TargetGoalNonTameRandom(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
return new TargetGoalNonTameRandom(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
filter::matches
);
} else {
return new TargetGoalNonTameRandom(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility")
);
}
}

View File

@@ -36,18 +36,9 @@ public record TargetGoalResetUniversalAnger(
return null;
}
try {
return new TargetGoalResetUniversalAnger(
config.getBool("triggerOthers")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
return new TargetGoalResetUniversalAnger(
config.getBool("triggerOthers")
);
}
@NotNull

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.events;
import com.willfp.eco.core.packet.PacketListener;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
@@ -25,4 +26,11 @@ public interface EventManager {
* Unregister all listeners associated with the plugin.
*/
void unregisterAllListeners();
/**
* Register a packet listener.
*
* @param listener The listener.
*/
void registerPacketListener(@NotNull PacketListener listener);
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.core.integrations.guidetection;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for GUI integrations.
*/
public interface GUIDetectionIntegration extends Integration {
/**
* Determine if a player is in a GUI.
*
* @param player The player.
* @return If the player is in a GUI.
*/
boolean hasGUIOpen(@NotNull final Player player);
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.core.integrations.guidetection;
import com.willfp.eco.util.MenuUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle GUI detection.
*/
public final class GUIDetectionManager {
/**
* A set of all registered integrations.
*/
private static final Set<GUIDetectionIntegration> REGISTERED = new HashSet<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public static void register(@NotNull final GUIDetectionIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
}
/**
* Get if a player is in a GUI.
*
* @param player The player.
* @return If the player has a GUI open.
*/
public static boolean hasGUIOpen(@NotNull final Player player) {
if (MenuUtils.getOpenMenu(player) != null) {
return true;
}
for (GUIDetectionIntegration integration : REGISTERED) {
if (integration.hasGUIOpen(player)) {
return true;
}
}
return false;
}
private GUIDetectionManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -1,13 +1,16 @@
package com.willfp.eco.core.integrations.placeholder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.DynamicPlaceholder;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.PlayerDynamicPlaceholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
import com.willfp.eco.core.placeholder.PlayerStaticPlaceholder;
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
@@ -24,6 +27,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
@@ -37,13 +41,20 @@ public final class PlaceholderManager {
/**
* All registered placeholders.
*/
private static final Map<EcoPlugin, Map<String, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
private static final Map<EcoPlugin, Map<Pattern, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
/**
* All registered placeholder integrations.
*/
private static final Set<PlaceholderIntegration> REGISTERED_INTEGRATIONS = new HashSet<>();
/**
* Placeholder Lookup Cache.
*/
private static final Cache<PlaceholderLookup, Optional<Placeholder>> PLACEHOLDER_LOOKUP_CACHE = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build();
/**
* Placeholder Cache.
*/
@@ -51,10 +62,17 @@ public final class PlaceholderManager {
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player));
/**
* Dynamic Placeholder Cache.
*/
private static final LoadingCache<DynamicEntryWithPlayer, String> DYNAMIC_PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.args, key.player));
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("[%]([^% ]+)[%]");
private static final Pattern PATTERN = Pattern.compile("%([^% ]+)%");
/**
* Empty injectable object.
@@ -88,14 +106,17 @@ public final class PlaceholderManager {
* @param placeholder The placeholder to register.
*/
public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
if (placeholder instanceof StaticPlaceholder) {
if (placeholder instanceof StaticPlaceholder || placeholder instanceof PlayerStaticPlaceholder) {
throw new IllegalArgumentException("Static placeholders cannot be registered!");
}
EcoPlugin plugin = placeholder.getPlugin() == null ? Eco.get().getEcoPlugin() : placeholder.getPlugin();
Map<String, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
Map<Pattern, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
.getOrDefault(plugin, new HashMap<>());
pluginPlaceholders.put(placeholder.getIdentifier(), placeholder);
pluginPlaceholders.put(placeholder.getPattern(), placeholder);
REGISTERED_PLACEHOLDERS.put(plugin, pluginPlaceholders);
}
@@ -136,21 +157,47 @@ public final class PlaceholderManager {
public static String getResult(@Nullable final Player player,
@NotNull final String identifier,
@Nullable final EcoPlugin plugin) {
EcoPlugin owner = plugin == null ? Eco.get().getEcoPlugin() : plugin;
Placeholder placeholder = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
// This is really janky, and it sucks, but it works so?
// Compensating for regex being slow so that's why we get it.
Placeholder placeholder = PLACEHOLDER_LOOKUP_CACHE.get(
new PlaceholderLookup(identifier, plugin),
(it) -> {
EcoPlugin owner = plugin == null ? Eco.get().getEcoPlugin() : plugin;
if (placeholder == null && plugin != null) {
Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.get().getEcoPlugin(), new HashMap<>())
.get(identifier.toLowerCase());
if (alternate != null) {
placeholder = alternate;
}
}
// I hate the streams API.
Optional<Placeholder> found = REGISTERED_PLACEHOLDERS
.getOrDefault(owner, new HashMap<>())
.entrySet()
.stream().filter(entry -> entry.getKey().matcher(identifier).matches())
.map(Map.Entry::getValue)
.findFirst();
if (found.isEmpty() && plugin != null) {
// Here we go again! Something about legacy support? I don't remember.
// I won't touch it though, I'm scared of the placeholder system.
found = REGISTERED_PLACEHOLDERS
.getOrDefault(Eco.get().getEcoPlugin(), new HashMap<>())
.entrySet()
.stream().filter(entry -> entry.getKey().matcher(identifier).matches())
.map(Map.Entry::getValue)
.findFirst();
}
return found;
}
).orElse(null);
if (placeholder == null) {
return "";
}
/*
This code here is *really* not very good. It's mega externalized logic hacked
together and made worse by the addition of dynamic placeholders. But it works,
and it means I don't have to rewrite the whole placeholder system. So it's
good enough for me.
*/
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
if (player == null) {
return "";
@@ -159,6 +206,14 @@ public final class PlaceholderManager {
}
} else if (placeholder instanceof PlayerlessPlaceholder playerlessPlaceholder) {
return playerlessPlaceholder.getValue();
} else if (placeholder instanceof PlayerDynamicPlaceholder playerDynamicPlaceholder) {
if (player == null) {
return "";
} else {
return DYNAMIC_PLACEHOLDER_CACHE.get(new DynamicEntryWithPlayer(playerDynamicPlaceholder, identifier, player));
}
} else if (placeholder instanceof DynamicPlaceholder dynamicPlaceholder) {
return dynamicPlaceholder.getValue(identifier);
} else {
return "";
}
@@ -245,8 +300,10 @@ public final class PlaceholderManager {
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
processed = integration.translate(processed, player);
}
for (InjectablePlaceholder injection : context.getPlaceholderInjections()) {
// Do I know this is a bad way of doing this? Yes.
// I know it's deprecated, but it's fast.
if (injection instanceof StaticPlaceholder placeholder) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue());
} else if (injection instanceof PlayerStaticPlaceholder placeholder && player != null) {
@@ -280,11 +337,22 @@ public final class PlaceholderManager {
return new ArrayList<>(found);
}
private record PlaceholderLookup(@NotNull String identifier,
@Nullable EcoPlugin plugin) {
}
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
@NotNull Player player) {
}
private record DynamicEntryWithPlayer(@NotNull PlayerDynamicPlaceholder entry,
@NotNull String args,
@NotNull Player player) {
}
private PlaceholderManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.core.packet;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Represents a packet.
*
* @param handle The NMS handle.
*/
public record Packet(@NotNull Object handle) {
/**
* Send to a player.
*
* @param player The player.
*/
void send(@NotNull final Player player) {
Eco.get().sendPacket(player, this);
}
}

View File

@@ -0,0 +1,67 @@
package com.willfp.eco.core.packet;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.NotNull;
/**
* Represents a packet being sent or received.
*/
public class PacketEvent implements Cancellable {
/**
* The packet.
*/
private final Packet packet;
/**
* The player.
*/
private final Player player;
/**
* If the event should be cancelled.
*/
private boolean cancelled = false;
/**
* Create a new packet event.
*
* @param packet The packet.
* @param player The player.
*/
public PacketEvent(@NotNull final Packet packet,
@NotNull final Player player) {
this.packet = packet;
this.player = player;
}
/**
* Get the packet.
*
* @return The packet.
*/
@NotNull
public Packet getPacket() {
return packet;
}
/**
* Get the player.
*
* @return The player.
*/
@NotNull
public Player getPlayer() {
return player;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(final boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.packet;
import org.jetbrains.annotations.NotNull;
/**
* Listens to packets.
*/
public interface PacketListener {
/**
* Called when a handle is sent.
*
* @param event The event.
*/
default void onSend(@NotNull final PacketEvent event) {
// Override when needed.
}
/**
* Called when a handle is received.
*
* @param event The event.
*/
default void onReceive(@NotNull final PacketEvent event) {
// Override when needed.
}
/**
* Get the priority of the listener.
*
* @return The priority.
*/
default PacketPriority getPriority() {
return PacketPriority.NORMAL;
}
}

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.core.packet;
/**
* The priority (order) of packet listeners.
*/
public enum PacketPriority {
/**
* Ran first.
*/
LOWEST,
/**
* Ran second.
*/
LOW,
/**
* Ran third.
*/
NORMAL,
/**
* Ran fourth.
*/
HIGH,
/**
* Ran last.
*/
HIGHEST
}

View File

@@ -0,0 +1,102 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player and supports dynamic styles.
*/
public final class DynamicPlaceholder implements Placeholder {
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
private final Function<String, String> function;
/**
* The plugin for the placeholder.
*/
private final EcoPlugin plugin;
/**
* Create a new dynamic placeholder.
*
* @param plugin The plugin.
* @param pattern The pattern.
* @param function The function to retrieve the value.
*/
public DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final Function<String, String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args) {
return function.apply(args);
}
/**
* Register the placeholder.
*
* @return The placeholder.
*/
public DynamicPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
@Override
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DynamicPlaceholder that)) {
return false;
}
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
}
}

View File

@@ -2,13 +2,14 @@ package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import org.jetbrains.annotations.NotNull;
/**
* Placeholders that can be injected into {@link PlaceholderInjectable} objects.
*/
public sealed interface InjectablePlaceholder extends Placeholder permits PlayerStaticPlaceholder, StaticPlaceholder {
@Override
default EcoPlugin getPlugin() {
default @NotNull EcoPlugin getPlugin() {
return Eco.get().getEcoPlugin();
}
}

View File

@@ -1,16 +1,22 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
/**
* A placeholder represents a string that can hold a value.
*/
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder, InjectablePlaceholder {
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder,
DynamicPlaceholder, PlayerDynamicPlaceholder, InjectablePlaceholder {
/**
* Get the plugin that holds the placeholder.
*
* @return The plugin.
*/
@Nullable
EcoPlugin getPlugin();
/**
@@ -18,5 +24,16 @@ public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceho
*
* @return The identifier.
*/
@NotNull
String getIdentifier();
/**
* Get the pattern for the placeholder.
*
* @return The pattern.
*/
@NotNull
default Pattern getPattern() {
return Pattern.compile(this.getIdentifier());
}
}

View File

@@ -0,0 +1,105 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player and supports dynamic styles.
*/
public final class PlayerDynamicPlaceholder implements Placeholder {
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
private final BiFunction<String, Player, String> function;
/**
* The plugin for the placeholder.
*/
private final EcoPlugin plugin;
/**
* Create a new dynamic placeholder.
*
* @param plugin The plugin.
* @param pattern The pattern.
* @param function The function to retrieve the value.
*/
public PlayerDynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final BiFunction<String, Player, String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args,
@NotNull final Player player) {
return function.apply(args, player);
}
/**
* Register the placeholder.
*
* @return The placeholder.
*/
public PlayerDynamicPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
@Override
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PlayerDynamicPlaceholder that)) {
return false;
}
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
}
}

View File

@@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that requires a player.
@@ -18,6 +19,11 @@ public final class PlayerPlaceholder implements Placeholder {
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder given a player.
*/
@@ -40,6 +46,7 @@ public final class PlayerPlaceholder implements Placeholder {
@NotNull final Function<Player, String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -49,6 +56,7 @@ public final class PlayerPlaceholder implements Placeholder {
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final Player player) {
return function.apply(player);
}
@@ -64,15 +72,21 @@ public final class PlayerPlaceholder implements Placeholder {
}
@Override
public EcoPlugin getPlugin() {
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that cannot be registered, and exists purely in injection.
@@ -16,6 +17,11 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
@@ -30,6 +36,7 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
public PlayerStaticPlaceholder(@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -39,15 +46,22 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final Player player) {
return function.apply(player);
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -7,16 +7,22 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player.
*/
public final class PlayerlessPlaceholder implements Placeholder {
/**
* The name of the placeholder.
* The placeholder identifier.
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
@@ -39,6 +45,7 @@ public final class PlayerlessPlaceholder implements Placeholder {
@NotNull final Supplier<String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -62,15 +69,21 @@ public final class PlayerlessPlaceholder implements Placeholder {
}
@Override
public EcoPlugin getPlugin() {
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -5,6 +5,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* A placeholder that cannot be registered, and exists purely in injection.
@@ -15,6 +16,11 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
@@ -29,6 +35,7 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
public StaticPlaceholder(@NotNull final String identifier,
@NotNull final Supplier<String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -37,15 +44,22 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
*
* @return The value.
*/
@NotNull
public String getValue() {
return function.get();
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -71,10 +71,6 @@ public final class Prices {
ctx
);
if (function.apply(context) <= 0) {
return new PriceFree();
}
// Default to economy
if (priceName == null) {
return new PriceEconomy(context, function);

View File

@@ -0,0 +1,9 @@
@file:JvmName("PacketExtensions")
package com.willfp.eco.core.packet
import org.bukkit.entity.Player
/** @see Packet.send */
fun Player.sendPacket(packet: Packet) =
packet.send(this)

View File

@@ -8,8 +8,11 @@ import org.bukkit.command.TabCompleter
class DelegatedBukkitCommand(
private val delegate: EcoPluginCommand
) : Command(delegate.name), TabCompleter, PluginIdentifiableCommand {
private var _aliases: List<String>? = null
private var _description: String? = null
override fun execute(sender: CommandSender, label: String, args: Array<out String>?): Boolean {
return false
return delegate.onCommand(sender, this, label, args)
}
override fun onTabComplete(
@@ -18,11 +21,21 @@ class DelegatedBukkitCommand(
label: String,
args: Array<out String>?
): List<String> {
return mutableListOf() // Mutable in case bukkit requires this (I haven't checked.)
return delegate.onTabComplete(sender, this, label, args) ?: emptyList()
}
override fun getPlugin() = delegate.plugin
override fun getPermission() = delegate.permission
override fun getDescription() = delegate.description ?: ""
override fun getAliases(): List<String> = delegate.aliases
override fun getDescription() = _description ?: delegate.description ?: ""
override fun getAliases(): List<String> = _aliases ?: delegate.aliases
override fun setDescription(description: String): Command {
this._description = description
return this
}
override fun setAliases(aliases: List<String>): Command {
this._aliases = aliases
return this
}
}

View File

@@ -17,27 +17,25 @@ class EcoPluginCommand(
override fun register() {
val command = Bukkit.getPluginCommand(name)
val knownDescription = command?.description ?: description ?: ""
val knownAliases = command?.aliases ?: aliases
if (command == null) {
unregister()
commandMap.register(plugin.name.lowercase(), DelegatedBukkitCommand(this))
} else {
command.setExecutor(this)
description?.let {
command.setDescription(it)
}
if (aliases.isNotEmpty()) {
command.aliases = aliases
}
command.tabCompleter = this
command.description = knownDescription
command.aliases = knownAliases
}
Eco.get().syncCommands()
}
override fun unregister() {
val found = commandMap.getCommand(name)
found?.unregister(commandMap)
commandMap.getCommand(name)?.unregister(commandMap)
Eco.get().unregisterCommand(this)
Eco.get().syncCommands()
}

View File

@@ -50,8 +50,8 @@ abstract class HandledCommand(
command: Command,
label: String,
args: Array<out String>?
): MutableList<String>? {
return handleTabComplete(sender, args?.toList() ?: listOf()).toMutableList()
): List<String>? {
return handleTabComplete(sender, args?.toList() ?: listOf())
}
override fun getPlugin() = this.plugin

View File

@@ -2,11 +2,38 @@ package com.willfp.eco.internal.events
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.events.EventManager
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.core.packet.PacketPriority
import org.bukkit.Bukkit
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
class EcoEventManager (private val plugin: EcoPlugin) : EventManager {
private class RegisteredPacketListener(
val plugin: EcoPlugin,
val listener: PacketListener
)
private val listeners = mutableMapOf<PacketPriority, MutableList<RegisteredPacketListener>>()
fun PacketEvent.handleSend() {
for (priority in PacketPriority.values()) {
for (listener in listeners[priority] ?: continue) {
listener.listener.onSend(this)
}
}
}
fun PacketEvent.handleReceive() {
for (priority in PacketPriority.values()) {
for (listener in listeners[priority] ?: continue) {
listener.listener.onReceive(this)
}
}
}
class EcoEventManager(private val plugin: EcoPlugin) : EventManager {
override fun registerListener(listener: Listener) {
Bukkit.getPluginManager().registerEvents(listener, plugin)
}
@@ -17,5 +44,17 @@ class EcoEventManager (private val plugin: EcoPlugin) : EventManager {
override fun unregisterAllListeners() {
HandlerList.unregisterAll(plugin)
for (value in listeners.values) {
value.removeIf { it.plugin == plugin }
}
}
}
override fun registerPacketListener(listener: PacketListener) {
listeners.getOrPut(listener.priority) { mutableListOf() }.add(
RegisteredPacketListener(
plugin,
listener
)
)
}
}

View File

@@ -6,12 +6,14 @@ import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.TargetGoalFactory
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
@@ -63,6 +65,9 @@ fun CompoundTag.makePdc(base: Boolean = false): PersistentDataContainer =
fun CompoundTag.setPdc(pdc: PersistentDataContainer?, item: net.minecraft.world.item.ItemStack? = null) =
impl.setPdc(this, pdc, item)
fun Player.toNMS(): ServerPlayer =
impl.toNMS(this)
interface CommonsProvider {
val nbtTagString: Int
@@ -94,6 +99,8 @@ interface CommonsProvider {
fun itemToMaterial(item: Item): Material
fun toNMS(player: Player): ServerPlayer
companion object {
fun setIfNeeded(provider: CommonsProvider) {
if (::impl.isInitialized) {

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.internal.spigot.proxy.common.packet
import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.internal.events.handleReceive
import com.willfp.eco.internal.events.handleSend
import io.netty.channel.ChannelDuplexHandler
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelPromise
import org.bukkit.Bukkit
import java.util.UUID
class EcoChannelDuplexHandler(
private val uuid: UUID
) : ChannelDuplexHandler() {
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
val player = Bukkit.getPlayer(uuid)
if (player != null) {
val event = PacketEvent(Packet(msg), player)
event.handleReceive()
if (!event.isCancelled) {
super.channelRead(ctx, msg)
}
} else {
super.channelRead(ctx, msg)
}
}
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
val player = Bukkit.getPlayer(uuid)
if (player != null) {
val event = PacketEvent(Packet(msg), player)
event.handleSend()
if (!event.isCancelled) {
super.write(ctx, msg, promise)
}
} else {
super.write(ctx, msg, promise)
}
}
}

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.internal.spigot.proxy.common.packet
import com.willfp.eco.internal.spigot.proxy.common.toNMS
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
object PacketInjectorListener : Listener {
@EventHandler
fun onJoin(event: PlayerJoinEvent) {
val player = event.player
val channel = player.toNMS().connection.connection.channel
channel.pipeline().addBefore("packet_handler", "eco_packets", EcoChannelDuplexHandler(player.uniqueId))
}
@EventHandler
fun onLeave(event: PlayerQuitEvent) {
val player = event.player
val channel = player.toNMS().connection.connection.channel
channel.eventLoop().submit {
if (channel.pipeline().get("eco_packets") != null) {
channel.pipeline().remove("eco_packets")
}
}
}
}

View File

@@ -0,0 +1,32 @@
package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class PacketAutoRecipe(
private val plugin: EcoPlugin
) : PacketListener {
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundPlaceGhostRecipePacket ?: return
if (!plugin.configYml.getBool("displayed-recipes")) {
return
}
if (!EcoPlugin.getPluginNames().contains(packet.recipe.namespace)) {
return
}
if (packet.recipe.path.contains("_displayed")) {
return
}
val fKey = packet.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[packet] as ResourceLocation
fKey[packet] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket
object PacketHeldItemSlot : PacketListener {
override fun onReceive(event: PacketEvent) {
if (event.packet.handle !is ServerboundSetCarriedItemPacket) {
return
}
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -0,0 +1,39 @@
package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.asNMSStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.trading.MerchantOffer
import net.minecraft.world.item.trading.MerchantOffers
object PacketOpenWindowMerchant : PacketListener {
private val field = ClientboundMerchantOffersPacket::class.java
.declaredFields
.first { it.type == MerchantOffers::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundMerchantOffersPacket ?: return
val offers = MerchantOffers()
for (offer in packet.offers) {
val nbt = offer.createTag()
for (tag in arrayOf("buy", "buyB", "sell")) {
val nms = ItemStack.of(nbt.getCompound(tag))
val displayed = Display.display(nms.asBukkitStack(), event.player)
val itemNBT = displayed.asNMSStack().save(CompoundTag())
nbt.put(tag, itemNBT)
}
offers += MerchantOffer(nbt)
}
field.set(packet, offers)
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
object PacketSetCreativeSlot : PacketListener {
override fun onReceive(event: PacketEvent) {
val packet = event.packet.handle as? ServerboundSetCreativeModeSlotPacket ?: return
Display.revert(packet.item.asBukkitStack())
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket
object PacketSetSlot : PacketListener {
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundContainerSetSlotPacket ?: return
Display.display(packet.item.asBukkitStack(), event.player)
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -1,39 +1,40 @@
package com.willfp.eco.internal.spigot.display
package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.comphenix.protocol.PacketType
import com.comphenix.protocol.events.PacketContainer
import com.comphenix.protocol.events.PacketEvent
import com.willfp.eco.core.AbstractPacketAdapter
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.items.HashedItem
import com.willfp.eco.internal.spigot.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.display.frame.lastDisplayFrame
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.asNMSStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
class PacketWindowItems(plugin: EcoPlugin) : AbstractPacketAdapter(plugin, PacketType.Play.Server.WINDOW_ITEMS, false) {
private val lastKnownWindowIDs = ConcurrentHashMap<String, Int>()
class PacketWindowItems(
private val plugin: EcoPlugin
) : PacketListener {
private val lastKnownWindowIDs = ConcurrentHashMap<UUID, Int>()
override fun onSend(
packet: PacketContainer,
player: Player,
event: PacketEvent
) {
packet.itemModifier.modify(0) {
Display.display(
it, player
)
}
private val field = ClientboundContainerSetContentPacket::class.java
.declaredFields
.first { it.type == List::class.java }
.apply { isAccessible = true }
val windowId = packet.integers.read(0)
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundContainerSetContentPacket ?: return
val player = event.player
// Using name because UUID is unreliable with ProtocolLib players.
val name = player.name
Display.display(packet.carriedItem.asBukkitStack(), player)
val lastKnownID = lastKnownWindowIDs[name]
lastKnownWindowIDs[name] = windowId
val windowId = packet.containerId
val lastKnownID = lastKnownWindowIDs[player.uniqueId]
lastKnownWindowIDs[player.uniqueId] = windowId
// If there is any change in window ID at any point,
// Remove the last display frame to prevent any potential conflicts.
@@ -43,17 +44,20 @@ class PacketWindowItems(plugin: EcoPlugin) : AbstractPacketAdapter(plugin, Packe
player.lastDisplayFrame = DisplayFrame.EMPTY
}
val itemStacks = packet.itemListModifier.read(0) ?: return
val itemStacks = packet.items.map { it.asBukkitStack() }
packet.itemListModifier.write(0, modifyWindowItems(itemStacks, windowId, player))
val newItems = modifyWindowItems(itemStacks.toMutableList(), windowId, player)
field.set(packet, newItems.map { it.asNMSStack() })
}
private fun modifyWindowItems(
itemStacks: MutableList<ItemStack>,
windowId: Int,
player: Player
): MutableList<ItemStack> {
if (this.getPlugin().configYml.getBool("use-display-frame") && windowId == 0) {
if (plugin.configYml.getBool("use-display-frame") && windowId == 0) {
val frameMap = mutableMapOf<Byte, HashedItem>()
for (index in itemStacks.indices) {

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.internal.spigot.display.frame
package com.willfp.eco.internal.spigot.proxy.common.packet.display.frame
import com.comphenix.protocol.injector.temporary.TemporaryPlayer
import com.willfp.eco.core.items.HashedItem
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@@ -33,22 +32,12 @@ private val frames = ConcurrentHashMap<UUID, DisplayFrame>()
var Player.lastDisplayFrame: DisplayFrame
get() {
// ProtocolLib fix
if (this is TemporaryPlayer) {
return DisplayFrame.EMPTY
}
return frames[this.uniqueId] ?: DisplayFrame.EMPTY
}
set(value) {
// ProtocolLib fix
if (this is TemporaryPlayer) {
return
}
frames[this.uniqueId] = value
}
fun clearFrames() {
frames.clear()
}
}

View File

@@ -1,15 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.internal.spigot.proxy.AutoCraftProxy
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class AutoCraft : AutoCraftProxy {
override fun modifyPacket(packet: Any) {
val recipePacket = packet as ClientboundPlaceGhostRecipePacket
val fKey = recipePacket.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[recipePacket] as ResourceLocation
fKey[recipePacket] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
@@ -28,7 +28,7 @@ class BukkitCommands : BukkitCommandsProxy {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}

View File

@@ -1,12 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
@@ -15,6 +18,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_17_R1.CraftServer
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftMob
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_17_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_17_R1.persistence.CraftPersistentDataTypeRegistry
@@ -22,13 +26,17 @@ import org.bukkit.craftbukkit.v1_17_R1.util.CraftMagicNumbers
import org.bukkit.craftbukkit.v1_17_R1.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init() {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
@@ -60,7 +68,7 @@ class CommonsInitializer : CommonsInitializerProxy {
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asBukkitCopy(itemStack)
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
@@ -143,5 +151,9 @@ class CommonsInitializer : CommonsInitializerProxy {
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_17_R1.display.PacketChat
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
return listOf(
PacketAutoRecipe(plugin),
PacketHeldItemSlot,
PacketOpenWindowMerchant,
PacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin),
PacketChat
)
}
}

View File

@@ -1,41 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.VillagerTradeProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.trading.MerchantOffer
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftMerchantRecipe
import org.bukkit.entity.Player
import org.bukkit.inventory.MerchantRecipe
import java.lang.reflect.Field
class VillagerTrade : VillagerTradeProxy {
private val handle: Field = CraftMerchantRecipe::class.java.getDeclaredField("handle")
override fun displayTrade(
recipe: MerchantRecipe,
player: Player
): MerchantRecipe {
recipe as CraftMerchantRecipe
val nbt = getHandle(recipe).createTag()
for (tag in arrayOf("buy", "buyB", "sell")) {
val nms = ItemStack.of(nbt.getCompound(tag))
val displayed = Display.display(CraftItemStack.asBukkitCopy(nms), player)
val itemNBT = CraftItemStack.asNMSCopy(displayed).save(CompoundTag())
nbt.put(tag, itemNBT)
}
return CraftMerchantRecipe(MerchantOffer(nbt))
}
private fun getHandle(recipe: CraftMerchantRecipe): MerchantOffer {
return handle[recipe] as MerchantOffer
}
init {
handle.isAccessible = true
}
}

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
package com.willfp.eco.internal.spigot.proxy.v1_17_R1.display
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.ChatComponentProxy
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import net.kyori.adventure.nbt.api.BinaryTagHolder
import net.kyori.adventure.text.BuildableComponent
import net.kyori.adventure.text.Component
@@ -9,20 +10,28 @@ import net.kyori.adventure.text.TranslatableComponent
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import net.minecraft.nbt.TagParser
import net.minecraft.network.protocol.game.ClientboundChatPacket
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@Suppress("UNCHECKED_CAST")
class ChatComponent : ChatComponentProxy {
object PacketChat : PacketListener {
private val gsonComponentSerializer = GsonComponentSerializer.gson()
override fun modifyComponent(obj: Any, player: Player): Any {
if (obj !is net.minecraft.network.chat.Component) {
return obj
}
private val field = ClientboundChatPacket::class.java.declaredFields
.first { it.type == net.minecraft.network.chat.Component::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundChatPacket ?: return
val newMessage = modifyComponent(packet.message, event.player)
field.set(packet, newMessage)
}
private fun modifyComponent(obj: net.minecraft.network.chat.Component, player: Player): Any {
val component = gsonComponentSerializer.deserialize(
net.minecraft.network.chat.Component.Serializer.toJson(
obj
@@ -53,6 +62,7 @@ class ChatComponent : ChatComponentProxy {
}
component = component.children(children)
@Suppress("UNCHECKED_CAST")
val hoverEvent: HoverEvent<Any> = component.style().hoverEvent() as HoverEvent<Any>? ?: return component
val showItem = hoverEvent.value()
@@ -91,4 +101,4 @@ class ChatComponent : ChatComponentProxy {
val style = component.style().hoverEvent(newHover)
return component.style(style)
}
}
}

View File

@@ -1,15 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.internal.spigot.proxy.AutoCraftProxy
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class AutoCraft : AutoCraftProxy {
override fun modifyPacket(packet: Any) {
val recipePacket = packet as ClientboundPlaceGhostRecipePacket
val fKey = recipePacket.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[recipePacket] as ResourceLocation
fKey[recipePacket] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
@@ -28,7 +28,7 @@ class BukkitCommands : BukkitCommandsProxy {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}

View File

@@ -1,12 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
@@ -15,6 +18,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_18_R1.CraftServer
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftEntity
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftMob
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_18_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_18_R1.persistence.CraftPersistentDataTypeRegistry
@@ -22,13 +26,17 @@ import org.bukkit.craftbukkit.v1_18_R1.util.CraftMagicNumbers
import org.bukkit.craftbukkit.v1_18_R1.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init() {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
@@ -60,7 +68,7 @@ class CommonsInitializer : CommonsInitializerProxy {
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asBukkitCopy(itemStack)
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
@@ -143,5 +151,9 @@ class CommonsInitializer : CommonsInitializerProxy {
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_18_R1.display.PacketChat
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler: PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
return listOf(
PacketAutoRecipe(plugin),
PacketHeldItemSlot,
PacketOpenWindowMerchant,
PacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin),
PacketChat
)
}
}

View File

@@ -1,41 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.VillagerTradeProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.trading.MerchantOffer
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftMerchantRecipe
import org.bukkit.entity.Player
import org.bukkit.inventory.MerchantRecipe
import java.lang.reflect.Field
class VillagerTrade : VillagerTradeProxy {
private val handle: Field = CraftMerchantRecipe::class.java.getDeclaredField("handle")
override fun displayTrade(
recipe: MerchantRecipe,
player: Player
): MerchantRecipe {
recipe as CraftMerchantRecipe
val nbt = getHandle(recipe).createTag()
for (tag in arrayOf("buy", "buyB", "sell")) {
val nms = ItemStack.of(nbt.getCompound(tag))
val displayed = Display.display(CraftItemStack.asBukkitCopy(nms), player)
val itemNBT = CraftItemStack.asNMSCopy(displayed).save(CompoundTag())
nbt.put(tag, itemNBT)
}
return CraftMerchantRecipe(MerchantOffer(nbt))
}
private fun getHandle(recipe: CraftMerchantRecipe): MerchantOffer {
return handle[recipe] as MerchantOffer
}
init {
handle.isAccessible = true
}
}

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
package com.willfp.eco.internal.spigot.proxy.v1_18_R1.display
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.ChatComponentProxy
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import net.kyori.adventure.nbt.api.BinaryTagHolder
import net.kyori.adventure.text.BuildableComponent
import net.kyori.adventure.text.Component
@@ -9,20 +10,28 @@ import net.kyori.adventure.text.TranslatableComponent
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import net.minecraft.nbt.TagParser
import net.minecraft.network.protocol.game.ClientboundChatPacket
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@Suppress("UNCHECKED_CAST")
class ChatComponent : ChatComponentProxy {
object PacketChat : PacketListener {
private val gsonComponentSerializer = GsonComponentSerializer.gson()
override fun modifyComponent(obj: Any, player: Player): Any {
if (obj !is net.minecraft.network.chat.Component) {
return obj
}
private val field = ClientboundChatPacket::class.java.declaredFields
.first { it.type == net.minecraft.network.chat.Component::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundChatPacket ?: return
val newMessage = modifyComponent(packet.message, event.player)
field.set(packet, newMessage)
}
private fun modifyComponent(obj: net.minecraft.network.chat.Component, player: Player): Any {
val component = gsonComponentSerializer.deserialize(
net.minecraft.network.chat.Component.Serializer.toJson(
obj
@@ -53,6 +62,7 @@ class ChatComponent : ChatComponentProxy {
}
component = component.children(children)
@Suppress("UNCHECKED_CAST")
val hoverEvent: HoverEvent<Any> = component.style().hoverEvent() as HoverEvent<Any>? ?: return component
val showItem = hoverEvent.value()
@@ -91,4 +101,4 @@ class ChatComponent : ChatComponentProxy {
val style = component.style().hoverEvent(newHover)
return component.style(style)
}
}
}

View File

@@ -1,15 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.internal.spigot.proxy.AutoCraftProxy
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class AutoCraft : AutoCraftProxy {
override fun modifyPacket(packet: Any) {
val recipePacket = packet as ClientboundPlaceGhostRecipePacket
val fKey = recipePacket.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[recipePacket] as ResourceLocation
fKey[recipePacket] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
@@ -28,7 +28,7 @@ class BukkitCommands : BukkitCommandsProxy {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}

View File

@@ -1,12 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
@@ -15,6 +18,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_18_R2.CraftServer
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftMob
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_18_R2.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_18_R2.persistence.CraftPersistentDataTypeRegistry
@@ -22,13 +26,17 @@ import org.bukkit.craftbukkit.v1_18_R2.util.CraftMagicNumbers
import org.bukkit.craftbukkit.v1_18_R2.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init() {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
@@ -60,7 +68,7 @@ class CommonsInitializer : CommonsInitializerProxy {
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asBukkitCopy(itemStack)
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
@@ -143,5 +151,9 @@ class CommonsInitializer : CommonsInitializerProxy {
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_18_R2.display.PacketChat
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler: PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
return listOf(
PacketAutoRecipe(plugin),
PacketHeldItemSlot,
PacketOpenWindowMerchant,
PacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin),
PacketChat
)
}
}

View File

@@ -1,41 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.VillagerTradeProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.trading.MerchantOffer
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftMerchantRecipe
import org.bukkit.entity.Player
import org.bukkit.inventory.MerchantRecipe
import java.lang.reflect.Field
class VillagerTrade : VillagerTradeProxy {
private val handle: Field = CraftMerchantRecipe::class.java.getDeclaredField("handle")
override fun displayTrade(
recipe: MerchantRecipe,
player: Player
): MerchantRecipe {
recipe as CraftMerchantRecipe
val nbt = getHandle(recipe).createTag()
for (tag in arrayOf("buy", "buyB", "sell")) {
val nms = ItemStack.of(nbt.getCompound(tag))
val displayed = Display.display(CraftItemStack.asBukkitCopy(nms), player)
val itemNBT = CraftItemStack.asNMSCopy(displayed).save(CompoundTag())
nbt.put(tag, itemNBT)
}
return CraftMerchantRecipe(MerchantOffer(nbt))
}
private fun getHandle(recipe: CraftMerchantRecipe): MerchantOffer {
return handle[recipe] as MerchantOffer
}
init {
handle.isAccessible = true
}
}

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
package com.willfp.eco.internal.spigot.proxy.v1_18_R2.display
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.ChatComponentProxy
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import net.kyori.adventure.nbt.api.BinaryTagHolder
import net.kyori.adventure.text.BuildableComponent
import net.kyori.adventure.text.Component
@@ -9,20 +10,28 @@ import net.kyori.adventure.text.TranslatableComponent
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import net.minecraft.nbt.TagParser
import net.minecraft.network.protocol.game.ClientboundChatPacket
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@Suppress("UNCHECKED_CAST")
class ChatComponent : ChatComponentProxy {
object PacketChat : PacketListener {
private val gsonComponentSerializer = GsonComponentSerializer.gson()
override fun modifyComponent(obj: Any, player: Player): Any {
if (obj !is net.minecraft.network.chat.Component) {
return obj
}
private val field = ClientboundChatPacket::class.java.declaredFields
.first { it.type == net.minecraft.network.chat.Component::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundChatPacket ?: return
val newMessage = modifyComponent(packet.message, event.player)
field.set(packet, newMessage)
}
private fun modifyComponent(obj: net.minecraft.network.chat.Component, player: Player): Any {
val component = gsonComponentSerializer.deserialize(
net.minecraft.network.chat.Component.Serializer.toJson(
obj
@@ -53,6 +62,7 @@ class ChatComponent : ChatComponentProxy {
}
component = component.children(children)
@Suppress("UNCHECKED_CAST")
val hoverEvent: HoverEvent<Any> = component.style().hoverEvent() as HoverEvent<Any>? ?: return component
val showItem = hoverEvent.value()
@@ -62,7 +72,8 @@ class ChatComponent : ChatComponentProxy {
}
val newShowItem = showItem.nbt(
BinaryTagHolder.binaryTagHolder(
@Suppress("UnstableApiUsage", "DEPRECATION")
BinaryTagHolder.of(
CraftItemStack.asNMSCopy(
Display.display(
CraftItemStack.asBukkitCopy(
@@ -90,4 +101,4 @@ class ChatComponent : ChatComponentProxy {
val style = component.style().hoverEvent(newHover)
return component.style(style)
}
}
}

View File

@@ -1,15 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.internal.spigot.proxy.AutoCraftProxy
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class AutoCraft : AutoCraftProxy {
override fun modifyPacket(packet: Any) {
val recipePacket = packet as ClientboundPlaceGhostRecipePacket
val fKey = recipePacket.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[recipePacket] as ResourceLocation
fKey[recipePacket] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
@@ -28,7 +28,7 @@ class BukkitCommands : BukkitCommandsProxy {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}

View File

@@ -1,93 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.ChatComponentProxy
import net.kyori.adventure.nbt.api.BinaryTagHolder
import net.kyori.adventure.text.BuildableComponent
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.TranslatableComponent
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import net.minecraft.nbt.TagParser
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@Suppress("UNCHECKED_CAST")
class ChatComponent : ChatComponentProxy {
private val gsonComponentSerializer = GsonComponentSerializer.gson()
override fun modifyComponent(obj: Any, player: Player): Any {
if (obj !is net.minecraft.network.chat.Component) {
return obj
}
val component = gsonComponentSerializer.deserialize(
net.minecraft.network.chat.Component.Serializer.toJson(
obj
)
).asComponent() as BuildableComponent<*, *>
val newComponent = modifyBaseComponent(component, player)
return net.minecraft.network.chat.Component.Serializer.fromJson(
gsonComponentSerializer.serialize(newComponent.asComponent())
) ?: obj
}
private fun modifyBaseComponent(baseComponent: Component, player: Player): Component {
var component = baseComponent
if (component is TranslatableComponent) {
val args = mutableListOf<Component>()
for (arg in component.args()) {
args.add(modifyBaseComponent(arg, player))
}
component = component.args(args)
}
val children = mutableListOf<Component>()
for (child in component.children()) {
children.add(modifyBaseComponent(child, player))
}
component = component.children(children)
val hoverEvent: HoverEvent<Any> = component.style().hoverEvent() as HoverEvent<Any>? ?: return component
val showItem = hoverEvent.value()
if (showItem !is HoverEvent.ShowItem) {
return component
}
val newShowItem = showItem.nbt(
BinaryTagHolder.binaryTagHolder(
CraftItemStack.asNMSCopy(
Display.display(
CraftItemStack.asBukkitCopy(
CraftItemStack.asNMSCopy(
ItemStack(
Material.matchMaterial(
showItem.item()
.toString()
) ?: return component,
showItem.count()
)
).apply {
this.tag = TagParser.parseTag(
showItem.nbt()?.string() ?: return component
) ?: return component
}
),
player
)
).orCreateTag.toString()
)
)
val newHover = hoverEvent.value(newShowItem)
val style = component.style().hoverEvent(newHover)
return component.style(style)
}
}

View File

@@ -1,12 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
@@ -15,6 +18,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_19_R1.CraftServer
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftMob
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_19_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_19_R1.persistence.CraftPersistentDataTypeRegistry
@@ -22,13 +26,17 @@ import org.bukkit.craftbukkit.v1_19_R1.util.CraftMagicNumbers
import org.bukkit.craftbukkit.v1_19_R1.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init() {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
@@ -60,7 +68,7 @@ class CommonsInitializer : CommonsInitializerProxy {
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asBukkitCopy(itemStack)
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
@@ -143,5 +151,9 @@ class CommonsInitializer : CommonsInitializerProxy {
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
return listOf(
PacketAutoRecipe(plugin),
PacketHeldItemSlot,
PacketOpenWindowMerchant,
PacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin)
)
}
}

View File

@@ -1,41 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.VillagerTradeProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.trading.MerchantOffer
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftMerchantRecipe
import org.bukkit.entity.Player
import org.bukkit.inventory.MerchantRecipe
import java.lang.reflect.Field
class VillagerTrade : VillagerTradeProxy {
private val handle: Field = CraftMerchantRecipe::class.java.getDeclaredField("handle")
override fun displayTrade(
recipe: MerchantRecipe,
player: Player
): MerchantRecipe {
recipe as CraftMerchantRecipe
val nbt = getHandle(recipe).createTag()
for (tag in arrayOf("buy", "buyB", "sell")) {
val nms = ItemStack.of(nbt.getCompound(tag))
val displayed = Display.display(CraftItemStack.asBukkitCopy(nms), player)
val itemNBT = CraftItemStack.asNMSCopy(displayed).save(CompoundTag())
nbt.put(tag, itemNBT)
}
return CraftMerchantRecipe(MerchantOffer(nbt))
}
private fun getHandle(recipe: CraftMerchantRecipe): MerchantOffer {
return handle[recipe] as MerchantOffer
}
init {
handle.isAccessible = true
}
}

View File

@@ -1,15 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.spigot.proxy.AutoCraftProxy
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class AutoCraft : AutoCraftProxy {
override fun modifyPacket(packet: Any) {
val recipePacket = packet as ClientboundPlaceGhostRecipePacket
val fKey = recipePacket.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[recipePacket] as ResourceLocation
fKey[recipePacket] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
@@ -28,7 +28,7 @@ class BukkitCommands : BukkitCommandsProxy {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}

Some files were not shown because too many files have changed in this diff Show More