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);
|
void SetBlockLight(Coordinates3D coordinates, byte value);
|
||||||
bool IsValidPosition(Coordinates3D position);
|
bool IsValidPosition(Coordinates3D position);
|
||||||
void Save();
|
void Save();
|
||||||
|
void Save(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -209,23 +209,34 @@ namespace TrueCraft.Core.World
|
|||||||
var chunk = (NbtCompound)Serializer.Serialize(this, tagName, true);
|
var chunk = (NbtCompound)Serializer.Serialize(this, tagName, true);
|
||||||
var entities = new NbtList("Entities", NbtTagType.Compound);
|
var entities = new NbtList("Entities", NbtTagType.Compound);
|
||||||
chunk.Add(entities);
|
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;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Deserialize(NbtTag value)
|
public void Deserialize(NbtTag value)
|
||||||
{
|
{
|
||||||
IsModified = true;
|
|
||||||
var chunk = (Chunk)Serializer.Deserialize(value, true);
|
var chunk = (Chunk)Serializer.Deserialize(value, true);
|
||||||
|
var tag = (NbtCompound)value;
|
||||||
|
|
||||||
this.Biomes = chunk.Biomes;
|
Biomes = chunk.Biomes;
|
||||||
this.HeightMap = chunk.HeightMap;
|
HeightMap = chunk.HeightMap;
|
||||||
this.LastUpdate = chunk.LastUpdate;
|
LastUpdate = chunk.LastUpdate;
|
||||||
this.TerrainPopulated = chunk.TerrainPopulated;
|
TerrainPopulated = chunk.TerrainPopulated;
|
||||||
this.X = chunk.X;
|
X = tag["xPos"].IntValue;
|
||||||
this.Z = chunk.Z;
|
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();
|
var nbt = new NbtFile();
|
||||||
nbt.LoadFromStream(regionFile, NbtCompression.ZLib, null);
|
nbt.LoadFromStream(regionFile, NbtCompression.ZLib, null);
|
||||||
var chunk = Chunk.FromNbt(nbt);
|
var chunk = Chunk.FromNbt(nbt);
|
||||||
Chunks.Add(position, (IChunk)chunk);
|
Chunks.Add(position, chunk);
|
||||||
break;
|
Console.WriteLine("Loaded chunk at {0}", chunk.Coordinates);
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidDataException("Invalid compression scheme provided by region file.");
|
throw new InvalidDataException("Invalid compression scheme provided by region file.");
|
||||||
@ -209,6 +163,7 @@ namespace TrueCraft.Core.World
|
|||||||
var chunk = kvp.Value;
|
var chunk = kvp.Value;
|
||||||
if (chunk.IsModified)
|
if (chunk.IsModified)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("Saving modified chunk at {0}", chunk.Coordinates);
|
||||||
var data = ((Chunk)chunk).ToNbt();
|
var data = ((Chunk)chunk).ToNbt();
|
||||||
byte[] raw = data.SaveToBuffer(NbtCompression.ZLib);
|
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));
|
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)
|
public void SetChunk(Coordinates2D coordinates, Chunk chunk)
|
||||||
{
|
{
|
||||||
int regionX = coordinates.X / Region.Width - ((coordinates.X < 0) ? 1 : 0);
|
int regionX = coordinates.X / Region.Width - ((coordinates.X < 0) ? 1 : 0);
|
||||||
|
@ -240,14 +240,8 @@ namespace TrueCraft
|
|||||||
RemoteClient client;
|
RemoteClient client;
|
||||||
lock (ClientLock)
|
lock (ClientLock)
|
||||||
client = Clients[i] as RemoteClient;
|
client = Clients[i] as RemoteClient;
|
||||||
var sendTimeout = DateTime.Now.AddMilliseconds(100);
|
|
||||||
while (client.PacketQueue.Count != 0)
|
while (client.PacketQueue.Count != 0)
|
||||||
{
|
{
|
||||||
if (DateTime.Now > sendTimeout)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Send timeout" + DateTime.Now);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
idle = false;
|
idle = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -284,14 +278,8 @@ namespace TrueCraft
|
|||||||
Clients.RemoveAt(i);
|
Clients.RemoveAt(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var receiveTimeout = DateTime.Now.AddMilliseconds(100);
|
|
||||||
while (client.DataAvailable)
|
while (client.DataAvailable)
|
||||||
{
|
{
|
||||||
if (DateTime.Now > receiveTimeout)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Receive timeout" + DateTime.Now);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
idle = false;
|
idle = false;
|
||||||
var packet = PacketReader.ReadPacket(client.MinecraftStream);
|
var packet = PacketReader.ReadPacket(client.MinecraftStream);
|
||||||
LogPacket(packet, true);
|
LogPacket(packet, true);
|
||||||
|
@ -10,6 +10,7 @@ using TrueCraft.API;
|
|||||||
using TrueCraft.Core.Windows;
|
using TrueCraft.Core.Windows;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TrueCraft.Commands;
|
using TrueCraft.Commands;
|
||||||
|
using TrueCraft.API.World;
|
||||||
|
|
||||||
namespace TrueCraft
|
namespace TrueCraft
|
||||||
{
|
{
|
||||||
@ -20,7 +21,19 @@ namespace TrueCraft
|
|||||||
{
|
{
|
||||||
// TODO: Make this more flexible
|
// TODO: Make this more flexible
|
||||||
var server = new MultiplayerServer();
|
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));
|
server.AddLogProvider(new ConsoleLogProvider(LogCategory.Notice | LogCategory.Warning | LogCategory.Error | LogCategory.Debug));
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
server.AddLogProvider(new FileLogProvider(new StreamWriter("packets.log", false), LogCategory.Packets));
|
server.AddLogProvider(new FileLogProvider(new StreamWriter("packets.log", false), LogCategory.Packets));
|
||||||
@ -29,7 +42,14 @@ namespace TrueCraft
|
|||||||
server.ChatMessageReceived += HandleChatMessageReceived;
|
server.ChatMessageReceived += HandleChatMessageReceived;
|
||||||
server.Start(new IPEndPoint(IPAddress.Any, 25565));
|
server.Start(new IPEndPoint(IPAddress.Any, 25565));
|
||||||
while (true)
|
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)
|
static void HandleChatMessageReceived(object sender, ChatMessageEventArgs e)
|
||||||
|
Reference in New Issue
Block a user