2using System.Collections.Generic;
5using System.Security.Cryptography;
6using Terraria.Graphics.Shaders;
7using Terraria.ModLoader.Default;
8using Terraria.ModLoader.Engine;
11using Terraria.Utilities;
15 internal static class PlayerIO
17 internal static void WriteVanillaHairDye(
short hairDye,
BinaryWriter writer) {
18 writer.Write((
byte)(hairDye > EffectsTracker.vanillaHairShaderCount ? 0 : hairDye));
23 internal static void Save(Player player,
string path,
bool isCloudSave) {
24 path = Path.ChangeExtension(path,
".tplr");
25 if (FileUtilities.Exists(path, isCloudSave))
26 FileUtilities.Copy(path, path +
".bak", isCloudSave);
28 var tag =
new TagCompound {
29 [
"armor"] = SaveInventory(player.armor),
30 [
"dye"] = SaveInventory(player.dye),
31 [
"inventory"] = SaveInventory(player.inventory),
32 [
"miscEquips"] = SaveInventory(player.miscEquips),
33 [
"miscDyes"] = SaveInventory(player.miscDyes),
34 [
"bank"] = SaveInventory(player.bank.item),
35 [
"bank2"] = SaveInventory(player.bank2.item),
36 [
"bank3"] = SaveInventory(player.bank3.item),
37 [
"hairDye"] = SaveHairDye(player.hairDye),
38 [
"modData"] = SaveModData(player),
39 [
"modBuffs"] = SaveModBuffs(player),
40 [
"usedMods"] = SaveUsedMods(player)
43 using (Stream stream = isCloudSave ? (Stream)
new MemoryStream() : (Stream)new FileStream(path, FileMode.Create)) {
44 TagIO.ToStream(tag, stream);
45 if (isCloudSave && SocialAPI.Cloud !=
null)
46 SocialAPI.Cloud.Write(path, ((MemoryStream)stream).ToArray());
50 internal static void Load(Player player,
string path,
bool isCloudSave) {
51 path = Path.ChangeExtension(path,
".tplr");
52 if (!FileUtilities.Exists(path, isCloudSave))
55 var buf = FileUtilities.ReadAllBytes(path, isCloudSave);
56 if (buf[0] != 0x1F || buf[1] != 0x8B) {
57 LoadLegacy(player, buf);
61 var tag = TagIO.FromStream(
new MemoryStream(buf));
62 LoadInventory(player.armor, tag.GetList<TagCompound>(
"armor"));
63 LoadInventory(player.dye, tag.GetList<TagCompound>(
"dye"));
64 LoadInventory(player.inventory, tag.GetList<TagCompound>(
"inventory"));
65 LoadInventory(player.miscEquips, tag.GetList<TagCompound>(
"miscEquips"));
66 LoadInventory(player.miscDyes, tag.GetList<TagCompound>(
"miscDyes"));
67 LoadInventory(player.bank.item, tag.GetList<TagCompound>(
"bank"));
68 LoadInventory(player.bank2.item, tag.GetList<TagCompound>(
"bank2"));
69 LoadInventory(player.bank3.item, tag.GetList<TagCompound>(
"bank3"));
70 LoadHairDye(player, tag.GetString(
"hairDye"));
71 LoadModData(player, tag.GetList<TagCompound>(
"modData"));
72 LoadModBuffs(player, tag.GetList<TagCompound>(
"modBuffs"));
73 LoadUsedMods(player, tag.GetList<
string>(
"usedMods"));
76 public static List<TagCompound> SaveInventory(Item[] inv) {
77 var list =
new List<TagCompound>();
78 for (
int k = 0; k < inv.Length; k++) {
80 var tag = ItemIO.Save(inv[k]);
81 tag.Set(
"slot", (
short)k);
85 return list.Count > 0 ? list :
null;
88 public static void LoadInventory(Item[] inv, IList<TagCompound> list) {
89 foreach (var tag
in list)
90 inv[tag.GetShort(
"slot")] = ItemIO.Load(tag);
93 public static string SaveHairDye(
short hairDye) {
94 if (hairDye <= EffectsTracker.vanillaHairShaderCount)
97 int itemId = GameShaders.Hair._reverseShaderLookupDictionary[hairDye];
99 return modItem.mod.Name +
'/' + modItem.
Name;
102 public static void LoadHairDye(Player player,
string hairDyeItemName) {
103 if (hairDyeItemName ==
"")
110 player.hairDye = (byte)GameShaders.Hair.GetShaderIdFromItemId(modItem.item.type);
113 internal static List<TagCompound> SaveModData(Player player) {
114 var list =
new List<TagCompound>();
115 foreach (var modPlayer
in player.modPlayers) {
116 var data = modPlayer.Save();
120 list.Add(
new TagCompound {
121 [
"mod"] = modPlayer.mod.Name,
122 [
"name"] = modPlayer.Name,
129 internal static void LoadModData(Player player, IList<TagCompound> list) {
130 foreach (var tag
in list) {
132 var modPlayer = mod ==
null ? null : player.GetModPlayer(mod, tag.GetString(
"name"));
133 if (modPlayer !=
null) {
135 if (tag.ContainsKey(
"legacyData"))
136 modPlayer.LoadLegacy(
new BinaryReader(
new MemoryStream(tag.GetByteArray(
"legacyData"))));
138 modPlayer.
Load(tag.GetCompound(
"data"));
142 "Error in reading custom player data for " + mod.Name, e);
146 player.GetModPlayer<MysteryPlayer>().data.Add(tag);
151 internal static List<TagCompound> SaveModBuffs(Player player) {
152 var list =
new List<TagCompound>();
153 for (
int k = 0; k < Player.MaxBuffs; k++) {
154 int buff = player.buffType[k];
155 if (buff == 0 || Main.buffNoSave[buff])
160 list.Add(
new TagCompound {
161 [
"mod"] = modBuff.mod.Name,
162 [
"name"] = modBuff.Name,
163 [
"time"] = player.buffTime[k]
167 list.Add(
new TagCompound {
168 [
"mod"] =
"Terraria",
170 [
"time"] = player.buffTime[k]
177 internal static void LoadModBuffs(Player player, IList<TagCompound> list) {
179 int buffCount = Player.MaxBuffs;
180 while (buffCount > 0 && player.buffType[buffCount - 1] == 0)
183 if (buffCount == 0) {
185 foreach (var tag
in list) {
186 if (buffCount == Player.MaxBuffs)
189 var modName = tag.GetString(
"mod");
190 int type = modName ==
"Terraria" ? tag.GetInt(
"id") :
ModLoader.
GetMod(modName)?.
BuffType(tag.GetString(
"name")) ?? 0;
192 player.buffType[buffCount] = type;
193 player.buffTime[buffCount] = tag.GetInt(
"time");
202 foreach (var tag
in list.Reverse()) {
204 int type = mod?.
BuffType(tag.GetString(
"name")) ?? 0;
208 int index = Math.Min(tag.GetByte(
"index"), buffCount);
209 Array.Copy(player.buffType, index, player.buffType, index + 1, Player.MaxBuffs - index - 1);
210 Array.Copy(player.buffTime, index, player.buffTime, index + 1, Player.MaxBuffs - index - 1);
211 player.buffType[index] = type;
212 player.buffTime[index] = tag.GetInt(
"time");
216 private static void LoadLegacy(Player player,
byte[] buffer) {
217 const int numFlagBytes = 2;
218 RijndaelManaged rijndaelManaged =
new RijndaelManaged();
219 rijndaelManaged.Padding = PaddingMode.None;
220 using (MemoryStream stream =
new MemoryStream(buffer)) {
221 using (CryptoStream cryptoStream =
new CryptoStream(stream, rijndaelManaged.CreateDecryptor(Player.ENCRYPTION_KEY, Player.ENCRYPTION_KEY), CryptoStreamMode.Read)) {
223 byte limit = reader.ReadByte();
227 byte[] flags = reader.ReadBytes(limit);
228 if (flags.Length < numFlagBytes) {
229 Array.Resize(ref flags, numFlagBytes);
231 LoadLegacyModPlayer(player, flags, reader);
237 private static void LoadLegacyModPlayer(Player player,
byte[] flags,
BinaryReader reader) {
238 if ((flags[0] & 1) == 1) {
239 ItemIO.LoadLegacyInventory(player.armor, reader);
241 if ((flags[0] & 2) == 2) {
242 ItemIO.LoadLegacyInventory(player.dye, reader);
244 if ((flags[0] & 4) == 4) {
245 ItemIO.LoadLegacyInventory(player.inventory, reader,
true,
true);
247 if ((flags[0] & 8) == 8) {
248 ItemIO.LoadLegacyInventory(player.miscEquips, reader);
250 if ((flags[0] & 16) == 16) {
251 ItemIO.LoadLegacyInventory(player.miscDyes, reader);
253 if ((flags[0] & 32) == 32) {
254 ItemIO.LoadLegacyInventory(player.bank.item, reader,
true);
256 if ((flags[0] & 64) == 64) {
257 ItemIO.LoadLegacyInventory(player.bank2.item, reader,
true);
259 if ((flags[0] & 128) == 128) {
260 LoadLegacyModData(player, reader);
262 if ((flags[1] & 1) == 1) {
263 LoadLegacyModBuffs(player, reader);
267 private static void LoadLegacyModData(Player player,
BinaryReader reader) {
268 int count = reader.ReadUInt16();
269 for (
int k = 0; k < count; k++) {
270 string modName = reader.ReadString();
271 string name = reader.ReadString();
272 byte[] data = reader.ReadBytes(reader.ReadUInt16());
274 ModPlayer modPlayer = mod ==
null ? null : player.GetModPlayer(mod, name);
275 if (modPlayer !=
null) {
276 using (MemoryStream stream =
new MemoryStream(data)) {
283 "Error in reading custom player data for " + mod.
Name, e);
289 var tag =
new TagCompound {
292 [
"legacyData"] = data
294 player.GetModPlayer<MysteryPlayer>().data.Add(tag);
299 private static void LoadLegacyModBuffs(Player player,
BinaryReader reader) {
300 int num = reader.ReadByte();
302 for (
int k = 0; k < num; k++) {
303 int index = reader.ReadByte() - minusIndex;
304 string modName = reader.ReadString();
305 string name = reader.ReadString();
306 int time = reader.ReadInt32();
308 int type = mod ==
null ? 0 : mod.
BuffType(name);
310 for (
int j = Player.MaxBuffs - 1; j > index; j--) {
311 player.buffType[j] = player.buffType[j - 1];
312 player.buffTime[j] = player.buffTime[j - 1];
314 player.buffType[index] = type;
315 player.buffTime[index] = time;
321 for (
int k = 1; k < Player.MaxBuffs; k++) {
322 if (player.buffType[k] > 0) {
324 while (player.buffType[j] == 0) {
325 player.buffType[j] = player.buffType[j + 1];
326 player.buffTime[j] = player.buffTime[j + 1];
327 player.buffType[j + 1] = 0;
328 player.buffTime[j + 1] = 0;
335 internal static void LoadUsedMods(Player player, IList<string> usedMods) {
336 player.usedMods = usedMods;
339 internal static List<string> SaveUsedMods(Player player) {
340 return ModLoader.
Mods.Select(m => m.Name).Except(
new[] {
"ModLoader" }).ToList();
344 internal static void MoveToCloud(
string localPath,
string cloudPath) {
345 localPath = Path.ChangeExtension(localPath,
".tplr");
346 cloudPath = Path.ChangeExtension(cloudPath,
".tplr");
347 if (File.Exists(localPath)) {
348 FileUtilities.MoveToCloud(localPath, cloudPath);
355 internal static void MoveToLocal(
string cloudPath,
string localPath) {
356 cloudPath = Path.ChangeExtension(cloudPath,
".tplr");
357 localPath = Path.ChangeExtension(localPath,
".tplr");
358 if (FileUtilities.Exists(cloudPath,
true)) {
359 FileUtilities.MoveToLocal(cloudPath, localPath);
363 internal static void LoadBackup(
string path,
bool cloudSave) {
364 path = Path.ChangeExtension(path,
".tplr");
365 if (FileUtilities.Exists(path +
".bak", cloudSave)) {
366 FileUtilities.Move(path +
".bak", path, cloudSave,
true);
371 internal static void ErasePlayer(
string path,
bool cloudSave) {
372 path = Path.ChangeExtension(path,
".tplr");
374 FileUtilities.Delete(path, cloudSave);
375 FileUtilities.Delete(path +
".bak", cloudSave);
This serves as the central class from which buff-related functions are supported and carried out.
static ModBuff GetBuff(int type)
Gets the ModBuff instance with the given type. If no ModBuff with the given type exists,...
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...
static bool NeedsModSaving(Item item)
Manages content added by mods. Liasons between mod content and Terraria's arrays and oversees the Loa...
static void SplitName(string name, out string domain, out string subName)
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
int BuffType(string name)
Gets the type of the ModBuff of this mod corresponding to the given name. Returns 0 if no ModBuff wit...
ModItem GetItem(string name)
Gets the ModItem instance corresponding to the name. Because this method is in the Mod class,...
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
virtual void Load()
Override this method to add most of your content to your mod. Here you will call other methods such a...
string Name
The internal name of this ModItem.
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.
A ModPlayer instance represents an extension of a Player instance. You can store fields in the ModPla...
virtual void LoadLegacy(BinaryReader reader)
Allows you to load pre-v0.9 custom data you have saved for this player.