/// <summary> Attempts to save this level (can be cancelled) </summary> /// <param name="force"> Whether to save even if nothing changed since last save </param> /// <returns> Whether this level was successfully saved to disc </returns> public bool Save(bool force = false) { if (blocks == null || IsMuseum) { return(false); // museums do not save changes } string path = LevelInfo.MapPath(MapName); bool cancel = false; OnLevelSaveEvent.Call(this, ref cancel); if (cancel) { return(false); } try { if (!Directory.Exists("levels")) { Directory.CreateDirectory("levels"); } if (!Directory.Exists("levels/level properties")) { Directory.CreateDirectory("levels/level properties"); } if (!Directory.Exists("levels/prev")) { Directory.CreateDirectory("levels/prev"); } if (Changed || force || !File.Exists(path)) { lock (saveLock) SaveCore(path); } else { Logger.Log(LogType.SystemActivity, "Skipping level save for " + name + "."); } } catch (Exception e) { Logger.Log(LogType.Warning, "FAILED TO SAVE :" + name); Chat.MessageGlobal("FAILED TO SAVE {0}", ColoredName); Logger.LogError(e); return(false); } Server.DoGC(); return(true); }
static void DoAll(string src, string dst, byte action) { DoAction(LevelInfo.MapPath(src) + ".backup", LevelInfo.MapPath(dst) + ".backup", action); DoAction(LevelInfo.PropsPath(src), LevelInfo.PropsPath(dst), action); DoAction("levels/level properties/" + src, LevelInfo.PropsPath(dst), action); DoAction(Paths.MapBlockDefs(src), Paths.MapBlockDefs(dst), action); DoAction(BlockPropsOldPath(src), BlockPropsOldPath(dst), action); DoAction(BlockPropsLvlPath(src), BlockPropsLvlPath(dst), action); DoAction(BotsFile.BotsPath(src), BotsFile.BotsPath(dst), action); }
static Level ReadLevel(Player p, string map) { Level lvl = Level.Load(map); if (lvl != null) { return(lvl); } string path = LevelInfo.MapPath(map) + ".backup"; if (!File.Exists(path)) { p.Message("%WBackup of {0} does not exist.", map); return(null); } Logger.Log(LogType.Warning, "Attempting to load backup map for " + map); lvl = Level.Load(map, path); if (lvl != null) { return(lvl); } p.Message("%WLoading backup of {0} failed too.", map); string backupDir = LevelInfo.BackupBasePath(map); if (Directory.Exists(backupDir)) { int latest = LevelInfo.LatestBackup(map); Logger.Log(LogType.Warning, "Attempting to load latest backup ({1}) of {0} instead", map, latest); path = LevelInfo.BackupFilePath(map, latest.ToString()); lvl = Level.Load(map, path); if (lvl == null) { p.Message("%WLoading latest backup failed too."); } } else { p.Message("%WLatest backup of {0} does not exist.", map); } return(lvl); }
/*static void MoveBackups(string src, string dst) { * string srcBase = LevelInfo.BackupBasePath(src); * string dstBase = LevelInfo.BackupBasePath(dst); * if (!Directory.Exists(srcBase)) return; * Directory.CreateDirectory(dstBase); * * string[] backups = Directory.GetDirectories(srcBase); * for (int i = 0; i < backups.Length; i++) { * string name = LevelInfo.BackupNameFrom(backups[i]); * string srcFile = LevelInfo.BackupFilePath(src, name); * string dstFile = LevelInfo.BackupFilePath(dst, name); * string dstDir = LevelInfo.BackupDirPath(dst, name); * * Directory.CreateDirectory(dstDir); * File.Move(srcFile, dstFile); * Directory.Delete(backups[i]); * } * Directory.Delete(srcBase); * }*/ /// <summary> Deletes a map and associated metadata. </summary> public static bool Delete(Player p, string map) { Level lvl = LevelInfo.FindExact(map); if (lvl == Server.mainLevel) { p.Message("Cannot delete the main level."); return(false); } if (lvl != null && !lvl.Unload()) { p.Message("Unable to delete the level, because it could not be unloaded. " + "A game may currently be running on it."); return(false); } p.Message("Created backup."); if (!Directory.Exists("levels/deleted")) { Directory.CreateDirectory("levels/deleted"); } if (File.Exists(Paths.DeletedMapFile(map))) { int num = 0; while (File.Exists(Paths.DeletedMapFile(map + num))) { num++; } File.Move(LevelInfo.MapPath(map), Paths.DeletedMapFile(map + num)); } else { File.Move(LevelInfo.MapPath(map), Paths.DeletedMapFile(map)); } DoAll(map, "", action_delete); DeleteDatabaseTables(map); BlockDBFile.DeleteBackingFile(map); OnLevelDeletedEvent.Call(map); return(true); }
static Level ReadLevel(Player p, string map) { Level lvl = Level.Load(map); if (lvl != null) { return(lvl); } string path = LevelInfo.MapPath(map) + ".backup"; if (!File.Exists(path)) { p.Message("Level \"{0}\" does not exist", map); return(lvl); } lvl = ReadBackup(p, map, path, "backup copy"); if (lvl != null) { return(lvl); } path = Paths.PrevMapFile(map); lvl = ReadBackup(p, map, path, "previous save"); if (lvl != null) { return(lvl); } string backupDir = LevelInfo.BackupBasePath(map); if (Directory.Exists(backupDir)) { int latest = LevelInfo.LatestBackup(map); path = LevelInfo.BackupFilePath(map, latest.ToString()); lvl = ReadBackup(p, map, path, "latest backup"); } else { p.Message("&WLatest backup of {0} does not exist.", map); } return(lvl); }
public static bool Backup(string map, string backupName) { string basePath = LevelInfo.BackupBasePath(map); if (!Directory.Exists(basePath)) { Directory.CreateDirectory(basePath); } string path = Path.Combine(basePath, backupName); Directory.CreateDirectory(path); bool lvl = DoAction(LevelInfo.MapPath(map), Path.Combine(path, map + ".lvl"), action_copy); bool props = DoAction(LevelInfo.PropsPath(map), Path.Combine(path, "map.properties"), action_copy); bool defs = DoAction(BlockDefsPath(map), Path.Combine(path, "blockdefs.json"), action_copy); bool blkOld = DoAction(BlockPropsOldPath(map), Path.Combine(path, "blockprops.txt"), action_copy); bool blkCur = DoAction(BlockPropsLvlPath(map), Path.Combine(path, "blockprops.txt"), action_copy); bool bots = DoAction(BotsFile.BotsPath(map), Path.Combine(path, "bots.json"), action_copy); return(lvl && props && defs && blkOld && blkCur && bots); }
/// <summary> Copies a map and related metadata. </summary> /// <remarks> Backups and BlockDB are NOT copied. </remarks> public static bool Copy(Player p, string src, string dst) { if (LevelInfo.MapExists(dst)) { p.Message("&WLevel \"{0}\" already exists.", dst); return(false); } // Make sure any changes to live map are saved first Level lvl = LevelInfo.FindExact(src); if (lvl != null && !lvl.Save(true)) { p.Message("&WUnable to save {0}! Some recent block changes may not be copied.", src); } File.Copy(LevelInfo.MapPath(src), LevelInfo.MapPath(dst)); DoAll(src, dst, action_copy); CopyDatabaseTables(src, dst); OnLevelCopiedEvent.Call(src, dst); return(true); }
/// <summary> Deletes the .lvl (and related) files and database tables. Unloads level if it is loaded. </summary> public static bool Delete(string name) { Level lvl = LevelInfo.FindExact(name); if (lvl != null && !lvl.Unload()) { return(false); } if (!Directory.Exists("levels/deleted")) { Directory.CreateDirectory("levels/deleted"); } if (File.Exists(LevelInfo.DeletedPath(name))) { int num = 0; while (File.Exists(LevelInfo.DeletedPath(name + num))) { num++; } File.Move(LevelInfo.MapPath(name), LevelInfo.DeletedPath(name + num)); } else { File.Move(LevelInfo.MapPath(name), LevelInfo.DeletedPath(name)); } DeleteIfExists(LevelInfo.MapPath(name) + ".backup"); DeleteIfExists("levels/level properties/" + name); DeleteIfExists("levels/level properties/" + name + ".properties"); DeleteIfExists("blockdefs/lvl_" + name + ".json"); DeleteIfExists("blockprops/lvl_" + name + ".txt"); DeleteIfExists(BotsFile.BotsPath(name)); DeleteDatabaseTables(name); BlockDBFile.DeleteBackingFile(name); return(true); }
public override void Use(Player p, string message) { string[] args = message.SplitSpaces(2); string src = args[0].ToLower(); string dst = args.Length > 1 ? args[1].ToLower() : src; if (message.Length == 0) { Help(p); return; } if (!Formatter.ValidMapName(p, src)) { return; } if (!Formatter.ValidMapName(p, dst)) { return; } string path = Path.Combine(oldServer, LevelInfo.MapPath(src)); if (!File.Exists(path)) { p.Message("%WLevel {0} does not exist on old server.", src); return; } if (LevelInfo.MapExists(dst)) { p.Message("%WLevel {0} already exists on new server.", dst); return; } File.Copy(path, LevelInfo.MapPath(dst), true); CopyProperties(src, dst); CopyBlockDefs(src, dst); CopyBlockProps(src, dst); ExportPortals(p, src, dst); ExportMessages(p, src, dst); p.Message("Successfully imported level {0} as {1}", src, dst); }
public static Level Load(string name) { return(Load(name, LevelInfo.MapPath(name))); }
public static void CopyLevel(string src, string dst) { File.Copy(LevelInfo.MapPath(src), LevelInfo.MapPath(dst)); DoAll(src, dst, action_copy); CopyDatabaseTables(src, dst); }
public static void Resize(ref Level lvl, int width, int height, int length) { Level res = new Level(lvl.name, (ushort)width, (ushort)height, (ushort)length); res.hasPortals = lvl.hasPortals; res.hasMessageBlocks = lvl.hasMessageBlocks; byte[] src = lvl.blocks, dst = res.blocks; // Copy blocks in bulk width = Math.Min(lvl.Width, res.Width); height = Math.Min(lvl.Height, res.Height); length = Math.Min(lvl.Length, res.Length); for (int y = 0; y < height; y++) { for (int z = 0; z < length; z++) { int srcI = lvl.Width * (z + y * lvl.Length); int dstI = res.Width * (z + y * res.Length); Buffer.BlockCopy(src, srcI, dst, dstI, width); } } // Copy extended blocks in bulk width = Math.Min(lvl.ChunksX, res.ChunksX); height = Math.Min(lvl.ChunksY, res.ChunksY); length = Math.Min(lvl.ChunksZ, res.ChunksZ); for (int cy = 0; cy < height; cy++) { for (int cz = 0; cz < length; cz++) { for (int cx = 0; cx < width; cx++) { src = lvl.CustomBlocks[(cy * lvl.ChunksZ + cz) * lvl.ChunksX + cx]; if (src == null) { continue; } dst = new byte[16 * 16 * 16]; res.CustomBlocks[(cy * res.ChunksZ + cz) * res.ChunksX + cx] = dst; Buffer.BlockCopy(src, 0, dst, 0, 16 * 16 * 16); } } } // TODO: This copying is really ugly and probably not 100% right res.spawnx = lvl.spawnx; res.spawny = lvl.spawny; res.spawnz = lvl.spawnz; res.rotx = lvl.rotx; res.roty = lvl.roty; lock (lvl.saveLock) { lvl.Backup(true); // Make sure zones are kept res.Zones = lvl.Zones; lvl.Zones = new VolatileArray <Zone>(false); IMapExporter.Formats[0].Write(LevelInfo.MapPath(lvl.name), res); lvl.SaveChanges = false; } res.backedup = true; Level.LoadMetadata(res); BotsFile.Load(res); LevelActions.Replace(lvl, res); lvl = res; }