A way for other plugins to exempt entities from cleanup
This commit is contained in:
1 parent
67cd86c532
commit
352621e2ba
4 files changed
+82
-2
No files matched your search
@@ -26,6 +26,41 @@ BadPylot, NotAlexNoyle.
|
||||
|
||||
- See how much time is left until entity cleanup with an in-game command (/cleanupin).
|
||||
|
||||
- Let other plugins exempt their own items and experience orbs from despawning and cleanup.
|
||||
|
||||
# Protecting another plugin's entities
|
||||
|
||||
Minigame objectives and similar plugin-owned items are ordinary dropped items, so entity cleanup and /clearentities delete them out from under the plugin that spawned them. To exempt one, tag it with the `nodespawn-og:protect` key:
|
||||
|
||||
```java
|
||||
// Do this once, e.g. in a static field.
|
||||
private static final NamespacedKey PROTECT = Objects.requireNonNull(NamespacedKey.fromString("nodespawn-og:protect"));
|
||||
|
||||
// Tag the entity as soon as it exists.
|
||||
Item item = world.dropItem(location, itemStack);
|
||||
item.getPersistentDataContainer().set(PROTECT, PersistentDataType.BYTE, (byte) 1);
|
||||
```
|
||||
|
||||
## Tagging rules
|
||||
|
||||
- **Tag the entity, not the ItemStack.** NoDespawn-OG reads `Item#getPersistentDataContainer()`, which is the entity's container. A key written to the stack's `ItemMeta` is a different container and has no effect.
|
||||
|
||||
- **Use `PersistentDataType.BYTE` with the value `1`.** Any other value, type, or a missing key means unprotected.
|
||||
|
||||
- **Tag immediately after spawning.** `World#dropItem` returns the entity; tag it before returning control to the server so no despawn or cleanup pass can see it untagged.
|
||||
|
||||
- **Re-tag every new entity.** The tag lives on the entity, not the item. Once a player picks the item up the entity is gone, so a plugin that re-drops the item must tag the new entity too.
|
||||
|
||||
- **`ExperienceOrb` works the same way**, using the same key on the orb entity.
|
||||
|
||||
- **The tag persists.** It is stored in entity NBT, so it survives chunk unload and server restarts.
|
||||
|
||||
## Effect
|
||||
|
||||
A tagged entity is skipped by the periodic entity cleanup, by /clearentities, and by despawning: its `ItemDespawnEvent` is cancelled and it is never given despawn timing data. It lives until something removes it, so **the owning plugin is responsible for removing its own protected entities**. To give one back to NoDespawn-OG, remove the key (`container.remove(PROTECT)`) or set it to `(byte) 0`.
|
||||
|
||||
Death piles are unaffected by this key; they keep using their existing `disable-death-despawns` and death-pile-protection behavior.
|
||||
|
||||
# Permissions
|
||||
|
||||
Clear dropped entities with a command (/clearentities):
|
||||
@@ -46,6 +81,10 @@ Running the plugin on your server requires [Utilities-OG](https://github.com/tru
|
||||
|
||||
# Changelog
|
||||
|
||||
*Version 4.1.1:*
|
||||
|
||||
- Items and experience orbs tagged `nodespawn-og:protect` by another plugin are now exempt from despawning, entity cleanup, and /clearentities.
|
||||
|
||||
*Version 4.1:*
|
||||
|
||||
- Death pile experience orbs now inherit the same pile ID, owner, and despawn timing as the items in their death pile.
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ kotlin { jvmToolchain(17) }
|
||||
/* ----------------------------- Metadata ------------------------------ */
|
||||
group = "net.trueog.nodespawn-og" // Declare bundle identifier.
|
||||
|
||||
version = "4.1" // Declare plugin version (will be in .jar).
|
||||
version = "4.1.1" // Declare plugin version (will be in .jar).
|
||||
|
||||
val apiVersion = "1.19" // Declare minecraft server target version.
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ public class EntityCleanupScheduler extends BukkitRunnable {
|
||||
private final Set<Integer> warningTicks = new HashSet<>();
|
||||
private final NamespacedKey keyDeath = new NamespacedKey(NoDespawnOG.getPlugin(), "nodespawn_death");
|
||||
private final NamespacedKey keyExpireAt = new NamespacedKey(NoDespawnOG.getPlugin(), "nodespawn_expire_at");
|
||||
private final NamespacedKey keyProtect = new NamespacedKey(NoDespawnOG.getPlugin(), "protect");
|
||||
|
||||
public EntityCleanupScheduler() {
|
||||
|
||||
@@ -325,6 +326,16 @@ public class EntityCleanupScheduler extends BukkitRunnable {
|
||||
|
||||
}
|
||||
|
||||
// Entities another plugin tagged nodespawn-og:protect are off limits to
|
||||
// cleanup.
|
||||
private boolean isProtectedEntity(Entity entity) {
|
||||
|
||||
final Byte protectFlag = entity.getPersistentDataContainer().get(keyProtect, PersistentDataType.BYTE);
|
||||
|
||||
return protectFlag != null && protectFlag.byteValue() == (byte) 1;
|
||||
|
||||
}
|
||||
|
||||
private void scheduleVanillaDespawnCheck(Entity entity, long now, long expireAt) {
|
||||
|
||||
if (!entity.isValid()) {
|
||||
@@ -414,7 +425,7 @@ public class EntityCleanupScheduler extends BukkitRunnable {
|
||||
|
||||
}
|
||||
|
||||
if (!isDeathPileEntity(entity)) {
|
||||
if (!isDeathPileEntity(entity) && !isProtectedEntity(entity)) {
|
||||
|
||||
entity.remove();
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ public class EventListener implements Listener {
|
||||
private final NamespacedKey keyCreated = new NamespacedKey(NoDespawnOG.getPlugin(), "nodespawn_created");
|
||||
private final NamespacedKey keyExpireAt = new NamespacedKey(NoDespawnOG.getPlugin(), "nodespawn_expire_at");
|
||||
|
||||
// Other plugins tag their own items/orbs with nodespawn-og:protect to opt them
|
||||
// out of despawning and cleanup entirely (minigame objectives and the like).
|
||||
private final NamespacedKey keyProtect = new NamespacedKey(NoDespawnOG.getPlugin(), "protect");
|
||||
|
||||
private static final int VANILLA_DESPAWN_TICKS = 6000;
|
||||
private static final long TICK_MS = 50L;
|
||||
|
||||
@@ -66,6 +70,17 @@ public class EventListener implements Listener {
|
||||
public void onItemDespawn(ItemDespawnEvent event) {
|
||||
|
||||
final Item item = event.getEntity();
|
||||
|
||||
if (isProtectedEntity(item)) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
item.setTicksLived(1);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
final long expireAt = getExpireAt(item);
|
||||
if (expireAt == 0L) {
|
||||
|
||||
@@ -212,6 +227,14 @@ public class EventListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
private boolean isProtectedEntity(Entity entity) {
|
||||
|
||||
final Byte flag = entity.getPersistentDataContainer().get(keyProtect, PersistentDataType.BYTE);
|
||||
|
||||
return Byte.valueOf((byte) 1).equals(flag);
|
||||
|
||||
}
|
||||
|
||||
private boolean isDeathPileItem(Item item) {
|
||||
|
||||
return isDeathPileEntity(item);
|
||||
@@ -323,6 +346,13 @@ public class EventListener implements Listener {
|
||||
|
||||
private void ensureExpireData(Entity entity) {
|
||||
|
||||
// Protected entities are never given despawn data, so nothing ages them out.
|
||||
if (isProtectedEntity(entity)) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
long expireAt = getExpireAt(entity);
|
||||
if (expireAt != 0L) {
|
||||
|
||||
Reference in new issue
Block a user