1using Microsoft.Xna.Framework;
 
    3using System.Collections.Generic;
 
    6using Terraria.ModLoader.Default;
 
    9using Terraria.Utilities;
 
   13    internal static class WorldIO
 
   18        internal static void Save(
string path, 
bool isCloudSave) {
 
   19            path = Path.ChangeExtension(path, 
".twld");
 
   20            if (FileUtilities.Exists(path, isCloudSave))
 
   21                FileUtilities.Copy(path, path + 
".bak", isCloudSave);
 
   23            var tag = 
new TagCompound {
 
   24                [
"chests"] = SaveChests(),
 
   25                [
"tiles"] = TileIO.SaveTiles(),
 
   26                [
"containers"] = TileIO.SaveContainers(),
 
   27                [
"npcs"] = SaveNPCs(),
 
   28                [
"tileEntities"] = TileIO.SaveTileEntities(),
 
   29                [
"killCounts"] = SaveNPCKillCounts(),
 
   30                [
"anglerQuest"] = SaveAnglerQuest(),
 
   31                [
"townManager"] = SaveTownManager(),
 
   32                [
"modData"] = SaveModData()
 
   35            var stream = 
new MemoryStream();
 
   36            TagIO.ToStream(tag, stream);
 
   37            var data = stream.ToArray();
 
   38            FileUtilities.Write(path, data, data.Length, isCloudSave);
 
   41        internal static void Load(
string path, 
bool isCloudSave) {
 
   42            customDataFail = 
null;
 
   43            path = Path.ChangeExtension(path, 
".twld");
 
   44            if (!FileUtilities.Exists(path, isCloudSave))
 
   47            var buf = FileUtilities.ReadAllBytes(path, isCloudSave);
 
   48            if (buf[0] != 0x1F || buf[1] != 0x8B) {
 
   53            var tag = TagIO.FromStream(
new MemoryStream(buf));
 
   54            LoadChests(tag.GetList<TagCompound>(
"chests"));
 
   55            TileIO.LoadTiles(tag.GetCompound(
"tiles"));
 
   56            TileIO.LoadContainers(tag.GetCompound(
"containers"));
 
   57            LoadNPCs(tag.GetList<TagCompound>(
"npcs"));
 
   59                TileIO.LoadTileEntities(tag.GetList<TagCompound>(
"tileEntities"));
 
   65            LoadNPCKillCounts(tag.GetList<TagCompound>(
"killCounts"));
 
   66            LoadAnglerQuest(tag.GetCompound(
"anglerQuest"));
 
   67            LoadTownManager(tag.GetList<TagCompound>(
"townManager"));
 
   69                LoadModData(tag.GetList<TagCompound>(
"modData"));
 
   77        internal static List<TagCompound> SaveChests() {
 
   78            var list = 
new List<TagCompound>();
 
   79            for (
int k = 0; k < 1000; k++) {
 
   80                var chest = Main.chest[k];
 
   84                var itemTagList = PlayerIO.SaveInventory(chest.item);
 
   85                if (itemTagList == 
null) 
 
   88                list.Add(
new TagCompound {
 
   89                    [
"items"] = itemTagList,
 
   97        internal static void LoadChests(IList<TagCompound> list) {
 
   98            foreach (var tag 
in list) {
 
   99                int x = tag.GetInt(
"x");
 
  100                int y = tag.GetInt(
"y");
 
  101                int chest = Chest.FindChest(x, y);
 
  103                    chest = Chest.CreateChest(x, y);
 
  105                    PlayerIO.LoadInventory(Main.chest[chest].item, tag.GetList<TagCompound>(
"items"));
 
  109        internal static List<TagCompound> SaveNPCs() {
 
  110            var list = 
new List<TagCompound>();
 
  111            for (
int k = 0; k < Main.npc.Length; k++) {
 
  112                NPC npc = Main.npc[k];
 
  113                if (npc.active && 
NPCLoader.IsModNPC(npc)) {
 
  115                        TagCompound tag = 
new TagCompound {
 
  116                            [
"mod"] = npc.modNPC.mod.Name,
 
  117                            [
"name"] = npc.modNPC.Name,
 
  118                            [
"displayName"] = npc.GivenName,
 
  119                            [
"x"] = npc.position.X,
 
  120                            [
"y"] = npc.position.Y,
 
  121                            [
"homeless"] = npc.homeless,
 
  122                            [
"homeTileX"] = npc.homeTileX,
 
  123                            [
"homeTileY"] = npc.homeTileY
 
  127                    else if (NPCID.Sets.SavesAndLoads[npc.type]) {
 
  128                        TagCompound tag = 
new TagCompound {
 
  129                            [
"mod"] = npc.modNPC.mod.Name,
 
  130                            [
"name"] = npc.modNPC.Name,
 
  131                            [
"x"] = npc.position.X,
 
  132                            [
"y"] = npc.position.Y
 
  141        internal static void LoadNPCs(IList<TagCompound> list) {
 
  146            foreach (TagCompound tag 
in list) {
 
  148                int type = mod?.
NPCType(tag.GetString(
"name")) ?? 0;
 
  150                    while (nextFreeNPC < 200 && Main.npc[nextFreeNPC].active) {
 
  153                    if (nextFreeNPC >= 200) {
 
  156                    NPC npc = Main.npc[nextFreeNPC];
 
  157                    npc.SetDefaults(type);
 
  158                    npc.position.X = tag.GetFloat(
"x");
 
  159                    npc.position.Y = tag.GetFloat(
"y");
 
  161                        npc.GivenName = tag.GetString(
"displayName");
 
  162                        npc.homeless = tag.GetBool(
"homeless");
 
  163                        npc.homeTileX = tag.GetInt(
"homeTileX");
 
  164                        npc.homeTileY = tag.GetInt(
"homeTileY");
 
  168                    ModContent.GetInstance<MysteryWorld>().mysteryNPCs.Add(tag);
 
  173        internal static List<TagCompound> SaveNPCKillCounts() {
 
  174            var list = 
new List<TagCompound>();
 
  176                if (NPC.killCount[type] <= 0)
 
  179                list.Add(
new TagCompound {
 
  182                    [
"count"] = NPC.killCount[type]
 
  188        internal static void LoadNPCKillCounts(IList<TagCompound> list) {
 
  189            foreach (var tag 
in list) {
 
  191                int type = mod?.
NPCType(tag.GetString(
"name")) ?? 0;
 
  193                    NPC.killCount[type] = tag.GetInt(
"count");
 
  196                    ModContent.GetInstance<MysteryWorld>().mysteryKillCounts.Add(tag);
 
  201        internal static TagCompound SaveAnglerQuest() {
 
  202            if (Main.anglerQuest < 
ItemLoader.vanillaQuestFishCount)
 
  205            int type = Main.anglerQuestItemNetIDs[Main.anglerQuest];
 
  208            return new TagCompound {
 
  210                [
"itemName"] = modItem.Name
 
  214        internal static void LoadAnglerQuest(TagCompound tag) {
 
  216            if (!tag.ContainsKey(
"mod")) {
 
  220            int type = mod?.
ItemType(tag.GetString(
"itemName")) ?? 0;
 
  222                for (
int k = 0; k < Main.anglerQuestItemNetIDs.Length; k++) {
 
  223                    if (Main.anglerQuestItemNetIDs[k] == type) {
 
  224                        Main.anglerQuest = k;
 
  229            Main.AnglerQuestSwap();
 
  232        internal static List<TagCompound> SaveTownManager() {
 
  233            var list = 
new List<TagCompound>();
 
  234            foreach (Tuple<int, Point> pair 
in WorldGen.TownManager._roomLocationPairs) {
 
  235                if (pair.Item1 >= NPCID.Count) {
 
  237                    TagCompound tag = 
new TagCompound {
 
  240                        [
"x"] = pair.Item2.X,
 
  249        internal static void LoadTownManager(IList<TagCompound> list) {
 
  253            foreach (TagCompound tag 
in list) {
 
  255                int type = mod?.
NPCType(tag.GetString(
"name")) ?? 0;
 
  257                    Point location = 
new Point(tag.GetInt(
"x"), tag.GetInt(
"y"));
 
  258                    WorldGen.TownManager._roomLocationPairs.Add(Tuple.Create(type, location));
 
  259                    WorldGen.TownManager._hasRoom[type] = 
true;
 
  264        internal static List<TagCompound> SaveModData() {
 
  265            var list = 
new List<TagCompound>();
 
  267                var data = modWorld.Save();
 
  271                list.Add(
new TagCompound {
 
  272                    [
"mod"] = modWorld.mod.Name,
 
  273                    [
"name"] = modWorld.Name,
 
  280        internal static void LoadModData(IList<TagCompound> list) {
 
  281            foreach (var tag 
in list) {
 
  283                var modWorld = mod?.
GetModWorld(tag.GetString(
"name"));
 
  284                if (modWorld != 
null) {
 
  286                        if (tag.ContainsKey(
"legacyData"))
 
  289                            modWorld.Load(tag.GetCompound(
"data"));
 
  293                            "Error in reading custom world data for " + mod.Name, e);
 
  297                    ModContent.GetInstance<MysteryWorld>().data.Add(tag);
 
  303            foreach (var modWorld 
in WorldHooks.NetWorlds)
 
  304                writer.SafeWrite(w => modWorld.NetSend(w));
 
  307        public static void ReceiveModData(
BinaryReader reader) {
 
  308            foreach (var modWorld 
in WorldHooks.NetWorlds) {
 
  310                    reader.SafeRead(r => modWorld.NetReceive(r));
 
  317                    Logging.tML.Error($
"Above IOException error caused by {modWorld.Name} from the {modWorld.mod.Name} mod.");
 
  322        public static void ValidateSigns() {
 
  323            for (
int i = 0; i < Main.sign.Length; i++) {
 
  324                if (Main.sign[i] != 
null) {
 
  325                    Tile tile = Main.tile[Main.sign[i].x, Main.sign[i].y];
 
  326                    if (!(tile.active() && Main.tileSign[(
int)tile.type])) {
 
  333        private static void LoadLegacy(
byte[] buffer) {
 
  334            const int numByteFlags = 1;
 
  335            using (MemoryStream stream = 
new MemoryStream(buffer)) {
 
  337                    byte limit = reader.ReadByte();
 
  341                    byte[] flags = reader.ReadBytes(limit);
 
  342                    if (flags.Length < numByteFlags) {
 
  343                        Array.Resize(ref flags, numByteFlags);
 
  346                        LoadLegacyModWorld(flags, reader);
 
  356        private static void LoadLegacyModWorld(
byte[] flags, 
BinaryReader reader) {
 
  357            if (flags.Length == 0) {
 
  360            if ((flags[0] & 1) == 1) {
 
  361                LoadLegacyChests(reader);
 
  363            if ((flags[0] & 2) == 2) {
 
  364                TileIO.LoadLegacyTiles(reader);
 
  366            if ((flags[0] & 4) == 4) {
 
  367                LoadLegacyNPCKillCounts(reader);
 
  369            if ((flags[0] & 8) == 8) {
 
  370                TileIO.ReadContainers(reader);
 
  372            if ((flags[0] & 16) == 16) {
 
  373                LoadLegacyAnglerQuest(reader);
 
  375            if ((flags[0] & 32) == 32) {
 
  376                LoadLegacyModData(reader);
 
  380        private static void LoadLegacyChests(
BinaryReader reader) {
 
  381            short count = reader.ReadInt16();
 
  382            for (
int k = 0; k < count; k++) {
 
  383                LoadLegacyChest(reader);
 
  387        private static void LoadLegacyChest(
BinaryReader reader) {
 
  388            int x = reader.ReadInt32();
 
  389            int y = reader.ReadInt32();
 
  390            int chest = Chest.FindChest(x, y);
 
  392                chest = Chest.CreateChest(x, y);
 
  395                ItemIO.LoadLegacyInventory(Main.chest[chest].item, reader, 
true);
 
  398                ItemIO.LoadLegacyInventory(
new Item[40], reader, 
true);
 
  402        private static void LoadLegacyNPCKillCounts(
BinaryReader reader) {
 
  403            ushort numCounts = reader.ReadUInt16();
 
  404            for (ushort k = 0; k < numCounts; k++) {
 
  405                string modName = reader.ReadString();
 
  406                string name = reader.ReadString();
 
  407                int count = reader.ReadInt32();
 
  409                int type = mod == 
null ? 0 : mod.
NPCType(name);
 
  411                    NPC.killCount[type] = count;
 
  416        private static void LoadLegacyAnglerQuest(
BinaryReader reader) {
 
  417            string modName = reader.ReadString();
 
  418            string name = reader.ReadString();
 
  426                for (
int k = 0; k < Main.anglerQuestItemNetIDs.Length; k++) {
 
  427                    if (Main.anglerQuestItemNetIDs[k] == type) {
 
  428                        Main.anglerQuest = k;
 
  435                Main.AnglerQuestSwap();
 
  439        private static void LoadLegacyModData(
BinaryReader reader) {
 
  440            int count = reader.ReadUInt16();
 
  441            for (
int k = 0; k < count; k++) {
 
  442                string modName = reader.ReadString();
 
  443                string name = reader.ReadString();
 
  444                byte[] data = reader.ReadBytes(reader.ReadUInt16());
 
  447                if (modWorld != 
null) {
 
  448                    using (MemoryStream stream = 
new MemoryStream(data)) {
 
  455                                    "Error in reading custom world data for " + mod.
Name, e);
 
  461                    var tag = 
new TagCompound {
 
  464                        [
"legacyData"] = data
 
  466                    ModContent.GetInstance<MysteryWorld>().data.Add(tag);
 
  472        internal static void MoveToCloud(
string localPath, 
string cloudPath) {
 
  473            localPath = Path.ChangeExtension(localPath, 
".twld");
 
  474            cloudPath = Path.ChangeExtension(cloudPath, 
".twld");
 
  475            if (File.Exists(localPath)) {
 
  476                FileUtilities.MoveToCloud(localPath, cloudPath);
 
  480        internal static void MoveToLocal(
string cloudPath, 
string localPath) {
 
  481            cloudPath = Path.ChangeExtension(cloudPath, 
".twld");
 
  482            localPath = Path.ChangeExtension(localPath, 
".twld");
 
  483            if (FileUtilities.Exists(cloudPath, 
true)) {
 
  484                FileUtilities.MoveToLocal(cloudPath, localPath);
 
  488        internal static void LoadBackup(
string path, 
bool cloudSave) {
 
  489            path = Path.ChangeExtension(path, 
".twld");
 
  490            if (FileUtilities.Exists(path + 
".bak", cloudSave)) {
 
  491                FileUtilities.Move(path + 
".bak", path, cloudSave, 
true);
 
  495        internal static void LoadDedServBackup(
string path, 
bool cloudSave) {
 
  496            path = Path.ChangeExtension(path, 
".twld");
 
  497            if (FileUtilities.Exists(path, cloudSave)) {
 
  498                FileUtilities.Copy(path, path + 
".bad", cloudSave, 
true);
 
  500            if (FileUtilities.Exists(path + 
".bak", cloudSave)) {
 
  501                FileUtilities.Copy(path + 
".bak", path, cloudSave, 
true);
 
  502                FileUtilities.Delete(path + 
".bak", cloudSave);
 
  506        internal static void RevertDedServBackup(
string path, 
bool cloudSave) {
 
  507            path = Path.ChangeExtension(path, 
".twld");
 
  508            if (FileUtilities.Exists(path, cloudSave)) {
 
  509                FileUtilities.Copy(path, path + 
".bak", cloudSave, 
true);
 
  511            if (FileUtilities.Exists(path + 
".bad", cloudSave)) {
 
  512                FileUtilities.Copy(path + 
".bad", path, cloudSave, 
true);
 
  513                FileUtilities.Delete(path + 
".bad", cloudSave);
 
  518        internal static void EraseWorld(
string path, 
bool cloudSave) {
 
  519            path = Path.ChangeExtension(path, 
".twld");
 
  522                FileOperationAPIWrapper.MoveToRecycleBin(path);
 
  523                FileOperationAPIWrapper.MoveToRecycleBin(path + 
".bak");
 
  526                File.Delete(path + 
".bak");
 
  529            else if (SocialAPI.Cloud != 
null) {
 
  530                SocialAPI.Cloud.Delete(path);
 
static readonly Framework Framework
 
This serves as the central class from which item-related functions are carried out....
 
static ModItem GetItem(int type)
Gets the ModItem instance corresponding to the specified type. Returns null if no modded item has the...
 
Manages content added by mods. Liasons between mod content and Terraria's arrays and oversees the Loa...
 
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
 
ModWorld GetModWorld(string name)
Gets the ModWorld instance with the given name from this mod.
 
int ItemType(string name)
Gets the internal ID / type of the ModItem corresponding to the name. Returns 0 if no ModItem with th...
 
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
 
int NPCType(string name)
Gets the type of the ModNPC of this mod with the given name. Returns 0 if no ModNPC with the given na...
 
This serves as the central class which loads mods. It contains many static fields and methods related...
 
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
 
This class serves as a place for you to place all your properties and hooks for each NPC....
 
string Name
The internal name of this NPC.
 
Mod mod
The mod that added this ModNPC.
 
A ModWorld instance represents an extension of a World. You can store fields in the ModWorld classes ...
 
virtual void LoadLegacy(BinaryReader reader)
Allows you to load pre-v0.9 custom data you have saved for this world.
 
This serves as the central class from which NPC-related functions are carried out....
 
static ModNPC GetNPC(int type)
Gets the ModNPC instance corresponding to the specified type.
 
This is where all ModWorld hooks are gathered and called.