Terraria ModLoader  0.11.4
A framework for Terraria mods
ItemIO.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using Terraria.ID;
5 using Terraria.ModLoader.Default;
7 
8 namespace Terraria.ModLoader.IO
9 {
10  public static class ItemIO
11  {
12  //replace netID writes in Terraria.Player.SavePlayer
13  //in Terraria.IO.WorldFile.SaveChests include IsModItem for no-item check
14  internal static void WriteVanillaID(Item item, BinaryWriter writer) {
15  writer.Write(item.modItem != null ? 0 : item.netID);
16  }
17 
18  public static TagCompound Save(Item item) {
19  var tag = new TagCompound();
20  if (item.type <= 0)
21  return tag;
22 
23  if (item.modItem == null) {
24  tag.Set("mod", "Terraria");
25  tag.Set("id", item.netID);
26  }
27  else {
28  tag.Set("mod", item.modItem.mod.Name);
29  tag.Set("name", item.modItem.Name);
30  tag.Set("data", item.modItem.Save());
31  }
32 
33  if (item.prefix != 0 && item.prefix < PrefixID.Count)
34  tag.Set("prefix", item.prefix);
35 
36  if (item.prefix >= PrefixID.Count) {
37  ModPrefix modPrefix = ModPrefix.GetPrefix(item.prefix);
38  if (modPrefix != null) {
39  tag.Set("modPrefixMod", modPrefix.mod.Name);
40  tag.Set("modPrefixName", modPrefix.Name);
41  }
42  }
43 
44  if (item.stack > 1)
45  tag.Set("stack", item.stack);
46 
47  if (item.favorited)
48  tag.Set("fav", true);
49 
50  tag.Set("globalData", SaveGlobals(item));
51 
52  return tag;
53  }
54 
55  public static void Load(Item item, TagCompound tag) {
56  if (tag.Count == 0) {
57  item.netDefaults(0);
58  return;
59  }
60 
61  string modName = tag.GetString("mod");
62  if (modName == "Terraria") {
63  item.netDefaults(tag.GetInt("id"));
64  if (tag.ContainsKey("legacyData"))
65  LoadLegacyModData(item, tag.GetByteArray("legacyData"), tag.GetBool("hasGlobalSaving"));
66  }
67  else {
68  int type = ModLoader.GetMod(modName)?.ItemType(tag.GetString("name")) ?? 0;
69  if (type > 0) {
70  item.netDefaults(type);
71  if (tag.ContainsKey("legacyData"))
72  LoadLegacyModData(item, tag.GetByteArray("legacyData"), tag.GetBool("hasGlobalSaving"));
73  else
74  item.modItem.Load(tag.GetCompound("data"));
75  }
76  else {
77  item.netDefaults(ModLoader.GetMod("ModLoader").ItemType("MysteryItem"));
78  ((MysteryItem)item.modItem).Setup(tag);
79  }
80  }
81 
82  if (tag.ContainsKey("modPrefixMod") && tag.ContainsKey("modPrefixName")) {
83  string prefixMod = tag.GetString("modPrefixMod");
84  string prefixName = tag.GetString("modPrefixName");
85  item.Prefix(ModLoader.GetMod(prefixMod)?.PrefixType(prefixName) ?? 0);
86  }
87  else if (tag.ContainsKey("prefix")) {
88  item.Prefix(tag.GetByte("prefix"));
89  }
90  item.stack = tag.Get<int?>("stack") ?? 1;
91  item.favorited = tag.GetBool("fav");
92 
93  if (!(item.modItem is MysteryItem))
94  LoadGlobals(item, tag.GetList<TagCompound>("globalData"));
95  }
96 
97  public static Item Load(TagCompound tag) {
98  var item = new Item();
99  Load(item, tag);
100  return item;
101  }
102 
103  internal static List<TagCompound> SaveGlobals(Item item) {
104  if (item.modItem is MysteryItem)
105  return null; //MysteryItems cannot have global data
106 
107  var list = new List<TagCompound>();
108  foreach (var globalItem in ItemLoader.globalItems) {
109  var globalItemInstance = globalItem.Instance(item);
110  if (!globalItemInstance.NeedsSaving(item))
111  continue;
112 
113  list.Add(new TagCompound {
114  ["mod"] = globalItemInstance.mod.Name,
115  ["name"] = globalItemInstance.Name,
116  ["data"] = globalItemInstance.Save(item)
117  });
118  }
119  return list.Count > 0 ? list : null;
120  }
121 
122  internal static void LoadGlobals(Item item, IList<TagCompound> list) {
123  foreach (var tag in list) {
124  var mod = ModLoader.GetMod(tag.GetString("mod"));
125  var globalItem = mod?.GetGlobalItem(tag.GetString("name"));
126  if (globalItem != null) {
127  var globalItemInstance = globalItem.Instance(item);
128  try {
129  globalItemInstance.Load(item, tag.GetCompound("data"));
130  }
131  catch (Exception e) {
132  throw new CustomModDataException(mod,
133  "Error in reading custom player data for " + mod.Name, e);
134  }
135  }
136  else {
137  item.GetGlobalItem<MysteryGlobalItem>(ModLoader.GetMod("ModLoader")).data.Add(tag);
138  }
139  }
140  }
141 
142  public static void Send(Item item, BinaryWriter writer, bool writeStack = false, bool writeFavourite = false) {
143  writer.Write((short)item.netID);
144  writer.Write(item.prefix);
145  if (writeStack) writer.Write((short)item.stack);
146  if (writeFavourite) writer.Write(item.favorited);
147  SendModData(item, writer);
148  }
149 
150  public static void Receive(Item item, BinaryReader reader, bool readStack = false, bool readFavorite = false) {
151  item.netDefaults(reader.ReadInt16());
152  item.Prefix(reader.ReadByte());
153  if (readStack) item.stack = reader.ReadInt16();
154  if (readFavorite) item.favorited = reader.ReadBoolean();
155  ReceiveModData(item, reader);
156  }
157 
158  public static Item Receive(BinaryReader reader, bool readStack = false, bool readFavorite = false) {
159  var item = new Item();
160  Receive(item, reader, readStack, readFavorite);
161  return item;
162  }
163 
164  public static void SendModData(Item item, BinaryWriter writer) {
165  if (item.IsAir) return;
166  writer.SafeWrite(w => item.modItem?.NetSend(w));
167  foreach (var globalItem in ItemLoader.NetGlobals)
168  writer.SafeWrite(w => globalItem.Instance(item).NetSend(item, w));
169  }
170 
171  public static void ReceiveModData(Item item, BinaryReader reader) {
172  if (item.IsAir) return;
173  try {
174  reader.SafeRead(r => item.modItem?.NetRecieve(r));
175  }
176  catch (IOException) {
177  Logging.tML.Error($"Above IOException error caused by {item.modItem.Name} from the {item.modItem.mod.Name} mod.");
178  }
179 
180  foreach (var globalItem in ItemLoader.NetGlobals) {
181  try {
182  reader.SafeRead(r => globalItem.Instance(item).NetReceive(item, r));
183  }
184  catch (IOException) {
185  Logging.tML.Error($"Above IOException error caused by {globalItem.Name} from the {globalItem.mod.Name} mod while reading {item.Name}.");
186  }
187  }
188  }
189 
190  public static void LoadLegacy(Item item, BinaryReader reader, bool readStack = false, bool readFavorite = false) {
191  string modName = reader.ReadString();
192  bool hasGlobalSaving = false;
193  if (modName.Length == 0) {
194  hasGlobalSaving = true;
195  modName = reader.ReadString();
196  }
197  if (modName == "Terraria") {
198  item.netDefaults(reader.ReadInt32());
199  LoadLegacyModData(item, LegacyModData(item.type, reader, hasGlobalSaving), hasGlobalSaving);
200  }
201  else {
202  string itemName = reader.ReadString();
203  int type = ModLoader.GetMod(modName)?.ItemType(itemName) ?? 0;
204  byte[] data = LegacyModData(type == 0 ? int.MaxValue : type, reader, hasGlobalSaving);
205  if (type != 0) {
206  item.netDefaults(type);
207  LoadLegacyModData(item, data, hasGlobalSaving);
208  }
209  else {
210  item.netDefaults(ModLoader.GetMod("ModLoader").ItemType("MysteryItem"));
211  var tag = new TagCompound {
212  ["mod"] = modName,
213  ["name"] = itemName,
214  ["hasGlobalSaving"] = hasGlobalSaving,
215  ["legacyData"] = data
216  };
217  ((MysteryItem)item.modItem).Setup(tag);
218  }
219  }
220 
221  item.Prefix(reader.ReadByte());
222 
223  if (readStack)
224  item.stack = reader.ReadInt32();
225 
226  if (readFavorite)
227  item.favorited = reader.ReadBoolean();
228  }
229 
230  internal static byte[] LegacyModData(int type, BinaryReader reader, bool hasGlobalSaving = true) {
231  using (MemoryStream memoryStream = new MemoryStream()) {
232  using (BinaryWriter writer = new BinaryWriter(memoryStream)) {
233  if (type >= ItemID.Count) {
234  ushort length = reader.ReadUInt16();
235  writer.Write(length);
236  writer.Write(reader.ReadBytes(length));
237  }
238  if (hasGlobalSaving) {
239  ushort count = reader.ReadUInt16();
240  writer.Write(count);
241  for (int k = 0; k < count; k++) {
242  writer.Write(reader.ReadString());
243  writer.Write(reader.ReadString());
244  ushort length = reader.ReadUInt16();
245  writer.Write(length);
246  writer.Write(reader.ReadBytes(length));
247  }
248  }
249  }
250  return memoryStream.ToArray();
251  }
252  }
253 
254  internal static void LoadLegacyModData(Item item, byte[] data, bool hasGlobalSaving = true) {
255  using (BinaryReader reader = new BinaryReader(new MemoryStream(data))) {
256  if (item.modItem != null) {
257  byte[] modData = reader.ReadBytes(reader.ReadUInt16());
258  if (modData.Length > 0) {
259  using (BinaryReader customReader = new BinaryReader(new MemoryStream(modData))) {
260  try {
261  item.modItem.LoadLegacy(customReader);
262  }
263  catch (Exception e) {
264  throw new CustomModDataException(item.modItem.mod,
265  "Error in reading custom item data for " + item.modItem.mod.Name, e);
266  }
267  }
268  }
269  }
270  if (hasGlobalSaving) {
271  int count = reader.ReadUInt16();
272  for (int k = 0; k < count; k++) {
273  string modName = reader.ReadString();
274  string globalName = reader.ReadString();
275  byte[] globalData = reader.ReadBytes(reader.ReadUInt16());
276  GlobalItem globalItem = ModLoader.GetMod(modName)?.GetGlobalItem(globalName);
277  //could support legacy global data in mystery globals but eh...
278  if (globalItem != null && globalData.Length > 0) {
279  using (BinaryReader customReader = new BinaryReader(new MemoryStream(globalData))) {
280  try {
281  globalItem.LoadLegacy(item, customReader);
282  }
283  catch (Exception e) {
284  throw new CustomModDataException(globalItem.mod,
285  "Error in reading custom global item data for " + globalItem.mod.Name, e);
286  }
287  }
288  }
289  }
290  }
291  }
292  }
293 
294  public static void LoadLegacyInventory(Item[] inv, BinaryReader reader, bool readStack = false, bool readFavorite = false) {
295  int count = reader.ReadUInt16();
296  for (int k = 0; k < count; k++) {
297  LoadLegacy(inv[reader.ReadUInt16()], reader, readStack, readFavorite);
298  }
299  }
300 
301  public static string ToBase64(Item item) {
302  MemoryStream ms = new MemoryStream();
303  TagIO.ToStream(ItemIO.Save(item), ms, true);
304  return Convert.ToBase64String(ms.ToArray());
305  }
306 
307  public static Item FromBase64(string base64) {
308  MemoryStream ms = new MemoryStream(Convert.FromBase64String(base64));
309  return ItemIO.Load(TagIO.FromStream(ms, true));
310  }
311  }
312 }
static TagCompound FromStream(Stream stream, bool compressed=true)
Definition: TagIO.cs:292
virtual void LoadLegacy(Item item, BinaryReader reader)
Allows you to load pre-v0.9 custom data that you have saved for the given item.
Definition: GlobalItem.cs:886
static Item Load(TagCompound tag)
Definition: ItemIO.cs:97
int ItemType(string name)
Gets the internal ID / type of the ModItem corresponding to the name. Returns 0 if no ModItem with th...
static void LoadLegacyInventory(Item[] inv, BinaryReader reader, bool readStack=false, bool readFavorite=false)
Definition: ItemIO.cs:294
This serves as the central class which loads mods. It contains many static fields and methods related...
Definition: ModLoader.cs:25
Mod mod
The mod to which this GlobalItem belongs.
Definition: GlobalItem.cs:20
static ModPrefix GetPrefix(byte type)
Returns the ModPrefix associated with specified type If not a ModPrefix, returns null.
Definition: ModPrefix.cs:40
static void Load(Item item, TagCompound tag)
Definition: ItemIO.cs:55
This class allows you to modify and use hooks for all items, including vanilla items. Create an instance of an overriding class then call Mod.AddGlobalItem to use this.
Definition: GlobalItem.cs:15
static TagCompound Save(Item item)
Definition: ItemIO.cs:18
byte PrefixType(string name)
Gets the internal ID / type of the ModPrefix corresponding to the name. Returns 0 if no ModPrefix wit...
static void LoadLegacy(Item item, BinaryReader reader, bool readStack=false, bool readFavorite=false)
Definition: ItemIO.cs:190
This serves as the central class from which item-related functions are carried out. It also stores a list of mod items by ID.
Definition: ItemLoader.cs:21
static void SendModData(Item item, BinaryWriter writer)
Definition: ItemIO.cs:164
TagCompound GetCompound(string key)
static void Receive(Item item, BinaryReader reader, bool readStack=false, bool readFavorite=false)
Definition: ItemIO.cs:150
static void ReceiveModData(Item item, BinaryReader reader)
Definition: ItemIO.cs:171
static Item Receive(BinaryReader reader, bool readStack=false, bool readFavorite=false)
Definition: ItemIO.cs:158
static void Send(Item item, BinaryWriter writer, bool writeStack=false, bool writeFavourite=false)
Definition: ItemIO.cs:142
byte[] GetByteArray(string key)
static void ToStream(TagCompound root, Stream stream, bool compress=true)
Definition: TagIO.cs:316
GlobalItem GetGlobalItem(string name)
Gets the GlobalItem instance with the given name from this mod.
virtual string Name
Stores the name of the mod. This name serves as the mod&#39;s identification, and also helps with saving ...
Definition: Mod.cs:41
virtual void Load(Item item, TagCompound tag)
Allows you to load custom data that you have saved for the given item.
Definition: GlobalItem.cs:880
GlobalItem Instance(Item item)
static string ToBase64(Item item)
Definition: ItemIO.cs:301
static Item FromBase64(string base64)
Definition: ItemIO.cs:307
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
Definition: ModLoader.cs:81