2900 lines
93 KiB
Java
Executable file
2900 lines
93 KiB
Java
Executable file
package server.world;
|
|
|
|
import java.io.File;
|
|
import java.io.FileFilter;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
import java.util.TreeSet;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.function.Predicate;
|
|
|
|
import common.biome.Biome;
|
|
import common.block.Block;
|
|
import common.block.BlockFalling;
|
|
import common.block.Material;
|
|
import common.block.artificial.BlockDoor;
|
|
import common.block.liquid.BlockLiquid;
|
|
import common.block.natural.BlockSnow;
|
|
import common.collect.Lists;
|
|
import common.collect.Maps;
|
|
import common.collect.Sets;
|
|
import common.dimension.Dimension;
|
|
import common.dimension.Lake;
|
|
import common.dimension.Liquid;
|
|
import common.dimension.Ore;
|
|
import common.entity.DamageSource;
|
|
import common.entity.Entity;
|
|
import common.entity.EntityTrackerEntry;
|
|
import common.entity.effect.EntityLightning;
|
|
import common.entity.npc.EntityNPC;
|
|
import common.entity.types.EntityLiving;
|
|
import common.init.Blocks;
|
|
import common.init.SoundEvent;
|
|
import common.init.UniverseRegistry;
|
|
import common.item.ItemDoor;
|
|
import common.log.Log;
|
|
import common.model.ParticleType;
|
|
import common.network.IPlayer;
|
|
import common.network.Packet;
|
|
import common.packet.SPacketEntityStatus;
|
|
import common.packet.SPacketExplosion;
|
|
import common.packet.SPacketEffect;
|
|
import common.packet.SPacketSoundEffect;
|
|
import common.packet.SPacketParticles;
|
|
import common.packet.SPacketChangeGameState;
|
|
import common.packet.SPacketSpawnGlobalEntity;
|
|
import common.packet.SPacketBiome;
|
|
import common.packet.SPacketBlockAction;
|
|
import common.packet.SPacketBlockBreakAnim;
|
|
import common.packet.SPacketBlockChange;
|
|
import common.packet.SPacketMultiBlockChange;
|
|
import common.rng.Random;
|
|
import common.rng.WeightedList;
|
|
import common.tags.TagObject;
|
|
import common.tileentity.TileEntity;
|
|
import common.util.BlockPos;
|
|
import common.util.BoundingBox;
|
|
import common.util.ChunkPos;
|
|
import common.util.ExtMath;
|
|
import common.util.IntHashMap;
|
|
import common.util.LongHashMap;
|
|
import common.util.PortalType;
|
|
import common.util.Position;
|
|
import common.util.Vec3;
|
|
import common.village.Village;
|
|
import common.world.BlockArray;
|
|
import common.world.Explosion;
|
|
import common.world.AWorldServer;
|
|
import common.world.LightType;
|
|
import common.world.State;
|
|
import common.world.Weather;
|
|
import common.world.World;
|
|
import server.Server;
|
|
import server.biome.GenBiome;
|
|
import server.biome.RngSpawn;
|
|
import server.clipboard.ClipboardBlock;
|
|
import server.network.Player;
|
|
import server.util.ServerConfig;
|
|
import server.village.VillageCollection;
|
|
import server.worldgen.BiomeGenLayered;
|
|
import server.worldgen.BiomeGenPerlin;
|
|
import server.worldgen.BiomeGenSingle;
|
|
import server.worldgen.BiomeGenerator;
|
|
import server.worldgen.BlockReplacer;
|
|
import server.worldgen.ChunkGenerator;
|
|
import server.worldgen.ChunkPrimer;
|
|
import server.worldgen.FeatureDungeons;
|
|
import server.worldgen.FeatureLakes;
|
|
import server.worldgen.FeatureLiquids;
|
|
import server.worldgen.FeatureOres;
|
|
import server.worldgen.GeneratorCavern;
|
|
import server.worldgen.GeneratorDestroyed;
|
|
import server.worldgen.GeneratorFlat;
|
|
import server.worldgen.GeneratorIsland;
|
|
import server.worldgen.GeneratorPerlin;
|
|
import server.worldgen.GeneratorSimple;
|
|
import server.worldgen.MobConstants;
|
|
import server.worldgen.ReplacerAltBiome;
|
|
import server.worldgen.ReplacerAltSurface;
|
|
import server.worldgen.ReplacerBiome;
|
|
import server.worldgen.ReplacerTopLayer;
|
|
import server.worldgen.caves.MapGenBigCaves;
|
|
import server.worldgen.caves.MapGenCaves;
|
|
import server.worldgen.caves.MapGenRavine;
|
|
import server.worldgen.structure.MapGenBridge;
|
|
import server.worldgen.structure.MapGenMineshaft;
|
|
import server.worldgen.structure.MapGenScatteredFeature;
|
|
import server.worldgen.structure.MapGenStronghold;
|
|
import server.worldgen.structure.MapGenVillage;
|
|
|
|
public final class WorldServer extends AWorldServer {
|
|
private static final int[][] XZ_DIRS = new int[][] {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
|
|
|
|
private final Server server;
|
|
private final File chunkDir;
|
|
private final Random grng;
|
|
private final Set<NextTickListEntry> ticks = Sets.<NextTickListEntry>newHashSet();
|
|
private final TreeSet<NextTickListEntry> ticksNext = new TreeSet();
|
|
private final EventList[] queue = new EventList[] {new EventList(), new EventList()};
|
|
private final List<NextTickListEntry> ticksNow = Lists.<NextTickListEntry>newArrayList();
|
|
private final Set<Long> dropped = Collections.<Long>newSetFromMap(new ConcurrentHashMap());
|
|
private final LongHashMap<ChunkServer> chunks = new LongHashMap();
|
|
private final List<ChunkServer> loaded = Lists.<ChunkServer>newArrayList();
|
|
private final Map<ChunkPos, TagObject> toRemove = new ConcurrentHashMap();
|
|
private final Set<ChunkPos> pending = Collections.<ChunkPos>newSetFromMap(new ConcurrentHashMap());
|
|
private final LongHashMap<BlockPos> loaders = new LongHashMap();
|
|
private final Set<BlockPos> loaderList = Sets.<BlockPos>newHashSet();
|
|
private final List<EntityNPC> managed = Lists.<EntityNPC>newArrayList();
|
|
private final LongHashMap<PlayerInstance> instances = new LongHashMap();
|
|
private final List<PlayerInstance> toUpdate = Lists.<PlayerInstance>newArrayList();
|
|
private final List<PlayerInstance> instList = Lists.<PlayerInstance>newArrayList();
|
|
private final Set<EntityTrackerEntry> tracked = Sets.<EntityTrackerEntry>newHashSet();
|
|
private final IntHashMap<EntityTrackerEntry> trackMap = new IntHashMap();
|
|
private final Map<String, WorldSavedData> dataMap = Maps.<String, WorldSavedData>newHashMap();
|
|
private final List<WorldSavedData> dataList = Lists.<WorldSavedData>newArrayList();
|
|
private final List<BlockArray> toTick = Lists.newArrayList();
|
|
private final Biome[] biomes = new Biome[256];
|
|
|
|
private MapGenCaves caveGen;
|
|
private MapGenBigCaves bigCaveGen;
|
|
private MapGenRavine ravineGen;
|
|
private MapGenStronghold strongholdGen;
|
|
private MapGenVillage villageGen;
|
|
private VillageCollection villageStorage;
|
|
private MapGenMineshaft mineshaftGen;
|
|
private MapGenScatteredFeature scatteredGen;
|
|
private MapGenBridge bridgeGen;
|
|
private ChunkGenerator generator;
|
|
private BiomeGenerator biomeGen;
|
|
private BlockReplacer replacer;
|
|
private FeatureDungeons dungeons;
|
|
private State liquid;
|
|
private boolean base;
|
|
private boolean ceil;
|
|
private FeatureOres[] ores;
|
|
private FeatureLakes[] lakes;
|
|
private FeatureLiquids[] liquids;
|
|
private long seed;
|
|
private int height;
|
|
private int seaLevel;
|
|
private boolean mobs;
|
|
private boolean snow;
|
|
private boolean populate;
|
|
|
|
private boolean updateForced;
|
|
// private boolean resetWeather;
|
|
private boolean loadersModified;
|
|
// private boolean warpsModified;
|
|
private boolean exterminated;
|
|
private int emptyTicks;
|
|
private int blockEvtIdx;
|
|
private int trackDistance;
|
|
private int viewRadius;
|
|
private int updateLCG = this.rand.intv();
|
|
private long prevUpdate;
|
|
private long time;
|
|
|
|
public static float clampGravity() {
|
|
return ExtMath.clampf(ServerConfig.gravity, -10.0f, 10.0f);
|
|
}
|
|
|
|
private BiomeGenerator createBiomeGenerator(Random rand) {
|
|
return this.dimension.getBiomeSize() > 0 ? new BiomeGenLayered(rand.longv(), this.dimension.getDefaultBiome(), this.dimension.isSemiFixed(), this.dimension.getBiomeSize(), this.dimension.getRiverSize(),
|
|
this.dimension.getSnowRarity(), this.dimension.getSeaRarity(), this.dimension.getAddBiomes() == null ? new Biome[0] : this.dimension.getAddBiomes(), this.dimension.getAddRarity(),
|
|
this.dimension.getHotBiomes() == null ? new Biome[] {this.dimension.getDefaultBiome()} : this.dimension.getHotBiomes(),
|
|
this.dimension.getMediumBiomes() == null ? new Biome[] {this.dimension.getDefaultBiome()} : this.dimension.getMediumBiomes(),
|
|
this.dimension.getColdBiomes() == null ? new Biome[] {this.dimension.getDefaultBiome()} : this.dimension.getColdBiomes(),
|
|
this.dimension.getFrostBiomes() == null ? new Biome[] {this.dimension.getDefaultBiome()} : this.dimension.getFrostBiomes()) : new BiomeGenSingle(this.dimension.getDefaultBiome());
|
|
}
|
|
|
|
private ChunkGenerator createChunkGenerator(Random rand) {
|
|
switch(this.dimension.getGeneratorType()) {
|
|
case FLAT:
|
|
return this.dimension.getLayers() == null ? new GeneratorFlat(this.dimension.getSeaLevel(), this.dimension.getFiller()) : new GeneratorFlat(this.dimension.getLayers());
|
|
case PERLIN:
|
|
default:
|
|
return new GeneratorPerlin(rand, this.dimension.getFiller(), this.dimension.getLiquid(), this.dimension.getNoiseGen());
|
|
case SIMPLE:
|
|
return new GeneratorSimple(rand, this.dimension.getFiller(), this.dimension.getLiquid(),
|
|
this.dimension.getBiomeSize() > 0 ? null : new BiomeGenPerlin(rand.longv()));
|
|
case ISLAND:
|
|
return new GeneratorIsland(rand, this.dimension.getFiller());
|
|
case CAVERN:
|
|
return new GeneratorCavern(rand, this.dimension.getFiller(), this.dimension.getLiquid());
|
|
case DESTROYED:
|
|
return new GeneratorDestroyed(this.dimension.getSeaLevel());
|
|
}
|
|
}
|
|
|
|
private BlockReplacer createBlockReplacer(Random rand) {
|
|
switch(this.dimension.getReplacerType()) {
|
|
case BIOMES:
|
|
default:
|
|
return new ReplacerBiome(rand);
|
|
case SIMPLE:
|
|
return new ReplacerAltBiome(rand, this.dimension.getFiller(), this.dimension.getLiquid(), this.dimension.getAlt2(), this.dimension.getAlt1());
|
|
case ALTERNATE:
|
|
return new ReplacerAltSurface(rand, this.dimension.getFiller(), this.dimension.getAlt1(), this.dimension.getAlt2(), this.dimension.getLiquid());
|
|
case TOPLAYER:
|
|
return new ReplacerTopLayer(this.dimension.getSurface(), this.dimension.getFiller().getBlock());
|
|
case NONE:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private FeatureDungeons createDungeonGenerator() {
|
|
return this.dimension.getDungeons() > 0 ? new FeatureDungeons(this.dimension.getDungeons()) : null;
|
|
}
|
|
|
|
private MapGenCaves createCaveGenerator() {
|
|
return this.dimension.hasCaves() ?
|
|
(new MapGenCaves(this.dimension.getCaveFiller(), this.dimension.getFiller().getBlock(), this.dimension.getTop().getBlock(),
|
|
this.dimension.getSurface().getBlock(), this.dimension.getAlt1().getBlock())) : null;
|
|
}
|
|
|
|
private MapGenRavine createRavineGenerator() {
|
|
return this.dimension.hasRavines() ?
|
|
(new MapGenRavine(this.dimension.getCaveFiller(), this.dimension.getFiller().getBlock(),
|
|
this.dimension.getTop().getBlock(), this.dimension.getSurface().getBlock())) : null;
|
|
}
|
|
|
|
private MapGenBigCaves createBigCaveGenerator() {
|
|
return this.dimension.hasStrideCaves() ?
|
|
(new MapGenBigCaves(this.dimension.getFiller().getBlock(),
|
|
this.dimension.getTop().getBlock(), this.dimension.getSurface().getBlock())) : null;
|
|
}
|
|
|
|
private FeatureOres[] createOres() {
|
|
if(this.dimension.getOres().isEmpty())
|
|
return null;
|
|
FeatureOres[] gens = new FeatureOres[this.dimension.getOres().size()];
|
|
for(int z = 0; z < gens.length; z++) {
|
|
Ore gen = this.dimension.getOres().get(z);
|
|
gens[z] = new FeatureOres(gen.state(), gen.count(), gen.more(), gen.size(), gen.min(), gen.max(), gen.dist());
|
|
}
|
|
return gens;
|
|
}
|
|
|
|
private FeatureLakes[] createLakes() {
|
|
if(this.dimension.getLakes().isEmpty())
|
|
return null;
|
|
FeatureLakes[] gens = new FeatureLakes[this.dimension.getLakes().size()];
|
|
for(int z = 0; z < gens.length; z++) {
|
|
Lake gen = this.dimension.getLakes().get(z);
|
|
gens[z] = new FeatureLakes(gen.state(), gen.filler(), gen.top(), gen.chance(), gen.minHeight(), gen.maxHeight(), gen.ratiod());
|
|
}
|
|
return gens;
|
|
}
|
|
|
|
private FeatureLiquids[] createLiquids() {
|
|
if(this.dimension.getLiquids().isEmpty())
|
|
return null;
|
|
FeatureLiquids[] gens = new FeatureLiquids[this.dimension.getLiquids().size()];
|
|
for(int z = 0; z < gens.length; z++) {
|
|
Liquid gen = this.dimension.getLiquids().get(z);
|
|
gens[z] = new FeatureLiquids(gen.state(), gen.chance(), gen.minHeight(), gen.maxHeight(), gen.lower());
|
|
}
|
|
return gens;
|
|
}
|
|
|
|
public WorldServer(Server server, long dtime, Dimension dim) {
|
|
super(dim);
|
|
this.server = server;
|
|
// this.time = time;
|
|
this.daytime = dtime;
|
|
this.updateViewRadius();
|
|
this.chunkDir = new File(new File("chunk"), dim.getDimensionName());
|
|
this.chunkDir.mkdirs();
|
|
if(!ServerConfig.seed.isEmpty())
|
|
this.rand.setSeed((long)ServerConfig.seed.hashCode() ^ ~((long)dim.getDimensionName().hashCode()));
|
|
this.seed = this.rand.longv();
|
|
this.dimension.setSeed(this.seed);
|
|
TagObject tag = null;
|
|
try {
|
|
File dat = new File(this.chunkDir, "data.cdt");
|
|
if(dat.exists() && dat.isFile())
|
|
tag = TagObject.readGZip(dat);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Weltdaten nicht laden");
|
|
}
|
|
if(tag != null) {
|
|
this.exterminated = tag.getBool("Exterminated");
|
|
this.time = tag.getLong("Time");
|
|
if(tag.hasObject("Generator")) {
|
|
this.dimension.fromTags(tag.getObject("Generator"));
|
|
if(this.dimension.getType().weather && !this.exterminated)
|
|
this.weather = this.dimension.getWeather();
|
|
this.seed = this.dimension.getSeed();
|
|
}
|
|
if(this.dimension.getType().weather && !this.exterminated)
|
|
this.weather = Weather.getByName(tag.getString("Weather"));
|
|
if(this.weather == null) {
|
|
this.weather = this.dimension.getWeather();
|
|
// this.dataModified = true;
|
|
}
|
|
// ...
|
|
}
|
|
else {
|
|
Log.TICK.info("Startwert für %s: %d" + (ServerConfig.seed.isEmpty() ? "" : " von Basiswert '%s'"), this.dimension.getFormattedName(false), this.seed, ServerConfig.seed);
|
|
}
|
|
if(this.exterminated)
|
|
this.weather = Weather.CLEAR;
|
|
this.grng = new Random(this.seed);
|
|
// GeneratorSettings settings = !debug && !this.exterminated ? dim.getSettings() : null;
|
|
if(this.exterminated) {
|
|
this.setExterminatedGen();
|
|
}
|
|
// else if(settings != null) {
|
|
//// settings = settings == null ? new GeneratorSettings() : settings;
|
|
// this.liquid = settings.useLavaSeas ? Blocks.lava.getDefaultState() : Blocks.water.getDefaultState();
|
|
// this.biomeGen = new BiomeGenNew(this.seed, settings);
|
|
// switch(settings.genMode) {
|
|
// case 0:
|
|
// this.generator = new GeneratorFlat(settings);
|
|
// break;
|
|
// case 1:
|
|
// default:
|
|
// this.generator = new GeneratorNew(this.grng, settings);
|
|
// break;
|
|
// case 2:
|
|
// this.generator = new GeneratorSimple(this.seed, this.grng, settings);
|
|
// break;
|
|
// }
|
|
// switch(settings.replMode) {
|
|
// case 0:
|
|
// this.replacer = null;
|
|
// break;
|
|
// case 1:
|
|
// default:
|
|
// this.replacer = new ReplacerBiome(this.grng);
|
|
// break;
|
|
// case 2:
|
|
// this.replacer = new ReplacerAltBiome(this.grng, settings);
|
|
// break;
|
|
// }
|
|
// this.populator = settings.populate == 0 ? null :
|
|
// (settings.populate == 2 ? new PopulatorSimple(this.grng, settings) : new PopulatorNew(this.grng, settings));
|
|
// this.caveGen = settings.useCaves ?
|
|
// (new MapGenCaves(settings.caveFill == 1 ? Blocks.lava.getDefaultState() :
|
|
// (settings.caveFill == 2 ? Blocks.water.getDefaultState() : Blocks.air.getDefaultState()), Blocks.stone,
|
|
// Blocks.dirt, Blocks.grass, Blocks.gravel)) : null;
|
|
// this.bigCaveGen = null;
|
|
// this.ravineGen = settings.useRavines ?
|
|
// (new MapGenRavine(settings.ravineFill == 1 ? Blocks.flowing_lava.getDefaultState() : (settings.ravineFill == 2
|
|
// ? Blocks.flowing_water.getDefaultState() : Blocks.air.getDefaultState()), Blocks.stone, Blocks.dirt, Blocks.grass)) : null;
|
|
// this.base = settings.useBedrock ? Blocks.bedrock.getDefaultState() : null;
|
|
// this.ceil = null;
|
|
// this.mobs = settings.mobs;
|
|
// this.snow = settings.snow;
|
|
// this.strongholdGen = settings.useStrongholds ? new MapGenStronghold() : null;
|
|
// this.villageGen = settings.useVillages ? new MapGenVillage() : null;
|
|
// this.mineshaftGen = settings.useMineShafts ? new MapGenMineshaft() : null;
|
|
// this.scatteredGen = settings.useScattered ? new MapGenScatteredFeature() : null;
|
|
// this.bridgeGen = null;
|
|
// this.seaLevel = settings.genMode == 0 ? this.generator.getMaximumHeight() : settings.seaLevel;
|
|
// this.ores = new FeatureOres[settings.oreList.size()];
|
|
// for(int z = 0; z < settings.oreList.size(); z++) {
|
|
// this.ores[z] = settings.oreList.get(z).createGenerator();
|
|
// }
|
|
//// PlanetTerra.setOres(settings.oreList);
|
|
// }
|
|
else {
|
|
this.liquid = this.dimension.getLiquid();
|
|
this.biomeGen = this.createBiomeGenerator(this.grng);
|
|
this.generator = this.createChunkGenerator(this.grng);
|
|
this.replacer = this.createBlockReplacer(this.grng);
|
|
this.populate = this.dimension.hasPopulator();
|
|
this.caveGen = this.createCaveGenerator();
|
|
this.bigCaveGen = this.createBigCaveGenerator();
|
|
this.ravineGen = this.createRavineGenerator();
|
|
this.base = this.dimension.getFiller().getBlock() != Blocks.air;
|
|
this.ceil = this.dimension.hasWorldCeiling();
|
|
this.mobs = this.dimension.hasMobs();
|
|
this.snow = this.dimension.hasSnow();
|
|
this.strongholdGen = this.dimension.hasStrongholds() ? new MapGenStronghold() : null;
|
|
this.villageGen = this.dimension.hasVillages() ? new MapGenVillage() : null;
|
|
this.mineshaftGen = this.dimension.hasMineshafts() ? new MapGenMineshaft() : null;
|
|
this.scatteredGen = this.dimension.hasScattered() ? new MapGenScatteredFeature() : null;
|
|
this.bridgeGen = this.dimension.hasFortresses() ? new MapGenBridge() : null;
|
|
this.seaLevel = this.dimension.getSeaLevel();
|
|
this.ores = this.createOres();
|
|
this.lakes = this.createLakes();
|
|
this.liquids = this.createLiquids();
|
|
this.dungeons = this.createDungeonGenerator();
|
|
}
|
|
this.height = this.generator.getMaximumHeight();
|
|
// this.teleporter = new Teleporter(this);
|
|
this.calculateInitialSkylight();
|
|
this.calculateInitialWeather();
|
|
this.updatePhysics();
|
|
tag = null;
|
|
try {
|
|
File dat = new File(this.chunkDir, "loaders.cdt");
|
|
if(dat.exists() && dat.isFile())
|
|
tag = TagObject.readGZip(dat);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Ladeliste nicht laden");
|
|
}
|
|
if(tag != null && tag.hasList("Loaders")) {
|
|
List<TagObject> list = tag.getList("Loaders");
|
|
for(int z = 0; z < list.size(); z++) {
|
|
TagObject pos = list.get(z);
|
|
this.addLoader(new BlockPos(pos.getInt("X"), pos.getInt("Y"), pos.getInt("Z")));
|
|
}
|
|
this.loadersModified = false;
|
|
}
|
|
if(this.villageGen != null) {
|
|
tag = null;
|
|
try {
|
|
File dat = new File(this.chunkDir, "villages.cdt");
|
|
if(dat.exists() && dat.isFile())
|
|
tag = TagObject.readGZip(dat);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Dorfliste nicht laden");
|
|
}
|
|
this.villageStorage = new VillageCollection(tag);
|
|
}
|
|
}
|
|
|
|
public Server getServer() {
|
|
return this.server;
|
|
}
|
|
|
|
public void updatePhysics() {
|
|
this.setTimeFactor(ServerConfig.timeFlow);
|
|
this.setGravity(clampGravity());
|
|
}
|
|
|
|
public void tick() {
|
|
this.updateWeather(false);
|
|
this.biomeGen.cleanupCache();
|
|
// this.profiler.start("mobSpawner");
|
|
if(this.mobs && ServerConfig.mobs && ServerConfig.tickSpawn) {
|
|
Spawner.spawn(this);
|
|
}
|
|
// this.profiler.next("chunkSource");
|
|
for(int i = 0; i < 100; ++i) {
|
|
if(!this.dropped.isEmpty()) {
|
|
Long v = (Long)this.dropped.iterator().next();
|
|
ChunkServer chunk = this.chunks.getValueByKey(v.longValue());
|
|
if(chunk != null) {
|
|
chunk.onChunkUnload();
|
|
this.saveChunkData(chunk);
|
|
this.chunks.remove(v.longValue());
|
|
this.loaded.remove(chunk);
|
|
}
|
|
this.dropped.remove(v);
|
|
}
|
|
}
|
|
int light = this.calculateSkylightSubtracted(true);
|
|
if(light != this.getSkylightSubtracted())
|
|
this.setSkylightSubtracted(light);
|
|
// if(this.primary)
|
|
// this.info.tick();
|
|
this.time += 1L;
|
|
// this.dataModified = true;
|
|
if(ServerConfig.dayCycle) // {
|
|
this.daytime += this.timeFactor;
|
|
// if(this.dimension.getType().dayCycle)
|
|
// this.season = this.getSeasonByTime();
|
|
// }
|
|
// this.profiler.next("tickPending");
|
|
this.tickUpdates(false);
|
|
// this.profiler.next("tickBlocks");
|
|
this.updateBlocks();
|
|
// this.profiler.next("chunkMap");
|
|
this.updatePlayerInstances();
|
|
if(this.villageStorage != null)
|
|
// this.profiler.next("village");
|
|
this.villageStorage.tick(this);
|
|
//// this.server.getVillageSiege().tick();
|
|
// }
|
|
// this.profiler.next("portalForcer");
|
|
// this.teleporter.removeStalePortalLocations(this.time);
|
|
// this.profiler.end();
|
|
this.sendQueuedBlockEvents();
|
|
}
|
|
|
|
// public int getNextMapId() {
|
|
// return this.debug ? 0 : this.server.getMapId();
|
|
// }
|
|
|
|
public Random getGenRandom() {
|
|
return this.grng;
|
|
}
|
|
|
|
public State getSurfaceLiquid() {
|
|
return this.liquid;
|
|
}
|
|
|
|
public FeatureOres[] getOres() {
|
|
return this.ores;
|
|
}
|
|
|
|
public boolean addLoader(BlockPos pos) {
|
|
long chunk = LongHashMap.packInt(pos.getX() / 16, pos.getZ() / 16);
|
|
if(this.loaders.containsItem(chunk))
|
|
return false;
|
|
this.loaders.add(chunk, pos);
|
|
this.loaderList.add(pos);
|
|
this.loadersModified = true;
|
|
return true;
|
|
}
|
|
|
|
// public void setWarpsDirty() {
|
|
// this.warpsModified = true;
|
|
// }
|
|
|
|
public boolean removeLoader(BlockPos pos) {
|
|
long chunk = LongHashMap.packInt(pos.getX() / 16, pos.getZ() / 16);
|
|
BlockPos loader = this.loaders.getValueByKey(chunk);
|
|
if(!pos.equals(loader))
|
|
return false;
|
|
this.loaders.remove(chunk);
|
|
this.loaderList.remove(pos);
|
|
this.loadersModified = true;
|
|
return true;
|
|
}
|
|
|
|
private WeightedList<RngSpawn> getSpawnTypes(BlockPos pos) {
|
|
Biome biome = this.getBiomeGenForCoords(pos);
|
|
if(this.bridgeGen != null && (this.bridgeGen.isPresent(pos)
|
|
|| (this.bridgeGen.isPositionInStructure(this, pos) && this.getState(pos.down()).getBlock() == Blocks.blood_brick)))
|
|
return MobConstants.FORTRESS_MOBS;
|
|
else if(this.scatteredGen != null && this.scatteredGen.hasMageHut(pos))
|
|
return MobConstants.MAGEHUT_MOBS;
|
|
return GenBiome.BIOMES[biome.id].getMobs();
|
|
}
|
|
|
|
public RngSpawn getSpawnListEntryForTypeAt(BlockPos pos) {
|
|
WeightedList<RngSpawn> list = this.getSpawnTypes(pos);
|
|
return list != null && !list.isEmpty() ? (RngSpawn)list.pick(this.rand) : null;
|
|
}
|
|
|
|
public boolean canCreatureTypeSpawnHere(RngSpawn spawnListEntry, BlockPos pos) {
|
|
WeightedList<RngSpawn> list = this.getSpawnTypes(pos);
|
|
return list != null && !list.isEmpty() ? list.contains(spawnListEntry) : false;
|
|
}
|
|
|
|
// public BlockPos getStrongholdPos(BlockPos pos) {
|
|
// return this.strongholdGen != null ? this.strongholdGen.getClosestStrongholdPos(this, pos) : null;
|
|
// }
|
|
|
|
public BiomeGenerator getBiomeGenerator() {
|
|
return this.biomeGen;
|
|
}
|
|
|
|
public Biome getBiomeGenForCoords(final BlockPos pos) {
|
|
if(this.isBlockLoaded(pos))
|
|
return this.getChunk(pos).getBiome(pos, this.biomeGen);
|
|
else
|
|
return this.biomeGen.getBiomeGenerator(pos, Biome.DEF_BIOME);
|
|
}
|
|
|
|
public void setItemData(String dataID, WorldSavedData worldSavedDataIn) {
|
|
this.setData(dataID, worldSavedDataIn);
|
|
}
|
|
|
|
public WorldSavedData loadItemData(String dataID) {
|
|
return this.loadData(dataID);
|
|
}
|
|
|
|
protected void updateBlocks() {
|
|
this.setActivePlayerChunksAndCheckLight(ServerConfig.distance);
|
|
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
for(ChunkPos chunkcoordintpair : this.active) {
|
|
int k = chunkcoordintpair.x * 16;
|
|
int l = chunkcoordintpair.z * 16;
|
|
// this.profiler.start("getChunk");
|
|
ChunkServer chunk = this.getChunk(chunkcoordintpair.x, chunkcoordintpair.z);
|
|
// this.profiler.next("moodSound");
|
|
// this.playMoodSound(k, l, chunk);
|
|
// this.profiler.next("checkLight");
|
|
chunk.enqueueRelight();
|
|
// this.profiler.next("tickChunk");
|
|
chunk.update(false);
|
|
// this.profiler.next("thunder");
|
|
int l2 = ServerConfig.boltChance;
|
|
|
|
if(l2 > 0 && this.rand.zrange(l2) == 0 && this.isThundering()) {
|
|
this.updateLCG = this.updateLCG * 3 + 1013904223;
|
|
int i1 = this.updateLCG >> 2;
|
|
BlockPos blockpos = this.adjustPosToNearbyEntity(new BlockPos(k + (i1 & 15), 0, l + (i1 >> 8 & 15)));
|
|
|
|
if(this.canStrikeAt(blockpos)) {
|
|
this.strikeLightning((double)blockpos.getX(), (double)blockpos.getY(), (double)blockpos.getZ(), 0x737380, 120, true, null);
|
|
}
|
|
}
|
|
|
|
// this.profiler.next("iceandsnow");
|
|
l2 = ServerConfig.weatherTick;
|
|
|
|
for(int z = 0; z < l2; z++) {
|
|
if(this.rand.zrange(16) == 0) {
|
|
this.updateLCG = this.updateLCG * 3 + 1013904223;
|
|
int k2 = this.updateLCG >> 2;
|
|
BlockPos blockpos2 = this.getPrecipitationHeight(new BlockPos(k + (k2 & 15), 0, l + (k2 >> 8 & 15)));
|
|
BlockPos blockpos1 = blockpos2.down();
|
|
|
|
if(this.canBlockFreeze(blockpos1, true)) {
|
|
this.setState(blockpos1, Blocks.ice.getState());
|
|
}
|
|
|
|
if(this.snow && this.isRaining() && this.canSnowAt(blockpos2, true, ServerConfig.snowStack)) {
|
|
State layer = ServerConfig.snowStack ? this.getState(blockpos2) : null;
|
|
this.setState(blockpos2, ServerConfig.snowStack && layer.getBlock() == Blocks.snow_layer
|
|
? (Blocks.snow_layer.getState().withProperty(BlockSnow.LAYERS,
|
|
Math.min(layer.getValue(BlockSnow.LAYERS) + 1, 2))) : Blocks.snow_layer.getState());
|
|
}
|
|
|
|
if(this.isRaining()) { // && this.getBiomeGenForCoords(blockpos1).canRain()) {
|
|
this.getState(blockpos1).getBlock().fillWithRain(this, blockpos1);
|
|
}
|
|
|
|
if(ServerConfig.igniteChance > 0 && ServerConfig.fire && !this.isRaining() &&
|
|
this.rand.chance(this.hasDownfall() ? Math.max(1, ServerConfig.igniteChance / 3) : ServerConfig.igniteChance)
|
|
&& this.canPlaceFireAt(blockpos2)) {
|
|
this.setState(blockpos2, Blocks.fire.getState());
|
|
}
|
|
}
|
|
}
|
|
|
|
// this.profiler.next("tickBlocks");
|
|
l2 = ServerConfig.randomTick;
|
|
|
|
if(l2 > 0) {
|
|
this.toTick.addAll(chunk.getStorage());
|
|
for(BlockArray extendedblockstorage : this.toTick) {
|
|
if(extendedblockstorage != null && extendedblockstorage.isTicked()) {
|
|
for(int j1 = 0; j1 < l2; ++j1) {
|
|
this.updateLCG = this.updateLCG * 3 + 1013904223;
|
|
int k1 = this.updateLCG >> 2;
|
|
int l1 = k1 & 15;
|
|
int i2 = k1 >> 8 & 15;
|
|
int j2 = k1 >> 16 & 15;
|
|
++j;
|
|
State iblockstate = extendedblockstorage.get(l1, j2, i2);
|
|
Block block = iblockstate.getBlock();
|
|
|
|
if(block.getTickRandomly()) {
|
|
++i;
|
|
block.randomTick(this, new BlockPos(l1 + k, j2 + extendedblockstorage.getY(), i2 + l), iblockstate,
|
|
this.rand);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.toTick.clear();
|
|
}
|
|
|
|
// this.profiler.end();
|
|
}
|
|
}
|
|
|
|
private BlockPos adjustPosToNearbyEntity(BlockPos pos) {
|
|
BlockPos blockpos = this.getPrecipitationHeight(pos);
|
|
BoundingBox axisalignedbb = (new BoundingBox(blockpos, new BlockPos(blockpos.getX(), World.MAX_SIZE_Y, blockpos.getZ()))).expand(3.0D,
|
|
3.0D, 3.0D);
|
|
List<EntityLiving> list = this.getEntitiesWithinAABB(EntityLiving.class, axisalignedbb, new Predicate<EntityLiving>() {
|
|
public boolean test(EntityLiving p_apply_1_) {
|
|
return p_apply_1_ != null && p_apply_1_.isEntityAlive() && WorldServer.this.canSeeSky(p_apply_1_.getPosition());
|
|
}
|
|
});
|
|
return !list.isEmpty() ? ((EntityLiving)list.get(this.rand.zrange(list.size()))).getPosition() : blockpos;
|
|
}
|
|
|
|
public boolean isBlockTickPending(BlockPos pos, Block blockType) {
|
|
NextTickListEntry nextticklistentry = new NextTickListEntry(pos, blockType);
|
|
return this.ticksNow.contains(nextticklistentry);
|
|
}
|
|
|
|
public void scheduleUpdate(BlockPos pos, Block blockIn, int delay) {
|
|
this.updateBlockTick(pos, blockIn, delay, 0);
|
|
}
|
|
|
|
public void updateBlockTick(BlockPos pos, Block blockIn, int delay, int priority) {
|
|
NextTickListEntry nextticklistentry = new NextTickListEntry(pos, blockIn);
|
|
int i = 0;
|
|
|
|
if(this.updateForced && blockIn != Blocks.air) {
|
|
if(blockIn.requiresUpdates()) {
|
|
i = 8;
|
|
|
|
if(this.isAreaLoaded(nextticklistentry.position.add(-i, -i, -i), nextticklistentry.position.add(i, i, i))) {
|
|
State iblockstate = this.getState(nextticklistentry.position);
|
|
|
|
if(iblockstate.getBlock() != Blocks.air && iblockstate.getBlock() == nextticklistentry.getBlock()) {
|
|
iblockstate.getBlock().updateTick(this, nextticklistentry.position, iblockstate, this.rand);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
delay = 1;
|
|
}
|
|
|
|
if(this.isAreaLoaded(pos.add(-i, -i, -i), pos.add(i, i, i))) {
|
|
if(blockIn != Blocks.air) {
|
|
nextticklistentry.setScheduledTime((long)delay + this.time);
|
|
nextticklistentry.setPriority(priority);
|
|
}
|
|
|
|
if(!this.ticks.contains(nextticklistentry)) {
|
|
this.ticks.add(nextticklistentry);
|
|
this.ticksNext.add(nextticklistentry);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void scheduleBlockUpdate(BlockPos pos, Block blockIn, int delay, int priority) {
|
|
NextTickListEntry nextticklistentry = new NextTickListEntry(pos, blockIn);
|
|
nextticklistentry.setPriority(priority);
|
|
|
|
if(blockIn != Blocks.air) {
|
|
nextticklistentry.setScheduledTime((long)delay + this.time);
|
|
}
|
|
|
|
if(!this.ticks.contains(nextticklistentry)) {
|
|
this.ticks.add(nextticklistentry);
|
|
this.ticksNext.add(nextticklistentry);
|
|
}
|
|
}
|
|
|
|
public void updateEntities() {
|
|
if(this.players.isEmpty()) {
|
|
if(this.emptyTicks++ >= ServerConfig.unloadTicks) {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
this.resetUpdateEntityTick();
|
|
}
|
|
|
|
super.updateEntities();
|
|
}
|
|
|
|
public void resetUpdateEntityTick() {
|
|
this.emptyTicks = 0;
|
|
}
|
|
|
|
public boolean shouldUnload() {
|
|
return this.emptyTicks >= ServerConfig.unloadTicks && this.loaderList.isEmpty();
|
|
}
|
|
|
|
public boolean tickUpdates(boolean p_72955_1_) {
|
|
int i = this.ticksNext.size();
|
|
|
|
if(i != this.ticks.size()) {
|
|
throw new IllegalStateException("TickNextTick list out of synch");
|
|
}
|
|
else {
|
|
if(i > 1000) {
|
|
i = 1000;
|
|
}
|
|
|
|
// this.profiler.start("cleaning");
|
|
|
|
for(int j = 0; j < i; ++j) {
|
|
NextTickListEntry nextticklistentry = (NextTickListEntry)this.ticksNext.first();
|
|
|
|
if(!p_72955_1_ && nextticklistentry.scheduledTime > this.time) {
|
|
break;
|
|
}
|
|
|
|
this.ticksNext.remove(nextticklistentry);
|
|
this.ticks.remove(nextticklistentry);
|
|
this.ticksNow.add(nextticklistentry);
|
|
}
|
|
|
|
// this.profiler.end();
|
|
// this.profiler.start("ticking");
|
|
Iterator<NextTickListEntry> iterator = this.ticksNow.iterator();
|
|
|
|
while(iterator.hasNext()) {
|
|
NextTickListEntry nextticklistentry1 = (NextTickListEntry)iterator.next();
|
|
iterator.remove();
|
|
int k = 0;
|
|
|
|
if(this.isAreaLoaded(nextticklistentry1.position.add(-k, -k, -k), nextticklistentry1.position.add(k, k, k))) {
|
|
State iblockstate = this.getState(nextticklistentry1.position);
|
|
|
|
if(iblockstate.getBlock() != Blocks.air
|
|
&& Block.isEqualTo(iblockstate.getBlock(), nextticklistentry1.getBlock())) {
|
|
iblockstate.getBlock().updateTick(this, nextticklistentry1.position, iblockstate, this.rand);
|
|
}
|
|
}
|
|
else {
|
|
this.scheduleUpdate(nextticklistentry1.position, nextticklistentry1.getBlock(), 0);
|
|
}
|
|
}
|
|
|
|
// this.profiler.end();
|
|
this.ticksNow.clear();
|
|
return !this.ticksNext.isEmpty();
|
|
}
|
|
}
|
|
|
|
public List<NextTickListEntry> getPendingBlockUpdates(ChunkServer chunk) {
|
|
int x1 = (chunk.xPos << 4) - 2;
|
|
int x2 = x1 + 16 + 2;
|
|
int z1 = (chunk.zPos << 4) - 2;
|
|
int z2 = z1 + 16 + 2;
|
|
List<NextTickListEntry> list = null;
|
|
for(int n = 0; n < 2; ++n) {
|
|
Iterator<NextTickListEntry> iter;
|
|
if(n == 0)
|
|
iter = this.ticksNext.iterator();
|
|
else
|
|
iter = this.ticksNow.iterator();
|
|
while(iter.hasNext()) {
|
|
NextTickListEntry tick = iter.next();
|
|
BlockPos pos = tick.position;
|
|
if(pos.getX() >= x1 && pos.getX() < x2 && pos.getZ() >= z1 && pos.getZ() < z2) {
|
|
if(list == null)
|
|
list = Lists.<NextTickListEntry>newArrayList();
|
|
list.add(tick);
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public List<TileEntity> getTileEntitiesIn(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
|
List<TileEntity> list = Lists.<TileEntity>newArrayList();
|
|
|
|
for(int i = 0; i < this.tiles.size(); ++i) {
|
|
TileEntity tileentity = (TileEntity)this.tiles.get(i);
|
|
BlockPos blockpos = tileentity.getPos();
|
|
|
|
if(blockpos.getX() >= minX && blockpos.getY() >= minY && blockpos.getZ() >= minZ && blockpos.getX() < maxX && blockpos.getY() < maxY
|
|
&& blockpos.getZ() < maxZ) {
|
|
list.add(tileentity);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public static boolean needsLoading(Dimension dim) {
|
|
TagObject tag = null;
|
|
try {
|
|
File dat = new File(new File(new File("chunk"), dim.getDimensionName()), "loaders.cdt");
|
|
if(dat.exists() && dat.isFile())
|
|
tag = TagObject.readGZip(dat);
|
|
}
|
|
catch(Exception e) {
|
|
return false;
|
|
}
|
|
return tag != null && tag.hasList("Loaders") && !tag.getList("Loaders").isEmpty();
|
|
}
|
|
|
|
public static void loadWarps(Dimension dim, Map<String, Position> warps) {
|
|
TagObject tag = null;
|
|
try {
|
|
File dat = new File(new File(new File("chunk"), dim.getDimensionName()), "warps.cdt");
|
|
if(dat.exists() && dat.isFile())
|
|
tag = TagObject.readGZip(dat);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Warpliste nicht laden");
|
|
return;
|
|
}
|
|
if(tag != null && tag.hasList("Warps")) {
|
|
List<TagObject> list = tag.getList("Warps");
|
|
for(int z = 0; z < list.size(); z++) {
|
|
TagObject pos = list.get(z);
|
|
warps.put(pos.getString("Name"), new Position(pos.getDouble("X"), pos.getDouble("Y"), pos.getDouble("Z"),
|
|
pos.getFloat("Yaw"), pos.getFloat("Pitch"), dim.getDimensionId()));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void saveWarps(Map<String, Position> warps) {
|
|
Map<Integer, List<TagObject>> map = Maps.newHashMap();
|
|
for(Entry<String, Position> pos : warps.entrySet()) {
|
|
Dimension dim = UniverseRegistry.getDimension(pos.getValue().dim());
|
|
if(dim != null) {
|
|
List<TagObject> list = map.get(pos.getValue().dim());
|
|
if(list == null)
|
|
map.put(pos.getValue().dim(), list = Lists.newArrayList());
|
|
TagObject warp = new TagObject();
|
|
warp.setString("Name", pos.getKey());
|
|
warp.setDouble("X", pos.getValue().x());
|
|
warp.setDouble("Y", pos.getValue().y());
|
|
warp.setDouble("Z", pos.getValue().z());
|
|
warp.setFloat("Yaw", pos.getValue().yaw());
|
|
warp.setFloat("Pitch", pos.getValue().pitch());
|
|
list.add(warp);
|
|
}
|
|
}
|
|
for(Dimension dim : UniverseRegistry.getDimensions()) {
|
|
List<TagObject> list = map.get(dim.getDimensionId());
|
|
File file = new File(new File(new File("chunk"), dim.getDimensionName()), "warps.cdt");
|
|
if(list == null) {
|
|
file.delete();
|
|
}
|
|
else {
|
|
TagObject tag = new TagObject();
|
|
tag.setList("Warps", list);
|
|
try {
|
|
TagObject.writeGZip(tag, file);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Warpliste nicht speichern");
|
|
}
|
|
}
|
|
}
|
|
map.clear();
|
|
}
|
|
|
|
private static boolean deleteFiles(File[] files) {
|
|
if(files == null) {
|
|
Log.IO.warn("Konnte Ordner nicht löschen");
|
|
return false;
|
|
}
|
|
|
|
for(int i = 0; i < files.length; ++i) {
|
|
File file = files[i];
|
|
Log.IO.info("Lösche " + file);
|
|
|
|
if(file.isDirectory() && !deleteFiles(file.listFiles())) {
|
|
Log.IO.warn("Konnte Ordner " + file + " nicht löschen");
|
|
return false;
|
|
}
|
|
|
|
if(!file.delete()) {
|
|
Log.IO.warn("Konnte Datei " + file + " nicht löschen");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void saveAllChunks() {
|
|
// if(this.primary) {
|
|
//
|
|
// }
|
|
if(this.loadersModified) {
|
|
this.loadersModified = false;
|
|
TagObject loaders = new TagObject();
|
|
List<TagObject> list = Lists.newArrayList();
|
|
for(BlockPos pos : this.loaderList) {
|
|
TagObject loader = new TagObject();
|
|
loader.setInt("X", pos.getX());
|
|
loader.setInt("Y", pos.getY());
|
|
loader.setInt("Z", pos.getZ());
|
|
list.add(loader);
|
|
}
|
|
loaders.setList("Loaders", list);
|
|
File file = new File(this.chunkDir, "loaders.cdt");
|
|
if(list.isEmpty()) {
|
|
file.delete();
|
|
}
|
|
else {
|
|
try {
|
|
TagObject.writeGZip(loaders, file);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Ladeliste nicht speichern");
|
|
}
|
|
}
|
|
}
|
|
// if(this.warpsModified) {
|
|
// this.warpsModified = false;
|
|
// }
|
|
// if(this.dataModified) {
|
|
// this.dataModified = false;
|
|
TagObject data = new TagObject();
|
|
// data.setLong("Seed", this.seed);
|
|
data.setObject("Generator", this.dimension.toTags(true));
|
|
data.setLong("Time", this.time);
|
|
data.setBool("Exterminated", this.exterminated);
|
|
data.setString("Weather", this.weather.getName());
|
|
// ...
|
|
File file = new File(this.chunkDir, "data.cdt");
|
|
try {
|
|
TagObject.writeGZip(data, file);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Weltdaten nicht speichern");
|
|
}
|
|
// }
|
|
for(int i = 0; i < this.dataList.size(); ++i) {
|
|
WorldSavedData wdata = this.dataList.get(i);
|
|
if(wdata.dirty) {
|
|
this.saveData(wdata);
|
|
wdata.dirty = false;
|
|
}
|
|
}
|
|
if(this.villageStorage != null && this.villageStorage.isDirty()) {
|
|
TagObject tag = this.villageStorage.toTags();
|
|
File dat = new File(this.chunkDir, "villages.cdt");
|
|
try {
|
|
TagObject.writeGZip(tag, dat);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Dorfliste nicht speichern");
|
|
}
|
|
}
|
|
List<ChunkServer> list = Lists.newArrayList(this.loaded);
|
|
for(int n = 0; n < list.size(); ++n) {
|
|
ChunkServer chunk = list.get(n);
|
|
if(chunk.isDirty(this.time)) {
|
|
this.saveChunkData(chunk);
|
|
chunk.setModified(false);
|
|
}
|
|
}
|
|
for(ChunkServer chunk : Lists.newArrayList(this.loaded)) {
|
|
if(chunk != null && !this.hasPlayerInstance(chunk.xPos, chunk.zPos)) {
|
|
this.dropChunk(chunk.xPos, chunk.zPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void onEntityAdded(Entity entityIn) {
|
|
this.trackEntity(entityIn);
|
|
this.entityIds.addKey(entityIn.getId(), entityIn);
|
|
Entity[] aentity = entityIn.getParts();
|
|
|
|
if(aentity != null) {
|
|
for(int i = 0; i < aentity.length; ++i) {
|
|
this.entityIds.addKey(aentity[i].getId(), aentity[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void onEntityRemoved(Entity entityIn) {
|
|
this.untrackEntity(entityIn);
|
|
this.entityIds.removeObject(entityIn.getId());
|
|
Entity[] aentity = entityIn.getParts();
|
|
|
|
if(aentity != null) {
|
|
for(int i = 0; i < aentity.length; ++i) {
|
|
this.entityIds.removeObject(aentity[i].getId());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void strikeLightning(double x, double y, double z, int color, int damage, boolean fire, EntityLiving summoner) {
|
|
EntityLightning entity = new EntityLightning(this, x, y, z, color, damage, fire, summoner);
|
|
this.effects.add(entity);
|
|
this.server.sendNear(entity.posX, entity.posY, entity.posZ, 512.0D, this.dimension.getDimensionId(),
|
|
new SPacketSpawnGlobalEntity(entity, 1, entity.color));
|
|
if(fire && ServerConfig.fire) {
|
|
BlockPos pos = new BlockPos(entity);
|
|
if(this.isAreaLoaded(pos, 10)) {
|
|
if(this.getState(pos).getBlock() == Blocks.air && Blocks.fire.canPlaceBlockAt(this, pos))
|
|
this.setState(pos, Blocks.fire.getState());
|
|
for(int n = 0; n < 4; n++) {
|
|
BlockPos extra = pos.add(this.rand.range(-1, 1), this.rand.range(-1, 1), this.rand.range(-1, 1));
|
|
if(this.getState(extra).getBlock() == Blocks.air && Blocks.fire.canPlaceBlockAt(this, extra))
|
|
this.setState(extra, Blocks.fire.getState());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setEntityState(Entity entityIn, byte state) {
|
|
this.sendToAllTrackingAndSelf(entityIn, new SPacketEntityStatus(entityIn, state));
|
|
}
|
|
|
|
public Explosion newExplosion(Entity entityIn, double x, double y, double z, float strength, boolean isFlaming, boolean isSmoking, boolean altSound) {
|
|
Explosion explosion = new Explosion(this, entityIn, x, y, z, strength, isFlaming, isSmoking);
|
|
explosion.doExplosionA();
|
|
explosion.doExplosionB(false, altSound);
|
|
|
|
if(!isSmoking) {
|
|
explosion.clearAffectedBlockPositions();
|
|
}
|
|
|
|
for(EntityNPC entityplayer : this.players) {
|
|
if(entityplayer.getDistanceSq(x, y, z) < 4096.0D) {
|
|
entityplayer.connection.sendPacket(new SPacketExplosion(x, y, z, strength, explosion.getAffectedBlockPositions(),
|
|
(Vec3)explosion.getPlayerKnockbackMap().get(entityplayer), altSound));
|
|
}
|
|
}
|
|
|
|
return explosion;
|
|
}
|
|
|
|
public void addBlockEvent(BlockPos pos, Block blockIn, int eventID, int eventParam) {
|
|
TickEvent blockeventdata = new TickEvent(pos, blockIn, eventID, eventParam);
|
|
|
|
for(TickEvent blockeventdata1 : this.queue[this.blockEvtIdx]) {
|
|
if(blockeventdata1.equals(blockeventdata)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.queue[this.blockEvtIdx].add(blockeventdata);
|
|
}
|
|
|
|
private void sendQueuedBlockEvents() {
|
|
while(!this.queue[this.blockEvtIdx].isEmpty()) {
|
|
int i = this.blockEvtIdx;
|
|
this.blockEvtIdx ^= 1;
|
|
|
|
for(TickEvent blockeventdata : this.queue[i]) {
|
|
if(this.fireBlockEvent(blockeventdata)) {
|
|
this.server.sendNear((double)blockeventdata.position().getX(), (double)blockeventdata.position().getY(),
|
|
(double)blockeventdata.position().getZ(), 64.0D, this.dimension.getDimensionId(),
|
|
new SPacketBlockAction(blockeventdata.position(), blockeventdata.block(), blockeventdata.id(),
|
|
blockeventdata.parameter()));
|
|
}
|
|
}
|
|
|
|
this.queue[i].clear();
|
|
}
|
|
}
|
|
|
|
private boolean fireBlockEvent(TickEvent event) {
|
|
State iblockstate = this.getState(event.position());
|
|
return iblockstate.getBlock() == event.block()
|
|
? iblockstate.getBlock().onBlockEventReceived(this, event.position(), iblockstate, event.id(), event.parameter())
|
|
: false;
|
|
}
|
|
|
|
public void resetWeather() {
|
|
this.calculateInitialWeather();
|
|
this.updateWeather(true);
|
|
}
|
|
|
|
public void setWeather(Weather weather) {
|
|
this.weather = weather;
|
|
// this.dataModified = true;
|
|
this.server.sendPacket(new SPacketChangeGameState(SPacketChangeGameState.Action.SET_WEATHER,
|
|
weather.getID()), this.dimension.getDimensionId());
|
|
}
|
|
|
|
protected void updateWeather(boolean force) {
|
|
// DimensionInfo dim = this.dimInfo;
|
|
|
|
// if(!this.dimension.hasNoSeasons()) {
|
|
// if(Config.seasonCycle && Config.seasonFlow > 0) {
|
|
// long time = dim.getSeasonTime();
|
|
// if(time <= 0L) {
|
|
// dim.setSeason(Season.getById(dim.getSeason().getIndex() + 1));
|
|
//// dim.setSeasonTime(dim.getSeason().getLength(this.dimension.getOrbitalPeriod()));
|
|
//// this.server.sendPacket(new S2BPacketChangeGameState(S2BPacketChangeGameState.Action.SET_SEASON,
|
|
//// dim.getSeason().getIndex()), this.dimension.getDimensionId());
|
|
// }
|
|
// else {
|
|
// this.dimInfo.setSeasonTime(time - (long)Config.seasonFlow);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
if((this.dimension.getType().weather && !this.exterminated) || force) {
|
|
float prevDarkness = this.darkness;
|
|
float prevRain = this.rain;
|
|
float prevFog = this.fog;
|
|
|
|
if(ServerConfig.weather && ServerConfig.weatherChance > 0) {
|
|
// int time = dim.getWeatherTime();
|
|
if(this.rand.chance(ServerConfig.weatherChance)) {
|
|
Weather nweather = Weather.pick(this.getBaseTemperature(), this.rand);
|
|
if(nweather != this.weather) {
|
|
// dim.setWeatherTime(this.rand.zrange(Config.weatherFlow) + Config.weatherFlow);
|
|
// }
|
|
// else {
|
|
// this.weather = nweather;
|
|
// this.dataModified = true;
|
|
this.setWeather(nweather);
|
|
// dim.setWeatherTime(this.rand.zrange(Config.weatherFlow * 14) + Config.weatherFlow);
|
|
// this.server.sendPacket(new S2BPacketChangeGameState(S2BPacketChangeGameState.Action.SET_WEATHER,
|
|
// this.weather.getID()), this.dimension.getDimensionId());
|
|
}
|
|
}
|
|
// else {
|
|
// dim.setWeatherTime(--time);
|
|
// }
|
|
}
|
|
|
|
if(this.weather.isDark()) {
|
|
this.darkness = (float)((double)this.darkness + 0.01D);
|
|
}
|
|
else {
|
|
this.darkness = (float)((double)this.darkness - 0.01D);
|
|
}
|
|
this.darkness = ExtMath.clampf(this.darkness, 0.0F, 1.0F);
|
|
|
|
if(this.weather.hasDownfall()) {
|
|
this.rain = (float)((double)this.rain + 0.01D);
|
|
}
|
|
else {
|
|
this.rain = (float)((double)this.rain - 0.01D);
|
|
}
|
|
this.rain = ExtMath.clampf(this.rain, 0.0F, 1.0F);
|
|
|
|
if(this.fog < this.weather.getFogIntensity()) {
|
|
this.fog = Math.min((float)((double)this.fog + 0.01D), this.weather.getFogIntensity());
|
|
}
|
|
else if(this.fog > this.weather.getFogIntensity()) {
|
|
this.fog = Math.max((float)((double)this.fog - 0.01D), this.weather.getFogIntensity());
|
|
}
|
|
|
|
if(prevRain != this.rain || force) {
|
|
this.server.sendPacket(new SPacketChangeGameState(SPacketChangeGameState.Action.RAIN_STRENGTH, this.rain), this.dimension.getDimensionId());
|
|
}
|
|
|
|
if(prevDarkness != this.darkness || force) {
|
|
this.server.sendPacket(new SPacketChangeGameState(SPacketChangeGameState.Action.DARKNESS, this.darkness), this.dimension.getDimensionId());
|
|
}
|
|
|
|
if(prevFog != this.fog || force) {
|
|
this.server.sendPacket(new SPacketChangeGameState(SPacketChangeGameState.Action.FOG_STRENGTH, this.fog), this.dimension.getDimensionId());
|
|
}
|
|
}
|
|
|
|
if((this.dimension.getType().days || (this.dimension.getType().weather && !this.exterminated)) || force) {
|
|
float prevTemp = this.temp;
|
|
|
|
float temp = this.getBaseTemperature() + this.weather.getTemperature();
|
|
if(this.temp < temp) {
|
|
this.temp = Math.min((float)((double)this.temp + 0.01D), temp);
|
|
}
|
|
else if(this.temp > temp) {
|
|
this.temp = Math.max((float)((double)this.temp - 0.01D), temp);
|
|
}
|
|
|
|
if(prevTemp != this.temp || force) {
|
|
this.server.sendPacket(new SPacketChangeGameState(SPacketChangeGameState.Action.TEMPERATURE, this.temp), this.dimension.getDimensionId());
|
|
}
|
|
}
|
|
|
|
// if(this.resetWeather)
|
|
// this.resetWeather = false;
|
|
}
|
|
|
|
public void spawnParticle(ParticleType particleType, double xCoord, double yCoord, double zCoord, int numberOfParticles, double xOffset,
|
|
double yOffset, double zOffset, double particleSpeed, int... particleArguments) {
|
|
this.spawnParticle(particleType, false, xCoord, yCoord, zCoord, numberOfParticles, xOffset, yOffset, zOffset, particleSpeed,
|
|
particleArguments);
|
|
}
|
|
|
|
public void spawnParticle(ParticleType particleType, boolean longDistance, double xCoord, double yCoord, double zCoord,
|
|
int numberOfParticles, double xOffset, double yOffset, double zOffset, double particleSpeed, int[] particleArguments) {
|
|
Packet packet = new SPacketParticles(particleType, longDistance, (float)xCoord, (float)yCoord, (float)zCoord, (float)xOffset,
|
|
(float)yOffset, (float)zOffset, (float)particleSpeed, numberOfParticles, particleArguments);
|
|
|
|
for(int i = 0; i < this.players.size(); ++i) {
|
|
EntityNPC entityplayermp = this.players.get(i);
|
|
BlockPos blockpos = entityplayermp.getPosition();
|
|
double d0 = blockpos.distanceSq(xCoord, yCoord, zCoord);
|
|
|
|
if(d0 <= 256.0D || longDistance && d0 <= 65536.0D) {
|
|
entityplayermp.connection.sendPacket(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected boolean isLoaded(int x, int z, boolean allowEmpty) {
|
|
return this.chunkExists(x, z);
|
|
}
|
|
|
|
public ChunkServer getChunk(int x, int z) {
|
|
ChunkServer chunk = this.chunks.getValueByKey(LongHashMap.packInt(x, z));
|
|
return chunk == null ? this.loadChunk(x, z) : chunk;
|
|
}
|
|
|
|
public ChunkServer getChunk(BlockPos pos) {
|
|
return this.getChunk(pos.getX() >> 4, pos.getZ() >> 4);
|
|
}
|
|
|
|
private boolean chunkExists(int x, int z) {
|
|
return this.chunks.containsItem(LongHashMap.packInt(x, z));
|
|
}
|
|
|
|
public void dropChunk(int x, int z) {
|
|
long chunk = LongHashMap.packInt(x, z);
|
|
if(!this.loaders.containsItem(chunk))
|
|
this.dropped.add(Long.valueOf(chunk));
|
|
}
|
|
|
|
public void loadForcedChunks() {
|
|
for(BlockPos pos : this.loaderList) {
|
|
this.loadChunk(pos.getX() / 16, pos.getZ() / 16);
|
|
}
|
|
}
|
|
|
|
public void unloadAllChunks() {
|
|
for(ChunkServer chunk : this.loaded) {
|
|
this.dropChunk(chunk.xPos, chunk.zPos);
|
|
}
|
|
}
|
|
|
|
public ChunkServer loadChunk(int x, int z) {
|
|
long id = LongHashMap.packInt(x, z);
|
|
this.dropped.remove(Long.valueOf(id));
|
|
ChunkServer chunk = this.chunks.getValueByKey(id);
|
|
|
|
if(chunk == null) {
|
|
chunk = this.loadChunkFromFile(x, z);
|
|
|
|
if(chunk == null) {
|
|
chunk = this.generate(x, z);
|
|
}
|
|
|
|
this.chunks.add(id, chunk);
|
|
this.loaded.add(chunk);
|
|
chunk.onChunkLoad();
|
|
this.popChunk(x, z);
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
private ChunkServer loadChunkFromFile(int x, int z) {
|
|
try {
|
|
ChunkPos coord = new ChunkPos(x, z);
|
|
TagObject tag = this.toRemove.get(coord);
|
|
if(tag == null) {
|
|
tag = Region.readChunk(this.chunkDir, x, z);
|
|
// DataInputStream in = ;
|
|
if(tag == null) {
|
|
return null;
|
|
}
|
|
// tag = CompressedStreamTools.read(in);
|
|
}
|
|
ChunkServer chunk = Region.readChunk(this, x, z, tag);
|
|
if(chunk != null) {
|
|
chunk.setSaved(this.time);
|
|
if(this.mineshaftGen != null) {
|
|
this.mineshaftGen.generate(this, x, z, null);
|
|
}
|
|
if(this.villageGen != null) {
|
|
this.villageGen.generate(this, x, z, null);
|
|
}
|
|
if(this.strongholdGen != null) {
|
|
this.strongholdGen.generate(this, x, z, null);
|
|
}
|
|
if(this.scatteredGen != null) {
|
|
this.scatteredGen.generate(this, x, z, null);
|
|
}
|
|
if(this.bridgeGen != null) {
|
|
this.bridgeGen.generate(this, x, z, null);
|
|
}
|
|
}
|
|
return chunk;
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Chunk nicht laden");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void saveChunkData(ChunkServer chunk) {
|
|
chunk.setSaved(this.time);
|
|
try {
|
|
TagObject tag = Region.writeChunk(this, chunk);
|
|
ChunkPos coord = new ChunkPos(chunk.xPos, chunk.zPos);
|
|
if(!this.pending.contains(coord)) {
|
|
this.toRemove.put(coord, tag);
|
|
}
|
|
Region.queueIO(this);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Chunk nicht speichern");
|
|
}
|
|
}
|
|
|
|
public boolean writeNextIO() {
|
|
if(this.toRemove.isEmpty()) {
|
|
// if(this.flushing) {
|
|
// Log.info(this.chunkDir + ": Alle Chunks sind gespeichert");
|
|
// }
|
|
return false;
|
|
}
|
|
else {
|
|
ChunkPos coord = this.toRemove.keySet().iterator().next();
|
|
boolean flag;
|
|
try {
|
|
this.pending.add(coord);
|
|
TagObject tag = this.toRemove.remove(coord);
|
|
|
|
if(tag != null) {
|
|
try {
|
|
// DataOutputStream out = RegionFile.getChunkOutputStream(this.chunkDir, coord.x, coord.z);
|
|
// CompressedStreamTools.write(tag, out);
|
|
// out.close();
|
|
Region.writeChunk(this.chunkDir, coord.x, coord.z, tag);
|
|
}
|
|
catch(Exception e) {
|
|
Log.IO.error(e, "Konnte Chunk nicht speichern");
|
|
}
|
|
}
|
|
flag = true;
|
|
}
|
|
finally {
|
|
this.pending.remove(coord);
|
|
}
|
|
return flag;
|
|
}
|
|
}
|
|
|
|
// public void flushData() {
|
|
// if(!this.debug) {
|
|
// while(this.writeNextIO()) {
|
|
// ;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
public long getSeed() {
|
|
return this.seed;
|
|
}
|
|
|
|
public Random getRng(int x, int z, int seed) {
|
|
long i = (long)x * 341873128712L + (long)z * 132897987541L + this.seed + (long)seed;
|
|
this.rand.setSeed(i);
|
|
return this.rand;
|
|
}
|
|
|
|
// public void regenerateRegion(Set<Vector2D> chunks) {
|
|
// for(Vector2D coord : chunks) {
|
|
// long pos = LongHashMap.packInt(coord.getBlockX(), coord.getBlockZ());
|
|
// Chunk chunk;
|
|
// if(this.chunkExists(coord.getBlockX(), coord.getBlockZ())) {
|
|
// chunk = this.loadChunk(coord.getBlockX(), coord.getBlockZ());
|
|
// chunk.onChunkUnload();
|
|
// }
|
|
// this.dropped.remove(pos);
|
|
// this.chunks.remove(pos);
|
|
// chunk = this.generate(coord.getBlockX(), coord.getBlockZ());
|
|
// this.chunks.add(pos, chunk);
|
|
// this.loaded.add(chunk);
|
|
// chunk.onChunkLoad();
|
|
// this.popChunk(coord.getBlockX(), coord.getBlockZ());
|
|
// }
|
|
// }
|
|
|
|
private void popChunk(int x, int z) {
|
|
boolean n = this.chunkExists(x, z - 1);
|
|
boolean e = this.chunkExists(x + 1, z);
|
|
boolean s = this.chunkExists(x, z + 1);
|
|
boolean w = this.chunkExists(x - 1, z);
|
|
boolean nw = this.chunkExists(x - 1, z - 1);
|
|
boolean se = this.chunkExists(x + 1, z + 1);
|
|
boolean sw = this.chunkExists(x - 1, z + 1);
|
|
boolean ne = this.chunkExists(x + 1, z - 1);
|
|
if(e && s && se) {
|
|
this.populate(x, z);
|
|
}
|
|
if(w && s && sw) {
|
|
this.populate(x - 1, z);
|
|
}
|
|
if(n && e && ne) {
|
|
this.populate(x, z - 1);
|
|
}
|
|
if(nw && n && w) {
|
|
this.populate(x - 1, z - 1);
|
|
}
|
|
}
|
|
|
|
private void populate(int x, int z) {
|
|
ChunkServer chunk = this.getChunk(x, z);
|
|
if(!chunk.isTerrainPopulated()) {
|
|
chunk.checkLight();
|
|
BlockFalling.fallInstantly = true;
|
|
int bx = x * 16;
|
|
int bz = z * 16;
|
|
BlockPos pos = new BlockPos(bx, 0, bz);
|
|
GenBiome biome = GenBiome.BIOMES[this.getBiomeGenForCoords(pos.add(16, 0, 16)).id];
|
|
this.grng.setSeed(this.seed);
|
|
long sx = this.grng.longv() / 2L * 2L + 1L;
|
|
long sz = this.grng.longv() / 2L * 2L + 1L;
|
|
this.grng.setSeed((long)x * sx + (long)z * sz ^ this.seed);
|
|
boolean lakes = true;
|
|
ChunkPos coord = new ChunkPos(x, z);
|
|
if(this.bridgeGen != null) {
|
|
this.bridgeGen.generateStructure(this, this.grng, coord);
|
|
}
|
|
if(this.mineshaftGen != null) {
|
|
this.mineshaftGen.generateStructure(this, this.grng, coord);
|
|
}
|
|
if(this.villageGen != null) {
|
|
lakes = !this.villageGen.generateStructure(this, this.grng, coord);
|
|
}
|
|
if(this.strongholdGen != null) {
|
|
this.strongholdGen.generateStructure(this, this.grng, coord);
|
|
}
|
|
if(this.scatteredGen != null) {
|
|
this.scatteredGen.generateStructure(this, this.grng, coord);
|
|
}
|
|
if(lakes && this.lakes != null && biome.generateLakes) {
|
|
for(FeatureLakes lake : this.lakes) {
|
|
lake.generate(this, this.grng, pos);
|
|
}
|
|
}
|
|
if(this.dungeons != null) {
|
|
this.dungeons.generate(this, this.grng, pos);
|
|
}
|
|
if(this.ores != null) {
|
|
for(FeatureOres ore : this.ores) {
|
|
ore.generate(this, this.grng, pos);
|
|
}
|
|
}
|
|
if(this.populate) {
|
|
biome.decorate(this, this.grng, pos);
|
|
}
|
|
if(this.liquids != null && biome.generateLiquids) {
|
|
for(FeatureLiquids liquid : this.liquids) {
|
|
liquid.generate(this, this.grng, pos);
|
|
}
|
|
}
|
|
if(this.mobs && ServerConfig.mobs && ServerConfig.genSpawn) {
|
|
Spawner.generate(this, biome, bx + 8, bz + 8, 16, 16, this.grng);
|
|
}
|
|
// if(this.snow) {
|
|
pos = pos.add(8, 0, 8);
|
|
for(int fx = 0; fx < 16; ++fx) {
|
|
for(int fz = 0; fz < 16; ++fz) {
|
|
BlockPos snow = this.getPrecipitationHeight(pos.add(fx, 0, fz));
|
|
BlockPos ice = snow.down();
|
|
if(this.canBlockFreeze(ice, false)) {
|
|
this.setState(ice, Blocks.ice.getState(), 2);
|
|
}
|
|
if(this.snow && this.canSnowAt(snow, true, false)) {
|
|
this.setState(snow, Blocks.snow_layer.getState(), 2);
|
|
}
|
|
}
|
|
}
|
|
// }
|
|
BlockFalling.fallInstantly = false;
|
|
chunk.setModified(true);
|
|
}
|
|
}
|
|
|
|
private ChunkServer generate(int x, int z) {
|
|
this.grng.setSeed((long)x * 341873128712L + (long)z * 132897987541L);
|
|
ChunkPrimer primer = new ChunkPrimer(this.height);
|
|
this.generator.generateChunk(this, x, z, primer);
|
|
this.biomeGen.getChunkBiomes(this.biomes, x * 16, z * 16, 16, 16);
|
|
if(this.replacer != null) {
|
|
this.replacer.replaceBlocks(this, x, z, primer, this.grng, this.biomes);
|
|
}
|
|
if(this.caveGen != null) {
|
|
this.caveGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.bigCaveGen != null) {
|
|
this.bigCaveGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.ravineGen != null) {
|
|
this.ravineGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.bridgeGen != null) {
|
|
this.bridgeGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.mineshaftGen != null) {
|
|
this.mineshaftGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.villageGen != null) {
|
|
this.villageGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.strongholdGen != null) {
|
|
this.strongholdGen.generate(this, x, z, primer);
|
|
}
|
|
if(this.scatteredGen != null) {
|
|
this.scatteredGen.generate(this, x, z, primer);
|
|
}
|
|
return new ChunkServer(this, primer.getData(), primer.height, this.base, this.ceil, this.grng, this.biomes, x, z);
|
|
}
|
|
|
|
public boolean isExterminated() {
|
|
return this.exterminated;
|
|
}
|
|
|
|
public boolean exterminate() {
|
|
if(this.exterminated)
|
|
return false;
|
|
this.setWeather(Weather.CLEAR);
|
|
this.resetWeather();
|
|
// this.updateWeather();
|
|
if(!this.loaderList.isEmpty())
|
|
this.loadersModified = true;
|
|
for(BlockPos pos : this.loaderList) {
|
|
this.loaders.remove(LongHashMap.packInt(pos.getX() / 16, pos.getZ() / 16));
|
|
}
|
|
this.loaderList.clear();
|
|
for(Iterator<Entry<String, Position>> iter = this.server.getWarps().entrySet().iterator(); iter.hasNext();) {
|
|
Entry<String, Position> pos = iter.next();
|
|
if(pos.getValue().dim() == this.dimension.getDimensionId())
|
|
iter.remove();
|
|
}
|
|
Region.finishWrite();
|
|
deleteFiles(this.chunkDir.listFiles(new FileFilter() {
|
|
public boolean accept(File file) {
|
|
return file.isDirectory();
|
|
}
|
|
}));
|
|
this.exterminated = true;
|
|
// this.dataModified = true;
|
|
for(Long v : this.dropped) {
|
|
ChunkServer chunk = this.chunks.getValueByKey(v.longValue());
|
|
if(chunk != null) {
|
|
chunk.onChunkUnload();
|
|
this.chunks.remove(v.longValue());
|
|
this.loaded.remove(chunk);
|
|
}
|
|
}
|
|
this.dropped.clear();
|
|
List<ChunkServer> loaded = Lists.<ChunkServer>newArrayList(this.loaded);
|
|
this.loaded.clear();
|
|
this.setExterminatedGen();
|
|
for(ChunkServer chunk : loaded) {
|
|
long pos = LongHashMap.packInt(chunk.xPos, chunk.zPos);
|
|
chunk.onChunkUnload();
|
|
this.chunks.remove(pos);
|
|
chunk = this.generate(chunk.xPos, chunk.zPos);
|
|
this.chunks.add(pos, chunk);
|
|
this.loaded.add(chunk);
|
|
chunk.onChunkLoad();
|
|
chunk.checkLight();
|
|
chunk.setModified(true);
|
|
}
|
|
for(ChunkServer chunk : this.loaded) {
|
|
chunk.update(false);
|
|
}
|
|
this.entities.removeAll(this.unloaded);
|
|
for(int l = 0; l < this.unloaded.size(); ++l) {
|
|
Entity ent = this.unloaded.get(l);
|
|
if(ent.isPlayer()) {
|
|
int i = ExtMath.floord(ent.posX / 16.0D);
|
|
int j = ExtMath.floord(ent.posZ / 16.0D);
|
|
this.getChunk(i, j).addEntity(ent);
|
|
this.entities.add(ent);
|
|
}
|
|
else {
|
|
this.onEntityRemoved(ent);
|
|
}
|
|
}
|
|
this.unloaded.clear();
|
|
for(int z = 0; z < this.instList.size(); ++z) {
|
|
this.instList.get(z).resend();
|
|
}
|
|
for(EntityNPC player : this.players) {
|
|
player.attackEntityFrom(DamageSource.causeExterminatusDamage(null), 5000);
|
|
Packet packet = new SPacketParticles(ParticleType.EXPLOSION_HUGE, true,
|
|
(float)player.posX, (float)this.getSeaLevel() + 4.0f, (float)player.posZ, (float)128.0,
|
|
(float)2.0, (float)128.0, (float)0.15, 1000, new int[0]);
|
|
player.connection.sendPacket(packet);
|
|
packet = new SPacketParticles(ParticleType.CLOUD, true,
|
|
(float)player.posX, (float)this.getSeaLevel() + 4.0f, (float)player.posZ, (float)128.0,
|
|
(float)2.0, (float)128.0, (float)0.15, 1000, new int[0]);
|
|
player.connection.sendPacket(packet);
|
|
packet = new SPacketEffect(1025, new BlockPos(player.posX, (double)this.getSeaLevel() + 4.0, player.posZ), 0);
|
|
player.connection.sendPacket(packet);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void setExterminatedGen() {
|
|
this.mobs = false;
|
|
this.snow = false;
|
|
this.caveGen = null;
|
|
this.bigCaveGen = null;
|
|
this.ravineGen = null;
|
|
this.strongholdGen = null;
|
|
this.villageGen = null;
|
|
this.villageStorage = null;
|
|
this.mineshaftGen = null;
|
|
this.scatteredGen = null;
|
|
this.bridgeGen = null;
|
|
this.generator = new GeneratorDestroyed(this.dimension.getSeaLevel());
|
|
this.biomeGen = new BiomeGenSingle(Biome.EXTERMINATED);
|
|
this.replacer = null;
|
|
this.populate = false;
|
|
this.liquid = Blocks.air.getState();
|
|
this.base = false;
|
|
this.ceil = false;
|
|
this.height = this.generator.getMaximumHeight();
|
|
this.seaLevel = this.dimension.getSeaLevel();
|
|
this.ores = null;
|
|
this.lakes = null;
|
|
this.liquids = null;
|
|
this.dungeons = null;
|
|
}
|
|
|
|
public static String getLoadedInfo(Server server) {
|
|
int chunks = 0;
|
|
int entities = 0;
|
|
int tiles = 0;
|
|
int ticked = 0;
|
|
int forced = 0;
|
|
try {
|
|
Object[] worlds = server.getWorlds().toArray();
|
|
for(Object obj : worlds) {
|
|
WorldServer world = (WorldServer)obj;
|
|
chunks += world.chunks.getNumHashElements();
|
|
entities += world.entities.size();
|
|
tiles += world.tiles.size();
|
|
ticked += world.tickable.size();
|
|
forced += world.loaderList.size();
|
|
}
|
|
}
|
|
catch(Throwable e) {
|
|
}
|
|
return String.format("%d C, %d O, %d t, %d T, %d F", chunks, entities, tiles, ticked, forced);
|
|
}
|
|
|
|
public void forceBlockUpdateTick(Block blockType, BlockPos pos, Random random) {
|
|
this.updateForced = true;
|
|
blockType.updateTick(this, pos, this.getState(pos), random);
|
|
this.updateForced = false;
|
|
}
|
|
|
|
public Village getNearestVillage(BlockPos doorBlock, int radius) {
|
|
return this.villageStorage == null ? null : this.villageStorage.getNearestVillage(doorBlock, radius);
|
|
}
|
|
|
|
public void addToVillagerPositionList(BlockPos blockpos) {
|
|
if(this.villageStorage != null)
|
|
this.villageStorage.addToVillagerPositionList(blockpos);
|
|
}
|
|
|
|
public int getSeaLevel() {
|
|
return this.seaLevel;
|
|
}
|
|
|
|
public void playSound(SoundEvent sound, double x, double y, double z, float volume)
|
|
{
|
|
this.server.sendNear(x, y, z, volume > 1.0F ? (double)(16.0F * volume) : 16.0D, this.dimension.getDimensionId(), new SPacketSoundEffect(sound, x, y, z, volume));
|
|
}
|
|
|
|
public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2)
|
|
{
|
|
}
|
|
|
|
protected void notifyLightSet(BlockPos pos)
|
|
{
|
|
}
|
|
|
|
public void playAuxSFX(EntityNPC player, int sfxType, BlockPos blockPosIn, int data)
|
|
{
|
|
this.server.sendNearExcept(player, (double)blockPosIn.getX(), (double)blockPosIn.getY(), (double)blockPosIn.getZ(), 64.0D, this.dimension.getDimensionId(), new SPacketEffect(sfxType, blockPosIn, data));
|
|
}
|
|
|
|
// public void broadcastSound(int soundID, BlockPos pos, int data)
|
|
// {
|
|
// this.server.sendPacket(new S28PacketEffect(soundID, pos, data, true));
|
|
// }
|
|
|
|
public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress)
|
|
{
|
|
for (Player conn : this.server.getPlayers())
|
|
{
|
|
EntityNPC player = conn.getPresentEntity();
|
|
if (player != null && player.worldObj == this && player.getId() != breakerId)
|
|
{
|
|
double d0 = (double)pos.getX() - player.posX;
|
|
double d1 = (double)pos.getY() - player.posY;
|
|
double d2 = (double)pos.getZ() - player.posZ;
|
|
|
|
if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D)
|
|
{
|
|
conn.sendPacket(new SPacketBlockBreakAnim(breakerId, pos, progress));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updatePlayerInstances() {
|
|
long time = this.time;
|
|
|
|
if(time - this.prevUpdate > 8000L) {
|
|
this.prevUpdate = time;
|
|
|
|
for(int z = 0; z < this.instList.size(); ++z) {
|
|
PlayerInstance inst = this.instList.get(z);
|
|
inst.onUpdate();
|
|
inst.processChunk();
|
|
}
|
|
}
|
|
else {
|
|
for(int z = 0; z < this.toUpdate.size(); ++z) {
|
|
PlayerInstance inst = this.toUpdate.get(z);
|
|
inst.onUpdate();
|
|
}
|
|
}
|
|
|
|
this.toUpdate.clear();
|
|
|
|
if(this.managed.isEmpty()) {
|
|
this.unloadAllChunks();
|
|
}
|
|
}
|
|
|
|
private boolean hasPlayerInstance(int chunkX, int chunkZ) {
|
|
long v = (long)chunkX + 2147483647L | (long)chunkZ + 2147483647L << 32;
|
|
return this.instances.getValueByKey(v) != null;
|
|
}
|
|
|
|
public void setBiome(BlockPos pos, Biome biome) {
|
|
ChunkServer chunk = this.getChunk(pos);
|
|
if(chunk == null || !chunk.isLoaded())
|
|
return;
|
|
chunk.getBiomes()[((pos.getZ() & 0xF) << 4 | pos.getX() & 0xF)] = (byte)biome.id;
|
|
chunk.setModified(true);
|
|
int chunkX = pos.getX() >> 4;
|
|
int chunkZ = pos.getZ() >> 4;
|
|
long v = (long)chunkX + 2147483647L | (long)chunkZ + 2147483647L << 32;
|
|
PlayerInstance ins = this.instances.getValueByKey(v);
|
|
if(ins != null)
|
|
ins.sendToAllPlayersWatchingChunk(new SPacketBiome(pos, biome));
|
|
}
|
|
|
|
private PlayerInstance getPlayerInstance(int chunkX, int chunkZ, boolean create) {
|
|
long v = (long)chunkX + 2147483647L | (long)chunkZ + 2147483647L << 32;
|
|
PlayerInstance inst = this.instances.getValueByKey(v);
|
|
|
|
if(inst == null && create) {
|
|
inst = new PlayerInstance(chunkX, chunkZ);
|
|
this.instances.add(v, inst);
|
|
this.instList.add(inst);
|
|
}
|
|
|
|
return inst;
|
|
}
|
|
|
|
public void markBlockForUpdate(BlockPos pos) {
|
|
int x = pos.getX() >> 4;
|
|
int z = pos.getZ() >> 4;
|
|
PlayerInstance inst = this.getPlayerInstance(x, z, false);
|
|
|
|
if(inst != null) {
|
|
inst.flagChunkForUpdate(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
|
|
}
|
|
}
|
|
|
|
public void addPlayer(EntityNPC player) {
|
|
int x = (int)player.posX >> 4;
|
|
int z = (int)player.posZ >> 4;
|
|
player.connection.setManagedPos(player.posX, player.posZ);
|
|
|
|
for(int cx = x - this.viewRadius; cx <= x + this.viewRadius; ++cx) {
|
|
for(int cz = z - this.viewRadius; cz <= z + this.viewRadius; ++cz) {
|
|
this.getPlayerInstance(cx, cz, true).addPlayer(player);
|
|
}
|
|
}
|
|
|
|
this.managed.add(player);
|
|
this.filterChunkLoadQueue(player);
|
|
}
|
|
|
|
private void filterChunkLoadQueue(EntityNPC player) {
|
|
List<ChunkPos> list = Lists.newArrayList(player.connection.getLoadedChunkList());
|
|
int p = 0;
|
|
int r = this.viewRadius;
|
|
int x = (int)player.posX >> 4;
|
|
int z = (int)player.posZ >> 4;
|
|
int cx = 0;
|
|
int cz = 0;
|
|
ChunkPos pos = this.getPlayerInstance(x, z, true).position;
|
|
player.connection.getLoadedChunkList().clear();
|
|
|
|
if(list.contains(pos)) {
|
|
player.connection.getLoadedChunkList().add(pos);
|
|
}
|
|
|
|
for(int n = 1; n <= r * 2; ++n) {
|
|
for(int m = 0; m < 2; ++m) {
|
|
int[] dirs = XZ_DIRS[p++ % 4];
|
|
|
|
for(int o = 0; o < n; ++o) {
|
|
cx += dirs[0];
|
|
cz += dirs[1];
|
|
pos = this.getPlayerInstance(x + cx, z + cz, true).position;
|
|
|
|
if(list.contains(pos)) {
|
|
player.connection.getLoadedChunkList().add(pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
p = p % 4;
|
|
|
|
for(int n = 0; n < r * 2; ++n) {
|
|
cx += XZ_DIRS[p][0];
|
|
cz += XZ_DIRS[p][1];
|
|
pos = this.getPlayerInstance(x + cx, z + cz, true).position;
|
|
|
|
if(list.contains(pos)) {
|
|
player.connection.getLoadedChunkList().add(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void removePlayer(EntityNPC player) {
|
|
int x = (int)player.connection.getManagedX() >> 4;
|
|
int z = (int)player.connection.getManagedZ() >> 4;
|
|
|
|
for(int cx = x - this.viewRadius; cx <= x + this.viewRadius; ++cx) {
|
|
for(int cz = z - this.viewRadius; cz <= z + this.viewRadius; ++cz) {
|
|
PlayerInstance inst = this.getPlayerInstance(cx, cz, false);
|
|
|
|
if(inst != null) {
|
|
inst.removePlayer(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.managed.remove(player);
|
|
}
|
|
|
|
private boolean overlaps(int x1, int z1, int x2, int z2, int radius) {
|
|
int dx = x1 - x2;
|
|
int dz = z1 - z2;
|
|
return dx >= -radius && dx <= radius ? dz >= -radius && dz <= radius : false;
|
|
}
|
|
|
|
public void updateMountedMovingPlayer(EntityNPC player) {
|
|
int x = (int)player.posX >> 4;
|
|
int z = (int)player.posZ >> 4;
|
|
double dx = player.connection.getManagedX() - player.posX;
|
|
double dz = player.connection.getManagedZ() - player.posZ;
|
|
double dist = dx * dx + dz * dz;
|
|
|
|
if(dist >= 64.0D) {
|
|
int px = (int)player.connection.getManagedX() >> 4;
|
|
int pz = (int)player.connection.getManagedZ() >> 4;
|
|
int r = this.viewRadius;
|
|
int mx = x - px;
|
|
int mz = z - pz;
|
|
|
|
if(mx != 0 || mz != 0) {
|
|
for(int cx = x - r; cx <= x + r; ++cx) {
|
|
for(int cz = z - r; cz <= z + r; ++cz) {
|
|
if(!this.overlaps(cx, cz, px, pz, r)) {
|
|
this.getPlayerInstance(cx, cz, true).addPlayer(player);
|
|
}
|
|
|
|
if(!this.overlaps(cx - mx, cz - mz, x, z, r)) {
|
|
PlayerInstance inst = this.getPlayerInstance(cx - mx, cz - mz, false);
|
|
|
|
if(inst != null) {
|
|
inst.removePlayer(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.filterChunkLoadQueue(player);
|
|
player.connection.setManagedPos(player.posX, player.posZ);
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isPlayerWatchingChunk(EntityNPC player, int chunkX, int chunkZ) {
|
|
PlayerInstance inst = this.getPlayerInstance(chunkX, chunkZ, false);
|
|
return inst != null && inst.watching.contains(player) && !player.connection.getLoadedChunkList().contains(inst.position);
|
|
}
|
|
|
|
public void updateViewRadius() {
|
|
int radius = ExtMath.clampi(ServerConfig.distance, 3, 128);
|
|
|
|
if(radius != this.viewRadius) {
|
|
int diff = radius - this.viewRadius;
|
|
|
|
for(EntityNPC player : Lists.newArrayList(this.managed)) {
|
|
int x = (int)player.posX >> 4;
|
|
int z = (int)player.posZ >> 4;
|
|
|
|
if(diff > 0) {
|
|
for(int cx = x - radius; cx <= x + radius; ++cx) {
|
|
for(int cz = z - radius; cz <= z + radius; ++cz) {
|
|
PlayerInstance inst = this.getPlayerInstance(cx, cz, true);
|
|
|
|
if(!inst.watching.contains(player)) {
|
|
inst.addPlayer(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for(int cx = x - this.viewRadius; cx <= x + this.viewRadius; ++cx) {
|
|
for(int cz = z - this.viewRadius; cz <= z + this.viewRadius; ++cz) {
|
|
if(!this.overlaps(cx, cz, x, z, radius)) {
|
|
this.getPlayerInstance(cx, cz, true).removePlayer(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.viewRadius = radius;
|
|
}
|
|
|
|
this.trackDistance = ServerConfig.distance * 16 - 16;
|
|
}
|
|
|
|
public void trackEntity(Entity entityIn) {
|
|
if(entityIn.getUpdateFrequency() > 0) {
|
|
this.addEntityToTracker(entityIn, entityIn.getTrackingRange(), entityIn.getUpdateFrequency(), entityIn.isSendingVeloUpdates());
|
|
}
|
|
if(entityIn.isPlayer()) {
|
|
// this.trackEntity(entityIn, 512, 2);
|
|
EntityNPC entityplayermp = (EntityNPC)entityIn;
|
|
|
|
for(EntityTrackerEntry entitytrackerentry : this.tracked) {
|
|
if(entitytrackerentry.trackedEntity != entityplayermp) {
|
|
entitytrackerentry.updatePlayerEntity(entityplayermp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addEntityToTracker(Entity entityIn, int trackingRange, final int updateFrequency, boolean sendVelocityUpdates) {
|
|
if(trackingRange > this.trackDistance) {
|
|
trackingRange = this.trackDistance;
|
|
}
|
|
|
|
try {
|
|
if(this.trackMap.containsItem(entityIn.getId())) {
|
|
throw new IllegalStateException("Objekt ist bereits auf der Tracking-Liste!");
|
|
}
|
|
|
|
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entityIn, trackingRange, updateFrequency, sendVelocityUpdates);
|
|
this.tracked.add(entitytrackerentry);
|
|
this.trackMap.addKey(entityIn.getId(), entitytrackerentry);
|
|
entitytrackerentry.updatePlayerEntities(this.players);
|
|
}
|
|
catch(Throwable throwable) {
|
|
Log.TICK.error((Throwable)throwable, (String)"Fange Objekt-Tracking-Fehler \"leise\" auf.");
|
|
}
|
|
}
|
|
|
|
public void untrackEntity(Entity entityIn) {
|
|
if(entityIn.isPlayer()) {
|
|
EntityNPC entityplayermp = (EntityNPC)entityIn;
|
|
|
|
for(EntityTrackerEntry entitytrackerentry : this.tracked) {
|
|
entitytrackerentry.removeFromTrackedPlayers(entityplayermp);
|
|
}
|
|
}
|
|
|
|
EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry)this.trackMap.removeObject(entityIn.getId());
|
|
|
|
if(entitytrackerentry1 != null) {
|
|
this.tracked.remove(entitytrackerentry1);
|
|
entitytrackerentry1.sendDestroyEntityPacketToTrackedPlayers();
|
|
}
|
|
}
|
|
|
|
public void updateTrackedEntities() {
|
|
List<EntityNPC> list = Lists.<EntityNPC>newArrayList();
|
|
|
|
for(EntityTrackerEntry entitytrackerentry : this.tracked) {
|
|
entitytrackerentry.updatePlayerList(this.players);
|
|
|
|
if(entitytrackerentry.isUpdated() && entitytrackerentry.trackedEntity.isPlayer()) {
|
|
list.add((EntityNPC)entitytrackerentry.trackedEntity);
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < ((List)list).size(); ++i) {
|
|
EntityNPC entityplayermp = list.get(i);
|
|
|
|
for(EntityTrackerEntry entitytrackerentry1 : this.tracked) {
|
|
if(entitytrackerentry1.trackedEntity != entityplayermp) {
|
|
entitytrackerentry1.updatePlayerEntity(entityplayermp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updateTrackedPlayer(EntityNPC player) {
|
|
for(EntityTrackerEntry entitytrackerentry : this.tracked) {
|
|
if(entitytrackerentry.trackedEntity == player) {
|
|
entitytrackerentry.updatePlayerEntities(this.players);
|
|
}
|
|
else {
|
|
entitytrackerentry.updatePlayerEntity(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void sendToAllTrackingEntity(Entity entityIn, Packet packet) {
|
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)this.trackMap.lookup(entityIn.getId());
|
|
|
|
if(entitytrackerentry != null) {
|
|
entitytrackerentry.sendPacketToTrackedPlayers(packet);
|
|
}
|
|
}
|
|
|
|
public void sendToAllTrackingAndSelf(Entity entityIn, Packet packet) {
|
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)this.trackMap.lookup(entityIn.getId());
|
|
|
|
if(entitytrackerentry != null) {
|
|
entitytrackerentry.sendPacketToTrackedAndSelf(packet);
|
|
}
|
|
}
|
|
|
|
public void removePlayerFromTrackers(EntityNPC player) {
|
|
for(EntityTrackerEntry entitytrackerentry : this.tracked) {
|
|
entitytrackerentry.removeTrackedPlayerSymmetric(player);
|
|
}
|
|
}
|
|
|
|
public void updateChunksForPlayer(EntityNPC player, ChunkServer chunk) {
|
|
for(EntityTrackerEntry entitytrackerentry : this.tracked) {
|
|
if(entitytrackerentry.trackedEntity != player && entitytrackerentry.trackedEntity.chunkCoordX == chunk.xPos
|
|
&& entitytrackerentry.trackedEntity.chunkCoordZ == chunk.zPos) {
|
|
entitytrackerentry.updatePlayerEntity(player);
|
|
}
|
|
}
|
|
}
|
|
|
|
public final boolean setBlock(BlockPos pos, ClipboardBlock block) {
|
|
// int x = position.getBlockX();
|
|
// int y = position.getBlockY();
|
|
// int z = position.getBlockZ();
|
|
ChunkServer chunk = this.getChunk(pos.getX() >> 4, pos.getZ() >> 4);
|
|
// BlockPos pos = new BlockPos(x, y, z);
|
|
State old = chunk.getState(pos);
|
|
State newState = block.getState();
|
|
if(newState.getBlock() instanceof BlockDoor) {
|
|
if(newState.getValue(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER) {
|
|
ItemDoor.placeDoor(this, pos, newState.getValue(BlockDoor.FACING)/* .rotateYCCW() */, newState.getBlock(), false);
|
|
}
|
|
return true;
|
|
}
|
|
State successState = chunk.setState(pos, newState);
|
|
boolean successful = successState != null;
|
|
if(successful) {
|
|
if(block.getData() != null) {
|
|
this.removeTileEntity(pos);
|
|
TagObject tag = block.getData();
|
|
tag.setString("id", tag.getString("id"));
|
|
tag.setInt("x", pos.getX());
|
|
tag.setInt("y", pos.getY());
|
|
tag.setInt("z", pos.getZ());
|
|
TileEntity tileEntity = TileEntity.createAndLoadEntity(tag);
|
|
if(tileEntity != null) {
|
|
this.setTileEntity(pos, tileEntity);
|
|
}
|
|
}
|
|
}
|
|
// if(update) {
|
|
// if(!successful)
|
|
// newState = old;
|
|
this.checkLight(pos);
|
|
if(chunk.isPopulated())
|
|
this.markBlockForUpdate(pos);
|
|
// this.notifyNeighborsRespectDebug(pos, old.getBlock());
|
|
// if(newState.getBlock().hasComparatorInputOverride())
|
|
// this.updateComparatorOutputLevel(pos, newState.getBlock());
|
|
// }
|
|
// else {
|
|
// this.markBlockForUpdate(pos);
|
|
// }
|
|
return successful;
|
|
}
|
|
|
|
public final ClipboardBlock getBlock(BlockPos pos) {
|
|
// BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
|
State state = this.getState(pos);
|
|
TileEntity tile = this.getTileEntity(pos);
|
|
if(tile != null) {
|
|
return new ClipboardBlock(state, tile);
|
|
}
|
|
else {
|
|
return new ClipboardBlock(state);
|
|
}
|
|
}
|
|
|
|
public final List<? extends Entity> getEntities() {
|
|
List<Entity> entities = Lists.newArrayList();
|
|
for(Entity entity : this.entities) {
|
|
entities.add(entity);
|
|
}
|
|
return entities;
|
|
}
|
|
|
|
public boolean isDaytime() {
|
|
return this.subtract < 4;
|
|
}
|
|
|
|
public int getSkylightSubtracted() {
|
|
return this.subtract;
|
|
}
|
|
|
|
public void setSkylightSubtracted(int newSkylightSubtracted) {
|
|
this.subtract = newSkylightSubtracted;
|
|
}
|
|
|
|
public boolean isBlockinHighHumidity(BlockPos pos) {
|
|
Biome biomegenbase = this.getBiomeGenForCoords(pos);
|
|
return biomegenbase.isHighHumidity();
|
|
}
|
|
|
|
public boolean canStrikeAt(BlockPos strikePosition) {
|
|
if(!this.canSeeSky(strikePosition)) {
|
|
return false;
|
|
}
|
|
else if(this.getPrecipitationHeight(strikePosition).getY() > strikePosition.getY()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
// else {
|
|
// Biome biomegenbase = this.getBiomeGenForCoords(strikePosition);
|
|
// return biomegenbase.canRain() || biomegenbase.isSnowyBiome();
|
|
// }
|
|
}
|
|
|
|
public int countEntities(Class<?> entityType) {
|
|
int i = 0;
|
|
|
|
for(Entity entity : this.entities) {
|
|
if( // (!(entity instanceof EntityLiving) || !((EntityLiving)entity).isNotDespawning())
|
|
/* && */ entityType.isAssignableFrom(entity.getClass())) {
|
|
++i;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// public Entity getEntityByPID(long id) {
|
|
// for(Entity entity : this.entities) {
|
|
// if(entity.hasPersistentId() && entity.getPersistentId() == id) {
|
|
// return entity;
|
|
// }
|
|
// }
|
|
// return null;
|
|
// }
|
|
|
|
public <T extends Entity> T findNearestEntityWithinAABB(Class<? extends T> entityType, BoundingBox aabb, T closestTo) {
|
|
List<T> list = this.<T>getEntitiesWithinAABB(entityType, aabb);
|
|
T t = null;
|
|
double d0 = Double.MAX_VALUE;
|
|
|
|
for(int i = 0; i < list.size(); ++i) {
|
|
T t1 = list.get(i);
|
|
|
|
if(t1 != closestTo) { // && EntitySelectors.NOT_SPECTATING.apply(t1)) {
|
|
double d1 = closestTo.getDistanceSqToEntity(t1);
|
|
|
|
if(d1 <= d0) {
|
|
t = t1;
|
|
d0 = d1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
public boolean canPlaceFireAt(BlockPos pos) {
|
|
if(!this.canBurnAt(pos)) {
|
|
return false;
|
|
}
|
|
if(pos.getY() >= -World.MAX_SIZE_Y && pos.getY() < World.MAX_SIZE_Y) {
|
|
Block block = this.getState(pos).getBlock();
|
|
|
|
if(block == Blocks.air && Blocks.fire.canPlaceBlockAt(this, pos)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public boolean canBlockFreeze(BlockPos pos, boolean noWaterAdj) {
|
|
if(!this.canFreezeAt(pos)) {
|
|
return false;
|
|
}
|
|
else {
|
|
if(pos.getY() >= -World.MAX_SIZE_Y && pos.getY() < World.MAX_SIZE_Y && this.getLightFor(LightType.BLOCK, pos) < 10) {
|
|
State iblockstate = this.getState(pos);
|
|
Block block = iblockstate.getBlock();
|
|
|
|
if((block == Blocks.water || block == Blocks.flowing_water) && ((Integer)iblockstate.getValue(BlockLiquid.LEVEL)).intValue() == 0) {
|
|
if(!noWaterAdj) {
|
|
return true;
|
|
}
|
|
|
|
boolean flag = this.getState(pos.west()).getBlock().getMaterial() == Material.WATER &&
|
|
this.getState(pos.east()).getBlock().getMaterial() == Material.WATER &&
|
|
this.getState(pos.north()).getBlock().getMaterial() == Material.WATER &&
|
|
this.getState(pos.south()).getBlock().getMaterial() == Material.WATER;
|
|
|
|
if(!flag) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean checkBlockCollision(BoundingBox bb) {
|
|
int i = ExtMath.floord(bb.minX);
|
|
int j = ExtMath.floord(bb.maxX);
|
|
int k = ExtMath.floord(bb.minY);
|
|
int l = ExtMath.floord(bb.maxY);
|
|
int i1 = ExtMath.floord(bb.minZ);
|
|
int j1 = ExtMath.floord(bb.maxZ);
|
|
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
|
|
|
|
for(int k1 = i; k1 <= j; ++k1) {
|
|
for(int l1 = k; l1 <= l; ++l1) {
|
|
for(int i2 = i1; i2 <= j1; ++i2) {
|
|
Block block = this.getState(blockpos$mutableblockpos.set(k1, l1, i2)).getBlock();
|
|
|
|
if(block != Blocks.air) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public BlockPos getTopSolidOrLiquidBlock(BlockPos pos) {
|
|
ChunkServer chunk = this.getChunk(pos);
|
|
int h = chunk.getTopSegment();
|
|
if(h == Integer.MIN_VALUE)
|
|
return new BlockPos(pos.getX(), 0, pos.getZ());
|
|
BlockPos blockpos = new BlockPos(pos.getX(), h + 16, pos.getZ());
|
|
BlockPos blockpos1;
|
|
h = chunk.getBottomSegment();
|
|
if(blockpos.getY() - h > 512)
|
|
h = blockpos.getY() - 512;
|
|
|
|
for(; blockpos.getY() >= h; blockpos = blockpos1) {
|
|
blockpos1 = blockpos.down();
|
|
Material material = chunk.getBlock(blockpos1).getMaterial();
|
|
|
|
if(material.blocksMovement() && material != Material.LEAVES) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return blockpos;
|
|
}
|
|
|
|
public void removePlayerEntityDangerously(Entity entityIn) {
|
|
entityIn.setDead();
|
|
|
|
if(entityIn.isPlayer()) {
|
|
this.players.remove(entityIn);
|
|
}
|
|
|
|
int i = entityIn.chunkCoordX;
|
|
int j = entityIn.chunkCoordZ;
|
|
|
|
if(entityIn.addedToChunk && this.isLoaded(i, j, true)) {
|
|
this.getChunk(i, j).removeEntity(entityIn);
|
|
}
|
|
|
|
this.entities.remove(entityIn);
|
|
this.onEntityRemoved(entityIn);
|
|
}
|
|
|
|
public Block getGroundAboveSeaLevel(BlockPos pos) {
|
|
BlockPos blockpos;
|
|
|
|
for(blockpos = new BlockPos(pos.getX(), this.getSeaLevel(), pos.getZ()); !this.isAirBlock(blockpos.up()); blockpos = blockpos.up()) {
|
|
;
|
|
}
|
|
|
|
return this.getState(blockpos).getBlock();
|
|
}
|
|
|
|
public long getTime() {
|
|
return this.time;
|
|
}
|
|
|
|
private File getSaveFile(String id) {
|
|
return new File(this.chunkDir, id.toLowerCase() + ".cdt");
|
|
}
|
|
|
|
private WorldSavedData loadData(String id) {
|
|
WorldSavedData data = this.dataMap.get(id);
|
|
if(data != null)
|
|
return data;
|
|
try {
|
|
File file = this.getSaveFile(id);
|
|
if(file.exists()) {
|
|
data = new WorldSavedData(id, TagObject.readGZip(file));
|
|
}
|
|
}
|
|
catch(Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
if(data != null) {
|
|
this.dataMap.put(id, data);
|
|
this.dataList.add(data);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
private void setData(String id, WorldSavedData data) {
|
|
if(this.dataMap.containsKey(id)) {
|
|
this.dataList.remove(this.dataMap.remove(id));
|
|
}
|
|
this.dataMap.put(id, data);
|
|
this.dataList.add(data);
|
|
}
|
|
|
|
private void saveData(WorldSavedData data) {
|
|
try {
|
|
File file = this.getSaveFile(data.id);
|
|
TagObject.writeGZip(data.tag, file);
|
|
}
|
|
catch(Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
// public boolean canBlockSeeSky(BlockPos pos) {
|
|
// if(pos.getY() >= this.getSeaLevel()) {
|
|
// return this.canSeeSky(pos);
|
|
// }
|
|
// else {
|
|
// BlockPos blockpos = new BlockPos(pos.getX(), this.getSeaLevel(), pos.getZ());
|
|
//
|
|
// if(!this.canSeeSky(blockpos)) {
|
|
// return false;
|
|
// }
|
|
// else {
|
|
// for(blockpos = blockpos.down(); blockpos.getY() > pos.getY(); blockpos = blockpos.down()) {
|
|
// Block block = this.getBlockState(blockpos).getBlock();
|
|
//
|
|
// if(block.getLightOpacity() > 0 && !block.getMaterial().isLiquid()) {
|
|
// return false;
|
|
// }
|
|
// }
|
|
//
|
|
// return true;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
public boolean makePortal(BlockPos pos, int maxHeight, PortalType type) // TODO: Portals
|
|
{
|
|
return false;
|
|
// int i = 16;
|
|
// double d0 = -1.0D;
|
|
// int j = pos.getX();
|
|
// int k = pos.getY();
|
|
// int l = pos.getZ();
|
|
// int i1 = j;
|
|
// int j1 = k;
|
|
// int k1 = l;
|
|
// int l1 = 0;
|
|
// int i2 = world.rand.zrange(4);
|
|
// BlockPos.MutableBlockPos bpos = new BlockPos.MutableBlockPos();
|
|
//
|
|
// for (int j2 = j - i; j2 <= j + i; ++j2)
|
|
// {
|
|
// double d1 = (double)j2 + 0.5D - (double)j;
|
|
//
|
|
// for (int l2 = l - i; l2 <= l + i; ++l2)
|
|
// {
|
|
// double d2 = (double)l2 + 0.5D - (double)l;
|
|
// label142:
|
|
//
|
|
// for (int j3 = maxHeight - 1; j3 >= 0; --j3)
|
|
// {
|
|
// if (world.isAirBlock(bpos.set(j2, j3, l2)))
|
|
// {
|
|
// while (j3 > 0 && world.isAirBlock(bpos.set(j2, j3 - 1, l2)))
|
|
// {
|
|
// --j3;
|
|
// }
|
|
//
|
|
// for (int k3 = i2; k3 < i2 + 4; ++k3)
|
|
// {
|
|
// int l3 = k3 % 2;
|
|
// int i4 = 1 - l3;
|
|
//
|
|
// if (k3 % 4 >= 2)
|
|
// {
|
|
// l3 = -l3;
|
|
// i4 = -i4;
|
|
// }
|
|
//
|
|
// for (int j4 = 0; j4 < 3; ++j4)
|
|
// {
|
|
// for (int k4 = 0; k4 < 4; ++k4)
|
|
// {
|
|
// for (int l4 = -1; l4 < 4; ++l4)
|
|
// {
|
|
// int i5 = j2 + (k4 - 1) * l3 + j4 * i4;
|
|
// int j5 = j3 + l4;
|
|
// int k5 = l2 + (k4 - 1) * i4 - j4 * l3;
|
|
// bpos.set(i5, j5, k5);
|
|
//
|
|
// if (l4 < 0 && !world.getBlockState(bpos).getBlock().getMaterial().isSolid()
|
|
// || l4 >= 0 && !world.isAirBlock(bpos))
|
|
// {
|
|
// continue label142;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// double d5 = (double)j3 + 0.5D - (double)k;
|
|
// double d7 = d1 * d1 + d5 * d5 + d2 * d2;
|
|
//
|
|
// if (d0 < 0.0D || d7 < d0)
|
|
// {
|
|
// d0 = d7;
|
|
// i1 = j2;
|
|
// j1 = j3;
|
|
// k1 = l2;
|
|
// l1 = k3 % 4;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (d0 < 0.0D)
|
|
// {
|
|
// for (int l5 = j - i; l5 <= j + i; ++l5)
|
|
// {
|
|
// double d3 = (double)l5 + 0.5D - (double)j;
|
|
//
|
|
// for (int j6 = l - i; j6 <= l + i; ++j6)
|
|
// {
|
|
// double d4 = (double)j6 + 0.5D - (double)l;
|
|
// label562:
|
|
//
|
|
// for (int i7 = maxHeight - 1; i7 >= 0; --i7)
|
|
// {
|
|
// if (world.isAirBlock(bpos.set(l5, i7, j6)))
|
|
// {
|
|
// while (i7 > 0 && world.isAirBlock(bpos.set(l5, i7 - 1, j6)))
|
|
// {
|
|
// --i7;
|
|
// }
|
|
//
|
|
// for (int k7 = i2; k7 < i2 + 2; ++k7)
|
|
// {
|
|
// int j8 = k7 % 2;
|
|
// int j9 = 1 - j8;
|
|
//
|
|
// for (int j10 = 0; j10 < 4; ++j10)
|
|
// {
|
|
// for (int j11 = -1; j11 < 4; ++j11)
|
|
// {
|
|
// int j12 = l5 + (j10 - 1) * j8;
|
|
// int i13 = i7 + j11;
|
|
// int j13 = j6 + (j10 - 1) * j9;
|
|
// bpos.set(j12, i13, j13);
|
|
//
|
|
// if (j11 < 0 && !world.getBlockState(bpos).getBlock().getMaterial().isSolid()
|
|
// || j11 >= 0 && !world.isAirBlock(bpos))
|
|
// {
|
|
// continue label562;
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// double d6 = (double)i7 + 0.5D - (double)k;
|
|
// double d8 = d3 * d3 + d6 * d6 + d4 * d4;
|
|
//
|
|
// if (d0 < 0.0D || d8 < d0)
|
|
// {
|
|
// d0 = d8;
|
|
// i1 = l5;
|
|
// j1 = i7;
|
|
// k1 = j6;
|
|
// l1 = k7 % 2;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// int i6 = i1;
|
|
// int k2 = j1;
|
|
// int k6 = k1;
|
|
// int l6 = l1 % 2;
|
|
// int i3 = 1 - l6;
|
|
//
|
|
// if (l1 % 4 >= 2)
|
|
// {
|
|
// l6 = -l6;
|
|
// i3 = -i3;
|
|
// }
|
|
//
|
|
// if (d0 < 0.0D)
|
|
// {
|
|
// j1 = MathHelper.clamp_int(j1, 70, maxHeight - 10);
|
|
// k2 = j1;
|
|
//
|
|
// for (int j7 = -1; j7 <= 1; ++j7)
|
|
// {
|
|
// for (int l7 = 1; l7 < 3; ++l7)
|
|
// {
|
|
// for (int k8 = -1; k8 < 3; ++k8)
|
|
// {
|
|
// int k9 = i6 + (l7 - 1) * l6 + j7 * i3;
|
|
// int k10 = k2 + k8;
|
|
// int k11 = k6 + (l7 - 1) * i3 - j7 * l6;
|
|
// boolean flag = k8 < 0;
|
|
// world.setBlockState(new BlockPos(k9, k10, k11), flag ? Blocks.obsidian.getDefaultState() :
|
|
// Blocks.air.getDefaultState());
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// IBlockState state = Blocks.portal.getDefaultState().withProperty(BlockPortal.AXIS, l6 != 0 ? EnumFacing.Axis.X :
|
|
// EnumFacing.Axis.Z).withProperty(BlockPortal.DIM, dim);
|
|
//
|
|
// for (int i8 = 0; i8 < 4; ++i8)
|
|
// {
|
|
// for (int l8 = 0; l8 < 4; ++l8)
|
|
// {
|
|
// for (int l9 = -1; l9 < 4; ++l9)
|
|
// {
|
|
// int l10 = i6 + (l8 - 1) * l6;
|
|
// int l11 = k2 + l9;
|
|
// int k12 = k6 + (l8 - 1) * i3;
|
|
// boolean flag1 = l8 == 0 || l8 == 3 || l9 == -1 || l9 == 3;
|
|
// world.setBlockState(new BlockPos(l10, l11, k12), flag1 ? Blocks.obsidian.getDefaultState() : state, 2);
|
|
// }
|
|
// }
|
|
//
|
|
// for (int i9 = 0; i9 < 4; ++i9)
|
|
// {
|
|
// for (int i10 = -1; i10 < 4; ++i10)
|
|
// {
|
|
// int i11 = i6 + (i9 - 1) * l6;
|
|
// int i12 = k2 + i10;
|
|
// int l12 = k6 + (i9 - 1) * i3;
|
|
// BlockPos blockpos = new BlockPos(i11, i12, l12);
|
|
// world.notifyNeighborsOfStateChange(blockpos, world.getBlockState(blockpos).getBlock());
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// return true;
|
|
}
|
|
|
|
public List<IPlayer> getAllPlayers() {
|
|
return (List)this.server.getPlayers();
|
|
}
|
|
|
|
public void placeInDimension(Entity entity, AWorldServer oldWorld, AWorldServer world, BlockPos pos, PortalType portal) {
|
|
this.server.placeInDimension(entity, (WorldServer)oldWorld, (WorldServer)world, pos, portal);
|
|
}
|
|
|
|
public AWorldServer getOtherWorld(int dimension) {
|
|
return this.server.getWorld(dimension);
|
|
}
|
|
|
|
public void markChunkDirty(BlockPos pos) {
|
|
if(this.isBlockLoaded(pos))
|
|
this.getChunk(pos).setModified(true);
|
|
}
|
|
|
|
private static class EventList extends ArrayList<TickEvent> {
|
|
private EventList() {
|
|
}
|
|
}
|
|
|
|
public static class WorldSavedData {
|
|
public final String id;
|
|
public final TagObject tag;
|
|
|
|
public boolean dirty;
|
|
|
|
public WorldSavedData(String id, TagObject tag) {
|
|
this.id = id;
|
|
this.tag = tag;
|
|
}
|
|
}
|
|
|
|
private static SPacketMultiBlockChange getPacket(int amount, long[] list, ChunkServer chunk) {
|
|
ChunkPos pos = new ChunkPos(chunk.xPos, chunk.zPos);
|
|
SPacketMultiBlockChange.BlockUpdateData[] changes = new SPacketMultiBlockChange.BlockUpdateData[amount];
|
|
for(int z = 0; z < changes.length; z++) {
|
|
changes[z] = new SPacketMultiBlockChange.BlockUpdateData(list[z], chunk.getState(SPacketMultiBlockChange.getPos(pos, list[z])));
|
|
}
|
|
return new SPacketMultiBlockChange(pos, changes);
|
|
}
|
|
|
|
private class PlayerInstance {
|
|
private final List<EntityNPC> watching = Lists.<EntityNPC>newArrayList();
|
|
private final Set<Integer> extend = Sets.newHashSet();
|
|
private final long[] changes = new long[64];
|
|
private final ChunkPos position;
|
|
|
|
private int updates;
|
|
private long prevTime;
|
|
private boolean biomes;
|
|
|
|
public PlayerInstance(int chunkX, int chunkZ) {
|
|
this.position = new ChunkPos(chunkX, chunkZ);
|
|
WorldServer.this.loadChunk(chunkX, chunkZ);
|
|
}
|
|
|
|
public void addPlayer(EntityNPC player) {
|
|
if(this.watching.contains(player)) {
|
|
Log.TICK.warn("Konnte Spieler nicht hinzufügen. #" + player.getId() + " ist bereits in Chunk " + this.position.x + ", "
|
|
+ this.position.z);
|
|
}
|
|
else {
|
|
if(this.watching.isEmpty()) {
|
|
this.prevTime = WorldServer.this.time;
|
|
}
|
|
|
|
this.watching.add(player);
|
|
player.connection.getLoadedChunkList().add(this.position);
|
|
}
|
|
}
|
|
|
|
public void removePlayer(EntityNPC player) {
|
|
if(this.watching.contains(player)) {
|
|
ChunkServer chunk = WorldServer.this.getChunk(this.position.x, this.position.z);
|
|
|
|
if(chunk.isPopulated()) {
|
|
player.connection.sendPacket(Player.getPacket(chunk, true, new int[0], !WorldServer.this.dimension.hasNoLight()));
|
|
}
|
|
|
|
this.watching.remove(player);
|
|
player.connection.getLoadedChunkList().remove(this.position);
|
|
|
|
if(this.watching.isEmpty()) {
|
|
long v = (long)this.position.x + 2147483647L | (long)this.position.z + 2147483647L << 32;
|
|
this.increaseInhabitedTime(chunk);
|
|
WorldServer.this.instances.remove(v);
|
|
WorldServer.this.instList.remove(this);
|
|
|
|
if(this.updates > 0) {
|
|
WorldServer.this.toUpdate.remove(this);
|
|
}
|
|
|
|
WorldServer.this.dropChunk(this.position.x, this.position.z);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void processChunk() {
|
|
this.increaseInhabitedTime(WorldServer.this.getChunk(this.position.x, this.position.z));
|
|
}
|
|
|
|
public void resend() {
|
|
if(this.updates == 0)
|
|
WorldServer.this.toUpdate.add(this);
|
|
this.updates = 64;
|
|
this.biomes = true;
|
|
}
|
|
|
|
private void increaseInhabitedTime(ChunkServer chunk) {
|
|
chunk.setInhabited(chunk.getInhabited() + WorldServer.this.time - this.prevTime);
|
|
this.prevTime = WorldServer.this.time;
|
|
}
|
|
|
|
public void flagChunkForUpdate(int x, int y, int z) {
|
|
if(this.updates == 0) {
|
|
WorldServer.this.toUpdate.add(this);
|
|
}
|
|
|
|
this.extend.add(y >> 4);
|
|
|
|
if(this.updates < 64) {
|
|
long pos = ((long)x & 4294967295L) << 36 | ((long)z & 4294967295L) << 32 | (y & 4294967295L);
|
|
|
|
for(int i = 0; i < this.updates; ++i) {
|
|
if(this.changes[i] == pos) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.changes[this.updates++] = pos;
|
|
}
|
|
}
|
|
|
|
public void sendToAllPlayersWatchingChunk(Packet packet) {
|
|
for(int z = 0; z < this.watching.size(); ++z) {
|
|
EntityNPC player = this.watching.get(z);
|
|
|
|
if(!player.connection.getLoadedChunkList().contains(this.position)) {
|
|
player.connection.sendPacket(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void onUpdate() {
|
|
if(this.updates != 0) {
|
|
if(this.updates == 1) {
|
|
int x = (int)(this.changes[0] >> 36 & 15L) + this.position.x * 16;
|
|
int y = (int)(this.changes[0] & 4294967295L);
|
|
int z = (int)(this.changes[0] >> 32 & 15L) + this.position.z * 16;
|
|
BlockPos pos = new BlockPos(x, y, z);
|
|
this.sendToAllPlayersWatchingChunk(new SPacketBlockChange(WorldServer.this, pos));
|
|
|
|
if(WorldServer.this.getState(pos).getBlock().hasTileEntity()) {
|
|
this.sendTileToAllPlayersWatchingChunk(WorldServer.this.getTileEntity(pos));
|
|
}
|
|
}
|
|
else if(this.updates == 64) {
|
|
int x = this.position.x * 16;
|
|
int z = this.position.z * 16;
|
|
int[] extend = null;
|
|
if(!this.biomes) {
|
|
extend = new int[this.extend.size()];
|
|
int n = 0;
|
|
for(Integer i : this.extend) {
|
|
extend[n++] = i;
|
|
}
|
|
}
|
|
this.sendToAllPlayersWatchingChunk(Player.getPacket(WorldServer.this.getChunk(this.position.x, this.position.z),
|
|
this.biomes, extend, !WorldServer.this.dimension.hasNoLight()));
|
|
|
|
if(this.biomes) {
|
|
List<TileEntity> list = WorldServer.this.getTileEntitiesIn(x, Integer.MIN_VALUE, z, x + 16, Integer.MAX_VALUE, z + 16);
|
|
|
|
for(int n = 0; n < list.size(); ++n) {
|
|
this.sendTileToAllPlayersWatchingChunk(list.get(n));
|
|
}
|
|
}
|
|
else {
|
|
for(Integer cy : this.extend) {
|
|
int y = cy << 4;
|
|
List<TileEntity> list = WorldServer.this.getTileEntitiesIn(x, y, z, x + 16, y + 16, z + 16);
|
|
|
|
for(int n = 0; n < list.size(); ++n) {
|
|
this.sendTileToAllPlayersWatchingChunk(list.get(n));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
this.sendToAllPlayersWatchingChunk(getPacket(this.updates, this.changes,
|
|
WorldServer.this.getChunk(this.position.x, this.position.z)));
|
|
|
|
for(int n = 0; n < this.updates; ++n) {
|
|
int x = (int)(this.changes[n] >> 36 & 15L) + this.position.x * 16;
|
|
int y = (int)(this.changes[n] & 4294967295L);
|
|
int z = (int)(this.changes[n] >> 32 & 15L) + this.position.z * 16;
|
|
BlockPos pos = new BlockPos(x, y, z);
|
|
|
|
if(WorldServer.this.getState(pos).getBlock().hasTileEntity()) {
|
|
this.sendTileToAllPlayersWatchingChunk(WorldServer.this.getTileEntity(pos));
|
|
}
|
|
}
|
|
}
|
|
|
|
this.updates = 0;
|
|
this.extend.clear();
|
|
this.biomes = false;
|
|
}
|
|
}
|
|
|
|
private void sendTileToAllPlayersWatchingChunk(TileEntity te) {
|
|
if(te != null) {
|
|
Packet packet = te.getDescriptionPacket();
|
|
|
|
if(packet != null) {
|
|
this.sendToAllPlayersWatchingChunk(packet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|