Skip to content

Commit

Permalink
Changed the way spawner rates is handled for better performance & det…
Browse files Browse the repository at this point in the history
…ection of new spawners, potentially fixes #1287
  • Loading branch information
OmerBenGera committed Jul 23, 2022
1 parent 8a31c01 commit e694ab5
Show file tree
Hide file tree
Showing 18 changed files with 872 additions and 121 deletions.
Expand Up @@ -2,6 +2,7 @@

import com.bgsoftware.superiorskyblock.module.BuiltinModules;
import com.bgsoftware.superiorskyblock.module.upgrades.type.UpgradeTypeSpawnerRates;
import com.bgsoftware.wildstacker.api.events.SpawnerPlaceEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

Expand All @@ -14,9 +15,9 @@ public class WildStackerListener implements Listener {
.getEnabledUpgradeType(UpgradeTypeSpawnerRates.class);

@EventHandler
public void onWildStackerStackSpawn(com.bgsoftware.wildstacker.api.events.SpawnerStackedEntitySpawnEvent e) {
public void onWildStackerStackSpawn(SpawnerPlaceEvent e) {
if (spawnerRates != null)
spawnerRates.handleSpawnerSpawn(e.getSpawner());
spawnerRates.handleSpawnerPlace(e.getSpawner().getSpawner());
}

}
Expand Up @@ -3,30 +3,27 @@
import com.bgsoftware.superiorskyblock.SuperiorSkyblockPlugin;
import com.bgsoftware.superiorskyblock.api.island.Island;
import com.bgsoftware.superiorskyblock.commands.ISuperiorCommand;
import com.bgsoftware.superiorskyblock.core.Materials;
import com.bgsoftware.superiorskyblock.core.threads.BukkitExecutor;
import com.bgsoftware.superiorskyblock.module.upgrades.commands.CmdAdminAddSpawnerRates;
import com.bgsoftware.superiorskyblock.module.upgrades.commands.CmdAdminSetSpawnerRates;
import com.bgsoftware.superiorskyblock.core.collections.AutoRemovalCollection;
import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.SpawnerSpawnEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.world.ChunkLoadEvent;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class UpgradeTypeSpawnerRates implements IUpgradeType {

private static final List<ISuperiorCommand> commands = Arrays.asList(new CmdAdminAddSpawnerRates(),
new CmdAdminSetSpawnerRates());

private final Collection<UUID> alreadyTrackedSpawning = AutoRemovalCollection.newHashSet(10L * 50, TimeUnit.MILLISECONDS);

private final SuperiorSkyblockPlugin plugin;

public UpgradeTypeSpawnerRates(SuperiorSkyblockPlugin plugin) {
Expand All @@ -43,34 +40,58 @@ public List<ISuperiorCommand> getCommands() {
return commands;
}

public void handleSpawnerSpawn(@Nullable CreatureSpawner creatureSpawner) {
if (creatureSpawner == null || creatureSpawner.getLocation() == null)
return;

public void handleSpawnerPlace(CreatureSpawner creatureSpawner) {
Island island = plugin.getGrid().getIslandAt(creatureSpawner.getLocation());

if (island == null)
return;

double spawnerRatesMultiplier = island.getSpawnerRatesMultiplier();
// We want to replace the spawner in a delay so other plugins that might change the spawner will be taken in action as well.
BukkitExecutor.sync(() -> plugin.getNMSWorld().listenSpawner(creatureSpawner,
spawnDelay -> calculateNewSpawnerDelay(island, spawnDelay)), 20L);
}

if (spawnerRatesMultiplier > 1 && alreadyTrackedSpawning.add(island.getOwner().getUniqueId())) {
BukkitExecutor.sync(() -> {
int spawnDelay = plugin.getNMSWorld().getSpawnerDelay(creatureSpawner);
if (spawnDelay > 0) {
plugin.getNMSWorld().setSpawnerDelay(creatureSpawner,
(int) Math.round(spawnDelay / spawnerRatesMultiplier));
}
}, 5L);
private int calculateNewSpawnerDelay(Island island, int spawnDelay) {
double spawnerRatesMultiplier = island.getSpawnerRatesMultiplier();
if (spawnerRatesMultiplier > 1) {
return (int) Math.round(spawnDelay / spawnerRatesMultiplier);
} else {
return spawnDelay;
}
}

private class SpawnerRatesListener implements Listener {

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSpawnerPlace(BlockPlaceEvent e) {
if (e.getBlock().getType() == Materials.SPAWNER.toBukkitType())
handleSpawnerPlace((CreatureSpawner) e.getBlock().getState());
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onChunkLoad(ChunkLoadEvent e) {
Island island = plugin.getGrid().getIslandAt(e.getChunk());

if (island == null)
return;

List<CreatureSpawner> creatureSpawners = new ArrayList<>();

for (BlockState tileEntity : e.getChunk().getTileEntities()) {
if (tileEntity instanceof CreatureSpawner) {
creatureSpawners.add((CreatureSpawner) tileEntity);
}
}

if (!creatureSpawners.isEmpty()) {
// We want to replace the spawner in a delay so other plugins that might change the spawner will be taken in action as well.
BukkitExecutor.sync(() -> {
creatureSpawners.forEach(creatureSpawner -> {
plugin.getNMSWorld().listenSpawner(creatureSpawner, spawnDelay -> calculateNewSpawnerDelay(island, spawnDelay));
});
}, 20L);
}

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onSpawn(SpawnerSpawnEvent e) {
handleSpawnerSpawn(e.getSpawner());
}

}
Expand Down
Expand Up @@ -4,8 +4,8 @@
import com.bgsoftware.superiorskyblock.api.island.Island;
import com.bgsoftware.superiorskyblock.api.key.Key;
import com.bgsoftware.superiorskyblock.api.wrappers.SuperiorPlayer;
import com.bgsoftware.superiorskyblock.tag.CompoundTag;
import com.bgsoftware.superiorskyblock.core.SchematicBlock;
import com.bgsoftware.superiorskyblock.tag.CompoundTag;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
Expand All @@ -17,14 +17,13 @@
import org.bukkit.generator.ChunkGenerator;

import java.util.List;
import java.util.function.IntFunction;

public interface NMSWorld {

Key getBlockKey(ChunkSnapshot chunkSnapshot, int x, int y, int z);

int getSpawnerDelay(CreatureSpawner creatureSpawner);

void setSpawnerDelay(CreatureSpawner creatureSpawner, int spawnDelay);
void listenSpawner(CreatureSpawner creatureSpawner, IntFunction<Integer> delayChangeCallback);

void setWorldBorder(SuperiorPlayer superiorPlayer, Island island);

Expand Down
Expand Up @@ -13,6 +13,7 @@
import com.bgsoftware.superiorskyblock.nms.ICachedBlock;
import com.bgsoftware.superiorskyblock.nms.NMSWorld;
import com.bgsoftware.superiorskyblock.nms.v1_12_R1.generator.IslandsGeneratorImpl;
import com.bgsoftware.superiorskyblock.nms.v1_12_R1.spawners.MobSpawnerAbstractNotifier;
import com.bgsoftware.superiorskyblock.tag.CompoundTag;
import net.minecraft.server.v1_12_R1.BiomeBase;
import net.minecraft.server.v1_12_R1.BlockDoubleStep;
Expand All @@ -22,6 +23,7 @@
import net.minecraft.server.v1_12_R1.EnumSkyBlock;
import net.minecraft.server.v1_12_R1.IBlockData;
import net.minecraft.server.v1_12_R1.IChatBaseComponent;
import net.minecraft.server.v1_12_R1.MobSpawnerAbstract;
import net.minecraft.server.v1_12_R1.NBTTagCompound;
import net.minecraft.server.v1_12_R1.PacketPlayOutBlockChange;
import net.minecraft.server.v1_12_R1.PacketPlayOutWorldBorder;
Expand Down Expand Up @@ -50,13 +52,17 @@
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.generator.ChunkGenerator;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.function.IntFunction;

public class NMSWorldImpl implements NMSWorld {

private static final ReflectField<BiomeBase[]> BIOME_BASE_ARRAY = new ReflectField<>(
"org.bukkit.craftbukkit.VERSION.generator.CustomChunkGenerator$CustomBiomeGrid", BiomeBase[].class, "biome");
private static final ReflectField<MobSpawnerAbstract> MOB_SPAWNER_ABSTRACT = new ReflectField<MobSpawnerAbstract>(
TileEntityMobSpawner.class, MobSpawnerAbstract.class, Modifier.PRIVATE | Modifier.FINAL, 1).removeFinal();

private final SuperiorSkyblockPlugin plugin;
private final Singleton<SignsListener> signsListener;
Expand All @@ -83,19 +89,27 @@ public Key getBlockKey(ChunkSnapshot chunkSnapshot, int x, int y, int z) {
}

@Override
public int getSpawnerDelay(CreatureSpawner creatureSpawner) {
public void listenSpawner(CreatureSpawner creatureSpawner, IntFunction<Integer> delayChangeCallback) {
Location location = creatureSpawner.getLocation();
TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) ((CraftWorld) location.getWorld())
.getTileEntityAt(location.getBlockX(), location.getBlockY(), location.getBlockZ());
return mobSpawner.getSpawner().spawnDelay;
}
org.bukkit.World world = location.getWorld();

@Override
public void setSpawnerDelay(CreatureSpawner creatureSpawner, int spawnDelay) {
Location location = creatureSpawner.getLocation();
TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) ((CraftWorld) location.getWorld())
.getTileEntityAt(location.getBlockX(), location.getBlockY(), location.getBlockZ());
mobSpawner.getSpawner().spawnDelay = spawnDelay;
if (world == null)
return;

WorldServer worldServer = ((CraftWorld) world).getHandle();
BlockPosition blockPosition = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ());
TileEntity mobSpawner = worldServer.getTileEntity(blockPosition);

if (!(mobSpawner instanceof TileEntityMobSpawner))
return;

MobSpawnerAbstract mobSpawnerAbstract = ((TileEntityMobSpawner) mobSpawner).getSpawner();

if (!(mobSpawnerAbstract instanceof MobSpawnerAbstractNotifier)) {
MobSpawnerAbstractNotifier mobSpawnerAbstractNotifier = new MobSpawnerAbstractNotifier(mobSpawnerAbstract, delayChangeCallback);
MOB_SPAWNER_ABSTRACT.set(mobSpawner, mobSpawnerAbstractNotifier);
mobSpawnerAbstractNotifier.updateDelay();
}
}

@Override
Expand Down
@@ -0,0 +1,85 @@
package com.bgsoftware.superiorskyblock.nms.v1_12_R1.spawners;

import net.minecraft.server.v1_12_R1.BlockPosition;
import net.minecraft.server.v1_12_R1.MinecraftKey;
import net.minecraft.server.v1_12_R1.MobSpawnerAbstract;
import net.minecraft.server.v1_12_R1.MobSpawnerData;
import net.minecraft.server.v1_12_R1.NBTTagCompound;
import net.minecraft.server.v1_12_R1.World;

import javax.annotation.Nullable;
import java.util.function.IntFunction;

public class MobSpawnerAbstractNotifier extends MobSpawnerAbstract {

private final MobSpawnerAbstract mobSpawnerAbstract;
private final IntFunction<Integer> delayChangeCallback;

public MobSpawnerAbstractNotifier(MobSpawnerAbstract mobSpawnerAbstract, IntFunction<Integer> delayChangeCallback) {
this.mobSpawnerAbstract = mobSpawnerAbstract;
this.delayChangeCallback = delayChangeCallback;
}

@Nullable
@Override
public MinecraftKey getMobName() {
return mobSpawnerAbstract.getMobName();
}

@Override
public void setMobName(@Nullable MinecraftKey minecraftkey) {
mobSpawnerAbstract.setMobName(minecraftkey);
}

@Override
public void c() {
int startDelay = mobSpawnerAbstract.spawnDelay;
try {
mobSpawnerAbstract.c();
} finally {
int newDelay = mobSpawnerAbstract.spawnDelay;
if (newDelay > startDelay)
updateDelay();
}
}

@Override
public void a(NBTTagCompound nbttagcompound) {
mobSpawnerAbstract.a(nbttagcompound);
}

@Override
public NBTTagCompound b(NBTTagCompound nbttagcompound) {
return mobSpawnerAbstract.b(nbttagcompound);
}

@Override
public boolean b(int i) {
return mobSpawnerAbstract.b(i);
}

@Override
public void a(MobSpawnerData mobspawnerdata) {
mobSpawnerAbstract.a(mobspawnerdata);
}

@Override
public void a(int i) {
mobSpawnerAbstract.a(i);
}

@Override
public World a() {
return mobSpawnerAbstract.a();
}

@Override
public BlockPosition b() {
return mobSpawnerAbstract.b();
}

public void updateDelay() {
mobSpawnerAbstract.spawnDelay = delayChangeCallback.apply(mobSpawnerAbstract.spawnDelay);
}

}
Expand Up @@ -15,6 +15,7 @@
import com.bgsoftware.superiorskyblock.nms.ICachedBlock;
import com.bgsoftware.superiorskyblock.nms.NMSWorld;
import com.bgsoftware.superiorskyblock.nms.v1_16_R3.generator.IslandsGeneratorImpl;
import com.bgsoftware.superiorskyblock.nms.v1_16_R3.spawners.MobSpawnerAbstractNotifier;
import com.bgsoftware.superiorskyblock.nms.v1_16_R3.world.BlockStatesMapper;
import com.bgsoftware.superiorskyblock.tag.ByteTag;
import com.bgsoftware.superiorskyblock.tag.CompoundTag;
Expand All @@ -38,6 +39,7 @@
import net.minecraft.server.v1_16_R3.IChatBaseComponent;
import net.minecraft.server.v1_16_R3.IRegistry;
import net.minecraft.server.v1_16_R3.LightEngine;
import net.minecraft.server.v1_16_R3.MobSpawnerAbstract;
import net.minecraft.server.v1_16_R3.NBTTagCompound;
import net.minecraft.server.v1_16_R3.PacketPlayOutBlockChange;
import net.minecraft.server.v1_16_R3.PacketPlayOutWorldBorder;
Expand Down Expand Up @@ -68,9 +70,11 @@
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.generator.ChunkGenerator;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.IntFunction;

public class NMSWorldImpl implements NMSWorld {

Expand All @@ -85,6 +89,8 @@ public class NMSWorldImpl implements NMSWorld {
private static final ReflectMethod<Float> SOUND_PITCH = new ReflectMethod<>(SoundEffectType.class, "b");
private static final ReflectField<Object> CHUNK_PACKET_BLOCK_CONTROLLER = new ReflectField<>(World.class,
Object.class, "chunkPacketBlockController").removeFinal();
private static final ReflectField<MobSpawnerAbstract> MOB_SPAWNER_ABSTRACT = new ReflectField<MobSpawnerAbstract>(
TileEntityMobSpawner.class, MobSpawnerAbstract.class, Modifier.PRIVATE | Modifier.FINAL, 1).removeFinal();

private final SuperiorSkyblockPlugin plugin;
private final Singleton<SignsListener> signsListener;
Expand All @@ -111,20 +117,27 @@ public Key getBlockKey(ChunkSnapshot chunkSnapshot, int x, int y, int z) {
}

@Override
public int getSpawnerDelay(CreatureSpawner creatureSpawner) {
public void listenSpawner(CreatureSpawner creatureSpawner, IntFunction<Integer> delayChangeCallback) {
Location location = creatureSpawner.getLocation();
BlockPosition blockPosition = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ());
TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) ((CraftWorld) location.getWorld()).getHandle().getTileEntity(blockPosition);
return mobSpawner == null ? 0 : mobSpawner.getSpawner().spawnDelay;
}
org.bukkit.World world = location.getWorld();

@Override
public void setSpawnerDelay(CreatureSpawner creatureSpawner, int spawnDelay) {
Location location = creatureSpawner.getLocation();
if (world == null)
return;

WorldServer worldServer = ((CraftWorld) world).getHandle();
BlockPosition blockPosition = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ());
TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) ((CraftWorld) location.getWorld()).getHandle().getTileEntity(blockPosition);
if (mobSpawner != null)
mobSpawner.getSpawner().spawnDelay = spawnDelay;
TileEntity mobSpawner = worldServer.getTileEntity(blockPosition);

if (!(mobSpawner instanceof TileEntityMobSpawner))
return;

MobSpawnerAbstract mobSpawnerAbstract = ((TileEntityMobSpawner) mobSpawner).getSpawner();

if (!(mobSpawnerAbstract instanceof MobSpawnerAbstractNotifier)) {
MobSpawnerAbstractNotifier mobSpawnerAbstractNotifier = new MobSpawnerAbstractNotifier(mobSpawnerAbstract, delayChangeCallback);
MOB_SPAWNER_ABSTRACT.set(mobSpawner, mobSpawnerAbstractNotifier);
mobSpawnerAbstractNotifier.updateDelay();
}
}

@Override
Expand Down

0 comments on commit e694ab5

Please sign in to comment.