mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-05-18 00:22:55 +00:00
chunk additions
This commit is contained in:
parent
ff7c04f253
commit
6e6697dbc2
|
|
@ -1795,6 +1795,7 @@ void MinecraftServer::run(int64_t seed, void *lpParameter)
|
|||
|
||||
chunkPacketManagement_PostTick();
|
||||
}
|
||||
lastTime = getCurrentTimeMillis();
|
||||
// int64_t afterall = System::currentTimeMillis();
|
||||
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
|
||||
// PIXReportCounter(L"Server ticks",(float)tickcount);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#include "..\Minecraft.World\compression.h"
|
||||
#include "..\Minecraft.World\OldChunkStorage.h"
|
||||
#include "..\Minecraft.World\Tile.h"
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
#include "..\Minecraft.Server\FourKitBridge.h"
|
||||
#endif
|
||||
|
||||
ServerChunkCache::ServerChunkCache(ServerLevel *level, ChunkStorage *storage, ChunkSource *source)
|
||||
{
|
||||
|
|
@ -152,7 +155,10 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J
|
|||
{
|
||||
EnterCriticalSection(&m_csLoadCreate);
|
||||
chunk = load(x, z);
|
||||
if (chunk == nullptr)
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
bool isNewChunk = (chunk == nullptr);
|
||||
#endif
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
if (source == nullptr)
|
||||
{
|
||||
|
|
@ -231,6 +237,10 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J
|
|||
if( hasChunk( x - 1, z ) && hasChunk( x + 1, z ) && hasChunk ( x, z - 1 ) && hasChunk( x, z + 1 ) ) chunk->checkChests( this, x, z );
|
||||
|
||||
LeaveCriticalSection(&m_csLoadCreate);
|
||||
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
FourKitBridge::FireChunkLoad(level->dimension->id, x, z, isNewChunk);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -941,23 +951,30 @@ bool ServerChunkCache::tick()
|
|||
// player's tick is called to remove them from the chunk they used to be in, and add them to their current chunk. This will only be a temporary state and
|
||||
// we should be able to unload the chunk on the next call to this tick.
|
||||
if( !chunk->containsPlayer() )
|
||||
{
|
||||
{
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
if (!FourKitBridge::FireChunkUnload(level->dimension->id, chunk->x, chunk->z))
|
||||
{
|
||||
#endif
|
||||
save(chunk);
|
||||
saveEntities(chunk);
|
||||
chunk->unload(true);
|
||||
|
||||
//loadedChunks.remove(cp);
|
||||
//loadedChunkList.remove(chunk);
|
||||
auto it = std::find(m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk);
|
||||
if(it != m_loadedChunkList.end()) m_loadedChunkList.erase(it);
|
||||
auto it = std::find(m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk);
|
||||
if(it != m_loadedChunkList.end()) m_loadedChunkList.erase(it);
|
||||
|
||||
int ix = chunk->x + XZOFFSET;
|
||||
int iz = chunk->z + XZOFFSET;
|
||||
int idx = ix * XZSIZE + iz;
|
||||
m_unloadedCache[idx] = chunk;
|
||||
cache[idx] = nullptr;
|
||||
}
|
||||
}
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
}
|
||||
#endif
|
||||
m_toDrop.pop_front();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
109
Minecraft.Server.FourKit/Block/Biome.cs
Normal file
109
Minecraft.Server.FourKit/Block/Biome.cs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
namespace Minecraft.Server.FourKit.Block;
|
||||
|
||||
|
||||
public enum Biome
|
||||
{
|
||||
OCEAN = 0,
|
||||
PLAINS = 1,
|
||||
DESERT = 2,
|
||||
EXTREME_HILLS = 3,
|
||||
FOREST = 4,
|
||||
TAIGA = 5,
|
||||
SWAMPLAND = 6,
|
||||
RIVER = 7,
|
||||
HELL = 8,
|
||||
SKY = 9,
|
||||
FROZEN_OCEAN = 10,
|
||||
FROZEN_RIVER = 11,
|
||||
ICE_PLAINS = 12,
|
||||
ICE_MOUNTAINS = 13,
|
||||
MUSHROOM_ISLAND = 14,
|
||||
MUSHROOM_SHORE = 15,
|
||||
BEACH = 16,
|
||||
DESERT_HILLS = 17,
|
||||
FOREST_HILLS = 18,
|
||||
TAIGA_HILLS = 19,
|
||||
SMALL_MOUNTAINS = 20,
|
||||
JUNGLE = 21,
|
||||
JUNGLE_HILLS = 22,
|
||||
}
|
||||
|
||||
|
||||
// more for internal
|
||||
// eliminates unnecessary overhead
|
||||
internal static class BiomeHelper
|
||||
{
|
||||
private static readonly double[] _temperatures = new double[23];
|
||||
private static readonly double[] _rainfalls = new double[23];
|
||||
|
||||
static BiomeHelper()
|
||||
{
|
||||
_temperatures[(int)Biome.OCEAN] = 0.5;
|
||||
_temperatures[(int)Biome.PLAINS] = 0.8;
|
||||
_temperatures[(int)Biome.DESERT] = 2.0;
|
||||
_temperatures[(int)Biome.EXTREME_HILLS] = 0.2;
|
||||
_temperatures[(int)Biome.FOREST] = 0.7;
|
||||
_temperatures[(int)Biome.TAIGA] = 0.05;
|
||||
_temperatures[(int)Biome.SWAMPLAND] = 0.8;
|
||||
_temperatures[(int)Biome.RIVER] = 0.5;
|
||||
_temperatures[(int)Biome.HELL] = 2.0;
|
||||
_temperatures[(int)Biome.SKY] = 0.5;
|
||||
_temperatures[(int)Biome.FROZEN_OCEAN] = 0.0;
|
||||
_temperatures[(int)Biome.FROZEN_RIVER] = 0.0;
|
||||
_temperatures[(int)Biome.ICE_PLAINS] = 0.0;
|
||||
_temperatures[(int)Biome.ICE_MOUNTAINS] = 0.0;
|
||||
_temperatures[(int)Biome.MUSHROOM_ISLAND] = 0.9;
|
||||
_temperatures[(int)Biome.MUSHROOM_SHORE] = 0.9;
|
||||
_temperatures[(int)Biome.BEACH] = 0.8;
|
||||
_temperatures[(int)Biome.DESERT_HILLS] = 2.0;
|
||||
_temperatures[(int)Biome.FOREST_HILLS] = 0.7;
|
||||
_temperatures[(int)Biome.TAIGA_HILLS] = 0.05;
|
||||
_temperatures[(int)Biome.SMALL_MOUNTAINS] = 0.2;
|
||||
_temperatures[(int)Biome.JUNGLE] = 1.2;
|
||||
_temperatures[(int)Biome.JUNGLE_HILLS] = 1.2;
|
||||
|
||||
_rainfalls[(int)Biome.OCEAN] = 0.5;
|
||||
_rainfalls[(int)Biome.PLAINS] = 0.4;
|
||||
_rainfalls[(int)Biome.DESERT] = 0.0;
|
||||
_rainfalls[(int)Biome.EXTREME_HILLS] = 0.3;
|
||||
_rainfalls[(int)Biome.FOREST] = 0.8;
|
||||
_rainfalls[(int)Biome.TAIGA] = 0.8;
|
||||
_rainfalls[(int)Biome.SWAMPLAND] = 0.9;
|
||||
_rainfalls[(int)Biome.RIVER] = 0.5;
|
||||
_rainfalls[(int)Biome.HELL] = 0.0;
|
||||
_rainfalls[(int)Biome.SKY] = 0.5;
|
||||
_rainfalls[(int)Biome.FROZEN_OCEAN] = 0.5;
|
||||
_rainfalls[(int)Biome.FROZEN_RIVER] = 0.5;
|
||||
_rainfalls[(int)Biome.ICE_PLAINS] = 0.5;
|
||||
_rainfalls[(int)Biome.ICE_MOUNTAINS] = 0.5;
|
||||
_rainfalls[(int)Biome.MUSHROOM_ISLAND] = 1.0;
|
||||
_rainfalls[(int)Biome.MUSHROOM_SHORE] = 1.0;
|
||||
_rainfalls[(int)Biome.BEACH] = 0.4;
|
||||
_rainfalls[(int)Biome.DESERT_HILLS] = 0.0;
|
||||
_rainfalls[(int)Biome.FOREST_HILLS] = 0.8;
|
||||
_rainfalls[(int)Biome.TAIGA_HILLS] = 0.8;
|
||||
_rainfalls[(int)Biome.SMALL_MOUNTAINS] = 0.3;
|
||||
_rainfalls[(int)Biome.JUNGLE] = 0.9;
|
||||
_rainfalls[(int)Biome.JUNGLE_HILLS] = 0.9;
|
||||
}
|
||||
|
||||
public static double getTemperature(this Biome biome)
|
||||
{
|
||||
int id = (int)biome;
|
||||
if (id >= 0 && id < _temperatures.Length) return _temperatures[id];
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
public static double getRainfall(this Biome biome)
|
||||
{
|
||||
int id = (int)biome;
|
||||
if (id >= 0 && id < _rainfalls.Length) return _rainfalls[id];
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
public static Biome fromId(int id)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(Biome), id)) return (Biome)id;
|
||||
return Biome.PLAINS;
|
||||
}
|
||||
}
|
||||
|
|
@ -134,6 +134,15 @@ public class Block
|
|||
return getWorld().getBlockAt(getX() + modX, getY() + modY, getZ() + modZ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk which contains this block.
|
||||
/// </summary>
|
||||
/// <returns>Containing Chunk.</returns>
|
||||
public Chunk.Chunk getChunk()
|
||||
{
|
||||
return getWorld().getChunkAt(getX() >> 4, getZ() >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block at the given face
|
||||
/// <para>This method is equal to getRelative(face, 1)</para>
|
||||
|
|
@ -162,5 +171,93 @@ public class Block
|
|||
{
|
||||
return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the biome that this block resides in.
|
||||
/// </summary>
|
||||
/// <returns>Biome type containing this block.</returns>
|
||||
public Biome getBiome()
|
||||
{
|
||||
if (NativeBridge.GetBiomeId != null)
|
||||
return BiomeHelper.fromId(NativeBridge.GetBiomeId(_world.getDimensionId(), _x, _z));
|
||||
return Biome.PLAINS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the biome that this block resides in.
|
||||
/// </summary>
|
||||
/// <param name="bio">New Biome type for this block.</param>
|
||||
public void setBiome(Biome bio)
|
||||
{
|
||||
NativeBridge.SetBiomeId?.Invoke(_world.getDimensionId(), _x, _z, (int)bio);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the humidity of the biome of this block.
|
||||
/// </summary>
|
||||
/// <returns>Humidity of this block.</returns>
|
||||
public double getHumidity()
|
||||
{
|
||||
return getBiome().getRainfall();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the temperature of the biome of this block.
|
||||
/// </summary>
|
||||
/// <returns>Temperature of this block.</returns>
|
||||
public double getTemperature()
|
||||
{
|
||||
return getBiome().getTemperature();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this block is liquid.
|
||||
/// <para>A block is considered liquid when <see cref="getType()"/> returns
|
||||
/// <see cref="Material.WATER"/>, <see cref="Material.STATIONARY_WATER"/>,
|
||||
/// <see cref="Material.LAVA"/> or <see cref="Material.STATIONARY_LAVA"/>.</para>
|
||||
/// </summary>
|
||||
/// <returns>true if this block is liquid.</returns>
|
||||
public bool isLiquid()
|
||||
{
|
||||
Material type = getType();
|
||||
return type == Material.WATER || type == Material.STATIONARY_WATER ||
|
||||
type == Material.LAVA || type == Material.STATIONARY_LAVA;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the light level between 0-15.
|
||||
/// </summary>
|
||||
/// <returns>Light level.</returns>
|
||||
public byte getLightLevel()
|
||||
{
|
||||
int sky = getLightFromSky();
|
||||
int block = getLightFromBlocks();
|
||||
return (byte)(sky > block ? sky : block);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of light at this block from the sky.
|
||||
/// Any light given from other sources (such as blocks like torches) will be ignored.
|
||||
/// </summary>
|
||||
/// <returns>Sky light level.</returns>
|
||||
public byte getLightFromSky()
|
||||
{
|
||||
if (NativeBridge.GetSkyLight != null)
|
||||
return (byte)NativeBridge.GetSkyLight(_world.getDimensionId(), _x, _y, _z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of light at this block from nearby blocks.
|
||||
/// Any light given from other sources (such as the sun) will be ignored.
|
||||
/// </summary>
|
||||
/// <returns>Block light level.</returns>
|
||||
public byte getLightFromBlocks()
|
||||
{
|
||||
if (NativeBridge.GetBlockLight != null)
|
||||
return (byte)NativeBridge.GetBlockLight(_world.getDimensionId(), _x, _y, _z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,15 @@ public class BlockState
|
|||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _z;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk which contains this block.
|
||||
/// </summary>
|
||||
/// <returns>Containing Chunk.</returns>
|
||||
public Chunk.Chunk getChunk()
|
||||
{
|
||||
return _world.getChunkAt(_x >> 4, _z >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of this block.
|
||||
/// </summary>
|
||||
|
|
@ -194,4 +203,13 @@ public class BlockState
|
|||
NativeBridge.SetTile(_world.getDimensionId(), _x, _y, _z, _typeId, _data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the light level between 0-15.
|
||||
/// </summary>
|
||||
/// <returns>Light level.</returns>
|
||||
public byte getLightLevel()
|
||||
{
|
||||
return getBlock().getLightLevel();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
260
Minecraft.Server.FourKit/Chunk/Chunk.cs
Normal file
260
Minecraft.Server.FourKit/Chunk/Chunk.cs
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
209
Minecraft.Server.FourKit/Chunk/ChunkSnapshot.cs
Normal file
209
Minecraft.Server.FourKit/Chunk/ChunkSnapshot.cs
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
22
Minecraft.Server.FourKit/Event/World/ChunkEvent.cs
Normal file
22
Minecraft.Server.FourKit/Event/World/ChunkEvent.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace Minecraft.Server.FourKit.Event.World;
|
||||
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Chunk related event.
|
||||
/// </summary>
|
||||
public abstract class ChunkEvent : WorldEvent
|
||||
{
|
||||
protected Chunk chunk;
|
||||
|
||||
protected ChunkEvent(Chunk chunk) : base(chunk.getWorld())
|
||||
{
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk being loaded/unloaded.
|
||||
/// </summary>
|
||||
/// <returns>Chunk that triggered this event.</returns>
|
||||
public Chunk getChunk() => chunk;
|
||||
}
|
||||
23
Minecraft.Server.FourKit/Event/World/ChunkLoadEvent.cs
Normal file
23
Minecraft.Server.FourKit/Event/World/ChunkLoadEvent.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
namespace Minecraft.Server.FourKit.Event.World;
|
||||
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a chunk is loaded.
|
||||
/// </summary>
|
||||
public class ChunkLoadEvent : ChunkEvent
|
||||
{
|
||||
private readonly bool _newChunk;
|
||||
|
||||
internal ChunkLoadEvent(Chunk chunk, bool newChunk) : base(chunk)
|
||||
{
|
||||
_newChunk = newChunk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this chunk was newly created or not. Note that if this chunk is
|
||||
/// new, it will not be populated at this time.
|
||||
/// </summary>
|
||||
/// <returns>true if the chunk is new, otherwise false.</returns>
|
||||
public bool isNewChunk() => _newChunk;
|
||||
}
|
||||
33
Minecraft.Server.FourKit/Event/World/ChunkUnloadEvent.cs
Normal file
33
Minecraft.Server.FourKit/Event/World/ChunkUnloadEvent.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
namespace Minecraft.Server.FourKit.Event.World;
|
||||
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a chunk is unloaded.
|
||||
/// </summary>
|
||||
public class ChunkUnloadEvent : ChunkEvent, Cancellable
|
||||
{
|
||||
private bool _cancel;
|
||||
|
||||
internal ChunkUnloadEvent(Chunk chunk) : base(chunk)
|
||||
{
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cancellation state of this event. A cancelled event will not
|
||||
/// be executed in the server, but will still pass to other plugins.
|
||||
/// </summary>
|
||||
/// <returns>true if this event is cancelled.</returns>
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cancellation state of this event. A cancelled event will not
|
||||
/// be executed in the server, but will still pass to other plugins.
|
||||
/// </summary>
|
||||
/// <param name="cancel">true if you wish to cancel this event.</param>
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
|
|
@ -121,4 +121,43 @@ public static partial class FourKitHost
|
|||
ServerLog.Error("fourkit", $"SetVehicleCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetChunkCallbacks(IntPtr isChunkLoaded, IntPtr loadChunk, IntPtr unloadChunk, IntPtr getLoadedChunks, IntPtr isChunkInUse, IntPtr getChunkSnapshot, IntPtr unloadChunkRequest, IntPtr regenerateChunk, IntPtr refreshChunk)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetChunkCallbacks(isChunkLoaded, loadChunk, unloadChunk, getLoadedChunks, isChunkInUse, getChunkSnapshot, unloadChunkRequest, regenerateChunk, refreshChunk);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetChunkCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetBlockInfoCallbacks(IntPtr getSkyLight, IntPtr getBlockLight, IntPtr getBiomeId, IntPtr setBiomeId)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetBlockInfoCallbacks(getSkyLight, getBlockLight, getBiomeId, setBiomeId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetBlockInfoCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetWorldEntityCallbacks(IntPtr getWorldEntities)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetWorldEntityCallbacks(getWorldEntities);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetWorldEntityCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1250,4 +1250,38 @@ public static partial class FourKitHost
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void FireChunkLoad(int dimId, int chunkX, int chunkZ, int isNewChunk)
|
||||
{
|
||||
try
|
||||
{
|
||||
var world = FourKit.getWorld(dimId);
|
||||
var chunk = new Chunk.Chunk(world, chunkX, chunkZ);
|
||||
var evt = new Event.World.ChunkLoadEvent(chunk, isNewChunk != 0);
|
||||
FourKit.FireEvent(evt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"FireChunkLoad error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static int FireChunkUnload(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
var world = FourKit.getWorld(dimId);
|
||||
var chunk = new Chunk.Chunk(world, chunkX, chunkZ);
|
||||
var evt = new Event.World.ChunkUnloadEvent(chunk);
|
||||
FourKit.FireEvent(evt);
|
||||
return evt.isCancelled() ? 1 : 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"FireChunkUnload error: {ex}");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,6 +186,15 @@ public class Location
|
|||
|
||||
public Location clone() => new Location(LocationWorld, X, Y, Z, Yaw, Pitch);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk at the represented location.
|
||||
/// </summary>
|
||||
/// <returns>Chunk at the represented location.</returns>
|
||||
public Chunk.Chunk getChunk()
|
||||
{
|
||||
return getWorld().getChunkAt(getBlockX() >> 4, getBlockZ() >> 4);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"Location(world={LocationWorld.getName()}, x={X}, y={Y}, z={Z}, yaw={Yaw}, pitch={Pitch})";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,47 @@ internal static class NativeBridge
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeGetEntityInfoDelegate(int entityId, IntPtr outBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeIsChunkLoadedDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeLoadChunkDelegate(int dimId, int chunkX, int chunkZ, int generate);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeUnloadChunkDelegate(int dimId, int chunkX, int chunkZ, int save, int safe);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetLoadedChunksDelegate(int dimId, out IntPtr coordBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeIsChunkInUseDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeGetChunkSnapshotDelegate(int dimId, int chunkX, int chunkZ, IntPtr blockIds, IntPtr blockData, IntPtr maxBlockY);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeUnloadChunkRequestDelegate(int dimId, int chunkX, int chunkZ, int safe);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeRegenerateChunkDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeRefreshChunkDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetWorldEntitiesDelegate(int dimId, out IntPtr outBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetSkyLightDelegate(int dimId, int x, int y, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetBlockLightDelegate(int dimId, int x, int y, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetBiomeIdDelegate(int dimId, int x, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetBiomeIdDelegate(int dimId, int x, int z, int biomeId);
|
||||
internal static NativeDamageDelegate? DamagePlayer;
|
||||
internal static NativeSetHealthDelegate? SetPlayerHealth;
|
||||
internal static NativeTeleportDelegate? TeleportPlayer;
|
||||
|
|
@ -240,6 +280,20 @@ internal static class NativeBridge
|
|||
internal static NativeGetVehicleIdDelegate? GetVehicleId;
|
||||
internal static NativeGetPassengerIdDelegate? GetPassengerId;
|
||||
internal static NativeGetEntityInfoDelegate? GetEntityInfo;
|
||||
internal static NativeIsChunkLoadedDelegate? IsChunkLoaded;
|
||||
internal static NativeLoadChunkDelegate? LoadChunk;
|
||||
internal static NativeUnloadChunkDelegate? UnloadChunk;
|
||||
internal static NativeGetLoadedChunksDelegate? GetLoadedChunks;
|
||||
internal static NativeIsChunkInUseDelegate? IsChunkInUse;
|
||||
internal static NativeGetChunkSnapshotDelegate? GetChunkSnapshot;
|
||||
internal static NativeUnloadChunkRequestDelegate? UnloadChunkRequest;
|
||||
internal static NativeRegenerateChunkDelegate? RegenerateChunk;
|
||||
internal static NativeRefreshChunkDelegate? RefreshChunk;
|
||||
internal static NativeGetWorldEntitiesDelegate? GetWorldEntities;
|
||||
internal static NativeGetSkyLightDelegate? GetSkyLight;
|
||||
internal static NativeGetBlockLightDelegate? GetBlockLight;
|
||||
internal static NativeGetBiomeIdDelegate? GetBiomeId;
|
||||
internal static NativeSetBiomeIdDelegate? SetBiomeId;
|
||||
|
||||
internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
|
||||
{
|
||||
|
|
@ -334,4 +388,30 @@ internal static class NativeBridge
|
|||
GetPassengerId = Marshal.GetDelegateForFunctionPointer<NativeGetPassengerIdDelegate>(getPassengerId);
|
||||
GetEntityInfo = Marshal.GetDelegateForFunctionPointer<NativeGetEntityInfoDelegate>(getEntityInfo);
|
||||
}
|
||||
|
||||
internal static void SetChunkCallbacks(IntPtr isChunkLoaded, IntPtr loadChunk, IntPtr unloadChunk, IntPtr getLoadedChunks, IntPtr isChunkInUse, IntPtr getChunkSnapshot, IntPtr unloadChunkRequest, IntPtr regenerateChunk, IntPtr refreshChunk)
|
||||
{
|
||||
IsChunkLoaded = Marshal.GetDelegateForFunctionPointer<NativeIsChunkLoadedDelegate>(isChunkLoaded);
|
||||
LoadChunk = Marshal.GetDelegateForFunctionPointer<NativeLoadChunkDelegate>(loadChunk);
|
||||
UnloadChunk = Marshal.GetDelegateForFunctionPointer<NativeUnloadChunkDelegate>(unloadChunk);
|
||||
GetLoadedChunks = Marshal.GetDelegateForFunctionPointer<NativeGetLoadedChunksDelegate>(getLoadedChunks);
|
||||
IsChunkInUse = Marshal.GetDelegateForFunctionPointer<NativeIsChunkInUseDelegate>(isChunkInUse);
|
||||
GetChunkSnapshot = Marshal.GetDelegateForFunctionPointer<NativeGetChunkSnapshotDelegate>(getChunkSnapshot);
|
||||
UnloadChunkRequest = Marshal.GetDelegateForFunctionPointer<NativeUnloadChunkRequestDelegate>(unloadChunkRequest);
|
||||
RegenerateChunk = Marshal.GetDelegateForFunctionPointer<NativeRegenerateChunkDelegate>(regenerateChunk);
|
||||
RefreshChunk = Marshal.GetDelegateForFunctionPointer<NativeRefreshChunkDelegate>(refreshChunk);
|
||||
}
|
||||
|
||||
internal static void SetWorldEntityCallbacks(IntPtr getWorldEntities)
|
||||
{
|
||||
GetWorldEntities = Marshal.GetDelegateForFunctionPointer<NativeGetWorldEntitiesDelegate>(getWorldEntities);
|
||||
}
|
||||
|
||||
internal static void SetBlockInfoCallbacks(IntPtr getSkyLight, IntPtr getBlockLight, IntPtr getBiomeId, IntPtr setBiomeId)
|
||||
{
|
||||
GetSkyLight = Marshal.GetDelegateForFunctionPointer<NativeGetSkyLightDelegate>(getSkyLight);
|
||||
GetBlockLight = Marshal.GetDelegateForFunctionPointer<NativeGetBlockLightDelegate>(getBlockLight);
|
||||
GetBiomeId = Marshal.GetDelegateForFunctionPointer<NativeGetBiomeIdDelegate>(getBiomeId);
|
||||
SetBiomeId = Marshal.GetDelegateForFunctionPointer<NativeSetBiomeIdDelegate>(setBiomeId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
|
|
@ -245,6 +246,115 @@ public class World
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all entities in this World.
|
||||
/// </summary>
|
||||
/// <returns>A list of all Entities currently residing in this world.</returns>
|
||||
public List<Entity.Entity> getEntities()
|
||||
{
|
||||
var result = new List<Entity.Entity>();
|
||||
if (NativeBridge.GetWorldEntities == null) return result;
|
||||
|
||||
int count = NativeBridge.GetWorldEntities(_dimensionId, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero) return result;
|
||||
|
||||
try
|
||||
{
|
||||
int[] data = new int[count * 3];
|
||||
Marshal.Copy(buf, data, 0, count * 3);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int entityId = data[i * 3];
|
||||
int mappedType = data[i * 3 + 1];
|
||||
int isLiving = data[i * 3 + 2];
|
||||
|
||||
var entityType = Enum.IsDefined(typeof(Entity.EntityType), mappedType)
|
||||
? (Entity.EntityType)mappedType
|
||||
: Entity.EntityType.UNKNOWN;
|
||||
|
||||
if (entityType == Entity.EntityType.PLAYER)
|
||||
{
|
||||
var player = FourKit.GetPlayerByEntityId(entityId);
|
||||
if (player != null)
|
||||
{
|
||||
result.Add(player);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLiving == 1)
|
||||
{
|
||||
result.Add(new Entity.LivingEntity(entityId, entityType, _dimensionId, 0, 0, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
var entity = new Entity.Entity();
|
||||
entity.SetEntityIdInternal(entityId);
|
||||
entity.SetEntityTypeInternal(entityType);
|
||||
entity.SetDimensionInternal(_dimensionId);
|
||||
result.Add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all living entities in this World.
|
||||
/// </summary>
|
||||
/// <returns>A list of all LivingEntities currently residing in this world.</returns>
|
||||
public List<Entity.LivingEntity> getLivingEntities()
|
||||
{
|
||||
var result = new List<Entity.LivingEntity>();
|
||||
if (NativeBridge.GetWorldEntities == null) return result;
|
||||
|
||||
int count = NativeBridge.GetWorldEntities(_dimensionId, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero) return result;
|
||||
|
||||
try
|
||||
{
|
||||
int[] data = new int[count * 3];
|
||||
Marshal.Copy(buf, data, 0, count * 3);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int entityId = data[i * 3];
|
||||
int mappedType = data[i * 3 + 1];
|
||||
int isLiving = data[i * 3 + 2];
|
||||
|
||||
if (isLiving != 1) continue;
|
||||
|
||||
var entityType = Enum.IsDefined(typeof(Entity.EntityType), mappedType)
|
||||
? (Entity.EntityType)mappedType
|
||||
: Entity.EntityType.UNKNOWN;
|
||||
|
||||
if (entityType == Entity.EntityType.PLAYER)
|
||||
{
|
||||
var player = FourKit.GetPlayerByEntityId(entityId);
|
||||
if (player != null)
|
||||
{
|
||||
result.Add(player);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(new Entity.LivingEntity(entityId, entityType, _dimensionId, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates explosion at given coordinates with given power.
|
||||
/// </summary>
|
||||
|
|
@ -374,4 +484,240 @@ public class World
|
|||
{
|
||||
NativeBridge.DropItem?.Invoke(_dimensionId, location.X, location.Y, location.Z, item.getTypeId(), item.getAmount(), item.getDurability(), 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Chunk at the given coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>Chunk at the given coordinates.</returns>
|
||||
public Chunk.Chunk getChunkAt(int x, int z)
|
||||
{
|
||||
return new Chunk.Chunk(this, x, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Chunk at the given Location.
|
||||
/// </summary>
|
||||
/// <param name="location">Location of the chunk.</param>
|
||||
/// <returns>Chunk at the given location.</returns>
|
||||
public Chunk.Chunk getChunkAt(Location location)
|
||||
{
|
||||
return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Chunk that contains the given Block.
|
||||
/// </summary>
|
||||
/// <param name="block">Block to get the containing chunk from.</param>
|
||||
/// <returns>The chunk that contains the given block.</returns>
|
||||
public Chunk.Chunk getChunkAt(Block.Block block)
|
||||
{
|
||||
return getChunkAt(block.getX() >> 4, block.getZ() >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified Chunk is loaded.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The chunk to check.</param>
|
||||
/// <returns>true if the chunk is loaded, otherwise false.</returns>
|
||||
public bool isChunkLoaded(Chunk.Chunk chunk)
|
||||
{
|
||||
return isChunkLoaded(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Chunk at the specified coordinates is loaded.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true if the chunk is loaded, otherwise false.</returns>
|
||||
public bool isChunkLoaded(int x, int z)
|
||||
{
|
||||
if (NativeBridge.IsChunkLoaded != null)
|
||||
return NativeBridge.IsChunkLoaded(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of all loaded Chunks.
|
||||
/// </summary>
|
||||
/// <returns>Chunk[] containing all loaded chunks.</returns>
|
||||
public Chunk.Chunk[] getLoadedChunks()
|
||||
{
|
||||
if (NativeBridge.GetLoadedChunks == null)
|
||||
return Array.Empty<Chunk.Chunk>();
|
||||
|
||||
int count = NativeBridge.GetLoadedChunks(_dimensionId, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero)
|
||||
return Array.Empty<Chunk.Chunk>();
|
||||
|
||||
try
|
||||
{
|
||||
int[] coords = new int[count * 2];
|
||||
Marshal.Copy(buf, coords, 0, count * 2);
|
||||
var chunks = new Chunk.Chunk[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
chunks[i] = new Chunk.Chunk(this, coords[i * 2], coords[i * 2 + 1]);
|
||||
return chunks;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the specified Chunk.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The chunk to load.</param>
|
||||
public void loadChunk(Chunk.Chunk chunk)
|
||||
{
|
||||
loadChunk(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the Chunk at the specified coordinates.
|
||||
/// If the chunk does not exist, it will be generated. This method is
|
||||
/// analogous to loadChunk(int, int, boolean) where generate is true.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
public void loadChunk(int x, int z)
|
||||
{
|
||||
loadChunk(x, z, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <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 loadChunk(int x, int z, bool generate)
|
||||
{
|
||||
if (NativeBridge.LoadChunk != null)
|
||||
return NativeBridge.LoadChunk(_dimensionId, x, z, generate ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Chunk at the specified coordinates is loaded and in use
|
||||
/// by one or more players.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true if the chunk is loaded and in use by one or more players, otherwise false.</returns>
|
||||
public bool isChunkInUse(int x, int z)
|
||||
{
|
||||
if (NativeBridge.IsChunkInUse != null)
|
||||
return NativeBridge.IsChunkInUse(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely unloads and saves the Chunk at the specified coordinates.
|
||||
/// This method is analogous to unloadChunk(int, int, boolean, boolean)
|
||||
/// where safe and save is true.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The chunk to unload.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(Chunk.Chunk chunk)
|
||||
{
|
||||
return unloadChunk(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely unloads and saves the Chunk at the specified coordinates.
|
||||
/// This method is analogous to unloadChunk(int, int, boolean, boolean)
|
||||
/// where safe and save is true.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(int x, int z)
|
||||
{
|
||||
return unloadChunk(x, z, true, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely unloads and optionally saves the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <param name="save">Whether or not to save the chunk.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(int x, int z, bool save)
|
||||
{
|
||||
return unloadChunk(x, z, save, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <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 unloadChunk(int x, int z, bool save, bool safe)
|
||||
{
|
||||
if (NativeBridge.UnloadChunk != null)
|
||||
return NativeBridge.UnloadChunk(_dimensionId, x, z, save ? 1 : 0, safe ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely queues the Chunk at the specified coordinates for unloading.
|
||||
/// This method is analogous to unloadChunkRequest(int, int, boolean)
|
||||
/// where safe is true.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true is the queue attempt was successful, otherwise false.</returns>
|
||||
public bool unloadChunkRequest(int x, int z)
|
||||
{
|
||||
return unloadChunkRequest(x, z, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the Chunk at the specified coordinates for unloading.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <param name="safe">Controls whether to queue the chunk when players are nearby.</param>
|
||||
/// <returns>Whether the chunk was actually queued.</returns>
|
||||
public bool unloadChunkRequest(int x, int z, bool safe)
|
||||
{
|
||||
if (NativeBridge.UnloadChunkRequest != null)
|
||||
return NativeBridge.UnloadChunkRequest(_dimensionId, x, z, safe ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>Whether the chunk was actually regenerated.</returns>
|
||||
public bool regenerateChunk(int x, int z)
|
||||
{
|
||||
if (NativeBridge.RegenerateChunk != null)
|
||||
return NativeBridge.RegenerateChunk(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resends the Chunk to all clients.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>Whether the chunk was actually refreshed.</returns>
|
||||
public bool refreshChunk(int x, int z)
|
||||
{
|
||||
if (NativeBridge.RefreshChunk != null)
|
||||
return NativeBridge.RefreshChunk(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@ typedef int(__stdcall *fn_fire_piston_extend)(int dimId, int x, int y, int z, in
|
|||
typedef int(__stdcall *fn_fire_piston_retract)(int dimId, int x, int y, int z, int direction);
|
||||
typedef int(__stdcall *fn_fire_command_preprocess)(int entityId, const char *cmdUtf8, int cmdByteLen, char *outBuf, int outBufSize, int *outLen);
|
||||
typedef int(__stdcall *fn_fire_block_from_to)(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face);
|
||||
typedef void(__stdcall *fn_set_chunk_callbacks)(void *isChunkLoaded, void *loadChunk, void *unloadChunk, void *getLoadedChunks, void *isChunkInUse, void *getChunkSnapshot, void *unloadChunkRequest, void *regenerateChunk, void *refreshChunk);
|
||||
typedef void(__stdcall *fn_set_block_info_callbacks)(void *getSkyLight, void *getBlockLight, void *getBiomeId, void *setBiomeId);
|
||||
typedef void(__stdcall *fn_set_world_entity_callbacks)(void *getWorldEntities);
|
||||
typedef void(__stdcall *fn_fire_chunk_load)(int dimId, int chunkX, int chunkZ, int isNewChunk);
|
||||
typedef int(__stdcall *fn_fire_chunk_unload)(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
struct OpenContainerInfo
|
||||
{
|
||||
|
|
@ -160,6 +165,11 @@ static fn_fire_piston_extend s_managedFirePistonExtend = nullptr;
|
|||
static fn_fire_piston_retract s_managedFirePistonRetract = nullptr;
|
||||
static fn_fire_command_preprocess s_managedFireCommandPreprocess = nullptr;
|
||||
static fn_fire_block_from_to s_managedFireBlockFromTo = nullptr;
|
||||
static fn_set_chunk_callbacks s_managedSetChunkCallbacks = nullptr;
|
||||
static fn_set_block_info_callbacks s_managedSetBlockInfoCallbacks = nullptr;
|
||||
static fn_set_world_entity_callbacks s_managedSetWorldEntityCallbacks = nullptr;
|
||||
static fn_fire_chunk_load s_managedFireChunkLoad = nullptr;
|
||||
static fn_fire_chunk_unload s_managedFireChunkUnload = nullptr;
|
||||
|
||||
static bool s_initialized = false;
|
||||
|
||||
|
|
@ -238,6 +248,11 @@ void Initialize()
|
|||
{L"FirePistonRetract", (void **)&s_managedFirePistonRetract},
|
||||
{L"FireCommandPreprocess", (void **)&s_managedFireCommandPreprocess},
|
||||
{L"FireBlockFromTo", (void **)&s_managedFireBlockFromTo},
|
||||
{L"SetChunkCallbacks", (void **)&s_managedSetChunkCallbacks},
|
||||
{L"SetBlockInfoCallbacks", (void **)&s_managedSetBlockInfoCallbacks},
|
||||
{L"SetWorldEntityCallbacks", (void **)&s_managedSetWorldEntityCallbacks},
|
||||
{L"FireChunkLoad", (void **)&s_managedFireChunkLoad},
|
||||
{L"FireChunkUnload", (void **)&s_managedFireChunkUnload},
|
||||
};
|
||||
|
||||
bool ok = true;
|
||||
|
|
@ -332,6 +347,26 @@ void Initialize()
|
|||
(void *)&NativeGetPassengerId,
|
||||
(void *)&NativeGetEntityInfo);
|
||||
|
||||
s_managedSetChunkCallbacks(
|
||||
(void *)&NativeIsChunkLoaded,
|
||||
(void *)&NativeLoadChunk,
|
||||
(void *)&NativeUnloadChunk,
|
||||
(void *)&NativeGetLoadedChunks,
|
||||
(void *)&NativeIsChunkInUse,
|
||||
(void *)&NativeGetChunkSnapshot,
|
||||
(void *)&NativeUnloadChunkRequest,
|
||||
(void *)&NativeRegenerateChunk,
|
||||
(void *)&NativeRefreshChunk);
|
||||
|
||||
s_managedSetBlockInfoCallbacks(
|
||||
(void *)&NativeGetSkyLight,
|
||||
(void *)&NativeGetBlockLight,
|
||||
(void *)&NativeGetBiomeId,
|
||||
(void *)&NativeSetBiomeId);
|
||||
|
||||
s_managedSetWorldEntityCallbacks(
|
||||
(void *)&NativeGetWorldEntities);
|
||||
|
||||
LogInfo("fourkit", "FourKit initialized successfully.");
|
||||
}
|
||||
|
||||
|
|
@ -1010,4 +1045,18 @@ bool FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int to
|
|||
return false;
|
||||
return s_managedFireBlockFromTo(dimId, fromX, fromY, fromZ, toX, toY, toZ, face) != 0;
|
||||
}
|
||||
|
||||
void FireChunkLoad(int dimId, int chunkX, int chunkZ, bool isNewChunk)
|
||||
{
|
||||
if (!s_initialized || !s_managedFireChunkLoad)
|
||||
return;
|
||||
s_managedFireChunkLoad(dimId, chunkX, chunkZ, isNewChunk ? 1 : 0);
|
||||
}
|
||||
|
||||
bool FireChunkUnload(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
if (!s_initialized || !s_managedFireChunkUnload)
|
||||
return false;
|
||||
return s_managedFireChunkUnload(dimId, chunkX, chunkZ) != 0;
|
||||
}
|
||||
} // namespace FourKitBridge
|
||||
|
|
|
|||
|
|
@ -92,4 +92,6 @@ namespace FourKitBridge
|
|||
bool FirePistonRetract(int dimId, int x, int y, int z, int direction);
|
||||
bool FireCommandPreprocess(int entityId, const std::wstring &commandLine, std::wstring &outCommand);
|
||||
bool FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face);
|
||||
void FireChunkLoad(int dimId, int chunkX, int chunkZ, bool isNewChunk);
|
||||
bool FireChunkUnload(int dimId, int chunkX, int chunkZ);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
#include "..\Minecraft.Client\ServerLevel.h"
|
||||
#include "..\Minecraft.Client\ServerPlayer.h"
|
||||
#include "..\Minecraft.Client\ServerPlayerGameMode.h"
|
||||
#include "..\Minecraft.Client\ServerChunkCache.h"
|
||||
#include "..\Minecraft.World\LevelChunk.h"
|
||||
#include "..\Minecraft.World\Biome.h"
|
||||
#include "..\Minecraft.World\LightLayer.h"
|
||||
#include "..\Minecraft.Client\Windows64\Network\WinsockNetLayer.h"
|
||||
#include "..\Minecraft.World\AbstractContainerMenu.h"
|
||||
#include "..\Minecraft.World\AddGlobalEntityPacket.h"
|
||||
|
|
@ -1268,4 +1272,232 @@ void __cdecl NativeGetEntityInfo(int entityId, double *outData)
|
|||
outData[4] = (double)entity->dimension;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetWorldEntities(int dimId, int **outBuf)
|
||||
{
|
||||
*outBuf = nullptr;
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
|
||||
EnterCriticalSection(&level->m_entitiesCS);
|
||||
int total = (int)level->entities.size();
|
||||
int *buf = (int *)CoTaskMemAlloc(total * 3 * sizeof(int));
|
||||
int count = 0;
|
||||
if (buf)
|
||||
{
|
||||
for (auto &entity : level->entities)
|
||||
{
|
||||
if (!entity)
|
||||
continue;
|
||||
int idx = count * 3;
|
||||
buf[idx] = entity->entityId;
|
||||
buf[idx + 1] = MapEntityType((int)entity->GetType());
|
||||
buf[idx + 2] = entity->instanceof(eTYPE_LIVINGENTITY) ? 1 : 0;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&level->m_entitiesCS);
|
||||
*outBuf = buf;
|
||||
return count;
|
||||
}
|
||||
|
||||
int __cdecl NativeIsChunkLoaded(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
return level->cache->hasChunk(chunkX, chunkZ) ? 1 : 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeLoadChunk(int dimId, int chunkX, int chunkZ, int generate)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
LevelChunk *chunk = level->cache->create(chunkX, chunkZ);
|
||||
return (chunk != nullptr) ? 1 : 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeUnloadChunk(int dimId, int chunkX, int chunkZ, int save, int safe)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
if (safe)
|
||||
{
|
||||
if (!level->cache->hasChunk(chunkX, chunkZ))
|
||||
return 0;
|
||||
LevelChunk *chunk = level->cache->getChunk(chunkX, chunkZ);
|
||||
if (chunk && chunk->containsPlayer())
|
||||
return 0;
|
||||
}
|
||||
level->cache->drop(chunkX, chunkZ);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetLoadedChunks(int dimId, int **coordBuf)
|
||||
{
|
||||
// wow gay
|
||||
*coordBuf = nullptr;
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
|
||||
std::vector<LevelChunk *> *list = level->cache->getLoadedChunkList();
|
||||
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
int total = (int)list->size();
|
||||
int *buf = (int *)CoTaskMemAlloc(total * 2 * sizeof(int));
|
||||
int count = 0;
|
||||
|
||||
if (buf)
|
||||
{
|
||||
for (auto *chunk : *list)
|
||||
{
|
||||
if (chunk)
|
||||
{
|
||||
buf[count * 2] = chunk->x;
|
||||
buf[count * 2 + 1] = chunk->z;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*coordBuf = buf;
|
||||
return count;
|
||||
}
|
||||
|
||||
int __cdecl NativeIsChunkInUse(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
PlayerList *list = MinecraftServer::getPlayerList();
|
||||
if (!list)
|
||||
return 0;
|
||||
for (auto &p : list->players)
|
||||
{
|
||||
if (p && p->dimension == dimId)
|
||||
{
|
||||
int px = (int)floor(p->x) >> 4;
|
||||
int pz = (int)floor(p->z) >> 4;
|
||||
if (px == chunkX && pz == chunkZ)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __cdecl NativeGetChunkSnapshot(int dimId, int chunkX, int chunkZ, int *blockIds, int *blockData, int *maxBlockY)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
{
|
||||
memset(blockIds, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(blockData, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(maxBlockY, 0, 16 * 16 * sizeof(int));
|
||||
return;
|
||||
}
|
||||
if (!level->cache->hasChunk(chunkX, chunkZ))
|
||||
{
|
||||
memset(blockIds, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(blockData, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(maxBlockY, 0, 16 * 16 * sizeof(int));
|
||||
return;
|
||||
}
|
||||
LevelChunk *chunk = level->cache->getChunk(chunkX, chunkZ);
|
||||
if (!chunk)
|
||||
{
|
||||
memset(blockIds, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(blockData, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(maxBlockY, 0, 16 * 16 * sizeof(int));
|
||||
return;
|
||||
}
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
{
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
{
|
||||
int highest = 0;
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
{
|
||||
int idx = (lx * 128 * 16) + (ly * 16) + lz;
|
||||
blockIds[idx] = chunk->getTile(lx, ly, lz);
|
||||
blockData[idx] = chunk->getData(lx, ly, lz);
|
||||
if (blockIds[idx] != 0)
|
||||
highest = ly;
|
||||
}
|
||||
maxBlockY[lx * 16 + lz] = highest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __cdecl NativeUnloadChunkRequest(int dimId, int chunkX, int chunkZ, int safe)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
if (safe)
|
||||
{
|
||||
if (!level->cache->hasChunk(chunkX, chunkZ))
|
||||
return 0;
|
||||
LevelChunk *chunk = level->cache->getChunk(chunkX, chunkZ);
|
||||
if (chunk && chunk->containsPlayer())
|
||||
return 0;
|
||||
}
|
||||
level->cache->drop(chunkX, chunkZ);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl NativeRegenerateChunk(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeRefreshChunk(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetSkyLight(int dimId, int x, int y, int z)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
return level->getBrightness(LightLayer::Sky, x, y, z);
|
||||
}
|
||||
|
||||
int __cdecl NativeGetBlockLight(int dimId, int x, int y, int z)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
return level->getBrightness(LightLayer::Block, x, y, z);
|
||||
}
|
||||
|
||||
int __cdecl NativeGetBiomeId(int dimId, int x, int z)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 1;
|
||||
Biome *biome = level->getBiome(x, z);
|
||||
return biome ? biome->id : 1;
|
||||
}
|
||||
|
||||
void __cdecl NativeSetBiomeId(int dimId, int x, int z, int biomeId)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return;
|
||||
LevelChunk *chunk = level->getChunk(x >> 4, z >> 4);
|
||||
if (!chunk)
|
||||
return;
|
||||
byteArray biomes = chunk->getBiomes();
|
||||
if (biomes.data == nullptr)
|
||||
return;
|
||||
int lx = x & 0xf;
|
||||
int lz = z & 0xf;
|
||||
biomes.data[(lz << 4) | lx] = static_cast<unsigned char>(biomeId & 0xff);
|
||||
}
|
||||
|
||||
} // namespace FourKitBridge
|
||||
|
|
|
|||
|
|
@ -78,4 +78,24 @@ namespace FourKitBridge
|
|||
int __cdecl NativeGetVehicleId(int entityId);
|
||||
int __cdecl NativeGetPassengerId(int entityId);
|
||||
void __cdecl NativeGetEntityInfo(int entityId, double *outData);
|
||||
|
||||
// chunk
|
||||
int __cdecl NativeIsChunkLoaded(int dimId, int chunkX, int chunkZ);
|
||||
int __cdecl NativeLoadChunk(int dimId, int chunkX, int chunkZ, int generate);
|
||||
int __cdecl NativeUnloadChunk(int dimId, int chunkX, int chunkZ, int save, int safe);
|
||||
int __cdecl NativeGetLoadedChunks(int dimId, int **coordBuf);
|
||||
int __cdecl NativeIsChunkInUse(int dimId, int chunkX, int chunkZ);
|
||||
void __cdecl NativeGetChunkSnapshot(int dimId, int chunkX, int chunkZ, int *blockIds, int *blockData, int *maxBlockY);
|
||||
int __cdecl NativeUnloadChunkRequest(int dimId, int chunkX, int chunkZ, int safe);
|
||||
int __cdecl NativeRegenerateChunk(int dimId, int chunkX, int chunkZ);
|
||||
int __cdecl NativeRefreshChunk(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
// world entity bs
|
||||
int __cdecl NativeGetWorldEntities(int dimId, int **outBuf);
|
||||
|
||||
// block info (light, biome)
|
||||
int __cdecl NativeGetSkyLight(int dimId, int x, int y, int z);
|
||||
int __cdecl NativeGetBlockLight(int dimId, int x, int y, int z);
|
||||
int __cdecl NativeGetBiomeId(int dimId, int x, int z);
|
||||
void __cdecl NativeSetBiomeId(int dimId, int x, int z, int biomeId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue