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:
parent
f1a98a562b
commit
cd20b987d4
@ -27,5 +27,6 @@ namespace TrueCraft.API.World
|
||||
void SetBlockLight(Coordinates3D coordinates, byte value);
|
||||
bool IsValidPosition(Coordinates3D position);
|
||||
void Save();
|
||||
void Save(string path);
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user