194 lines
5.0 KiB
C#
194 lines
5.0 KiB
C#
using Unity.Netcode;
|
|
|
|
/// <summary>
|
|
/// Compact block state struct for chunk-based storage.
|
|
/// 3 bytes per block: type (1), health (1), flags (1)
|
|
/// </summary>
|
|
public struct BlockData : INetworkSerializable
|
|
{
|
|
/// <summary>
|
|
/// Block type: 0=empty/air, 1=normal stone, 2=resource ore
|
|
/// </summary>
|
|
public byte blockType;
|
|
|
|
/// <summary>
|
|
/// Block health: 0-255 (0 = destroyed)
|
|
/// </summary>
|
|
public byte health;
|
|
|
|
/// <summary>
|
|
/// Block flags: bit 0 = isDiscovered (fog of war)
|
|
/// </summary>
|
|
public byte flags;
|
|
|
|
// Block type constants
|
|
public const byte TYPE_EMPTY = 0;
|
|
public const byte TYPE_NORMAL = 1;
|
|
public const byte TYPE_RESOURCE = 2;
|
|
|
|
// Flag bit masks
|
|
public const byte FLAG_DISCOVERED = 1 << 0; // Has been seen at least once (networked/permanent)
|
|
|
|
/// <summary>
|
|
/// Whether this block is empty (air or destroyed)
|
|
/// </summary>
|
|
public bool IsEmpty => blockType == TYPE_EMPTY || health == 0;
|
|
|
|
/// <summary>
|
|
/// Whether this block has been discovered by players
|
|
/// </summary>
|
|
public bool IsDiscovered
|
|
{
|
|
get => (flags & FLAG_DISCOVERED) != 0;
|
|
set
|
|
{
|
|
if (value)
|
|
flags |= FLAG_DISCOVERED;
|
|
else
|
|
flags &= unchecked((byte)~FLAG_DISCOVERED);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether this block is a resource block
|
|
/// </summary>
|
|
public bool IsResource => blockType == TYPE_RESOURCE;
|
|
|
|
/// <summary>
|
|
/// Create an empty block
|
|
/// </summary>
|
|
public static BlockData Empty => new BlockData
|
|
{
|
|
blockType = TYPE_EMPTY,
|
|
health = 0,
|
|
flags = 0
|
|
};
|
|
|
|
/// <summary>
|
|
/// Create a normal stone block with full health
|
|
/// </summary>
|
|
public static BlockData Normal(byte maxHealth = 100) => new BlockData
|
|
{
|
|
blockType = TYPE_NORMAL,
|
|
health = maxHealth,
|
|
flags = 0
|
|
};
|
|
|
|
/// <summary>
|
|
/// Create a resource ore block with full health
|
|
/// </summary>
|
|
public static BlockData Resource(byte maxHealth = 150) => new BlockData
|
|
{
|
|
blockType = TYPE_RESOURCE,
|
|
health = maxHealth,
|
|
flags = 0
|
|
};
|
|
|
|
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
|
{
|
|
serializer.SerializeValue(ref blockType);
|
|
serializer.SerializeValue(ref health);
|
|
serializer.SerializeValue(ref flags);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Network-serializable container for entire chunk state.
|
|
/// Used for initial sync when clients connect.
|
|
/// </summary>
|
|
public struct ChunkState : INetworkSerializable
|
|
{
|
|
public BlockData[] blocks;
|
|
|
|
public const int CHUNK_SIZE = 4;
|
|
public const int BLOCKS_PER_CHUNK = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; // 64
|
|
|
|
public ChunkState(int size)
|
|
{
|
|
blocks = new BlockData[size];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensure blocks array is initialized
|
|
/// </summary>
|
|
private void EnsureInitialized()
|
|
{
|
|
if (blocks == null)
|
|
{
|
|
blocks = new BlockData[BLOCKS_PER_CHUNK];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get block at local coordinates within the chunk
|
|
/// </summary>
|
|
public BlockData GetBlock(int x, int y, int z)
|
|
{
|
|
if (blocks == null) return BlockData.Empty;
|
|
int index = x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
|
if (index < 0 || index >= blocks.Length) return BlockData.Empty;
|
|
return blocks[index];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set block at local coordinates within the chunk
|
|
/// </summary>
|
|
public void SetBlock(int x, int y, int z, BlockData block)
|
|
{
|
|
EnsureInitialized();
|
|
int index = x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
|
if (index >= 0 && index < blocks.Length)
|
|
{
|
|
blocks[index] = block;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert local index to local 3D coordinates
|
|
/// </summary>
|
|
public static (int x, int y, int z) IndexToLocal(int index)
|
|
{
|
|
int x = index % CHUNK_SIZE;
|
|
int y = (index / CHUNK_SIZE) % CHUNK_SIZE;
|
|
int z = index / (CHUNK_SIZE * CHUNK_SIZE);
|
|
return (x, y, z);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert local 3D coordinates to index
|
|
/// </summary>
|
|
public static int LocalToIndex(int x, int y, int z)
|
|
{
|
|
return x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
|
}
|
|
|
|
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
|
{
|
|
// Serialize array length first
|
|
int length = BLOCKS_PER_CHUNK;
|
|
serializer.SerializeValue(ref length);
|
|
|
|
if (serializer.IsReader)
|
|
{
|
|
blocks = new BlockData[length];
|
|
}
|
|
else if (blocks == null)
|
|
{
|
|
blocks = new BlockData[length];
|
|
}
|
|
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
blocks[i].NetworkSerialize(serializer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a default initialized ChunkState
|
|
/// </summary>
|
|
public static ChunkState CreateEmpty()
|
|
{
|
|
return new ChunkState(BLOCKS_PER_CHUNK);
|
|
}
|
|
}
|