Partially implement world persistence

This does not account for entities or tile entities, and the seed is not
saved because there is no level.

TODO: Save levels
This commit is contained in:
Drew DeVault 2015-02-02 15:52:25 -07:00
parent f1a98a562b
commit cd20b987d4
6 changed files with 46 additions and 82 deletions

View File

@ -27,5 +27,6 @@ namespace TrueCraft.API.World
void SetBlockLight(Coordinates3D coordinates, byte value);
bool IsValidPosition(Coordinates3D position);
void Save();
void Save(string path);
}
}

View File

@ -209,23 +209,34 @@ namespace TrueCraft.Core.World
var chunk = (NbtCompound)Serializer.Serialize(this, tagName, true);
var entities = new NbtList("Entities", NbtTagType.Compound);
chunk.Add(entities);
// TODO: Save block data
chunk.Add(new NbtByteArray("Blocks", Blocks));
chunk.Add(new NbtByteArray("Data", Metadata.Data));
chunk.Add(new NbtByteArray("SkyLight", SkyLight.Data));
chunk.Add(new NbtByteArray("BlockLight", BlockLight.Data));
// TODO: Tile entities, entities
return chunk;
}
public void Deserialize(NbtTag value)
{
IsModified = true;
var chunk = (Chunk)Serializer.Deserialize(value, true);
var tag = (NbtCompound)value;
this.Biomes = chunk.Biomes;
this.HeightMap = chunk.HeightMap;
this.LastUpdate = chunk.LastUpdate;
this.TerrainPopulated = chunk.TerrainPopulated;
this.X = chunk.X;
this.Z = chunk.Z;
Biomes = chunk.Biomes;
HeightMap = chunk.HeightMap;
LastUpdate = chunk.LastUpdate;
TerrainPopulated = chunk.TerrainPopulated;
X = tag["xPos"].IntValue;
Z = tag["zPos"].IntValue;
Blocks = tag["Blocks"].ByteArrayValue;
Metadata = new NibbleArray();
Metadata.Data = tag["Data"].ByteArrayValue;
BlockLight = new NibbleArray();
BlockLight.Data = tag["BlockLight"].ByteArrayValue;
SkyLight = new NibbleArray();
SkyLight.Data = tag["SkyLight"].ByteArrayValue;
// TODO: Load block data
// TODO: Tile entities, entities
}
}
}

View File

@ -94,54 +94,8 @@ namespace TrueCraft.Core.World
var nbt = new NbtFile();
nbt.LoadFromStream(regionFile, NbtCompression.ZLib, null);
var chunk = Chunk.FromNbt(nbt);
Chunks.Add(position, (IChunk)chunk);
break;
default:
throw new InvalidDataException("Invalid compression scheme provided by region file.");
}
}
}
else if (World.ChunkProvider == null)
throw new ArgumentException("The requested chunk is not loaded.", "position");
else
GenerateChunk(position);
}
return Chunks[position];
}
}
/// <summary>
/// Retrieves the requested chunk from the region, without using the
/// world generator if it does not exist.
/// </summary>
/// <param name="position">The position of the requested local chunk coordinates.</param>
public IChunk GetChunkWithoutGeneration(Coordinates2D position)
{
// TODO: This could use some refactoring
lock (Chunks)
{
if (!Chunks.ContainsKey(position))
{
if (regionFile != null)
{
// Search the stream for that region
lock (regionFile)
{
var chunkData = GetChunkFromTable(position);
if (chunkData == null)
return null;
regionFile.Seek(chunkData.Item1, SeekOrigin.Begin);
/*int length = */new MinecraftStream(regionFile).ReadInt32(); // TODO: Avoid making new objects here, and in the WriteInt32
int compressionMode = regionFile.ReadByte();
switch (compressionMode)
{
case 1: // gzip
break;
case 2: // zlib
var nbt = new NbtFile();
nbt.LoadFromStream(regionFile, NbtCompression.ZLib, null);
var chunk = Chunk.FromNbt(nbt);
Chunks.Add(position, (IChunk)chunk);
Chunks.Add(position, chunk);
Console.WriteLine("Loaded chunk at {0}", chunk.Coordinates);
break;
default:
throw new InvalidDataException("Invalid compression scheme provided by region file.");
@ -209,6 +163,7 @@ namespace TrueCraft.Core.World
var chunk = kvp.Value;
if (chunk.IsModified)
{
Console.WriteLine("Saving modified chunk at {0}", chunk.Coordinates);
var data = ((Chunk)chunk).ToNbt();
byte[] raw = data.SaveToBuffer(NbtCompression.ZLib);

View File

@ -81,17 +81,6 @@ namespace TrueCraft.Core.World
region.GenerateChunk(new Coordinates2D(coordinates.X - regionX * 32, coordinates.Z - regionZ * 32));
}
public Chunk GetChunkWithoutGeneration(Coordinates2D coordinates)
{
int regionX = coordinates.X / Region.Width - ((coordinates.X < 0) ? 1 : 0);
int regionZ = coordinates.Z / Region.Depth - ((coordinates.Z < 0) ? 1 : 0);
var regionPosition = new Coordinates2D(regionX, regionZ);
if (!Regions.ContainsKey(regionPosition)) return null;
return (Chunk)((Region)Regions[regionPosition]).GetChunkWithoutGeneration(
new Coordinates2D(coordinates.X - regionX * 32, coordinates.Z - regionZ * 32));
}
public void SetChunk(Coordinates2D coordinates, Chunk chunk)
{
int regionX = coordinates.X / Region.Width - ((coordinates.X < 0) ? 1 : 0);

View File

@ -240,14 +240,8 @@ namespace TrueCraft
RemoteClient client;
lock (ClientLock)
client = Clients[i] as RemoteClient;
var sendTimeout = DateTime.Now.AddMilliseconds(100);
while (client.PacketQueue.Count != 0)
{
if (DateTime.Now > sendTimeout)
{
Console.WriteLine("Send timeout" + DateTime.Now);
break;
}
idle = false;
try
{
@ -284,14 +278,8 @@ namespace TrueCraft
Clients.RemoveAt(i);
break;
}
var receiveTimeout = DateTime.Now.AddMilliseconds(100);
while (client.DataAvailable)
{
if (DateTime.Now > receiveTimeout)
{
Console.WriteLine("Receive timeout" + DateTime.Now);
break;
}
idle = false;
var packet = PacketReader.ReadPacket(client.MinecraftStream);
LogPacket(packet, true);

View File

@ -10,6 +10,7 @@ using TrueCraft.API;
using TrueCraft.Core.Windows;
using System.IO;
using TrueCraft.Commands;
using TrueCraft.API.World;
namespace TrueCraft
{
@ -20,7 +21,19 @@ namespace TrueCraft
{
// TODO: Make this more flexible
var server = new MultiplayerServer();
server.AddWorld(new World("default", new StandardGenerator()));
IWorld world;
try
{
// TODO: Save and load levels, with seeds and everything
world = World.LoadWorld("world");
world.ChunkProvider = new StandardGenerator();
}
catch
{
world = new World("default", new StandardGenerator());
world.Save("world");
}
server.AddWorld(world);
server.AddLogProvider(new ConsoleLogProvider(LogCategory.Notice | LogCategory.Warning | LogCategory.Error | LogCategory.Debug));
#if DEBUG
server.AddLogProvider(new FileLogProvider(new StreamWriter("packets.log", false), LogCategory.Packets));
@ -29,7 +42,14 @@ namespace TrueCraft
server.ChatMessageReceived += HandleChatMessageReceived;
server.Start(new IPEndPoint(IPAddress.Any, 25565));
while (true)
Thread.Sleep(1000);
{
Thread.Sleep(1000 * 30); // TODO: Allow users to customize world save interval
foreach (var w in server.Worlds)
{
server.Log(LogCategory.Debug, "Saved world '{0}'", w.Name);
w.Save();
}
}
}
static void HandleChatMessageReceived(object sender, ChatMessageEventArgs e)