diff --git a/TrueCraft.API/World/IChunk.cs b/TrueCraft.API/World/IChunk.cs
index 886536c..bfeb4f7 100644
--- a/TrueCraft.API/World/IChunk.cs
+++ b/TrueCraft.API/World/IChunk.cs
@@ -8,6 +8,7 @@ namespace TrueCraft.API.World
{
int X { get; }
int Z { get; }
+ int MaxHeight { get; }
Coordinates2D Coordinates { get; set; }
bool IsModified { get; set; }
bool LightPopulated { get; set; }
diff --git a/TrueCraft.API/World/IWorld.cs b/TrueCraft.API/World/IWorld.cs
index 95ff086..5b4c643 100644
--- a/TrueCraft.API/World/IWorld.cs
+++ b/TrueCraft.API/World/IWorld.cs
@@ -29,7 +29,6 @@ namespace TrueCraft.API.World
byte GetMetadata(Coordinates3D coordinates);
byte GetBlockLight(Coordinates3D coordinates);
byte GetSkyLight(Coordinates3D coordinates);
- Coordinates3D AdjustCoordinates(Coordinates3D coordinates);
Coordinates3D FindBlockPosition(Coordinates3D coordinates, out IChunk chunk, bool generate = true);
NbtCompound GetTileEntity(Coordinates3D coordinates);
BlockDescriptor GetBlockData(Coordinates3D coordinates);
diff --git a/TrueCraft.Core.Test/Lighting/WorldLighterTest.cs b/TrueCraft.Core.Test/Lighting/WorldLighterTest.cs
index c0bdbd8..7b23ddb 100644
--- a/TrueCraft.Core.Test/Lighting/WorldLighterTest.cs
+++ b/TrueCraft.Core.Test/Lighting/WorldLighterTest.cs
@@ -26,17 +26,13 @@ namespace TrueCraft.Core.Test.Lighting
world.BlockRepository = repository;
var lighter = new WorldLighting(world, repository);
world.GetBlockID(Coordinates3D.Zero); // Generate a chunk
- lighter.EnqueueOperation(new BoundingBox(
- new Vector3(0, 0, 0),
- new Vector3(16, 128, 16)), true, true);
- while (lighter.TryLightNext())
- {
- }
+ lighter.InitialLighting(world.GetChunk(Coordinates2D.Zero));
for (int y = 5; y >= 0; y--)
{
+ Console.Write("Y: {0} ", y);
Console.Write(world.GetBlockID(new Coordinates3D(0, y, 0)));
- Console.Write(" ");
+ Console.Write(" -> ");
Console.WriteLine(world.GetSkyLight(new Coordinates3D(0, y, 0)));
}
@@ -50,9 +46,9 @@ namespace TrueCraft.Core.Test.Lighting
var coords = new Coordinates3D(x, y, z);
var sky = world.GetSkyLight(coords);
if (y < 4)
- Assert.AreEqual(0, sky);
+ Assert.AreEqual(0, sky, coords.ToString());
else
- Assert.AreEqual(15, sky);
+ Assert.AreEqual(15, sky, coords.ToString());
}
}
}
@@ -70,12 +66,7 @@ namespace TrueCraft.Core.Test.Lighting
world.BlockRepository = repository;
var lighter = new WorldLighting(world, repository);
world.GetBlockID(Coordinates3D.Zero); // Generate a chunk
- lighter.EnqueueOperation(new BoundingBox(
- new Vector3(0, 0, 0),
- new Vector3(16, 128, 16)), true, true);
- while (lighter.TryLightNext()) // Initial lighting
- {
- }
+ lighter.InitialLighting(world.GetChunk(Coordinates2D.Zero));
world.SetBlockID(new Coordinates3D(5, 3, 5), 0); // Create area that looks like so:
world.SetBlockID(new Coordinates3D(5, 2, 5), 0); // x x Light goes like so: |
@@ -111,12 +102,7 @@ namespace TrueCraft.Core.Test.Lighting
world.BlockRepository = repository;
var lighter = new WorldLighting(world, repository);
world.GetBlockID(Coordinates3D.Zero); // Generate a chunk
- lighter.EnqueueOperation(new BoundingBox(
- new Vector3(0, 0, 0),
- new Vector3(16, 128, 16)), true, true);
- while (lighter.TryLightNext()) // Initial lighting
- {
- }
+ lighter.InitialLighting(world.GetChunk(Coordinates2D.Zero));
world.SetBlockID(new Coordinates3D(5, 3, 5), 0); // Create area that looks like so:
world.SetBlockID(new Coordinates3D(5, 2, 5), 0); // x x Light goes like so: |
@@ -166,12 +152,7 @@ namespace TrueCraft.Core.Test.Lighting
world.BlockRepository = repository;
var lighter = new WorldLighting(world, repository);
world.GetBlockID(Coordinates3D.Zero); // Generate a chunk
- lighter.EnqueueOperation(new BoundingBox(
- new Vector3(0, 0, 0),
- new Vector3(16, 128, 16)), true, true);
- while (lighter.TryLightNext()) // Initial lighting
- {
- }
+ lighter.InitialLighting(world.GetChunk(Coordinates2D.Zero));
// Test this layout:
// xxx x y=3
@@ -273,12 +254,7 @@ namespace TrueCraft.Core.Test.Lighting
}
world.GetChunk(Coordinates2D.Zero).UpdateHeightMap();
- lighter.EnqueueOperation(new BoundingBox(
- new Vector3(0, 0, 0),
- new Vector3(16, 128, 16)), true, true);
- while (lighter.TryLightNext()) // Initial lighting
- {
- }
+ lighter.InitialLighting(world.GetChunk(Coordinates2D.Zero));
// Test this layout:
// xox o == leaves
diff --git a/TrueCraft.Core/Lighting/WorldLighting.cs b/TrueCraft.Core/Lighting/WorldLighting.cs
index 8772927..627c461 100644
--- a/TrueCraft.Core/Lighting/WorldLighting.cs
+++ b/TrueCraft.Core/Lighting/WorldLighting.cs
@@ -4,12 +4,13 @@ using TrueCraft.Core.World;
using TrueCraft.API.Logic;
using TrueCraft.API;
using System.Collections.Generic;
-
-// https://github.com/SirCmpwn/TrueCraft/wiki/Lighting
using System.Diagnostics;
namespace TrueCraft.Core.Lighting
{
+ // https://github.com/SirCmpwn/TrueCraft/wiki/Lighting
+
+ // Note: Speed-critical code
public class WorldLighting
{
private static readonly Coordinates3D[] Neighbors =
@@ -57,11 +58,11 @@ namespace TrueCraft.Core.Lighting
{
Coordinates3D coords;
var map = new byte[Chunk.Width, Chunk.Depth];
- for (int x = 0; x < Chunk.Width; x++)
+ for (byte x = 0; x < Chunk.Width; x++)
{
- for (int z = 0; z < Chunk.Depth; z++)
+ for (byte z = 0; z < Chunk.Depth; z++)
{
- for (byte y = (byte)(chunk.GetHeight((byte)x, (byte)z) + 2); y > 0; y--)
+ for (byte y = (byte)(chunk.GetHeight(x, z) + 2); y > 0; y--)
{
if (y >= Chunk.Height)
continue;
@@ -88,9 +89,9 @@ namespace TrueCraft.Core.Lighting
if (!HeightMaps.ContainsKey(chunk.Coordinates))
return;
var map = HeightMaps[chunk.Coordinates];
- int x = adjusted.X; int z = adjusted.Z;
+ byte x = (byte)adjusted.X; byte z = (byte)adjusted.Z;
Coordinates3D _;
- for (byte y = (byte)(chunk.GetHeight((byte)x, (byte)z) + 2); y > 0; y--)
+ for (byte y = (byte)(chunk.GetHeight(x, z) + 2); y > 0; y--)
{
if (y >= Chunk.Height)
continue;
@@ -120,33 +121,6 @@ namespace TrueCraft.Core.Lighting
}
}
- private IChunk CurrentChunk;
-
- private Coordinates3D AdjustCoordinates(Coordinates3D coords, out IChunk chunk)
- {
- int chunkX = coords.X / Chunk.Width;
- int chunkZ = coords.Z / Chunk.Depth;
-
- if (coords.X < 0)
- chunkX--;
- if (coords.Z < 0)
- chunkZ--;
-
- // Use a cached chunk if possible
- if (CurrentChunk != null && chunkX == CurrentChunk.Coordinates.X && chunkZ == CurrentChunk.Coordinates.Z)
- chunk = CurrentChunk;
- else
- {
- CurrentChunk = World.GetChunk(new Coordinates2D(chunkX, chunkZ), generate: false);
- chunk = CurrentChunk;
- }
-
- return new Coordinates3D(
- (coords.X - chunkX * Chunk.Width) % Chunk.Width,
- coords.Y,
- (coords.Z - chunkZ * Chunk.Depth) % Chunk.Depth);
- }
-
///
/// Propegates a lighting change to an adjacent voxel (if neccesary)
///
@@ -156,7 +130,7 @@ namespace TrueCraft.Core.Lighting
if (!World.IsValidPosition(coords))
return;
IChunk chunk;
- var adjustedCoords = AdjustCoordinates(coords, out chunk);
+ var adjustedCoords = World.FindBlockPosition(coords, out chunk, generate: false);
if (chunk == null || !chunk.TerrainPopulated)
return;
byte current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords);
@@ -167,9 +141,7 @@ namespace TrueCraft.Core.Lighting
{
byte emissiveness = provider.Luminance;
if (chunk.GetHeight((byte)adjustedCoords.X, (byte)adjustedCoords.Z) <= y)
- {
emissiveness = 15;
- }
if (emissiveness >= current)
return;
}
@@ -184,7 +156,7 @@ namespace TrueCraft.Core.Lighting
var coords = new Coordinates3D(x, y, z);
IChunk chunk;
- var adjustedCoords = AdjustCoordinates(coords, out chunk);
+ var adjustedCoords = World.FindBlockPosition(coords, out chunk, generate: false);
if (chunk == null || !chunk.TerrainPopulated) // Move on if this chunk is empty
return;
@@ -246,9 +218,9 @@ namespace TrueCraft.Core.Lighting
{
// Apply changes
if (op.SkyLight)
- World.SetSkyLight(coords, final);
+ chunk.SetSkyLight(adjustedCoords, final);
else
- World.SetBlockLight(coords, final);
+ chunk.SetBlockLight(adjustedCoords, final);
byte propegated = (byte)Math.Max(final - 1, 0);
@@ -296,5 +268,29 @@ namespace TrueCraft.Core.Lighting
PendingOperations.Add(new LightingOperation { SkyLight = skyLight, Box = box, Initial = initial });
}
}
+
+ private void SetUpperVoxels(IChunk chunk)
+ {
+ for (int x = 0; x < Chunk.Width; x++)
+ for (int z = 0; z < Chunk.Depth; z++)
+ for (int y = chunk.MaxHeight + 1; y < Chunk.Height; y++)
+ chunk.SetSkyLight(new Coordinates3D(x, y, z), 15);
+ }
+
+ ///
+ /// Queues the initial lighting pass for a newly generated chunk.
+ ///
+ public void InitialLighting(IChunk chunk, bool flush = true)
+ {
+ // Set voxels above max height to 0xFF
+ SetUpperVoxels(chunk);
+ var coords = chunk.Coordinates * new Coordinates2D(Chunk.Width, Chunk.Depth);
+ EnqueueOperation(new BoundingBox(new Vector3(coords.X, 0, coords.Z),
+ new Vector3(coords.X + Chunk.Width, chunk.MaxHeight + 2, coords.Z + Chunk.Depth)),
+ true, true);
+ while (flush && TryLightNext())
+ {
+ }
+ }
}
}
\ No newline at end of file
diff --git a/TrueCraft.Core/World/Chunk.cs b/TrueCraft.Core/World/Chunk.cs
index b8838df..18e6c7f 100644
--- a/TrueCraft.Core/World/Chunk.cs
+++ b/TrueCraft.Core/World/Chunk.cs
@@ -34,6 +34,7 @@ namespace TrueCraft.Core.World
public NibbleArray SkyLight { get; set; }
public byte[] Biomes { get; set; }
public int[] HeightMap { get; set; }
+ public int MaxHeight { get; private set; }
[TagName("xPos")]
public int X { get; set; }
[TagName("zPos")]
@@ -81,6 +82,7 @@ namespace TrueCraft.Core.World
TileEntities = new Dictionary();
TerrainPopulated = false;
LightPopulated = false;
+ MaxHeight = 0;
}
public Chunk(Coordinates2D coordinates) : this()
@@ -139,7 +141,11 @@ namespace TrueCraft.Core.World
{
coordinates.Y--;
if (GetBlockID(coordinates) != 0)
+ {
SetHeight((byte)coordinates.X, (byte)coordinates.Z, coordinates.Y);
+ if (coordinates.Y > MaxHeight)
+ MaxHeight = coordinates.Y;
+ }
}
}
}
@@ -232,6 +238,8 @@ namespace TrueCraft.Core.World
if (Blocks[index] != 0)
{
SetHeight(x, z, y);
+ if (y > MaxHeight)
+ MaxHeight = y;
break;
}
}
diff --git a/TrueCraft.Core/World/World.cs b/TrueCraft.Core/World/World.cs
index 920e45b..1ccc207 100644
--- a/TrueCraft.Core/World/World.cs
+++ b/TrueCraft.Core/World/World.cs
@@ -315,24 +315,7 @@ namespace TrueCraft.Core.World
Save();
}
- public Coordinates3D AdjustCoordinates(Coordinates3D coordinates)
- {
- if (coordinates.Y < 0 || coordinates.Y >= Chunk.Height)
- throw new ArgumentOutOfRangeException("coordinates", "Coordinates are out of range");
-
- int chunkX = coordinates.X / Chunk.Width;
- int chunkZ = coordinates.Z / Chunk.Depth;
-
- if (coordinates.X < 0)
- chunkX--;
- if (coordinates.Z < 0)
- chunkZ--;
-
- return new Coordinates3D(
- (coordinates.X - chunkX * Chunk.Width) % Chunk.Width,
- coordinates.Y,
- (coordinates.Z - chunkZ * Chunk.Depth) % Chunk.Depth);
- }
+ private IChunk CachedChunk;
public Coordinates3D FindBlockPosition(Coordinates3D coordinates, out IChunk chunk, bool generate = true)
{
@@ -343,15 +326,23 @@ namespace TrueCraft.Core.World
int chunkZ = coordinates.Z / Chunk.Depth;
if (coordinates.X < 0)
- chunkX--;
+ chunkX = (coordinates.X + 1) / Chunk.Width - 1;
if (coordinates.Z < 0)
- chunkZ--;
+ chunkZ = (coordinates.Z + 1) / Chunk.Depth - 1;
+
+ if (CachedChunk != null && chunkX == CachedChunk.Coordinates.X && chunkZ == CachedChunk.Coordinates.Z)
+ chunk = CachedChunk;
+ else
+ {
+ CachedChunk = GetChunk(new Coordinates2D(chunkX, chunkZ), generate);
+ chunk = CachedChunk;
+ }
chunk = GetChunk(new Coordinates2D(chunkX, chunkZ), generate);
return new Coordinates3D(
- (coordinates.X - chunkX * Chunk.Width) % Chunk.Width,
+ (coordinates.X % Chunk.Width + Chunk.Width) % Chunk.Width,
coordinates.Y,
- (coordinates.Z - chunkZ * Chunk.Depth) % Chunk.Depth);
+ (coordinates.Z % Chunk.Depth + Chunk.Depth) % Chunk.Depth);
}
public bool IsValidPosition(Coordinates3D position)
diff --git a/TrueCraft/Commands/DebugCommands.cs b/TrueCraft/Commands/DebugCommands.cs
index 97df4ad..748f151 100644
--- a/TrueCraft/Commands/DebugCommands.cs
+++ b/TrueCraft/Commands/DebugCommands.cs
@@ -45,6 +45,38 @@ namespace TrueCraft.Commands
}
}
+ public class SkyLightCommand : Command
+ {
+ public override string Name
+ {
+ get { return "sl"; }
+ }
+
+ public override string Description
+ {
+ get { return "Shows sky light at your current position."; }
+ }
+
+ public override string[] Aliases
+ {
+ get { return new string[0]; }
+ }
+
+ public override void Handle(IRemoteClient client, string alias, string[] arguments)
+ {
+ int mod = 0;
+ if (arguments.Length == 1)
+ int.TryParse(arguments[0], out mod);
+ client.SendMessage(client.World.GetSkyLight(
+ (Coordinates3D)(client.Entity.Position + new Vector3(0, -mod, 0))).ToString());
+ }
+
+ public override void Help(IRemoteClient client, string alias, string[] arguments)
+ {
+ client.SendMessage("/sl");
+ }
+ }
+
public class SpawnCommand : Command
{
public override string Name
diff --git a/TrueCraft/MultiplayerServer.cs b/TrueCraft/MultiplayerServer.cs
index 67617f4..6ecce65 100644
--- a/TrueCraft/MultiplayerServer.cs
+++ b/TrueCraft/MultiplayerServer.cs
@@ -191,16 +191,7 @@ namespace TrueCraft
if (Program.ServerConfiguration.EnableLighting)
{
var lighter = new WorldLighting(sender as IWorld, BlockRepository);
- var coords = e.Coordinates * new Coordinates2D(Chunk.Width, Chunk.Depth);
- var watch = new Stopwatch();
- watch.Start();
- lighter.EnqueueOperation(new BoundingBox(new Vector3(coords.X, 0, coords.Z),
- new Vector3(coords.X + Chunk.Width, Chunk.Height, coords.Z + Chunk.Depth)), true, true);
- while (lighter.TryLightNext()) // Initial lighting
- {
- }
- watch.Stop();
- Console.WriteLine("Initial chunk lighting took {0}ms", watch.ElapsedMilliseconds);
+ lighter.InitialLighting(e.Chunk);
}
else
{
diff --git a/TrueCraft/ServerConfiguration.cs b/TrueCraft/ServerConfiguration.cs
index 6f7f516..b09cf29 100644
--- a/TrueCraft/ServerConfiguration.cs
+++ b/TrueCraft/ServerConfiguration.cs
@@ -30,31 +30,31 @@ namespace TrueCraft
Singleplayer = false;
Query = true;
QueryPort = 25566;
- EnableLighting = false;
+ EnableLighting = true;
}
[YamlMember(Alias = "motd")]
public string MOTD { get; set; }
- [YamlMember(Alias = "serverPort")]
+ [YamlMember(Alias = "bind-port")]
public int ServerPort {get; set; }
- [YamlMember(Alias = "serverAddress")]
+ [YamlMember(Alias = "bind-endpoint")]
public string ServerAddress { get; set; }
[YamlMember(Alias = "debug")]
public DebugConfiguration Debug { get; set; }
- [YamlMember(Alias = "worldSaveInterval")]
+ [YamlMember(Alias = "save-interval")]
public int WorldSaveInterval { get; set; }
[YamlIgnore]
public bool Singleplayer { get; set; }
- [YamlMember(Alias = "query")]
+ [YamlMember(Alias = "query-enabled")]
public bool Query { get; set; }
- [YamlMember(Alias = "queryPort")]
+ [YamlMember(Alias = "query-port")]
public int QueryPort { get; set; }
[YamlMember(Alias = "enable-lighting")]