MoveOrReplace() public static method

public static MoveOrReplace ( [ source, [ destination ) : void
source [
destination [
return void
Example #1
0
        public static void Save()
        {
            CheckIfLoaded();
            const string tempFileName = Paths.PlayerDBFileName + ".temp";

            lock ( SaveLoadLocker ) {
                PlayerInfo[] listCopy = PlayerInfoList;
                Stopwatch    sw       = Stopwatch.StartNew();
                using (FileStream fs = OpenWrite(tempFileName)) {
                    using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8, BufferSize)) {
                        writer.WriteLine("{0} {1} {2}", maxID, FormatVersion, Header);

                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < listCopy.Length; i++)
                        {
                            listCopy[i].Serialize(sb);
                            writer.WriteLine(sb.ToString());
                            sb.Length = 0;
                        }
                    }
                }
                sw.Stop();
                Logger.Log(LogType.Debug,
                           "PlayerDB.Save: Saved player database ({0} records) in {1}ms",
                           Trie.Count, sw.ElapsedMilliseconds);

                try {
                    Paths.MoveOrReplace(tempFileName, Paths.PlayerDBFileName);
                } catch (Exception ex) {
                    Logger.Log(LogType.Error,
                               "PlayerDB.Save: An error occured while trying to save PlayerDB: {0}", ex);
                }
            }
        }
Example #2
0
        void TrimFile(int maxCapacity)
        {
            if (maxCapacity == 0)
            {
                using (File.Create(FileName)) { }
                return;
            }
            string tempFileName = FileName + ".tmp";

            using (FileStream source = File.OpenRead(FileName)) {
                int entries = (int)(source.Length / BlockDBEntry.Size);
                if (entries <= maxCapacity)
                {
                    return;
                }

                // skip beginning of the file (that's where old entries are)
                source.Seek((entries - maxCapacity) * BlockDBEntry.Size, SeekOrigin.Begin);

                // copy end of the existing file to a new one
                using (FileStream destination = File.Create(tempFileName)) {
                    while (source.Position < source.Length)
                    {
                        int bytesRead = source.Read(ioBuffer, 0, ioBuffer.Length);
                        destination.Write(ioBuffer, 0, bytesRead);
                    }
                }
            }
            Paths.MoveOrReplace(tempFileName, FileName);
        }
Example #3
0
        static void Beat(SchedulerTask scheduledTask)
        {
            if (Server.IsShuttingDown)
            {
                return;
            }

            if (ConfigKey.HeartbeatEnabled.Enabled())
            {
                SendClassiCubeBeat();
                HbSave();
            }
            else
            {
                // If heartbeats are disabled, the server data is written
                // to a text file instead (heartbeatdata.txt)
                string[] data = new[] {
                    Salt,
                    Server.InternalIP.ToString(),
                    Server.Port.ToString(),
                    Server.CountPlayers(false).ToString(),
                    ConfigKey.MaxPlayers.GetString(),
                    ConfigKey.ServerName.GetString(),
                    ConfigKey.IsPublic.GetString(),
                };
                const string tempFile = Paths.HeartbeatDataFileName + ".tmp";
                File.WriteAllLines(tempFile, data, Encoding.ASCII);
                Paths.MoveOrReplace(tempFile, Paths.HeartbeatDataFileName);
            }
        }
Example #4
0
        internal static void Save()
        {
            if (!IsLoaded)
            {
                return;
            }
            Logger.Log(LogType.Debug,
                       "IPBanList.Save: Saving IP ban list ({0} records).", Bans.Count);
            const string tempFile = Paths.IPBanListFileName + ".temp";

            lock ( BanListLock ) {
                using (StreamWriter writer = File.CreateText(tempFile)) {
                    writer.WriteLine("{0} {1}", FormatVersion, Header);
                    foreach (IPBanInfo entry in Bans.Values)
                    {
                        writer.WriteLine(entry.Serialize());
                    }
                }
            }
            try {
                Paths.MoveOrReplace(tempFile, Paths.IPBanListFileName);
            } catch (Exception ex) {
                Logger.Log(LogType.Error,
                           "IPBanList.Save: An error occured while trying to save ban list file: {0}", ex);
            }
        }
Example #5
0
        public static void Save()
        {
            const string tempFileName = Paths.PlayerDBFileName + ".temp";

            lock ( SaveLoadLocker ) {
                PlayerInfo[] listCopy = GetPlayerListCopy();
                Stopwatch    sw       = Stopwatch.StartNew();
                using (FileStream fs = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.None, 64 * 1024)) {
                    using (StreamWriter writer = new StreamWriter(fs, System.Text.Encoding.ASCII, 64 * 1024)) {
                        writer.WriteLine("{0} {1} {2}", maxID, FormatVersion, Header);

                        for (int i = 0; i < listCopy.Length; i++)
                        {
                            // TODO: Reuse StringBuilder after switching to 4.0
                            writer.WriteLine(listCopy[i].Serialize());
                        }
                    }
                }
                sw.Stop();
                Logger.Log("PlayerDB.Save: Saved player database ({0} records) in {1}ms", LogType.Debug,
                           Trie.Count, sw.ElapsedMilliseconds);

                try {
                    Paths.MoveOrReplace(tempFileName, Paths.PlayerDBFileName);
                } catch (Exception ex) {
                    Logger.Log("PlayerDB.Save: An error occured while trying to save PlayerDB: " + ex, LogType.Error);
                }
            }
        }
Example #6
0
 internal static void Save()
 {
     lock ( BanListLock ) {
         CheckIfLoaded();
         const string tempFile = Paths.IPBanListFileName + ".temp";
         Stopwatch    sw       = Stopwatch.StartNew();
         using (StreamWriter writer = File.CreateText(tempFile)) {
             writer.WriteLine("{0} {1}", FormatVersion, Header);
             foreach (IPBanInfo entry in Bans.Values)
             {
                 writer.WriteLine(entry.Serialize());
             }
         }
         try {
             Paths.MoveOrReplace(tempFile, Paths.IPBanListFileName);
             sw.Stop();
             Logger.Log(LogType.Debug,
                        "IPBanList.Save: Saved IP-ban database ({0} records) in {1}ms",
                        Count, sw.ElapsedMilliseconds);
         } catch (Exception ex) {
             Logger.Log(LogType.Error,
                        "IPBanList.Save: An error occured while trying to save ban list file: {0}", ex);
         }
     }
 }
Example #7
0
        /// <summary> Saves this map to a file in the default format (FCMv3). </summary>
        /// <returns> Whether the saving succeeded. </returns>
        public bool Save([NotNull] string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName");
            }
            string tempFileName = fileName + ".temp";

            // save to a temporary file
            try
            {
                HasChangedSinceSave = false;
                if (!MapUtility.TrySave(this, tempFileName, SaveFormat))
                {
                    HasChangedSinceSave = true;
                }
            }
            catch (IOException ex)
            {
                HasChangedSinceSave = true;
                Logger.Log(LogType.Error,
                           "Map.Save: Unable to open file \"{0}\" for writing: {1}",
                           tempFileName, ex);
                if (File.Exists(tempFileName))
                {
                    File.Delete(tempFileName);
                }
                return(false);
            }

            // move newly-written file into its permanent destination
            try
            {
                Paths.MoveOrReplace(tempFileName, fileName);
                Logger.Log(LogType.SystemActivity,
                           "Saved map to {0}", fileName);
                HasChangedSinceBackup = true;
            }
            catch (Exception ex)
            {
                HasChangedSinceSave = true;
                Logger.Log(LogType.Error,
                           "Map.Save: Error trying to replace file \"{0}\": {1}",
                           fileName, ex);
                if (File.Exists(tempFileName))
                {
                    File.Delete(tempFileName);
                }
                return(false);
            }
            return(true);
            // ReSharper restore EmptyGeneralCatchClause
        }
Example #8
0
        public static void HbSave()
        {
            try
            {
                const string SaverFile = "heartbeatdata.txt";

                if (File.Exists(SaverFile))
                {
                    File.Delete(SaverFile);
                }
                if (Salt == null)
                {
                    return;
                }

                if (Server.CountPlayers(false).ToString() == null)
                {
                    return;
                }
                string[] data = new[] {
                    Salt,
                    Server.InternalIP.ToString(),
                    Server.Port.ToString(),
                    Server.CountPlayers(false).ToString(),
                    ConfigKey.MaxPlayers.GetString(),
                    ConfigKey.ServerName.GetString(),
                    ConfigKey.IsPublic.GetString(),
                    Salt2
                };

                //"port=" + Server.Port.ToString() + "&max=" + ConfigKey.MaxPlayers.GetString() + "&name=" +
                //Uri.EscapeDataString(ConfigKey.ServerName.GetString()) +
                //"&public=True" + "&salt=" + Salt + "&users=" + Server.CountPlayers(false).ToString();
                const string tempFile = Paths.HeartbeatDataFileName + ".tmp";
                File.WriteAllLines(tempFile, data, Encoding.ASCII);
                Paths.MoveOrReplace(tempFile, Paths.HeartbeatDataFileName);
            }
            catch (Exception ex) { Logger.Log(LogType.Error, "" + ex); }
        }
Example #9
0
        public static void SaveWorldList()
        {
            const string worldListTempFileName = Paths.WorldListFileName + ".tmp";

            // Save world list
            try {
                XDocument doc  = new XDocument();
                XElement  root = new XElement("fCraftWorldList");
                XElement  temp;
                World[]   worldListCache = WorldList;

                foreach (World world in worldListCache)
                {
                    temp = new XElement("World");
                    temp.Add(new XAttribute("name", world.Name));
                    temp.Add(new XAttribute("access", world.AccessSecurity.MinRank));   // LEGACY
                    temp.Add(new XAttribute("build", world.BuildSecurity.MinRank));     // LEGACY
                    temp.Add(world.AccessSecurity.Serialize("accessSecurity"));
                    temp.Add(world.BuildSecurity.Serialize("buildSecurity"));
                    if (world.NeverUnload)
                    {
                        temp.Add(new XAttribute("noUnload", true));
                    }
                    if (world.IsHidden)
                    {
                        temp.Add(new XAttribute("hidden", true));
                    }
                    root.Add(temp);
                }
                root.Add(new XAttribute("main", MainWorld.Name));

                doc.Add(root);
                doc.Save(worldListTempFileName);
                Paths.MoveOrReplace(worldListTempFileName, Paths.WorldListFileName);
            } catch (Exception ex) {
                Logger.Log("Server.SaveWorldList: An error occured while trying to save the world list: {0}", LogType.Error, ex);
            }
        }
Example #10
0
        public static void SaveBinary()
        {
            CheckIfLoaded();
            const string tempFileName = Paths.PlayerDBFileName + ".bin.temp";

            lock ( SaveLoadLocker ) {
                PlayerInfo[] listCopy = PlayerInfoList;
                Stopwatch    sw       = Stopwatch.StartNew();
                using (FileStream fs = OpenWrite(tempFileName)) {
                    BinaryWriter writer = new BinaryWriter(fs);
                    writer.Write(FormatVersion);
                    writer.Write(maxID);
                    writer.Write(RankManager.Ranks.Count);
                    foreach (Rank rank in RankManager.Ranks)
                    {
                        writer.Write((byte)rank.Index);
                        writer.Write(rank.FullName);
                    }
                    writer.Write(listCopy.Length);
                    for (int i = 0; i < listCopy.Length; i++)
                    {
                        listCopy[i].Serialize(writer);
                    }
                }
                sw.Stop();
                Logger.Log(LogType.Debug,
                           "PlayerDB.SaveBinary: Saved player database ({0} records) in {1}ms",
                           Trie.Count, sw.ElapsedMilliseconds);

                try {
                    Paths.MoveOrReplace(tempFileName, Paths.PlayerDBFileName + ".bin");
                } catch (Exception ex) {
                    Logger.Log(LogType.Error,
                               "PlayerDB.SaveBinary: An error occured while trying to save PlayerDB: {0}", ex);
                }
            }
        }
Example #11
0
        public bool Save(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName");
            }
            string tempFileName = fileName + ".temp";

            try {
                ChangedSinceSave = false;
                if (!MapUtility.TrySave(this, tempFileName, MapFormat.FCMv3))
                {
                    ChangedSinceSave = true;
                }
            } catch (IOException ex) {
                ChangedSinceSave = true;
                Logger.Log("Map.Save: Unable to open file \"{0}\" for writing: {1}", LogType.Error,
                           tempFileName, ex.Message);
                try { File.Delete(tempFileName); } catch { }
                return(false);
            }

            try {
                Paths.MoveOrReplace(tempFileName, fileName);
                Logger.Log("Saved map successfully to {0}", LogType.SystemActivity,
                           fileName);
                ChangedSinceBackup = true;
            } catch (Exception ex) {
                ChangedSinceSave = true;
                Logger.Log("Error trying to replace file \"{0}\": {1}", LogType.Error,
                           fileName, ex);
                try { File.Delete(tempFileName); } catch { }
                return(false);
            }
            return(true);
        }
Example #12
0
        /// <summary> Saves the current world list to worlds.xml. Thread-safe. </summary>
        public static void SaveWorldList()
        {
            const string worldListTempFileName = Paths.WorldListFileName + ".tmp";

            // Save world list
            lock ( SyncRoot ) {
                XDocument doc  = new XDocument();
                XElement  root = new XElement("fCraftWorldList");

                foreach (World world in Worlds)
                {
                    XElement temp = new XElement("World");
                    temp.Add(new XAttribute("name", world.Name));

                    if (world.AccessSecurity.HasRestrictions)
                    {
                        temp.Add(world.AccessSecurity.Serialize(AccessSecurityXmlTagName));
                    }
                    if (world.BuildSecurity.HasRestrictions)
                    {
                        temp.Add(world.BuildSecurity.Serialize(BuildSecurityXmlTagName));
                    }

                    if (world.BackupInterval != WorldManager.DefaultBackupInterval)
                    {
                        temp.Add(new XAttribute("backup", world.BackupInterval.ToTickString()));
                    }

                    if (world.NeverUnload)
                    {
                        temp.Add(new XAttribute("noUnload", true));
                    }
                    if (world.VisitCount > 0)
                    {
                        temp.Add(new XAttribute("visitCount", world.VisitCount));
                    }
                    if (world.IsHidden)
                    {
                        temp.Add(new XAttribute("hidden", true));
                    }
                    temp.Add(world.BlockDB.SaveSettings());

                    temp.Add(Physics.SaveSettings(world));
                    temp.Add(Physics.SaveOtherSettings(world));

                    temp.Add(world.SaveRealmState());
                    if (world.Greeting != null)
                    {
                        temp.Add(new XElement("Greeting", world.Greeting));
                    }
                    World world1 = world;
                    foreach (Rank mainedRank in RankManager.Ranks.Where(r => r.MainWorld == world1))
                    {
                        temp.Add(new XElement(RankMainXmlTagName, mainedRank.FullName));
                    }

                    if (!String.IsNullOrEmpty(world.LoadedBy))
                    {
                        temp.Add(new XElement("LoadedBy", world.LoadedBy));
                    }
                    if (world.LoadedOn != DateTime.MinValue)
                    {
                        temp.Add(new XElement("LoadedOn", world.LoadedOn.ToUnixTime()));
                    }
                    if (!String.IsNullOrEmpty(world.MapChangedBy))
                    {
                        temp.Add(new XElement("MapChangedBy", world.MapChangedBy));
                    }
                    if (world.MapChangedOn != DateTime.MinValue)
                    {
                        temp.Add(new XElement("MapChangedOn", world.MapChangedOn.ToUnixTime()));
                    }

                    XElement elEnv = new XElement(EnvironmentXmlTagName);
                    if (world.CloudColor > -1)
                    {
                        elEnv.Add(new XAttribute("cloud", world.CloudColor));
                    }
                    if (world.FogColor > -1)
                    {
                        elEnv.Add(new XAttribute("fog", world.FogColor));
                    }
                    if (world.SkyColor > -1)
                    {
                        elEnv.Add(new XAttribute("sky", world.SkyColor));
                    }
                    if (world.EdgeLevel > -1)
                    {
                        elEnv.Add(new XAttribute("level", world.EdgeLevel));
                    }
                    if (world.Terrain != null)
                    {
                        elEnv.Add(new XAttribute("terrain", world.Terrain));
                    }
                    if (world.EdgeBlock != Block.Water)
                    {
                        elEnv.Add(new XAttribute("edge", world.EdgeBlock));
                    }
                    if (elEnv.HasAttributes)
                    {
                        temp.Add(elEnv);
                    }
                    root.Add(temp);
                }
                root.Add(new XAttribute("main", MainWorld.Name));

                doc.Add(root);
                doc.Save(worldListTempFileName);
                Paths.MoveOrReplace(worldListTempFileName, Paths.WorldListFileName);
            }
        }
Example #13
0
        public static bool Save(bool saveSalt)
        {
            XDocument file = new XDocument();

            XElement config = new XElement(ConfigXmlRootName);

            config.Add(new XAttribute("version", ConfigVersion));
            if (saveSalt)
            {
                config.Add(new XAttribute("salt", Server.Salt));
            }

            // save general settings
            foreach (ConfigSection section in Enum.GetValues(typeof(ConfigSection)))
            {
                XElement sectionEl = new XElement("Section");
                sectionEl.Add(new XAttribute("name", section));
                foreach (ConfigKey key in KeyMetadata.Values.Where(a => a.Section == section).Select(a => a.Key))
                {
                    if (IsDefault(key))
                    {
                        sectionEl.Add(new XComment(new XElement(key.ToString(), Settings[key]).ToString()));
                    }
                    else
                    {
                        sectionEl.Add(new XElement(key.ToString(), Settings[key]));
                    }
                }
                config.Add(sectionEl);
            }

            // save console options
            XElement consoleOptions = new XElement("ConsoleOptions");

            for (int i = 0; i < Logger.ConsoleOptions.Length; i++)
            {
                if (Logger.ConsoleOptions[i])
                {
                    consoleOptions.Add(new XElement(((LogType)i).ToString()));
                }
            }
            config.Add(consoleOptions);

            // save logfile options
            XElement logFileOptions = new XElement("LogFileOptions");

            for (int i = 0; i < Logger.LogFileOptions.Length; i++)
            {
                if (Logger.LogFileOptions[i])
                {
                    logFileOptions.Add(new XElement(((LogType)i).ToString()));
                }
            }
            config.Add(logFileOptions);

            // save ranks
            XElement ranksTag = new XElement("Ranks");

            foreach (Rank rank in RankManager.Ranks)
            {
                ranksTag.Add(rank.Serialize());
            }
            config.Add(ranksTag);

            // save legacy rank mapping
            XElement legacyRankMappingTag = new XElement("LegacyRankMapping");

            foreach (KeyValuePair <string, string> pair in RankManager.LegacyRankMapping)
            {
                XElement rankPair = new XElement("LegacyRankPair");
                rankPair.Add(new XAttribute("from", pair.Key), new XAttribute("to", pair.Value));
                legacyRankMappingTag.Add(rankPair);
            }
            config.Add(legacyRankMappingTag);


            file.Add(config);
            try {
                // write out the changes
                string tempFileName = Paths.ConfigFileName + ".temp";
                file.Save(tempFileName);
                Paths.MoveOrReplace(tempFileName, Paths.ConfigFileName);
                return(true);
            } catch (Exception ex) {
                Logger.LogAndReportCrash("Config failed to save", "fCraft", ex, true);
                return(false);
            }
        }
Example #14
0
        static void Beat(SchedulerTask scheduledTask)
        {
            if (Server.IsShuttingDown)
            {
                return;
            }

            data = new HeartbeatData {
                IsPublic        = ConfigKey.IsPublic.GetBool(),
                MaxPlayers      = ConfigKey.MaxPlayers.GetInt(),
                PlayerCount     = Server.CountPlayers(false),
                ServerIP        = Server.IP,
                Port            = Server.Port,
                ProtocolVersion = Config.ProtocolVersion,
                Salt            = Server.Salt,
                ServerName      = ConfigKey.ServerName.GetString()
            };

            // This needs to be wrapped in try/catch because and exception in an event handler
            // would permanently stop heartbeat sending.
            try {
                if (RaiseHeartbeatSendingEvent(data))
                {
                    RescheduleHeartbeat();
                    return;
                }
            } catch (Exception ex) {
                Logger.LogAndReportCrash("Heartbeat.Sending handler failed", "fCraft", ex, false);
            }

            if (ConfigKey.HeartbeatEnabled.GetBool())
            {
                UriBuilder    ub = new UriBuilder(Uri);
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("public={0}&max={1}&users={2}&port={3}&version={4}&salt={5}&name={6}",
                                data.IsPublic,
                                data.MaxPlayers,
                                data.PlayerCount,
                                data.Port,
                                data.ProtocolVersion,
                                Uri.EscapeDataString(data.Salt),
                                Uri.EscapeDataString(data.ServerName));

                foreach (var pair in data.CustomData)
                {
                    sb.AppendFormat("&{0}={1}",
                                    Uri.EscapeDataString(pair.Key),
                                    Uri.EscapeDataString(pair.Value));
                }
                ub.Query = sb.ToString();

                request = (HttpWebRequest)WebRequest.Create(ub.Uri);
                request.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(BindIPEndPointCallback);
                request.Method      = "GET";
                request.Timeout     = Timeout;
                request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);

                request.BeginGetResponse(ResponseCallback, null);
            }
            else
            {
                // If heartbeats are disabled, the data is written to a text file (heartbeatdata.txt)
                const string tempFile = Paths.HeartbeatDataFileName + ".tmp";

                File.WriteAllLines(tempFile,
                                   new[] {
                    Server.Salt,
                    Server.IP.ToString(),
                    Server.Port.ToString(),
                    Server.CountPlayers(false).ToString(),
                    ConfigKey.MaxPlayers.GetString(),
                    ConfigKey.ServerName.GetString(),
                    ConfigKey.IsPublic.GetString()
                },
                                   Encoding.ASCII);

                Paths.MoveOrReplace(tempFile, Paths.HeartbeatDataFileName);
                RescheduleHeartbeat();
            }
        }
Example #15
0
        /// <summary> Saves the current world list to worlds.xml. Thread-safe. </summary>
        public static void SaveWorldList()
        {
            const string worldListTempFileName = Paths.WorldListFileName + ".tmp";

            // Save world list
            lock ( SyncRoot ) {
                XDocument doc  = new XDocument();
                XElement  root = new XElement("fCraftWorldList");

                foreach (World world in Worlds)
                {
                    XElement temp = new XElement("World");
                    temp.Add(new XAttribute("name", world.Name));

                    if (world.AccessSecurity.HasRestrictions)
                    {
                        temp.Add(world.AccessSecurity.Serialize(AccessSecurityXmlTagName));
                    }
                    if (world.BuildSecurity.HasRestrictions)
                    {
                        temp.Add(world.BuildSecurity.Serialize(BuildSecurityXmlTagName));
                    }

                    // save backup settings
                    switch (world.BackupEnabledState)
                    {
                    case YesNoAuto.Yes:
                        temp.Add(new XAttribute("backup", world.BackupInterval.ToTickString()));
                        break;

                    case YesNoAuto.No:
                        temp.Add(new XAttribute("backup", 0));
                        break;
                    }

                    if (world.NeverUnload)
                    {
                        temp.Add(new XAttribute("noUnload", true));
                    }
                    if (world.IsHidden)
                    {
                        temp.Add(new XAttribute("hidden", true));
                    }
                    temp.Add(world.BlockDB.SaveSettings());

                    World world1 = world; // keeping ReSharper happy
                    foreach (Rank mainedRank in RankManager.Ranks.Where(r => r.MainWorld == world1))
                    {
                        temp.Add(new XElement(RankMainXmlTagName, mainedRank.FullName));
                    }

                    // save loaded/map-changed information
                    if (!String.IsNullOrEmpty(world.LoadedBy))
                    {
                        temp.Add(new XElement("LoadedBy", world.LoadedBy));
                    }
                    if (world.LoadedOn != DateTime.MinValue)
                    {
                        temp.Add(new XElement("LoadedOn", world.LoadedOn.ToUnixTime()));
                    }
                    if (!String.IsNullOrEmpty(world.MapChangedBy))
                    {
                        temp.Add(new XElement("MapChangedBy", world.MapChangedBy));
                    }
                    if (world.MapChangedOn != DateTime.MinValue)
                    {
                        temp.Add(new XElement("MapChangedOn", world.MapChangedOn.ToUnixTime()));
                    }

                    // save environmental settings
                    XElement elEnv = new XElement(EnvironmentXmlTagName);
                    if (world.CloudColor > -1)
                    {
                        elEnv.Add(new XAttribute("cloud", world.CloudColor));
                    }
                    if (world.FogColor > -1)
                    {
                        elEnv.Add(new XAttribute("fog", world.FogColor));
                    }
                    if (world.SkyColor > -1)
                    {
                        elEnv.Add(new XAttribute("sky", world.SkyColor));
                    }
                    if (world.EdgeLevel > -1)
                    {
                        elEnv.Add(new XAttribute("level", world.EdgeLevel));
                    }
                    if (world.EdgeBlock != Block.Water)
                    {
                        elEnv.Add(new XAttribute("edge", world.EdgeBlock));
                    }
                    if (elEnv.HasAttributes)
                    {
                        temp.Add(elEnv);
                    }

                    // save lock information
                    if (world.IsLocked)
                    {
                        temp.Add(new XAttribute("locked", true));
                        if (!String.IsNullOrEmpty(world.LockedBy))
                        {
                            temp.Add(new XElement("LockedBy", world.LockedBy));
                        }
                        if (world.LockedOn != DateTime.MinValue)
                        {
                            temp.Add(new XElement("LockedOn", world.LockedOn.ToUnixTime()));
                        }
                    }
                    else
                    {
                        if (!String.IsNullOrEmpty(world.UnlockedBy))
                        {
                            temp.Add(new XElement("UnlockedBy", world.UnlockedBy));
                        }
                        if (world.UnlockedOn != DateTime.MinValue)
                        {
                            temp.Add(new XElement("UnlockedOn", world.UnlockedOn.ToUnixTime()));
                        }
                    }

                    root.Add(temp);
                }
                root.Add(new XAttribute("main", MainWorld.Name));

                doc.Add(root);
                doc.Save(worldListTempFileName);
                Paths.MoveOrReplace(worldListTempFileName, Paths.WorldListFileName);
            }
        }