chunk additions
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a chunk of blocks.
|
||||
/// </summary>
|
||||
public class Chunk
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly int _chunkX;
|
||||
private readonly int _chunkZ;
|
||||
|
||||
internal Chunk(World world, int chunkX, int chunkZ)
|
||||
{
|
||||
_world = world;
|
||||
_chunkX = chunkX;
|
||||
_chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>X-coordinate.</returns>
|
||||
public int getX() => _chunkX;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Z-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _chunkZ;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world containing this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Parent World.</returns>
|
||||
public World getWorld() => _world;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a block from this chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>The Block.</returns>
|
||||
public Block.Block getBlock(int x, int y, int z)
|
||||
{
|
||||
return _world.getBlockAt((_chunkX << 4) + x, y, (_chunkZ << 4) + z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture thread-safe read-only snapshot of chunk data.
|
||||
/// </summary>
|
||||
/// <returns>ChunkSnapshot.</returns>
|
||||
public ChunkSnapshot getChunkSnapshot()
|
||||
{
|
||||
return getChunkSnapshot(false, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture thread-safe read-only snapshot of chunk data.
|
||||
/// </summary>
|
||||
/// <param name="includeBiome">If true, snapshot includes per-coordinate biome type.</param>
|
||||
/// <param name="includeBiomeTempRain">If true, snapshot includes per-coordinate raw biome temperature and rainfall.</param>
|
||||
/// <returns>ChunkSnapshot.</returns>
|
||||
public ChunkSnapshot getChunkSnapshot(bool includeBiome, bool includeBiomeTempRain)
|
||||
{
|
||||
// this has a lot of overhead
|
||||
// (SYLV)todo: clean this up
|
||||
int dimId = _world.getDimensionId();
|
||||
int[] blockIds = new int[16 * 128 * 16];
|
||||
int[] blockData = new int[16 * 128 * 16];
|
||||
int[] maxBlockY = new int[16 * 16];
|
||||
int[] skyLight = new int[16 * 128 * 16];
|
||||
int[] blockLight = new int[16 * 128 * 16];
|
||||
int[]? biomeIds = includeBiome ? new int[16 * 16] : null;
|
||||
double[]? biomeTemp = includeBiomeTempRain ? new double[16 * 16] : null;
|
||||
double[]? biomeRainfall = includeBiomeTempRain ? new double[16 * 16] : null;
|
||||
|
||||
if (NativeBridge.GetChunkSnapshot != null)
|
||||
{
|
||||
var hIds = GCHandle.Alloc(blockIds, GCHandleType.Pinned);
|
||||
var hData = GCHandle.Alloc(blockData, GCHandleType.Pinned);
|
||||
var hMaxY = GCHandle.Alloc(maxBlockY, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.GetChunkSnapshot(dimId, _chunkX, _chunkZ,
|
||||
hIds.AddrOfPinnedObject(),
|
||||
hData.AddrOfPinnedObject(),
|
||||
hMaxY.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
hIds.Free();
|
||||
hData.Free();
|
||||
hMaxY.Free();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
{
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
{
|
||||
int worldX = (_chunkX << 4) + lx;
|
||||
int worldZ = (_chunkZ << 4) + lz;
|
||||
int highest = 0;
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
{
|
||||
int idx = (lx * 128 * 16) + (ly * 16) + lz;
|
||||
if (NativeBridge.GetTileId != null)
|
||||
blockIds[idx] = NativeBridge.GetTileId(dimId, worldX, ly, worldZ);
|
||||
if (NativeBridge.GetTileData != null)
|
||||
blockData[idx] = NativeBridge.GetTileData(dimId, worldX, ly, worldZ);
|
||||
if (blockIds[idx] != 0)
|
||||
highest = ly;
|
||||
}
|
||||
maxBlockY[lx * 16 + lz] = highest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
{
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
{
|
||||
int worldX = (_chunkX << 4) + lx;
|
||||
int worldZ = (_chunkZ << 4) + lz;
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
{
|
||||
int idx = (lx * 128 * 16) + (ly * 16) + lz;
|
||||
if (NativeBridge.GetSkyLight != null)
|
||||
skyLight[idx] = NativeBridge.GetSkyLight(dimId, worldX, ly, worldZ);
|
||||
if (NativeBridge.GetBlockLight != null)
|
||||
blockLight[idx] = NativeBridge.GetBlockLight(dimId, worldX, ly, worldZ);
|
||||
}
|
||||
if (includeBiome && NativeBridge.GetBiomeId != null)
|
||||
{
|
||||
int colIdx = lx * 16 + lz;
|
||||
int biomeId = NativeBridge.GetBiomeId(dimId, worldX, worldZ);
|
||||
biomeIds![colIdx] = biomeId;
|
||||
if (includeBiomeTempRain)
|
||||
{
|
||||
var biome = Block.BiomeHelper.fromId(biomeId);
|
||||
biomeTemp![colIdx] = biome.getTemperature();
|
||||
biomeRainfall![colIdx] = biome.getRainfall();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long captureTime = 0;
|
||||
if (NativeBridge.GetWorldInfo != null)
|
||||
{
|
||||
double[] info = new double[7];
|
||||
var hInfo = GCHandle.Alloc(info, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.GetWorldInfo(dimId, hInfo.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
hInfo.Free();
|
||||
}
|
||||
captureTime = (long)info[4];
|
||||
}
|
||||
|
||||
return new ChunkSnapshot(_chunkX, _chunkZ, _world.getName(), captureTime,
|
||||
blockIds, blockData, maxBlockY,
|
||||
skyLight, blockLight, biomeIds, biomeTemp, biomeRainfall);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture thread-safe read-only snapshot of chunk data.
|
||||
/// </summary>
|
||||
/// <param name="includeMaxblocky">(NONFUNCTIONAL) Only here for parity.</param>
|
||||
/// <param name="includeBiome">If true, snapshot includes per-coordinate biome type.</param>
|
||||
/// <param name="includeBiomeTempRain">If true, snapshot includes per-coordinate raw biome temperature and rainfall.</param>
|
||||
/// <returns>ChunkSnapshot.</returns>
|
||||
public ChunkSnapshot getChunkSnapshot(bool includeMaxblocky, bool includeBiome, bool includeBiomeTempRain)
|
||||
{
|
||||
return getChunkSnapshot(includeBiome, includeBiomeTempRain);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all entities in the chunk.
|
||||
/// </summary>
|
||||
/// <returns>The entities.</returns>
|
||||
public Entity.Entity[] getEntities()
|
||||
{
|
||||
return Array.Empty<Entity.Entity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the chunk is loaded.
|
||||
/// </summary>
|
||||
/// <returns>True if it is loaded.</returns>
|
||||
public bool isLoaded()
|
||||
{
|
||||
if (NativeBridge.IsChunkLoaded != null)
|
||||
return NativeBridge.IsChunkLoaded(_world.getDimensionId(), _chunkX, _chunkZ) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the chunk.
|
||||
/// </summary>
|
||||
/// <param name="generate">Whether or not to generate a chunk if it doesn't already exist.</param>
|
||||
/// <returns>true if the chunk has loaded successfully, otherwise false.</returns>
|
||||
public bool load(bool generate)
|
||||
{
|
||||
if (NativeBridge.LoadChunk != null)
|
||||
return NativeBridge.LoadChunk(_world.getDimensionId(), _chunkX, _chunkZ, generate ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the chunk.
|
||||
/// </summary>
|
||||
/// <returns>true if the chunk has loaded successfully, otherwise false.</returns>
|
||||
public bool load()
|
||||
{
|
||||
return load(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk.
|
||||
/// </summary>
|
||||
/// <param name="save">Controls whether the chunk is saved.</param>
|
||||
/// <param name="safe">Controls whether to unload the chunk when players are nearby.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unload(bool save, bool safe)
|
||||
{
|
||||
if (NativeBridge.UnloadChunk != null)
|
||||
return NativeBridge.UnloadChunk(_world.getDimensionId(), _chunkX, _chunkZ, save ? 1 : 0, safe ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk.
|
||||
/// </summary>
|
||||
/// <param name="save">Controls whether the chunk is saved.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unload(bool save)
|
||||
{
|
||||
return unload(save, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk.
|
||||
/// </summary>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unload()
|
||||
{
|
||||
return unload(true, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
namespace Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a static, thread-safe snapshot of chunk of blocks.
|
||||
/// Purpose is to allow clean, efficient copy of a chunk data to be made, and
|
||||
/// then handed off for processing in another thread (e.g. map rendering).
|
||||
/// </summary>
|
||||
public class ChunkSnapshot
|
||||
{
|
||||
private readonly int _chunkX;
|
||||
private readonly int _chunkZ;
|
||||
private readonly string _worldName;
|
||||
private readonly long _captureFullTime;
|
||||
private readonly int[] _blockIds;
|
||||
private readonly int[] _blockData;
|
||||
private readonly int[] _maxBlockY;
|
||||
private readonly int[]? _skyLight;
|
||||
private readonly int[]? _blockLight;
|
||||
private readonly int[]? _biome;
|
||||
private readonly double[]? _biomeTemp;
|
||||
private readonly double[]? _biomeRainfall;
|
||||
|
||||
internal ChunkSnapshot(int chunkX, int chunkZ, string worldName, long captureFullTime,
|
||||
int[] blockIds, int[] blockData, int[] maxBlockY,
|
||||
int[]? skyLight = null, int[]? blockLight = null,
|
||||
int[]? biome = null, double[]? biomeTemp = null, double[]? biomeRainfall = null)
|
||||
{
|
||||
_chunkX = chunkX;
|
||||
_chunkZ = chunkZ;
|
||||
_worldName = worldName;
|
||||
_captureFullTime = captureFullTime;
|
||||
_blockIds = blockIds;
|
||||
_blockData = blockData;
|
||||
_maxBlockY = maxBlockY;
|
||||
_skyLight = skyLight;
|
||||
_blockLight = blockLight;
|
||||
_biome = biome;
|
||||
_biomeTemp = biomeTemp;
|
||||
_biomeRainfall = biomeRainfall;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>X-coordinate.</returns>
|
||||
public int getX() => _chunkX;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Z-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _chunkZ;
|
||||
|
||||
/// <summary>
|
||||
/// Gets name of the world containing this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Parent World Name.</returns>
|
||||
public string getWorldName() => _worldName;
|
||||
|
||||
/// <summary>
|
||||
/// Get block type for block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-255</returns>
|
||||
public int getBlockTypeId(int x, int y, int z)
|
||||
{
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _blockIds.Length) return 0;
|
||||
return _blockIds[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block data for block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-15</returns>
|
||||
public int getBlockData(int x, int y, int z)
|
||||
{
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _blockData.Length) return 0;
|
||||
return _blockData[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the highest non-air coordinate at the given coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the blocks.</param>
|
||||
/// <param name="z">Z-coordinate of the blocks.</param>
|
||||
/// <returns>Y-coordinate of the highest non-air block.</returns>
|
||||
public int getHighestBlockYAt(int x, int z)
|
||||
{
|
||||
int idx = x * 16 + z;
|
||||
if (idx < 0 || idx >= _maxBlockY.Length) return 0;
|
||||
return _maxBlockY[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get world full time when chunk snapshot was captured.
|
||||
/// </summary>
|
||||
/// <returns>Time in ticks.</returns>
|
||||
public long getCaptureFullTime() => _captureFullTime;
|
||||
|
||||
/// <summary>
|
||||
/// Test if section is empty.
|
||||
/// </summary>
|
||||
/// <param name="sy">Section Y coordinate (block Y / 16).</param>
|
||||
/// <returns>true if empty, false if not.</returns>
|
||||
public bool isSectionEmpty(int sy)
|
||||
{
|
||||
int startY = sy * 16;
|
||||
int endY = startY + 16;
|
||||
if (endY > 128) endY = 128;
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
for (int z = 0; z < 16; z++)
|
||||
{
|
||||
for (int y = startY; y < endY; y++)
|
||||
{
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx >= 0 && idx < _blockIds.Length && _blockIds[idx] != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get sky light level for block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-15</returns>
|
||||
public int getBlockSkyLight(int x, int y, int z)
|
||||
{
|
||||
if (_skyLight == null) return 0;
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _skyLight.Length) return 0;
|
||||
return _skyLight[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get light level emitted by block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-15</returns>
|
||||
public int getBlockEmittedLight(int x, int y, int z)
|
||||
{
|
||||
if (_blockLight == null) return 0;
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _blockLight.Length) return 0;
|
||||
return _blockLight[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get biome at given coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate (0-15)</param>
|
||||
/// <param name="z">Z-coordinate (0-15)</param>
|
||||
/// <returns>Biome at given coordinate.</returns>
|
||||
public Biome getBiome(int x, int z)
|
||||
{
|
||||
if (_biome == null) return Biome.PLAINS;
|
||||
int idx = x * 16 + z;
|
||||
if (idx < 0 || idx >= _biome.Length) return Biome.PLAINS;
|
||||
return BiomeHelper.fromId(_biome[idx]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get raw biome temperature (0.0-1.0) at given coordinate.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate (0-15)</param>
|
||||
/// <param name="z">Z-coordinate (0-15)</param>
|
||||
/// <returns>Temperature at given coordinate.</returns>
|
||||
public double getRawBiomeTemperature(int x, int z)
|
||||
{
|
||||
if (_biomeTemp != null)
|
||||
{
|
||||
int idx = x * 16 + z;
|
||||
if (idx >= 0 && idx < _biomeTemp.Length) return _biomeTemp[idx];
|
||||
}
|
||||
return getBiome(x, z).getTemperature();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get raw biome rainfall (0.0-1.0) at given coordinate.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate (0-15)</param>
|
||||
/// <param name="z">Z-coordinate (0-15)</param>
|
||||
/// <returns>Rainfall at given coordinate.</returns>
|
||||
public double getRawBiomeRainfall(int x, int z)
|
||||
{
|
||||
if (_biomeRainfall != null)
|
||||
{
|
||||
int idx = x * 16 + z;
|
||||
if (idx >= 0 && idx < _biomeRainfall.Length) return _biomeRainfall[idx];
|
||||
}
|
||||
return getBiome(x, z).getRainfall();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user