432 lines
11 KiB
Java
Executable file
432 lines
11 KiB
Java
Executable file
package common.world;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.function.Predicate;
|
|
|
|
import common.block.Block;
|
|
import common.block.ITileEntityProvider;
|
|
import common.block.Material;
|
|
import common.collect.Maps;
|
|
import common.collect.Sets;
|
|
import common.entity.Entity;
|
|
import common.init.Blocks;
|
|
import common.log.Log;
|
|
import common.tileentity.TileEntity;
|
|
import common.util.BlockPos;
|
|
import common.util.BoundingBox;
|
|
import common.util.InheritanceMultiMap;
|
|
import common.util.ExtMath;
|
|
import common.util.IntHashMap;
|
|
|
|
public abstract class Chunk {
|
|
public final int xPos;
|
|
public final int zPos;
|
|
protected final World world;
|
|
protected final State filler;
|
|
protected final Block fillerBlock;
|
|
protected final IntHashMap<BlockArray> blocks = new IntHashMap();
|
|
protected final Set<BlockArray> blockList = Sets.newHashSet();
|
|
protected final int[] precHeight = new int[256];
|
|
protected final int[] height = new int[256];
|
|
protected final Map<BlockPos, TileEntity> tiles = Maps.<BlockPos, TileEntity>newHashMap();
|
|
protected final InheritanceMultiMap<Entity>[] entities = new InheritanceMultiMap[32];
|
|
|
|
protected boolean loaded;
|
|
protected boolean modified;
|
|
protected boolean hasEntity;
|
|
protected int minHeight;
|
|
protected int bottom = Integer.MAX_VALUE;
|
|
protected int top = Integer.MIN_VALUE;
|
|
|
|
public Chunk(World world, int x, int z) {
|
|
this.world = world;
|
|
this.xPos = x;
|
|
this.zPos = z;
|
|
int size = world.dimension.getSize() / 16;
|
|
this.filler = x < -size || z < -size || x >= size || z >= size ? Blocks.air.getState() : (world.dimension.isExterminated() ? Blocks.coal_block.getState() : world.dimension.getFiller());
|
|
this.fillerBlock = this.filler.getBlock();
|
|
for(int y = 0; y < this.entities.length; ++y) {
|
|
this.entities[y] = new InheritanceMultiMap(Entity.class);
|
|
}
|
|
Arrays.fill(this.precHeight, Integer.MIN_VALUE);
|
|
}
|
|
|
|
public int getHeight(int x, int z) {
|
|
return this.height[z << 4 | x];
|
|
}
|
|
|
|
public BlockArray getArray(int y) {
|
|
return this.blocks.lookup(y);
|
|
}
|
|
|
|
protected void setArray(BlockArray array) {
|
|
int y = array.getY() >> 4;
|
|
this.blocks.addKey(y, array);
|
|
this.blockList.add(array);
|
|
y <<= 4;
|
|
this.bottom = y < this.bottom ? y : this.bottom;
|
|
this.top = y > this.top ? y : this.top;
|
|
}
|
|
|
|
public void genHeightMap() {
|
|
int top = this.top;
|
|
int bottom = this.bottom < -64 ? -64 : this.bottom;
|
|
this.minHeight = Integer.MAX_VALUE;
|
|
|
|
for(int x = 0; x < 16; ++x) {
|
|
for(int z = 0; z < 16; ++z) {
|
|
this.precHeight[x + (z << 4)] = Integer.MIN_VALUE;
|
|
|
|
for(int y = top + 16; y > bottom; --y) {
|
|
if(this.getOpacity(x, y - 1, z) != 0) {
|
|
this.height[z << 4 | x] = y;
|
|
|
|
if(y < this.minHeight) {
|
|
this.minHeight = y;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.modified = true;
|
|
}
|
|
|
|
private void updateHeight(int x, int y, int z) {
|
|
int h = this.height[z << 4 | x];
|
|
int min = this.bottom < -64 ? -64 : this.bottom;
|
|
int cy = h;
|
|
|
|
if(y > h) {
|
|
cy = y;
|
|
}
|
|
|
|
while(cy > min && this.getOpacity(x, cy - 1, z) == 0) {
|
|
--cy;
|
|
}
|
|
|
|
if(cy != h) {
|
|
this.world.markBlocksDirtyVertical(x + this.xPos * 16, z + this.zPos * 16, cy, h);
|
|
this.height[z << 4 | x] = cy;
|
|
int cx = this.xPos * 16 + x;
|
|
int cz = this.zPos * 16 + z;
|
|
|
|
int sh = this.height[z << 4 | x];
|
|
int sy = h;
|
|
int ey = sh;
|
|
|
|
if(sh < h) {
|
|
sy = sh;
|
|
ey = h;
|
|
}
|
|
|
|
if(sh < this.minHeight) {
|
|
this.minHeight = sh;
|
|
}
|
|
|
|
this.modified = true;
|
|
}
|
|
}
|
|
|
|
private int getOpacity(BlockPos pos) {
|
|
return this.getBlock(pos).getLightOpacity();
|
|
}
|
|
|
|
private int getOpacity(int x, int y, int z) {
|
|
return this.getBlock0(x, y, z).getLightOpacity();
|
|
}
|
|
|
|
protected Block getBlock0(int x, int y, int z) {
|
|
BlockArray stor = this.getArray(y >> 4);
|
|
return stor != null ? stor.getBlock(x, y & 15, z) : (y < 0 ? this.fillerBlock : Blocks.air);
|
|
}
|
|
|
|
public State getState(BlockPos pos) {
|
|
BlockArray stor = this.getArray(pos.getY() >> 4);
|
|
return stor != null ? stor.get(pos.getX() & 15, pos.getY() & 15, pos.getZ() & 15) : (pos.getY() < 0 ? this.filler : Blocks.air.getState());
|
|
}
|
|
|
|
public Block getBlock(BlockPos pos) {
|
|
return this.getBlock0(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
|
|
}
|
|
|
|
public State setState(BlockPos pos, State state, boolean updateHeight) {
|
|
int x = pos.getX() & 15;
|
|
int y = pos.getY();
|
|
int z = pos.getZ() & 15;
|
|
int o = z << 4 | x;
|
|
|
|
if(y >= this.precHeight[o] - 1) {
|
|
this.precHeight[o] = Integer.MIN_VALUE;
|
|
}
|
|
|
|
int h = this.height[o];
|
|
State old = this.getState(pos);
|
|
|
|
if(old == state) {
|
|
return null;
|
|
}
|
|
else {
|
|
Block block = state.getBlock();
|
|
Block oldb = old.getBlock();
|
|
BlockArray stor = this.getArray(y >> 4);
|
|
boolean up = false;
|
|
|
|
if(stor == null) {
|
|
if(block == Blocks.air && (y >= 0 || this.fillerBlock == Blocks.air)) {
|
|
return null;
|
|
}
|
|
|
|
stor = new BlockArray(y >> 4 << 4, y < 0 ? this.filler : null);
|
|
this.setArray(stor);
|
|
up = y >= h;
|
|
}
|
|
|
|
stor.set(x, y & 15, z, state);
|
|
|
|
if(oldb != block) {
|
|
if(!this.world.client) {
|
|
oldb.onRemoved((AWorldServer)this.world, pos, old);
|
|
}
|
|
if(oldb instanceof ITileEntityProvider) {
|
|
this.world.removeTileEntity(pos);
|
|
}
|
|
}
|
|
|
|
if(stor.getBlock(x, y & 15, z) != block) {
|
|
return null;
|
|
}
|
|
else {
|
|
if(updateHeight) {
|
|
if(up) {
|
|
this.genHeightMap();
|
|
}
|
|
else {
|
|
int b = block.getLightOpacity();
|
|
int ob = oldb.getLightOpacity();
|
|
|
|
if(b > 0) {
|
|
if(y >= h) {
|
|
this.updateHeight(x, y + 1, z);
|
|
}
|
|
}
|
|
else if(y == h - 1) {
|
|
this.updateHeight(x, y, z);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(oldb instanceof ITileEntityProvider) {
|
|
TileEntity tile = this.getTileEntity(pos, TileEntity.CreateMode.CHECK);
|
|
|
|
if(tile != null) {
|
|
tile.resetBlock();
|
|
}
|
|
}
|
|
|
|
if(!this.world.client && oldb != block) {
|
|
block.onAdded((AWorldServer)this.world, pos, state);
|
|
}
|
|
|
|
if(block instanceof ITileEntityProvider) {
|
|
TileEntity tile = this.getTileEntity(pos, TileEntity.CreateMode.CHECK);
|
|
|
|
if(tile == null) {
|
|
tile = ((ITileEntityProvider)block).createNewTileEntity();
|
|
this.world.setTileEntity(pos, tile);
|
|
}
|
|
|
|
if(tile != null) {
|
|
tile.resetBlock();
|
|
}
|
|
}
|
|
|
|
this.modified = true;
|
|
return old;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void addEntity(Entity entity) {
|
|
this.hasEntity = true;
|
|
int x = ExtMath.floord(entity.posX / 16.0D);
|
|
int z = ExtMath.floord(entity.posZ / 16.0D);
|
|
|
|
if(x != this.xPos || z != this.zPos) {
|
|
Log.TICK.warn("Falsche Position! (" + x + ", " + z + ") sollte (" + this.xPos + ", " + this.zPos + ") sein, " + entity);
|
|
entity.setDead();
|
|
}
|
|
|
|
int y = ExtMath.floord(entity.posY / 16.0D);
|
|
|
|
entity.addedToChunk = true;
|
|
entity.chunkCoordX = this.xPos;
|
|
entity.chunkCoordY = y;
|
|
entity.chunkCoordZ = this.zPos;
|
|
|
|
if(y < 0) {
|
|
y = 0;
|
|
}
|
|
|
|
if(y >= this.entities.length) {
|
|
y = this.entities.length - 1;
|
|
}
|
|
|
|
this.entities[y].add(entity);
|
|
}
|
|
|
|
public void removeEntity(Entity entity) {
|
|
int y = entity.chunkCoordY;
|
|
|
|
if(y < 0) {
|
|
y = 0;
|
|
}
|
|
|
|
if(y >= this.entities.length) {
|
|
y = this.entities.length - 1;
|
|
}
|
|
|
|
this.entities[y].remove(entity);
|
|
}
|
|
|
|
protected TileEntity createNewTileEntity(BlockPos pos) {
|
|
Block block = this.getBlock(pos);
|
|
return !(block instanceof ITileEntityProvider provider) ? null : provider.createNewTileEntity();
|
|
}
|
|
|
|
public TileEntity getTileEntity(BlockPos pos, TileEntity.CreateMode type) {
|
|
TileEntity tile = this.tiles.get(pos);
|
|
|
|
if(tile == null) {
|
|
if(type == TileEntity.CreateMode.IMMEDIATE) {
|
|
tile = this.createNewTileEntity(pos);
|
|
this.world.setTileEntity(pos, tile);
|
|
}
|
|
}
|
|
else if(tile.isInvalid()) {
|
|
this.tiles.remove(pos);
|
|
return null;
|
|
}
|
|
|
|
return tile;
|
|
}
|
|
|
|
public void addTileEntity(BlockPos pos, TileEntity tile) {
|
|
tile.setWorld(this.world);
|
|
tile.setPos(pos);
|
|
|
|
if(this.getBlock(pos) instanceof ITileEntityProvider) {
|
|
if(this.tiles.containsKey(pos)) {
|
|
this.tiles.get(pos).invalidate();
|
|
}
|
|
|
|
if(tile.validate()) {
|
|
this.modified = true;
|
|
}
|
|
this.tiles.put(pos, tile);
|
|
}
|
|
}
|
|
|
|
public void removeTileEntity(BlockPos pos) {
|
|
if(this.loaded) {
|
|
TileEntity tile = this.tiles.remove(pos);
|
|
|
|
if(tile != null) {
|
|
tile.invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void onChunkUnload() {
|
|
this.loaded = false;
|
|
|
|
for(TileEntity tile : this.tiles.values()) {
|
|
this.world.markTileEntityForRemoval(tile);
|
|
}
|
|
|
|
for(int n = 0; n < this.entities.length; ++n) {
|
|
this.world.unloadEntities(this.entities[n]);
|
|
}
|
|
}
|
|
|
|
public void getEntities(Entity exclude, BoundingBox bb, List<Entity> list, Predicate<? super Entity> pred) {
|
|
int sy = ExtMath.floord((bb.minY - 2.0D) / 16.0D);
|
|
int ey = ExtMath.floord((bb.maxY + 2.0D) / 16.0D);
|
|
sy = ExtMath.clampi(sy, 0, this.entities.length - 1);
|
|
ey = ExtMath.clampi(ey, 0, this.entities.length - 1);
|
|
|
|
for(int y = sy; y <= ey; ++y) {
|
|
if(!this.entities[y].isEmpty()) {
|
|
for(Entity entity : this.entities[y]) {
|
|
if(entity.getEntityBoundingBox().intersectsWith(bb) && entity != exclude) {
|
|
if(pred == null || pred.test(entity))
|
|
list.add(entity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public <T extends Entity> void getEntities(Class<? extends T> clazz, BoundingBox bb, List<T> list, Predicate<? super T> pred) {
|
|
int sy = ExtMath.floord((bb.minY - 2.0D) / 16.0D);
|
|
int ey = ExtMath.floord((bb.maxY + 2.0D) / 16.0D);
|
|
sy = ExtMath.clampi(sy, 0, this.entities.length - 1);
|
|
ey = ExtMath.clampi(ey, 0, this.entities.length - 1);
|
|
|
|
for(int y = sy; y <= ey; ++y) {
|
|
for(T entity : this.entities[y].getByClass(clazz)) {
|
|
if(entity.getEntityBoundingBox().intersectsWith(bb) && (pred == null || pred.test(entity))) {
|
|
list.add(entity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public BlockPos getPrecipitation(BlockPos pos) {
|
|
int x = pos.getX() & 15;
|
|
int z = pos.getZ() & 15;
|
|
int o = x | z << 4;
|
|
BlockPos loc = new BlockPos(pos.getX(), this.precHeight[o], pos.getZ());
|
|
|
|
if(loc.getY() == Integer.MIN_VALUE) {
|
|
int y = this.top + 15;
|
|
int min = this.bottom < -64 ? -64 : this.bottom;
|
|
loc = new BlockPos(pos.getX(), y, pos.getZ());
|
|
|
|
while(loc.getY() > min) {
|
|
Block block = this.getBlock(loc);
|
|
Material mat = block.getMaterial();
|
|
|
|
if((!mat.blocksMovement() && !mat.isLiquid())
|
|
// || (mat == Material.LEAVES && ((mat = this.getBlock(loc.up()).getMaterial()) == Material.POWDER)
|
|
|| mat == Material.LEAVES) {
|
|
loc = loc.down();
|
|
}
|
|
else {
|
|
return new BlockPos(pos.getX(), this.precHeight[o] = loc.getY() + 1, pos.getZ());
|
|
}
|
|
}
|
|
|
|
return new BlockPos(pos.getX(), this.precHeight[o] = -64, pos.getZ());
|
|
}
|
|
|
|
return new BlockPos(pos.getX(), this.precHeight[o], pos.getZ());
|
|
}
|
|
|
|
public boolean isLoaded() {
|
|
return this.loaded;
|
|
}
|
|
|
|
public InheritanceMultiMap<Entity>[] getEntities() {
|
|
return this.entities;
|
|
}
|
|
|
|
public int getLowest() {
|
|
return this.minHeight;
|
|
}
|
|
}
|