Terraria ModLoader  0.11.7.8
A mod to make and play Terraria mods
TileIO.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using Terraria.DataStructures;
6 using Terraria.GameContent.Tile_Entities;
7 using Terraria.ID;
8 using Terraria.ModLoader.Default;
10 
11 namespace Terraria.ModLoader.IO
12 {
13  internal static class TileIO
14  {
15  //in Terraria.IO.WorldFile.SaveWorldTiles add type check to tile.active() check and wall check
16  internal struct TileTables
17  {
18  internal IDictionary<ushort, ushort> tiles;
19  internal IDictionary<ushort, bool> frameImportant;
20  internal IDictionary<ushort, ushort> walls;
21  internal IDictionary<ushort, string> tileModNames;
22  internal IDictionary<ushort, string> tileNames;
23 
24  internal static TileTables Create() {
25  TileTables tables = new TileTables {
26  tiles = new Dictionary<ushort, ushort>(),
27  frameImportant = new Dictionary<ushort, bool>(),
28  walls = new Dictionary<ushort, ushort>(),
29  tileModNames = new Dictionary<ushort, string>(),
30  tileNames = new Dictionary<ushort, string>()
31  };
32  return tables;
33  }
34  }
35 
36  internal static TagCompound SaveTiles() {
37  var hasTile = new bool[TileLoader.TileCount];
38  var hasWall = new bool[WallLoader.WallCount];
39  using (var ms = new MemoryStream())
40  using (var writer = new BinaryWriter(ms)) {
41  WriteTileData(writer, hasTile, hasWall);
42 
43  var tileList = new List<TagCompound>();
44  for (int type = TileID.Count; type < hasTile.Length; type++) {
45  if (!hasTile[type])
46  continue;
47 
48  var modTile = TileLoader.GetTile(type);
49  tileList.Add(new TagCompound {
50  ["value"] = (short)type,
51  ["mod"] = modTile.mod.Name,
52  ["name"] = modTile.Name,
53  ["framed"] = Main.tileFrameImportant[type],
54  });
55  }
56  var wallList = new List<TagCompound>();
57  for (int wall = WallID.Count; wall < hasWall.Length; wall++) {
58  if (!hasWall[wall])
59  continue;
60 
61  var modWall = WallLoader.GetWall(wall);
62  wallList.Add(new TagCompound {
63  ["value"] = (short)wall,
64  ["mod"] = modWall.mod.Name,
65  ["name"] = modWall.Name,
66  });
67  }
68  if (tileList.Count == 0 && wallList.Count == 0)
69  return null;
70 
71  return new TagCompound {
72  ["tileMap"] = tileList,
73  ["wallMap"] = wallList,
74  ["data"] = ms.ToArray()
75  };
76  }
77  }
78 
79  internal static void LoadTiles(TagCompound tag) {
80  if (!tag.ContainsKey("data"))
81  return;
82 
83  var tables = TileTables.Create();
84  foreach (var tileTag in tag.GetList<TagCompound>("tileMap")) {
85  ushort type = (ushort)tileTag.GetShort("value");
86  string modName = tileTag.GetString("mod");
87  string name = tileTag.GetString("name");
88  Mod mod = ModLoader.GetMod(modName);
89  tables.tiles[type] = mod == null ? (ushort)0 : (ushort)mod.TileType(name);
90  if (tables.tiles[type] == 0) {
91  tables.tiles[type] = (ushort)ModContent.GetInstance<ModLoaderMod>().TileType("PendingMysteryTile");
92  tables.tileModNames[type] = modName;
93  tables.tileNames[type] = name;
94  }
95  tables.frameImportant[type] = tileTag.GetBool("framed");
96  }
97  foreach (var wallTag in tag.GetList<TagCompound>("wallMap")) {
98  ushort wall = (ushort)wallTag.GetShort("value");
99  string modName = wallTag.GetString("mod");
100  string name = wallTag.GetString("name");
101  Mod mod = ModLoader.GetMod(modName);
102  tables.walls[wall] = mod == null ? (ushort)0 : (ushort)mod.WallType(name);
103  }
104  using (var memoryStream = new MemoryStream(tag.GetByteArray("data")))
105  using (var reader = new BinaryReader(memoryStream))
106  ReadTileData(reader, tables);
107  WorldIO.ValidateSigns();
108  }
109 
110  internal static void LoadLegacyTiles(BinaryReader reader) {
111  TileTables tables = TileTables.Create();
112  ushort count = reader.ReadUInt16();
113  for (int k = 0; k < count; k++) {
114  ushort type = reader.ReadUInt16();
115  string modName = reader.ReadString();
116  string name = reader.ReadString();
117  Mod mod = ModLoader.GetMod(modName);
118  tables.tiles[type] = mod == null ? (ushort)0 : (ushort)mod.TileType(name);
119  if (tables.tiles[type] == 0) {
120  tables.tiles[type] = (ushort)ModContent.GetInstance<ModLoaderMod>().TileType("PendingMysteryTile");
121  tables.tileModNames[type] = modName;
122  tables.tileNames[type] = name;
123  }
124  tables.frameImportant[type] = reader.ReadBoolean();
125  }
126  count = reader.ReadUInt16();
127  for (int k = 0; k < count; k++) {
128  ushort wall = reader.ReadUInt16();
129  string modName = reader.ReadString();
130  string name = reader.ReadString();
131  Mod mod = ModLoader.GetMod(modName);
132  tables.walls[wall] = mod == null ? (ushort)0 : (ushort)mod.WallType(name);
133  }
134  ReadTileData(reader, tables);
135  }
136 
137  internal static void WriteTileData(BinaryWriter writer, bool[] hasTile, bool[] hasWall) {
138  byte skip = 0;
139  bool nextModTile = false;
140  int i = 0;
141  int j = 0;
142  do {
143  Tile tile = Main.tile[i, j];
144  if (HasModData(tile)) {
145  if (!nextModTile) {
146  writer.Write(skip);
147  skip = 0;
148  }
149  else {
150  nextModTile = false;
151  }
152  WriteModTile(ref i, ref j, writer, ref nextModTile, hasTile, hasWall);
153  }
154  else {
155  skip++;
156  if (skip == 255) {
157  writer.Write(skip);
158  skip = 0;
159  }
160  }
161  }
162  while (NextTile(ref i, ref j));
163  if (skip > 0) {
164  writer.Write(skip);
165  }
166  }
167 
168  internal static void ReadTileData(BinaryReader reader, TileTables tables) {
169  int i = 0;
170  int j = 0;
171  bool nextModTile = false;
172  do {
173  if (!nextModTile) {
174  byte skip = reader.ReadByte();
175  while (skip == 255) {
176  for (byte k = 0; k < 255; k++) {
177  if (!NextTile(ref i, ref j)) {
178  return;
179  }
180  }
181  skip = reader.ReadByte();
182  }
183  for (byte k = 0; k < skip; k++) {
184  if (!NextTile(ref i, ref j)) {
185  return;
186  }
187  }
188  }
189  else {
190  nextModTile = false;
191  }
192  ReadModTile(ref i, ref j, tables, reader, ref nextModTile);
193  }
194  while (NextTile(ref i, ref j));
195  }
196 
197  internal static void WriteModTile(ref int i, ref int j, BinaryWriter writer, ref bool nextModTile, bool[] hasTile, bool[] hasWall) {
198  Tile tile = Main.tile[i, j];
199  byte flags = 0;
200  byte[] data = new byte[11];
201  int index = 1;
202  if (tile.active() && tile.type >= TileID.Count) {
203  hasTile[tile.type] = true;
204  flags |= 1;
205  data[index] = (byte)tile.type;
206  index++;
207  data[index] = (byte)(tile.type >> 8);
208  index++;
209  if (Main.tileFrameImportant[tile.type]) {
210  data[index] = (byte)tile.frameX;
211  index++;
212  if (tile.frameX >= 256) {
213  flags |= 2;
214  data[index] = (byte)(tile.frameX >> 8);
215  index++;
216  }
217  data[index] = (byte)tile.frameY;
218  index++;
219  if (tile.frameY >= 256) {
220  flags |= 4;
221  data[index] = (byte)(tile.frameY >> 8);
222  index++;
223  }
224  }
225  if (tile.color() != 0) {
226  flags |= 8;
227  data[index] = tile.color();
228  index++;
229  }
230  }
231  if (tile.wall >= WallID.Count) {
232  hasWall[tile.wall] = true;
233  flags |= 16;
234  data[index] = (byte)tile.wall;
235  index++;
236  data[index] = (byte)(tile.wall >> 8);
237  index++;
238  if (tile.wallColor() != 0) {
239  flags |= 32;
240  data[index] = tile.wallColor();
241  index++;
242  }
243  }
244  int nextI = i;
245  int nextJ = j;
246  byte sameCount = 0;
247  while (NextTile(ref nextI, ref nextJ)) {
248  if (tile.isTheSameAs(Main.tile[nextI, nextJ]) && sameCount < 255) {
249  sameCount++;
250  i = nextI;
251  j = nextJ;
252  }
253  else if (HasModData(Main.tile[nextI, nextJ])) {
254  flags |= 128;
255  nextModTile = true;
256  break;
257  }
258  else {
259  break;
260  }
261  }
262  if (sameCount > 0) {
263  flags |= 64;
264  data[index] = sameCount;
265  index++;
266  }
267  data[0] = flags;
268  writer.Write(data, 0, index);
269  }
270 
271  internal static void ReadModTile(ref int i, ref int j, TileTables tables, BinaryReader reader, ref bool nextModTile) {
272  byte flags;
273  flags = reader.ReadByte();
274  Tile tile = Main.tile[i, j];
275  if ((flags & 1) == 1) {
276  tile.active(true);
277  ushort saveType = reader.ReadUInt16();
278  tile.type = tables.tiles[saveType];
279  if (tables.frameImportant[saveType]) {
280  if ((flags & 2) == 2) {
281  tile.frameX = reader.ReadInt16();
282  }
283  else {
284  tile.frameX = reader.ReadByte();
285  }
286  if ((flags & 4) == 4) {
287  tile.frameY = reader.ReadInt16();
288  }
289  else {
290  tile.frameY = reader.ReadByte();
291  }
292  }
293  else {
294  tile.frameX = -1;
295  tile.frameY = -1;
296  }
297  if (tile.type == ModContent.GetInstance<ModLoaderMod>().TileType("PendingMysteryTile")
298  && tables.tileNames.ContainsKey(saveType)) {
299  MysteryTileInfo info;
300  if (tables.frameImportant[saveType]) {
301  info = new MysteryTileInfo(tables.tileModNames[saveType], tables.tileNames[saveType],
302  tile.frameX, tile.frameY);
303  }
304  else {
305  info = new MysteryTileInfo(tables.tileModNames[saveType], tables.tileNames[saveType]);
306  }
307  MysteryTilesWorld modWorld = ModContent.GetInstance<MysteryTilesWorld>();
308  int pendingFrameID = modWorld.pendingInfos.IndexOf(info);
309  if (pendingFrameID < 0) {
310  pendingFrameID = modWorld.pendingInfos.Count;
311  modWorld.pendingInfos.Add(info);
312  }
313  MysteryTileFrame pendingFrame = new MysteryTileFrame(pendingFrameID);
314  tile.frameX = pendingFrame.FrameX;
315  tile.frameY = pendingFrame.FrameY;
316  }
317  if ((flags & 8) == 8) {
318  tile.color(reader.ReadByte());
319  }
320  WorldGen.tileCounts[tile.type] += j <= Main.worldSurface ? 5 : 1;
321  }
322  if ((flags & 16) == 16) {
323  tile.wall = tables.walls[reader.ReadUInt16()];
324  if ((flags & 32) == 32) {
325  tile.wallColor(reader.ReadByte());
326  }
327  }
328  if ((flags & 64) == 64) {
329  byte sameCount = reader.ReadByte();
330  for (byte k = 0; k < sameCount; k++) {
331  NextTile(ref i, ref j);
332  Main.tile[i, j].CopyFrom(tile);
333  WorldGen.tileCounts[tile.type] += j <= Main.worldSurface ? 5 : 1;
334  }
335  }
336  if ((flags & 128) == 128) {
337  nextModTile = true;
338  }
339  }
340 
341  private static bool HasModData(Tile tile) {
342  return (tile.active() && tile.type >= TileID.Count) || tile.wall >= WallID.Count;
343  }
344 
345  private static bool NextTile(ref int i, ref int j) {
346  j++;
347  if (j >= Main.maxTilesY) {
348  j = 0;
349  i++;
350  if (i >= Main.maxTilesX) {
351  return false;
352  }
353  }
354  return true;
355  }
356  //in Terraria.IO.WorldFile.SaveWorldTiles for saving tile frames add
357  // short frameX = tile.frameX; TileIO.VanillaSaveFrames(tile, ref frameX);
358  // and replace references to tile.frameX with frameX
359  internal static void VanillaSaveFrames(Tile tile, ref short frameX) {
360  if (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin) {
361  int slot = tile.frameX / 100;
362  int position = tile.frameY / 18;
363  if (HasModArmor(slot, position)) {
364  frameX %= 100;
365  }
366  }
367  }
368 
369  internal struct ContainerTables
370  {
371  internal IDictionary<int, int> headSlots;
372  internal IDictionary<int, int> bodySlots;
373  internal IDictionary<int, int> legSlots;
374 
375  internal static ContainerTables Create() {
376  ContainerTables tables = new ContainerTables {
377  headSlots = new Dictionary<int, int>(),
378  bodySlots = new Dictionary<int, int>(),
379  legSlots = new Dictionary<int, int>()
380  };
381  return tables;
382  }
383  }
384  //in Terraria.GameContent.Tile_Entities.TEItemFrame.WriteExtraData
385  // if item is a mod item write 0 as the ID
386  internal static TagCompound SaveContainers() {
387  var ms = new MemoryStream();
388  var writer = new BinaryWriter(ms);
389  byte[] flags = new byte[1];
390  byte numFlags = 0;
391  ISet<int> headSlots = new HashSet<int>();
392  ISet<int> bodySlots = new HashSet<int>();
393  ISet<int> legSlots = new HashSet<int>();
394  IDictionary<int, int> itemFrames = new Dictionary<int, int>();
395  for (int i = 0; i < Main.maxTilesX; i++) {
396  for (int j = 0; j < Main.maxTilesY; j++) {
397  Tile tile = Main.tile[i, j];
398  if (tile.active() && (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin)) {
399  int slot = tile.frameX / 100;
400  int position = tile.frameY / 18;
401  if (HasModArmor(slot, position)) {
402  if (position == 0) {
403  headSlots.Add(slot);
404  }
405  else if (position == 1) {
406  bodySlots.Add(slot);
407  }
408  else if (position == 2) {
409  legSlots.Add(slot);
410  }
411  flags[0] |= 1;
412  numFlags = 1;
413  }
414  }
415  }
416  }
417  int tileEntity = 0;
418  foreach (KeyValuePair<int, TileEntity> entity in TileEntity.ByID) {
419  TEItemFrame itemFrame = entity.Value as TEItemFrame;
420  if (itemFrame != null && ItemLoader.NeedsModSaving(itemFrame.item)) {
421  itemFrames.Add(itemFrame.ID, tileEntity);
422  //flags[0] |= 2; legacy
423  numFlags = 1;
424  }
425  if(!(entity.Value is ModTileEntity))
426  tileEntity++;
427  }
428  if (numFlags == 0) {
429  return null;
430  }
431  writer.Write(numFlags);
432  writer.Write(flags, 0, numFlags);
433  if ((flags[0] & 1) == 1) {
434  writer.Write((ushort)headSlots.Count);
435  foreach (int slot in headSlots) {
436  writer.Write((ushort)slot);
437  ModItem item = ItemLoader.GetItem(EquipLoader.slotToId[EquipType.Head][slot]);
438  writer.Write(item.mod.Name);
439  writer.Write(item.Name);
440  }
441  writer.Write((ushort)bodySlots.Count);
442  foreach (int slot in bodySlots) {
443  writer.Write((ushort)slot);
444  ModItem item = ItemLoader.GetItem(EquipLoader.slotToId[EquipType.Body][slot]);
445  writer.Write(item.mod.Name);
446  writer.Write(item.Name);
447  }
448  writer.Write((ushort)legSlots.Count);
449  foreach (int slot in legSlots) {
450  writer.Write((ushort)slot);
451  ModItem item = ItemLoader.GetItem(EquipLoader.slotToId[EquipType.Legs][slot]);
452  writer.Write(item.mod.Name);
453  writer.Write(item.Name);
454  }
455  WriteContainerData(writer);
456  }
457  var tag = new TagCompound();
458  tag.Set("data", ms.ToArray());
459 
460  if (itemFrames.Count > 0) {
461  tag.Set("itemFrames", itemFrames.Select(entry =>
462  new TagCompound {
463  ["id"] = entry.Value,
464  ["item"] = ItemIO.Save(((TEItemFrame)TileEntity.ByID[entry.Key]).item)
465  }
466  ).ToList());
467  }
468  return tag;
469  }
470 
471  internal static void LoadContainers(TagCompound tag) {
472  if (tag.ContainsKey("data"))
473  ReadContainers(new BinaryReader(new MemoryStream(tag.GetByteArray("data"))));
474 
475  foreach (var frameTag in tag.GetList<TagCompound>("itemFrames")) {
476  if (TileEntity.ByID.TryGetValue(frameTag.GetInt("id"), out TileEntity tileEntity) && tileEntity is TEItemFrame itemFrame)
477  ItemIO.Load(itemFrame.item, frameTag.GetCompound("item"));
478  else
479  Logging.tML.Warn($"Due to a bug in previous versions of tModLoader, the following ItemFrame data has been lost: {frameTag.ToString()}");
480  }
481  }
482 
483  internal static void ReadContainers(BinaryReader reader) {
484  byte[] flags = new byte[1];
485  reader.Read(flags, 0, reader.ReadByte());
486  if ((flags[0] & 1) == 1) {
487  ContainerTables tables = ContainerTables.Create();
488  int count = reader.ReadUInt16();
489  for (int k = 0; k < count; k++) {
490  int slot = reader.ReadUInt16();
491  string modName = reader.ReadString();
492  string name = reader.ReadString();
493  Mod mod = ModLoader.GetMod(modName);
494  tables.headSlots[slot] = mod?.GetItem(name).item.headSlot ?? 0;
495  }
496  count = reader.ReadUInt16();
497  for (int k = 0; k < count; k++) {
498  int slot = reader.ReadUInt16();
499  string modName = reader.ReadString();
500  string name = reader.ReadString();
501  Mod mod = ModLoader.GetMod(modName);
502  tables.bodySlots[slot] = mod?.GetItem(name).item.bodySlot ?? 0;
503  }
504  count = reader.ReadUInt16();
505  for (int k = 0; k < count; k++) {
506  int slot = reader.ReadUInt16();
507  string modName = reader.ReadString();
508  string name = reader.ReadString();
509  Mod mod = ModLoader.GetMod(modName);
510  tables.legSlots[slot] = mod?.GetItem(name).item.legSlot ?? 0;
511  }
512  ReadContainerData(reader, tables);
513  }
514  //legacy load
515  if ((flags[0] & 2) == 2) {
516  int count = reader.ReadInt32();
517  for (int k = 0; k < count; k++) {
518  int id = reader.ReadInt32();
519  TEItemFrame itemFrame = TileEntity.ByID[id] as TEItemFrame;
520  ItemIO.LoadLegacy(itemFrame.item, reader, true);
521  }
522  }
523  }
524 
525  internal static void WriteContainerData(BinaryWriter writer) {
526  for (int i = 0; i < Main.maxTilesX; i++) {
527  for (int j = 0; j < Main.maxTilesY; j++) {
528  Tile tile = Main.tile[i, j];
529  if (tile.active() && (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin)) {
530  int slot = tile.frameX / 100;
531  int frameX = tile.frameX % 100;
532  int position = tile.frameY / 18;
533  if (HasModArmor(slot, position) && frameX % 36 == 0) {
534  writer.Write(i);
535  writer.Write(j);
536  writer.Write((byte)position);
537  writer.Write((ushort)slot);
538  }
539  }
540  }
541  }
542  writer.Write(-1);
543  }
544 
545  internal static void ReadContainerData(BinaryReader reader, ContainerTables tables) {
546  int i = reader.ReadInt32();
547  while (i > 0) {
548  int j = reader.ReadInt32();
549  int position = reader.ReadByte();
550  int slot = reader.ReadUInt16();
551  Tile left = Main.tile[i, j];
552  Tile right = Main.tile[i + 1, j];
553  if (left.active() && right.active() && (left.type == TileID.Mannequin || left.type == TileID.Womannequin)
554  && left.type == right.type && (left.frameX == 0 || left.frameX == 36) && right.frameX == left.frameX + 18
555  && left.frameY / 18 == position && left.frameY == right.frameY) {
556  if (position == 0) {
557  slot = tables.headSlots[slot];
558  }
559  else if (position == 1) {
560  slot = tables.bodySlots[slot];
561  }
562  else if (position == 2) {
563  slot = tables.legSlots[slot];
564  }
565  left.frameX += (short)(100 * slot);
566  }
567  i = reader.ReadInt32();
568  }
569  }
570 
571  private static bool HasModArmor(int slot, int position) {
572  if (position == 0) {
573  return slot >= Main.numArmorHead;
574  }
575  else if (position == 1) {
576  return slot >= Main.numArmorBody;
577  }
578  else if (position == 2) {
579  return slot >= Main.numArmorLegs;
580  }
581  return false;
582  }
583 
584  internal static List<TagCompound> SaveTileEntities() {
585  List<TagCompound> list = new List<TagCompound>();
586  foreach (KeyValuePair<int, TileEntity> pair in TileEntity.ByID) {
587  if (pair.Value.type >= ModTileEntity.numVanilla) {
588  ModTileEntity tileEntity = (ModTileEntity)pair.Value;
589  list.Add(new TagCompound {
590  ["mod"] = tileEntity.mod.Name,
591  ["name"] = tileEntity.Name,
592  ["X"] = tileEntity.Position.X,
593  ["Y"] = tileEntity.Position.Y,
594  ["data"] = tileEntity.Save()
595  });
596  }
597  }
598  return list;
599  }
600 
601  internal static void LoadTileEntities(IList<TagCompound> list) {
602  foreach (TagCompound tag in list) {
603  Mod mod = ModLoader.GetMod(tag.GetString("mod"));
604  ModTileEntity tileEntity = mod?.GetTileEntity(tag.GetString("name"));
605  ModTileEntity newEntity;
606  if (tileEntity != null) {
607  newEntity = ModTileEntity.ConstructFromBase(tileEntity);
608  newEntity.type = (byte)tileEntity.Type;
609  newEntity.Position = new Point16(tag.GetShort("X"), tag.GetShort("Y"));
610  if (tag.ContainsKey("data")) {
611  try {
612  newEntity.Load(tag.GetCompound("data"));
613  if (newEntity is MysteryTileEntity) {
614  ((MysteryTileEntity)newEntity).TryRestore(ref newEntity);
615  }
616  }
617  catch (Exception e) {
618  throw new CustomModDataException(mod,
619  "Error in reading " + tileEntity.Name + " tile entity data for " + mod.Name, e);
620  }
621  }
622  }
623  else {
624  tileEntity = ModContent.GetInstance<ModLoaderMod>().GetTileEntity("MysteryTileEntity");
625  newEntity = ModTileEntity.ConstructFromBase(tileEntity);
626  newEntity.type = (byte)tileEntity.Type;
627  newEntity.Position = new Point16(tag.GetShort("X"), tag.GetShort("Y"));
628  ((MysteryTileEntity)newEntity).SetData(tag);
629  }
630  if (tileEntity.ValidTile(newEntity.Position.X, newEntity.Position.Y)) {
631  newEntity.ID = TileEntity.AssignNewID();
632  TileEntity.ByID[newEntity.ID] = newEntity;
633  TileEntity other;
634  if (TileEntity.ByPosition.TryGetValue(newEntity.Position, out other)) {
635  TileEntity.ByID.Remove(other.ID);
636  }
637  TileEntity.ByPosition[newEntity.Position] = newEntity;
638  }
639  }
640  }
641  }
642 }
EquipType
This is an enum of all the types of equipment that exist. An equipment type is defined as a type or l...
Definition: EquipType.cs:6