/// <summary> /// Save the world to disk. Let the caller decide if this should be in a thread because in some situations it shouldnt (ie: when loading a newly generated world the file has to be saved first). /// This is only called by a standalone server or a server thread running in single player. In single player the user can also manually initiate a save in which case this will be called using a Task. /// </summary> internal static void SaveToDisk() { if (File.Exists(Settings.WorldFileTempPath)) { File.Delete(Settings.WorldFileTempPath); } var fstream = new FileStream(Settings.WorldFileTempPath, FileMode.Create); var gzstream = new GZipStream(fstream, CompressionMode.Compress); //GZipStream only applies compression during .Write, writing 2 bytes at a time ends up inflating it a lot. Adding this saves up to 99.3% var buffstream = new BufferedStream(gzstream, 65536); var chunkBytes = new byte[Chunk.SIZE_IN_BYTES]; var worldSettings = WorldSettings.GetXmlByteArray(); buffstream.Write(BitConverter.GetBytes(worldSettings.Length), 0, sizeof(int)); buffstream.Write(worldSettings, 0, worldSettings.Length); //write the length of the world config xml for (var x = 0; x < SizeInChunksX; x++) { for (var z = 0; z < SizeInChunksZ; z++) { Buffer.BlockCopy(Chunks[x, z].Blocks.Array, 0, chunkBytes, 0, chunkBytes.Length); //Buffer.BlockCopy(Chunks[x,z].Blocks.DiffArray, 0, chunkBytes, 0, chunkBytes.Length); 'bm: this will save a diff instead, WIP buffstream.Write(chunkBytes, 0, chunkBytes.Length); } } buffstream.Flush(); buffstream.Close(); gzstream.Close(); fstream.Close(); buffstream.Dispose(); gzstream.Dispose(); fstream.Dispose(); File.Copy(Settings.WorldFileTempPath, Settings.WorldFilePath, true); File.Delete(Settings.WorldFileTempPath); }
/// <summary> /// Called from Server.Controller class only. The scenarios where we load from disk are if this is a server launching with a previously saved world /// or if this is a single player and the server thread is loading the previously saved world. /// </summary> internal static void LoadFromDisk() { if (Config.Mode == ModeType.JoinServer) { throw new Exception("World should not be loaded from disk when joining a server."); } var stopwatch = new Stopwatch(); stopwatch.Start(); var fstream = new FileStream(Settings.WorldFilePath, FileMode.Open); var gzstream = new GZipStream(fstream, CompressionMode.Decompress); var bytesRead = 0; var worldSettingsSizeBytes = new byte[sizeof(int)]; while (bytesRead < sizeof(int)) { bytesRead += gzstream.Read(worldSettingsSizeBytes, bytesRead, sizeof(int) - bytesRead); //read the size of the world config xml } var worldSettingsBytes = new byte[BitConverter.ToInt32(worldSettingsSizeBytes, 0)]; bytesRead = 0; while (bytesRead < worldSettingsBytes.Length) { bytesRead += gzstream.Read(worldSettingsBytes, bytesRead, worldSettingsBytes.Length - bytesRead); } WorldSettings.LoadSettings(worldSettingsBytes); var chunkTotal = SizeInChunksX * SizeInChunksZ; var chunkCount = 1; var tasks = new Task[chunkTotal]; for (var x = 0; x < SizeInChunksX; x++) //loop through each chunk and load it { for (var z = 0; z < SizeInChunksZ; z++) { if (Config.IsSinglePlayer) { Settings.Launcher.UpdateProgressInvokable(string.Format("Loading Chunks: {0} / {1}", chunkCount, chunkTotal), chunkCount, chunkTotal); } var chunkBytes = new byte[Chunk.SIZE_IN_BYTES]; bytesRead = 0; while (bytesRead < chunkBytes.Length) { bytesRead += gzstream.Read(chunkBytes, bytesRead, chunkBytes.Length - bytesRead); } int x1 = x, z1 = z; var task = Task.Factory.StartNew(() => LoadChunk(Chunks[x1, z1], chunkBytes)); tasks[chunkCount - 1] = task; chunkCount++; } } Task.WaitAll(tasks); gzstream.Close(); fstream.Close(); stopwatch.Stop(); Debug.WriteLine("World load from disk time: {0}ms", stopwatch.ElapsedMilliseconds); InitializeAllLightMaps(); }