79 lines
2.7 KiB
C#
79 lines
2.7 KiB
C#
using System.Collections;
|
|
using OpenTK.Mathematics;
|
|
using VoxelEngine.World.Blocks;
|
|
using VoxelEngine.World.Persistence;
|
|
|
|
namespace VoxelEngine.World;
|
|
|
|
public sealed class World : IWorld, IEnumerable<KeyValuePair<Vector3i, Chunk.Chunk>>
|
|
{
|
|
private readonly Dictionary<Vector3i, Chunk.Chunk> _chunks = new();
|
|
private readonly BlockRegistry _registry;
|
|
|
|
public IChunkModificationStore ModificationStore { get; }
|
|
|
|
public World(BlockRegistry registry, IChunkModificationStore modStore)
|
|
{
|
|
this._registry = registry;
|
|
this.ModificationStore = modStore;
|
|
}
|
|
|
|
public void SetChunk(Vector3i chunkPos, Chunk.Chunk chunk) => this._chunks[chunkPos] = chunk;
|
|
|
|
public void RemoveChunk(Vector3i chunkPos) => this._chunks.Remove(chunkPos);
|
|
|
|
public Chunk.Chunk? GetChunk(Vector3i chunkPos) => this._chunks.GetValueOrDefault(chunkPos);
|
|
|
|
public IBlock GetBlock(int worldX, int worldY, int worldZ)
|
|
{
|
|
Vector3i chunkPos = new(
|
|
World.FloorDiv(worldX, Chunk.Chunk.Size),
|
|
World.FloorDiv(worldY, Chunk.Chunk.Size),
|
|
World.FloorDiv(worldZ, Chunk.Chunk.Size));
|
|
|
|
Chunk.Chunk? chunk = this.GetChunk(chunkPos);
|
|
if (chunk == null) return this._registry.Air;
|
|
|
|
int localX = worldX - chunkPos.X * Chunk.Chunk.Size;
|
|
int localY = worldY - chunkPos.Y * Chunk.Chunk.Size;
|
|
int localZ = worldZ - chunkPos.Z * Chunk.Chunk.Size;
|
|
return this._registry.Get(chunk[localX, localY, localZ]);
|
|
}
|
|
|
|
public bool SetBlock(int worldX, int worldY, int worldZ, ushort blockId)
|
|
{
|
|
Vector3i chunkPos = new(
|
|
World.FloorDiv(worldX, Chunk.Chunk.Size),
|
|
World.FloorDiv(worldY, Chunk.Chunk.Size),
|
|
World.FloorDiv(worldZ, Chunk.Chunk.Size));
|
|
|
|
Chunk.Chunk? chunk = this.GetChunk(chunkPos);
|
|
|
|
int localX = worldX - chunkPos.X * Chunk.Chunk.Size;
|
|
int localY = worldY - chunkPos.Y * Chunk.Chunk.Size;
|
|
int localZ = worldZ - chunkPos.Z * Chunk.Chunk.Size;
|
|
int localIndex = (localX * Chunk.Chunk.Size + localY) * Chunk.Chunk.Size + localZ;
|
|
|
|
// For a loaded chunk, no-op writes shouldn't pollute the diff store.
|
|
if (chunk != null && chunk[localX, localY, localZ] == blockId) return true;
|
|
|
|
this.ModificationStore.RecordEdit(chunkPos, localIndex, blockId);
|
|
|
|
if (chunk == null) return false;
|
|
|
|
chunk[localX, localY, localZ] = blockId;
|
|
return true;
|
|
}
|
|
|
|
private static int FloorDiv(int value, int size)
|
|
{
|
|
int q = value / size;
|
|
if (value % size != 0 && value < 0) q--;
|
|
return q;
|
|
}
|
|
|
|
public IEnumerator<KeyValuePair<Vector3i, Chunk.Chunk>> GetEnumerator() => this._chunks.GetEnumerator();
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
|
}
|