Files
AkarinMC/src/main/java/net/minecraft/server/WorldServer.java

1319 lines
54 KiB
Java

package net.minecraft.server;
import co.aikar.timings.Timings;
import io.akarin.server.core.AkarinAsyncExecutor;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start
import java.util.logging.Level;
import org.bukkit.WeatherType;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.util.HashTreeSet;
import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.weather.LightningStrikeEvent;
// CraftBukkit end
public class WorldServer extends World implements IAsyncTaskHandler {
private static final Logger a = LogManager.getLogger();
boolean hasPhysicsEvent = true; // Paper
private final MinecraftServer server;
public EntityTracker tracker;
private final PlayerChunkMap manager;
public final Map<UUID, Entity> entitiesByUUID = Maps.newHashMap(); // Paper
public boolean savingDisabled;
private boolean J;
private int emptyTime;
private final PortalTravelAgent portalTravelAgent;
private final SpawnerCreature spawnerCreature = new SpawnerCreature();
private final TickListServer<Block> nextTickListBlock;
private final TickListServer<FluidType> nextTickListFluid;
protected final VillageSiege siegeManager;
ObjectLinkedOpenHashSet<BlockActionData> d;
private boolean P;
// CraftBukkit start
public final DimensionManager dimension;
private static Throwable getAddToWorldStackTrace(Entity entity) {
return new Throwable(entity + " Added to world at " + new java.util.Date());
}
// Add env and gen to constructor
public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, PersistentCollection persistentcollection, WorldData worlddata, DimensionManager dimensionmanager, MethodProfiler methodprofiler, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) {
super(idatamanager, persistentcollection, worlddata, DimensionManager.a(env.getId()).e(), methodprofiler, false, gen, env);
this.dimension = dimensionmanager;
this.pvpMode = minecraftserver.getPVP();
worlddata.world = this;
// CraftBukkit end
this.nextTickListBlock = new TickListServer<>(this, (block) -> {
return block == null || block.getBlockData().isAir();
}, IRegistry.BLOCK::getKey, IRegistry.BLOCK::getOrDefault, this::b, "Blocks"); // Paper - timings
this.nextTickListFluid = new TickListServer<>(this, (fluidtype) -> {
return fluidtype == null || fluidtype == FluidTypes.EMPTY;
}, IRegistry.FLUID::getKey, IRegistry.FLUID::getOrDefault, this::a, "Fluids"); // Paper - timings
this.siegeManager = new VillageSiege(this);
this.d = new ObjectLinkedOpenHashSet();
this.server = minecraftserver;
this.tracker = new EntityTracker(this);
this.manager = new PlayerChunkMap(this);
this.worldProvider.a((World) this);
this.chunkProvider = this.r();
this.portalTravelAgent = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit
this.P();
this.Q();
this.getWorldBorder().a(minecraftserver.au());
MCUtil.scheduleAsyncTask(() -> this.getChunkProvider().chunkLoader.getPersistentStructureLegacy(worldProvider.getDimensionManager(), worldMaps)); // Paper
}
public WorldServer i_() {
String s = PersistentVillage.a(this.worldProvider);
PersistentVillage persistentvillage = (PersistentVillage) this.a(DimensionManager.OVERWORLD, PersistentVillage::new, s);
if (persistentvillage == null) {
this.villages = new PersistentVillage(this);
this.a(DimensionManager.OVERWORLD, s, (PersistentBase) this.villages);
} else {
this.villages = persistentvillage;
this.villages.a((World) this);
}
if (getServer().getScoreboardManager() == null) { // CraftBukkit
PersistentScoreboard persistentscoreboard = (PersistentScoreboard) this.a(DimensionManager.OVERWORLD, PersistentScoreboard::new, "scoreboard");
if (persistentscoreboard == null) {
persistentscoreboard = new PersistentScoreboard();
this.a(DimensionManager.OVERWORLD, "scoreboard", (PersistentBase) persistentscoreboard);
}
persistentscoreboard.a((Scoreboard) this.server.getScoreboard());
this.server.getScoreboard().a((Runnable) (new RunnableSaveScoreboard(persistentscoreboard)));
} // CraftBukkit
this.getWorldBorder().setCenter(this.worldData.B(), this.worldData.C());
this.getWorldBorder().setDamageAmount(this.worldData.H());
this.getWorldBorder().setDamageBuffer(this.worldData.G());
this.getWorldBorder().setWarningDistance(this.worldData.I());
this.getWorldBorder().setWarningTime(this.worldData.J());
if (this.worldData.E() > 0L) {
this.getWorldBorder().transitionSizeBetween(this.worldData.D(), this.worldData.F(), this.worldData.E());
} else {
this.getWorldBorder().setSize(this.worldData.D());
}
// CraftBukkit start
if (generator != null) {
getWorld().getPopulators().addAll(generator.getDefaultPopulators(getWorld()));
}
// CraftBukkit end
return this;
}
// CraftBukkit start
@Override
public TileEntity getTileEntity(BlockPosition pos) {
TileEntity result = super.getTileEntity(pos);
Block type = getType(pos).getBlock();
if (type == Blocks.CHEST || type == Blocks.TRAPPED_CHEST) { // Spigot
if (!(result instanceof TileEntityChest)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.FURNACE) {
if (!(result instanceof TileEntityFurnace)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.DROPPER) {
if (!(result instanceof TileEntityDropper)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.DISPENSER) {
if (!(result instanceof TileEntityDispenser)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.JUKEBOX) {
if (!(result instanceof TileEntityJukeBox)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.SPAWNER) {
if (!(result instanceof TileEntityMobSpawner)) {
result = fixTileEntity(pos, type, result);
}
} else if ((type == Blocks.SIGN) || (type == Blocks.WALL_SIGN)) {
if (!(result instanceof TileEntitySign)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.ENDER_CHEST) {
if (!(result instanceof TileEntityEnderChest)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.BREWING_STAND) {
if (!(result instanceof TileEntityBrewingStand)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.BEACON) {
if (!(result instanceof TileEntityBeacon)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.HOPPER) {
if (!(result instanceof TileEntityHopper)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.ENCHANTING_TABLE) {
if (!(result instanceof TileEntityEnchantTable)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.END_PORTAL) {
if (!(result instanceof TileEntityEnderPortal)) {
result = fixTileEntity(pos, type, result);
}
} else if (type instanceof BlockSkullAbstract) {
if (!(result instanceof TileEntitySkull)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.DAYLIGHT_DETECTOR) {
if (!(result instanceof TileEntityLightDetector)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.COMPARATOR) {
if (!(result instanceof TileEntityComparator)) {
result = fixTileEntity(pos, type, result);
}
}else if (type instanceof BlockBannerAbstract) {
if (!(result instanceof TileEntityBanner)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.STRUCTURE_BLOCK) {
if (!(result instanceof TileEntityStructure)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.END_GATEWAY) {
if (!(result instanceof TileEntityEndGateway)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.COMMAND_BLOCK) {
if (!(result instanceof TileEntityCommand)) {
result = fixTileEntity(pos, type, result);
}
} else if (type == Blocks.STRUCTURE_BLOCK) {
if (!(result instanceof TileEntityStructure)) {
result = fixTileEntity(pos, type, result);
}
} else if (type instanceof BlockBed) {
if (!(result instanceof TileEntityBed)) {
result = fixTileEntity(pos, type, result);
}
}
// Paper Start - add TE fix checks for shulkers, see nms.BlockShulkerBox
else if (type instanceof BlockShulkerBox) {
if (!(result instanceof TileEntityShulkerBox)) {
result = fixTileEntity(pos, type, result);
}
}
// Paper end
return result;
}
private TileEntity fixTileEntity(BlockPosition pos, Block type, TileEntity found) {
this.getServer().getLogger().log(Level.SEVERE, "Block at {0},{1},{2} is {3} but has {4}" + ". "
+ "Bukkit will attempt to fix this, but there may be additional damage that we cannot recover.", new Object[]{pos.getX(), pos.getY(), pos.getZ(), type, found});
if (type instanceof ITileEntity) {
TileEntity replacement = ((ITileEntity) type).a(this);
replacement.world = this;
this.setTileEntity(pos, replacement);
return replacement;
} else {
this.getServer().getLogger().severe("Don't know how to fix for this type... Can't do anything! :(");
return found;
}
}
// CraftBukkit end
public void doTick(BooleanSupplier booleansupplier) {
this.P = true;
super.doTick(booleansupplier);
// Akarin start
/*
if (this.getWorldData().isHardcore() && this.getDifficulty() != EnumDifficulty.HARD) {
this.getWorldData().setDifficulty(EnumDifficulty.HARD);
}
*/
// Akarin end
this.chunkProvider.getChunkGenerator().getWorldChunkManager().tick();
if (this.everyoneDeeplySleeping()) {
// Akarin start
/*
if (this.getGameRules().getBoolean("doDaylightCycle")) {
long i = this.worldData.getDayTime() + 24000L;
this.worldData.setDayTime(i - i % 24000L);
}
*/
// Akarin end
this.i();
}
//this.methodProfiler.enter(* // Akarin - remove caller
// CraftBukkit start - Only call spawner if we have players online and the world allows for mobs or animals
long time = this.worldData.getTime();
if (this.getGameRules().getBoolean("doMobSpawning") && this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES && (this.allowMonsters || this.allowAnimals) && (this instanceof WorldServer && this.players.size() > 0)) {
timings.mobSpawn.startTimingUnsafe(); // Spigot
this.spawnerCreature.a(this, this.allowMonsters && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.allowAnimals && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L), this.worldData.getTime() % 400L == 0L);
this.getChunkProvider().a(this, this.allowMonsters && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.allowAnimals && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L));
timings.mobSpawn.stopTimingUnsafe(); // Spigot
// CraftBukkit end
}
timings.doChunkUnload.startTimingUnsafe(); // Spigot // Akarin
//this.methodProfiler.exitEnter("chunkSource"); // Akarin - remove caller
this.chunkProvider.unloadChunks(booleansupplier);
int j = this.a(1.0F);
if (j != this.c()) {
this.c(j);
}
this.worldData.setTime(this.worldData.getTime() + 1L);
if (this.getGameRules().getBoolean("doDaylightCycle")) {
this.worldData.setDayTime(this.worldData.getDayTime() + 1L);
}
timings.doChunkUnload.stopTimingUnsafe(); // Spigot // Akarin
//this.methodProfiler.exitEnter("tickPending"); // Akarin - remove caller
timings.scheduledBlocks.startTimingUnsafe(); // Paper // Akarin
this.q();
timings.scheduledBlocks.stopTimingUnsafe(); // Paper // Akarin
//this.methodProfiler.exitEnter("tickBlocks"); // Akarin - remove caller
timings.chunkTicks.startTimingUnsafe(); // Paper // Akarin
this.n_();
timings.chunkTicks.stopTimingUnsafe(); // Paper // Akarin
//this.methodProfiler.exitEnter("chunkMap"); // Akarin - remove caller
timings.doChunkMap.startTimingUnsafe(); // Spigot // Akarin
this.manager.flush();
timings.doChunkMap.stopTimingUnsafe(); // Spigot // Akarin
//this.methodProfiler.exitEnter("village"); // Akarin - remove caller
timings.doVillages.startTimingUnsafe(); // Spigot // Akarin
this.villages.tick();
this.siegeManager.a();
timings.doVillages.stopTimingUnsafe(); // Spigot // Akarin
//this.methodProfiler.exitEnter("portalForcer"); // Akarin - remove caller
timings.doPortalForcer.startTimingUnsafe(); // Spigot // Akarin
this.portalTravelAgent.a(this.getTime());
timings.doPortalForcer.stopTimingUnsafe(); // Spigot // Akarin
//this.methodProfiler.exit(); // Akarin - remove caller
timings.doSounds.startTimingUnsafe(); // Spigot // Akarin
this.an();
timings.doSounds.stopTimingUnsafe(); // Spigot // Akarin
this.P = false;
timings.doChunkGC.startTimingUnsafe();// Spigot // Akarin
this.getWorld().processChunkGC(); // CraftBukkit
timings.doChunkGC.stopTimingUnsafe(); // Spigot // Akarin
}
public boolean j_() {
return this.P;
}
@Nullable public BiomeBase.BiomeMeta getBiomeMetaAt(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { return a(enumcreaturetype, blockposition); } // Akarin
@Nullable
public BiomeBase.BiomeMeta a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
List<BiomeBase.BiomeMeta> list = this.getChunkProvider().a(enumcreaturetype, blockposition);
return list.isEmpty() ? null : (BiomeBase.BiomeMeta) WeightedRandom.a(this.random, list);
}
public boolean isBiomeMetaValidAt(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) { return this.a(enumcreaturetype, biomebase_biomemeta, blockposition); } // Akarin
public boolean a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) {
List<BiomeBase.BiomeMeta> list = this.getChunkProvider().a(enumcreaturetype, blockposition);
return list != null && !list.isEmpty() ? list.contains(biomebase_biomemeta) : false;
}
public void everyoneSleeping() {
this.J = false;
if (!this.players.isEmpty()) {
int i = 0;
int j = 0;
Iterator iterator = this.players.iterator();
while (iterator.hasNext()) {
EntityHuman entityhuman = (EntityHuman) iterator.next();
if (entityhuman.isSpectator()) {
++i;
} else if (entityhuman.isSleeping() || entityhuman.fauxSleeping) {
++j;
}
}
this.J = j > 0 && j >= this.players.size() - i;
}
}
public ScoreboardServer getScoreboard() {
return this.server.getScoreboard();
}
protected void i() {
this.J = false;
List<EntityHuman> list = (List) this.players.stream().filter(EntityHuman::isSleeping).collect(Collectors.toList());
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
EntityHuman entityhuman = (EntityHuman) iterator.next();
entityhuman.a(false, false, true);
}
// Akarin start
/*
if (this.getGameRules().getBoolean("doWeatherCycle")) {
this.b();
}
*/
// Akarin end
}
public void clearWeather() { this.b(); } // Akarin - OBFHLPER
private void b() {
// CraftBukkit start
this.worldData.setStorm(false);
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
// Not that everyone ever manages to get the whole server to sleep at the same time....
if (!this.worldData.hasStorm()) {
this.worldData.setWeatherDuration(0);
}
// CraftBukkit end
this.worldData.setThundering(false);
// CraftBukkit start
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
// Not that everyone ever manages to get the whole server to sleep at the same time....
if (!this.worldData.isThundering()) {
this.worldData.setThunderDuration(0);
}
// CraftBukkit end
}
public boolean everyoneDeeplySleeping() {
if (this.J && !this.isClientSide) {
Iterator iterator = this.players.iterator();
// CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers
boolean foundActualSleepers = false;
EntityHuman entityhuman;
do {
if (!iterator.hasNext()) {
return foundActualSleepers;
}
entityhuman = (EntityHuman) iterator.next();
// CraftBukkit start
if (entityhuman.isDeeplySleeping()) {
foundActualSleepers = true;
}
} while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping);
// CraftBukkit end
return false;
} else {
return false;
}
}
public boolean isChunkLoaded(int i, int j, boolean flag) {
return this.a(i, j);
}
public boolean a(int i, int j) {
return this.getChunkProvider().isLoaded(i, j);
}
public void randomLightUpdates() { this.l(); } // Akarin - OBFHELPER
protected void l() {
//this.methodProfiler.enter(* // Akarin - remove caller
if (spigotConfig.randomLightUpdates && !this.players.isEmpty()) { // Spigot
int i = ThreadLocalRandom.current().nextInt(this.players.size()); // Akarin - ThreadLocalRandom
EntityHuman entityhuman = (EntityHuman) this.players.get(i);
if (entityhuman == null) return;
int j = MathHelper.floor(entityhuman.locX) + this.random.nextInt(11) - 5;
int k = MathHelper.floor(entityhuman.locY) + this.random.nextInt(11) - 5;
int l = MathHelper.floor(entityhuman.locZ) + this.random.nextInt(11) - 5;
this.r(new BlockPosition(j, k, l));
}
//this.methodProfiler.exit(); // Akarin - remove caller
}
protected void n_() {
//this.l(); // Akarin
if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) {
Iterator iterator = this.manager.b();
while (iterator.hasNext()) {
((Chunk) iterator.next()).d(false);
}
} else {
int i = this.getGameRules().c("randomTickSpeed");
boolean flag = this.isRaining();
boolean flag1 = this.Y();
//this.methodProfiler.enter(* // Akarin - remove caller
for (Iterator iterator1 = this.manager.b(); iterator1.hasNext(); /*this.methodProfiler.exit()*/) { // Akarin - remove caller
//this.methodProfiler.enter(* // Akarin - remove caller
Chunk chunk = (Chunk) iterator1.next();
int j = chunk.locX * 16;
int k = chunk.locZ * 16;
//this.methodProfiler.exitEnter("checkNextLight"); // Akarin - remove caller
AkarinAsyncExecutor.scheduleAsyncTask(chunk::x); // Akarin
//this.methodProfiler.exitEnter("tickChunk"); // Akarin - remove caller
chunk.d(false);
if ( !chunk.areNeighborsLoaded( 1 ) ) continue; // Spigot
//this.methodProfiler.exitEnter("thunder"); // Akarin - remove caller
int l;
BlockPosition blockposition;
if (!this.paperConfig.disableThunder && flag && flag1 && this.random.nextInt(100000) == 0) { // Paper - Disable thunder
this.m = this.m * 3 + 1013904223;
l = this.m >> 2;
blockposition = this.a(new BlockPosition(j + (l & 15), 0, k + (l >> 8 & 15)));
if (this.isRainingAt(blockposition)) {
DifficultyDamageScaler difficultydamagescaler = this.getDamageScaler(blockposition);
boolean flag2 = this.getGameRules().getBoolean("doMobSpawning") && this.random.nextDouble() < (double) difficultydamagescaler.b() * paperConfig.skeleHorseSpawnChance; // Paper
if (flag2) {
EntityHorseSkeleton entityhorseskeleton = EntityTypes.SKELETON_HORSE.create(this); // Paper
entityhorseskeleton.s(true);
entityhorseskeleton.setAgeRaw(0);
entityhorseskeleton.setPosition((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
this.addEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
}
this.strikeLightning(new EntityLightning(this, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, flag2), org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit
}
}
//this.methodProfiler.exitEnter("iceandsnow"); // Akarin - remove caller
if (!this.paperConfig.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow
this.m = this.m * 3 + 1013904223;
l = this.m >> 2;
blockposition = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, new BlockPosition(j + (l & 15), 0, k + (l >> 8 & 15)));
BlockPosition blockposition1 = blockposition.down();
BiomeBase biomebase = this.getBiome(blockposition);
if (biomebase.a((IWorldReader) this, blockposition1)) {
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.ICE.getBlockData(), null); // CraftBukkit
}
if (flag && biomebase.b(this, blockposition)) {
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.SNOW.getBlockData(), null); // CraftBukkit
}
if (flag && this.getBiome(blockposition1).c() == BiomeBase.Precipitation.RAIN) {
this.getType(blockposition1).getBlock().c(this, blockposition1);
}
}
//this.methodProfiler.exitEnter("tickBlocks"); // Akarin - remove caller
timings.chunkTicksBlocks.startTimingUnsafe(); // Paper
if (i > 0) {
ChunkSection[] achunksection = chunk.getSections();
int i1 = achunksection.length;
for (int j1 = 0; j1 < i1; ++j1) {
ChunkSection chunksection = achunksection[j1];
if (chunksection != Chunk.a && chunksection.b()) {
for (int k1 = 0; k1 < i; ++k1) {
this.m = this.m * 3 + 1013904223;
int l1 = this.m >> 2;
int i2 = l1 & 15;
int j2 = l1 >> 8 & 15;
int k2 = l1 >> 16 & 15;
IBlockData iblockdata = chunksection.getType(i2, k2, j2);
Fluid fluid = chunksection.b(i2, k2, j2);
//this.methodProfiler.enter(* // Akarin - remove caller
if (iblockdata.t()) {
iblockdata.b((World) this, new BlockPosition(i2 + j, k2 + chunksection.getYPosition(), j2 + k), this.random);
}
if (fluid.h()) {
fluid.b(this, new BlockPosition(i2 + j, k2 + chunksection.getYPosition(), j2 + k), this.random);
}
//this.methodProfiler.exit(); // Akarin - remove caller
}
}
}
}
timings.chunkTicksBlocks.stopTimingUnsafe(); // Paper
}
//this.methodProfiler.exit(); // Akarin - remove caller
}
}
protected BlockPosition a(BlockPosition blockposition) {
BlockPosition blockposition1 = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, blockposition);
AxisAlignedBB axisalignedbb = (new AxisAlignedBB(blockposition1, new BlockPosition(blockposition1.getX(), this.getHeight(), blockposition1.getZ()))).g(3.0D);
List<EntityLiving> list = this.a(EntityLiving.class, axisalignedbb, (java.util.function.Predicate<EntityLiving>) (entityliving) -> { // CraftBukkit - decompile error
return entityliving != null && entityliving.isAlive() && this.e(entityliving.getChunkCoordinates());
});
if (!list.isEmpty()) {
return ((EntityLiving) list.get(this.random.nextInt(list.size()))).getChunkCoordinates();
} else {
if (blockposition1.getY() == -1) {
blockposition1 = blockposition1.up(2);
}
return blockposition1;
}
}
public void tickEntities() {
if (false && this.players.isEmpty()) { // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
if (this.emptyTime++ >= 300) {
return;
}
} else {
this.p();
}
this.worldProvider.l();
super.tickEntities();
spigotConfig.currentPrimedTnt = 0; // Spigot
}
protected void p_() {
super.p_();
//this.methodProfiler.exitEnter("players"); // Akarin - remove caller
for (int i = 0; i < this.players.size(); ++i) {
Entity entity = (Entity) this.players.get(i);
Entity entity1 = entity.getVehicle();
if (entity1 != null) {
if (!entity1.dead && entity1.w(entity)) {
continue;
}
entity.stopRiding();
}
//this.methodProfiler.enter(* // Akarin - remove caller
if (!entity.dead) {
try {
this.g(entity);
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Ticking player");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Player being ticked");
entity.appendEntityCrashDetails(crashreportsystemdetails);
throw new ReportedException(crashreport);
}
}
//this.methodProfiler.exit(); // Akarin - remove caller
//this.methodProfiler.enter(* // Akarin - remove caller
if (entity.dead) {
int j = entity.chunkX;
int k = entity.chunkZ;
if (entity.inChunk && this.isChunkLoaded(j, k, true)) {
this.getChunkAt(j, k).b(entity);
}
this.entityList.remove(entity);
this.c(entity);
}
//this.methodProfiler.exit(); // Akarin - remove caller
}
}
public void p() {
this.emptyTime = 0;
}
public void q() {
if (this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) {
this.nextTickListBlock.a();
this.nextTickListFluid.a();
}
}
private void a(NextTickListEntry<FluidType> nextticklistentry) {
Fluid fluid = this.getFluid(nextticklistentry.a);
if (fluid.c() == nextticklistentry.a()) {
fluid.a((World) this, nextticklistentry.a);
}
}
private void b(NextTickListEntry<Block> nextticklistentry) {
IBlockData iblockdata = this.getType(nextticklistentry.a);
if (iblockdata.getBlock() == nextticklistentry.a()) {
iblockdata.a((World) this, nextticklistentry.a, this.random);
}
}
/* CraftBukkit start - We prevent spawning in general, so this butchering is not needed
public void entityJoinedWorld(Entity entity, boolean flag) {
if (!this.getSpawnAnimals() && (entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal)) {
entity.die();
}
if (!this.getSpawnNPCs() && entity instanceof NPC) {
entity.die();
}
super.entityJoinedWorld(entity, flag);
}
// CraftBukkit end */
private boolean getSpawnNPCs() {
return this.server.getSpawnNPCs();
}
private boolean getSpawnAnimals() {
return this.server.getSpawnAnimals();
}
protected IChunkProvider r() {
IChunkLoader ichunkloader = this.dataManager.createChunkLoader(this.worldProvider);
// CraftBukkit start
org.bukkit.craftbukkit.generator.InternalChunkGenerator gen;
if (this.generator != null) {
gen = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, this.getSeed(), this.generator);
} else if (this.worldProvider instanceof WorldProviderHell) {
gen = new org.bukkit.craftbukkit.generator.NetherChunkGenerator(this, this.getSeed());
} else if (this.worldProvider instanceof WorldProviderTheEnd) {
gen = new org.bukkit.craftbukkit.generator.SkyLandsChunkGenerator(this, this.getSeed());
} else {
gen = new org.bukkit.craftbukkit.generator.NormalChunkGenerator(this, this.getSeed());
}
return com.destroystokyo.paper.PaperConfig.asyncChunks ? new PaperAsyncChunkProvider(this, ichunkloader, gen, this.server) : new ChunkProviderServer(this, ichunkloader, gen, this.server); // Paper - async chunks
// CraftBukkit end
}
public boolean a(EntityHuman entityhuman, BlockPosition blockposition) {
return !this.server.a(this, blockposition, entityhuman) && this.getWorldBorder().a(blockposition);
}
public void a(WorldSettings worldsettings) {
if (!this.worldData.v()) {
try {
this.b(worldsettings);
if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) {
this.am();
}
super.a(worldsettings);
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Exception initializing level");
try {
this.a(crashreport);
} catch (Throwable throwable1) {
;
}
throw new ReportedException(crashreport);
}
this.worldData.d(true);
}
}
private void am() {
this.worldData.f(false);
this.worldData.c(true);
this.worldData.setStorm(false);
this.worldData.setThundering(false);
this.worldData.g(1000000000);
this.worldData.setDayTime(6000L);
this.worldData.setGameType(EnumGamemode.SPECTATOR);
this.worldData.g(false);
this.worldData.setDifficulty(EnumDifficulty.PEACEFUL);
this.worldData.e(true);
this.getGameRules().set("doDaylightCycle", "false", this.server);
}
private void b(WorldSettings worldsettings) {
if (!this.worldProvider.canRespawn()) {
this.worldData.setSpawn(BlockPosition.ZERO.up(this.chunkProvider.getChunkGenerator().getSpawnHeight()));
} else if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) {
this.worldData.setSpawn(BlockPosition.ZERO.up());
} else {
WorldChunkManager worldchunkmanager = this.chunkProvider.getChunkGenerator().getWorldChunkManager();
List<BiomeBase> list = worldchunkmanager.a();
Random random = new Random(this.getSeed());
BlockPosition blockposition = worldchunkmanager.a(0, 0, 256, list, random);
ChunkCoordIntPair chunkcoordintpair = blockposition == null ? new ChunkCoordIntPair(0, 0) : new ChunkCoordIntPair(blockposition);
// CraftBukkit start
if (this.generator != null) {
Random rand = new Random(this.getSeed());
org.bukkit.Location spawn = this.generator.getFixedSpawnLocation(((WorldServer) this).getWorld(), rand);
if (spawn != null) {
if (spawn.getWorld() != ((WorldServer) this).getWorld()) {
throw new IllegalStateException("Cannot set spawn point for " + this.worldData.getName() + " to be in another world (" + spawn.getWorld().getName() + ")");
} else {
this.worldData.setSpawn(new BlockPosition(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ()));
return;
}
}
}
// CraftBukkit end
if (blockposition == null) {
WorldServer.a.warn("Unable to find spawn biome");
}
boolean flag = false;
Iterator iterator = TagsBlock.VALID_SPAWN.a().iterator();
while (iterator.hasNext()) {
Block block = (Block) iterator.next();
if (worldchunkmanager.b().contains(block.getBlockData())) {
flag = true;
break;
}
}
this.worldData.setSpawn(chunkcoordintpair.h().a(8, this.chunkProvider.getChunkGenerator().getSpawnHeight(), 8));
int i = 0;
int j = 0;
int k = 0;
int l = -1;
boolean flag1 = true;
for (int i1 = 0; i1 < 1024; ++i1) {
if (i > -16 && i <= 16 && j > -16 && j <= 16) {
BlockPosition blockposition1 = this.worldProvider.a(new ChunkCoordIntPair(chunkcoordintpair.x + i, chunkcoordintpair.z + j), flag);
if (blockposition1 != null) {
this.worldData.setSpawn(blockposition1);
break;
}
}
if (i == j || i < 0 && i == -j || i > 0 && i == 1 - j) {
int j1 = k;
k = -l;
l = j1;
}
i += k;
j += l;
}
if (worldsettings.c()) {
this.s();
}
}
}
protected void s() {
WorldGenBonusChest worldgenbonuschest = new WorldGenBonusChest();
for (int i = 0; i < 10; ++i) {
int j = this.worldData.b() + this.random.nextInt(6) - this.random.nextInt(6);
int k = this.worldData.d() + this.random.nextInt(6) - this.random.nextInt(6);
BlockPosition blockposition = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, new BlockPosition(j, 0, k)).up();
if (worldgenbonuschest.a(this, this.chunkProvider.getChunkGenerator(), this.random, blockposition, WorldGenFeatureConfiguration.e)) {
break;
}
}
}
@Nullable
public BlockPosition getDimensionSpawn() {
return this.worldProvider.d();
}
public void save(boolean flag, @Nullable IProgressUpdate iprogressupdate) throws ExceptionWorldConflict {
ChunkProviderServer chunkproviderserver = this.getChunkProvider();
if (chunkproviderserver.d()) {
if (flag) org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit // Paper - Incremental Auto Saving - Only fire event on full save
timings.worldSave.startTimingUnsafe(); // Paper
if (flag || server.serverAutoSave) { // Paper
if (iprogressupdate != null) {
iprogressupdate.a(new ChatMessage("menu.savingLevel", new Object[0]));
}
this.a();
if (iprogressupdate != null) {
iprogressupdate.c(new ChatMessage("menu.savingChunks", new Object[0]));
}
} // Paper
timings.worldSaveChunks.startTimingUnsafe(); // Paper
chunkproviderserver.a(flag);
timings.worldSaveChunks.stopTimingUnsafe(); // Paper
// CraftBukkit - ArrayList -> Collection
/* //Paper start - disable vanilla chunk GC
java.util.Collection<Chunk> list = chunkproviderserver.a();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Chunk chunk = (Chunk) iterator.next();
if (chunk != null && !this.manager.a(chunk.locX, chunk.locZ)) {
chunkproviderserver.unload(chunk);
}
}*/
// Paper end
timings.worldSave.stopTimingUnsafe(); // Paper
}
}
public void flushSave() {
ChunkProviderServer chunkproviderserver = this.getChunkProvider();
if (chunkproviderserver.d()) {
chunkproviderserver.c();
}
}
protected void a() throws ExceptionWorldConflict {
timings.worldSaveLevel.startTimingUnsafe(); // Paper
this.checkSession();
Iterator iterator = this.server.getWorlds().iterator();
while (iterator.hasNext()) {
WorldServer worldserver = (WorldServer) iterator.next();
if (worldserver instanceof SecondaryWorldServer) {
((SecondaryWorldServer) worldserver).t_();
}
}
this.worldData.a(this.getWorldBorder().getSize());
this.worldData.d(this.getWorldBorder().getCenterX());
this.worldData.c(this.getWorldBorder().getCenterZ());
this.worldData.e(this.getWorldBorder().getDamageBuffer());
this.worldData.f(this.getWorldBorder().getDamageAmount());
this.worldData.h(this.getWorldBorder().getWarningDistance());
this.worldData.i(this.getWorldBorder().getWarningTime());
this.worldData.b(this.getWorldBorder().j());
this.worldData.c(this.getWorldBorder().i());
this.worldData.c(this.server.getBossBattleCustomData().c());
this.dataManager.saveWorldData(this.worldData, this.server.getPlayerList().t());
this.h().a();
timings.worldSaveLevel.stopTimingUnsafe(); // Paper
}
// CraftBukkit start
public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason
// World.addEntity(Entity) will call this, and we still want to perform
// existing entity checking when it's called with a SpawnReason
return this.j(entity) ? super.addEntity(entity, spawnReason) : false;
}
// CraftBukkit end
public void a(Stream<Entity> stream) {
stream.forEach((entity) -> {
if (this.j(entity)) {
this.entityList.add(entity);
this.b(entity);
}
});
}
private boolean j(Entity entity) {
if (entity.dead) {
// Paper start
if (DEBUG_ENTITIES) {
new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit
getAddToWorldStackTrace(entity).printStackTrace();
}
// Paper end
return false;
} else {
UUID uuid = entity.getUniqueID();
if (this.entitiesByUUID.containsKey(uuid)) {
Entity entity1 = (Entity) this.entitiesByUUID.get(uuid);
if (this.g.contains(entity1) || entity1.dead) { // Paper - if dupe is dead, overwrite
this.g.remove(entity1);
} else {
if (!(entity instanceof EntityHuman)) {
if (DEBUG_ENTITIES && entity.world.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) {
WorldServer.a.error("Keeping entity {} that already exists with UUID {}", entity1, uuid.toString()); // CraftBukkit // Paper
WorldServer.a.error("Deleting duplicate entity {}", entity); // Paper
if (entity1.addedToWorldStack != null) {
entity1.addedToWorldStack.printStackTrace();
}
getAddToWorldStackTrace(entity).printStackTrace();
}
return false;
}
WorldServer.a.warn("Force-added player with duplicate UUID {}", uuid.toString());
}
this.removeEntity(entity1);
}
return true;
}
}
protected void b(Entity entity) {
super.b(entity);
this.entitiesById.a(entity.getId(), entity);
// Paper start
if (DEBUG_ENTITIES) {
entity.addedToWorldStack = getAddToWorldStackTrace(entity);
}
Entity old = this.entitiesByUUID.put(entity.getUniqueID(), entity);
if (old != null && old.getId() != entity.getId() && old.valid && entity.world.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) {
Logger logger = LogManager.getLogger();
logger.error("Overwrote an existing entity " + old + " with " + entity);
if (DEBUG_ENTITIES) {
if (old.addedToWorldStack != null) {
old.addedToWorldStack.printStackTrace();
} else {
logger.error("Oddly, the old entity was not added to the world in the normal way. Plugins?");
}
entity.addedToWorldStack.printStackTrace();
}
}
// Paper end
Entity[] aentity = entity.bi();
if (aentity != null) {
Entity[] aentity1 = aentity;
int i = aentity.length;
for (int j = 0; j < i; ++j) {
Entity entity1 = aentity1[j];
this.entitiesById.a(entity1.getId(), entity1);
}
}
}
protected void c(Entity entity) {
if (!this.entitiesByUUID.containsKey(entity.getUniqueID()) && !entity.valid) return; // Paper - Already removed, dont fire twice - this looks like it can happen even without our changes
super.c(entity);
this.entitiesById.d(entity.getId());
this.entitiesByUUID.remove(entity.getUniqueID());
Entity[] aentity = entity.bi();
if (aentity != null) {
Entity[] aentity1 = aentity;
int i = aentity.length;
for (int j = 0; j < i; ++j) {
Entity entity1 = aentity1[j];
this.entitiesById.d(entity1.getId());
}
}
}
// CraftBukkit start
public boolean strikeLightning(Entity entity) {
return this.strikeLightning(entity, LightningStrikeEvent.Cause.UNKNOWN);
}
public boolean strikeLightning(Entity entity, LightningStrikeEvent.Cause cause) {
LightningStrikeEvent lightning = new LightningStrikeEvent(this.getWorld(), (org.bukkit.entity.LightningStrike) entity.getBukkitEntity(), cause);
this.getServer().getPluginManager().callEvent(lightning);
if (lightning.isCancelled()) {
return false;
}
// CraftBukkit end
if (super.strikeLightning(entity)) {
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, this.paperConfig.maxLightningFlashDistance, this, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension, // Paper - use world instead of dimension, limit lightning strike effect distance
return true;
} else {
return false;
}
}
public void broadcastEntityEffect(Entity entity, byte b0) {
this.getTracker().sendPacketToEntity(entity, new PacketPlayOutEntityStatus(entity, b0));
}
public ChunkProviderServer getChunkProvider() {
return (ChunkProviderServer) super.getChunkProvider();
}
public Explosion createExplosion(@Nullable Entity entity, DamageSource damagesource, double d0, double d1, double d2, float f, boolean flag, boolean flag1) {
// CraftBukkit start
Explosion explosion = super.createExplosion(entity, damagesource, d0, d1, d2, f, flag, flag1);
if (explosion.wasCanceled) {
return explosion;
}
/* Remove
Explosion explosion = new Explosion(this, entity, d0, d1, d2, f, flag, flag1);
if (damagesource != null) {
explosion.a(damagesource);
}
explosion.a();
explosion.a(false);
*/
// CraftBukkit end - TODO: Check if explosions are still properly implemented
if (!flag1) {
explosion.clearBlocks();
}
Iterator iterator = this.players.iterator();
while (iterator.hasNext()) {
EntityHuman entityhuman = (EntityHuman) iterator.next();
if (entityhuman.d(d0, d1, d2) < 4096.0D) {
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutExplosion(d0, d1, d2, f, explosion.getBlocks(), (Vec3D) explosion.c().get(entityhuman)));
}
}
return explosion;
}
public void playBlockAction(BlockPosition blockposition, Block block, int i, int j) {
this.d.add(new BlockActionData(blockposition, block, i, j));
}
private void an() {
while (!this.d.isEmpty()) {
BlockActionData blockactiondata = (BlockActionData) this.d.removeFirst();
if (this.a(blockactiondata)) {
// CraftBukkit - this.worldProvider.dimension -> this.dimension, // Paper - dimension -> world
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, (double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.b(), blockactiondata.c(), blockactiondata.d()));
}
}
}
private boolean a(BlockActionData blockactiondata) {
IBlockData iblockdata = this.getType(blockactiondata.a());
return iblockdata.getBlock() == blockactiondata.b() ? iblockdata.a(this, blockactiondata.a(), blockactiondata.c(), blockactiondata.d()) : false;
}
public void close() {
this.dataManager.a();
super.close();
}
protected void w() {
boolean flag = this.isRaining();
super.w();
/* CraftBukkit start
if (this.o != this.p) {
this.server.getPlayerList().a((Packet) (new PacketPlayOutGameStateChange(7, this.p)), this.worldProvider.getDimensionManager());
}
if (this.q != this.r) {
this.server.getPlayerList().a((Packet) (new PacketPlayOutGameStateChange(8, this.r)), this.worldProvider.getDimensionManager());
}
if (flag != this.isRaining()) {
if (flag) {
this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(2, 0.0F));
} else {
this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(1, 0.0F));
}
this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(7, this.p));
this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(8, this.r));
}
// */
if (flag != this.isRaining()) {
// Only send weather packets to those affected
for (int i = 0; i < this.players.size(); ++i) {
if (((EntityPlayer) this.players.get(i)).world == this) {
((EntityPlayer) this.players.get(i)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false);
}
}
}
for (int i = 0; i < this.players.size(); ++i) {
if (((EntityPlayer) this.players.get(i)).world == this) {
((EntityPlayer) this.players.get(i)).updateWeather(this.o, this.p, this.q, this.r);
}
}
// CraftBukkit end
}
public TickListServer<Block> getBlockTickList() {
return this.nextTickListBlock;
}
public TickListServer<FluidType> getFluidTickList() {
return this.nextTickListFluid;
}
@Nonnull
public MinecraftServer getMinecraftServer() {
return this.server;
}
public EntityTracker getTracker() {
return this.tracker;
}
public PlayerChunkMap getPlayerChunkMap() {
return this.manager;
}
public PortalTravelAgent getTravelAgent() {
return this.portalTravelAgent;
}
public DefinedStructureManager D() {
return this.dataManager.h();
}
public <T extends ParticleParam> int a(T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) {
// CraftBukkit - visibility api support
return sendParticles(null, t0, d0, d1, d2, i, d3, d4, d5, d6, false);
}
public <T extends ParticleParam> int sendParticles(EntityPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) {
// Paper start - Particle API Expansion
return sendParticles(players, sender, t0, d0, d1, d2, i, d3, d4, d5, d6, force);
}
public <T extends ParticleParam> int sendParticles(List<EntityHuman> receivers, EntityPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) {
// Paper end
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(t0, force, (float) d0, (float) d1, (float) d2, (float) d3, (float) d4, (float) d5, (float) d6, i);
// CraftBukkit end
int j = 0;
for (EntityHuman entityhuman : receivers) { // Paper - Particle API Expansion
EntityPlayer entityplayer = (EntityPlayer) entityhuman; // Paper - Particle API Expansion
if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit
if (this.a(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit
++j;
}
}
return j;
}
public <T extends ParticleParam> boolean a(EntityPlayer entityplayer, T t0, boolean flag, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) {
Packet<?> packet = new PacketPlayOutWorldParticles(t0, flag, (float) d0, (float) d1, (float) d2, (float) d3, (float) d4, (float) d5, (float) d6, i);
return this.a(entityplayer, flag, d0, d1, d2, packet);
}
private boolean a(EntityPlayer entityplayer, boolean flag, double d0, double d1, double d2, Packet<?> packet) {
if (entityplayer.getWorldServer() != this) {
return false;
} else {
BlockPosition blockposition = entityplayer.getChunkCoordinates();
double d3 = blockposition.distanceSquared(d0, d1, d2);
if (d3 > 1024.0D && (!flag || d3 > 262144.0D)) {
return false;
} else {
entityplayer.playerConnection.sendPacket(packet);
return true;
}
}
}
@Nullable
public Entity getEntity(UUID uuid) {
return (Entity) this.entitiesByUUID.get(uuid);
}
public ListenableFuture<Object> postToMainThread(Runnable runnable) {
return this.server.postToMainThread(runnable);
}
// Akarin start
@Override
public void ensuresMainThread(Runnable runnable) {
this.server.f.offer(ListenableFutureTask.create(runnable, null));
}
// Akarin end
public boolean isMainThread() {
return this.server.isMainThread();
}
@Nullable
public BlockPosition a(String s, BlockPosition blockposition, int i, boolean flag) {
return this.getChunkProvider().a(this, s, blockposition, i, flag);
}
public CraftingManager getCraftingManager() {
return this.server.getCraftingManager();
}
public TagRegistry F() {
return this.server.getTagRegistry();
}
}