1
0
Fork 0
tcr/common/src/main/java/common/world/Chunk.java
2025-08-30 12:03:46 +02:00

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;
}
}