diff --git a/CHANGELOG.md b/CHANGELOG.md index 7313922c..858535c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin ## Upcoming Release * Fix pet licenses. (@Olink) +* Initial support for Journey mode in SSC worlds. (@Olink) ## TShock 4.4.0 (Pre-release 8) * Update for OTAPI 2.0.0.36 and Terraria 1.4.0.4. (@hakusaro, @Patrikkk, @DeathCradle) diff --git a/TShockAPI/DB/ResearchDatastore.cs b/TShockAPI/DB/ResearchDatastore.cs new file mode 100644 index 00000000..cb7d933c --- /dev/null +++ b/TShockAPI/DB/ResearchDatastore.cs @@ -0,0 +1,138 @@ +using MySql.Data.MySqlClient; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using Terraria.ID; + +namespace TShockAPI.DB +{ + /// + /// This class is used as the data interface for Journey mode research. + /// This information is maintained such that SSC characters will be properly set up with + /// the world's current research. + /// + public class ResearchDatastore + { + private IDbConnection database; + + /// + /// In-memory cache of what items have been sacrificed. + /// The first call to GetSacrificedItems will load this with data from the database. + /// + private Dictionary _itemsSacrificed; + + /// + /// Initializes a new instance of the class. + /// + /// A valid connection to the TShock database + public ResearchDatastore(IDbConnection db) + { + database = db; + + var table = new SqlTable("Research", + new SqlColumn("WorldId", MySqlDbType.Int32), + new SqlColumn("PlayerId", MySqlDbType.Int32), + new SqlColumn("ItemId", MySqlDbType.Int32), + new SqlColumn("AmountSacrificed", MySqlDbType.Int32), + new SqlColumn("TimeSacrificed", MySqlDbType.DateTime) + ); + var creator = new SqlTableCreator(db, + db.GetSqlType() == SqlType.Sqlite + ? (IQueryBuilder)new SqliteQueryCreator() + : new MysqlQueryCreator()); + try + { + creator.EnsureTableStructure(table); + } + catch (DllNotFoundException) + { + Console.WriteLine("Possible problem with your database - is Sqlite3.dll present?"); + throw new Exception("Could not find a database library (probably Sqlite3.dll)"); + } + } + + /// + /// This call will return the memory-cached list of items sacrificed. + /// If the cache is not initialized, it will be initialized from the database. + /// + /// + public Dictionary GetSacrificedItems() + { + if (_itemsSacrificed == null) + { + _itemsSacrificed = ReadFromDatabase(); + } + + return _itemsSacrificed; + } + + /// + /// This function will return a Dictionary<ItemId, AmountSacrificed> representing + /// what the progress of research on items is for this world. + /// + /// A dictionary of ItemID keys and Amount Sacrificed values. + private Dictionary ReadFromDatabase() + { + Dictionary sacrificedItems = new Dictionary(); + + var sql = @"select itemId, sum(AmountSacrificed) totalSacrificed + from Research + where WorldId = @0 + group by itemId"; + + try { + using (var reader = database.QueryReader(sql, Main.worldID)) + { + while (reader.Read()) + { + var itemId = reader.Get("itemId"); + var amount = reader.Get("totalSacrificed"); + sacrificedItems[itemId] = amount; + } + } + } + catch (Exception ex) + { + TShock.Log.Error(ex.ToString()); + } + return sacrificedItems; + } + + /// + /// This method will sacrifice an amount of an item for research. + /// + /// The net ItemId that is being researched. + /// The amount of items being sacrificed. + /// The player who sacrificed the item for research. + /// The cumulative total sacrifices for this item. + public int SacrificeItem(int itemId, int amount, TSPlayer player) + { + var itemsSacrificed = GetSacrificedItems(); + if (!(itemsSacrificed.ContainsKey(itemId))) + itemsSacrificed[itemId] = 0; + + var sql = @"insert into Research (WorldId, PlayerId, ItemId, AmountSacrificed, TimeSacrificed) values (@0, @1, @2, @3, @4)"; + + var result = 0; + try + { + result = database.Query(sql, Main.worldID, player.Account.ID, itemId, amount, DateTime.Now); + } + catch (Exception ex) + { + TShock.Log.Error(ex.ToString()); + } + + if (result == 1) + { + itemsSacrificed[itemId] += amount; + } + + return itemsSacrificed[itemId]; + } + } +} diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 93da1ccb..3eee034f 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -39,6 +39,8 @@ using TShockAPI.Localization; using TShockAPI.Models; using TShockAPI.Models.PlayerUpdate; using TShockAPI.Models.Projectiles; +using Terraria.Net; +using Terraria.GameContent.NetModules; namespace TShockAPI { @@ -3135,7 +3137,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timefreeze)) { - args.Player.SendErrorMessage("You have no permission to freeze the time of the server!"); + args.Player.SendErrorMessage("You don't have permission to freeze the time of the server!"); return true; } break; @@ -3147,7 +3149,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timeset)) { - args.Player.SendErrorMessage("You have no permission to modify the time of the server!"); + args.Player.SendErrorMessage("You don't have permission to modify the time of the server!"); return true; } break; @@ -3156,7 +3158,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_godmode)) { - args.Player.SendErrorMessage("You have no permission to toggle godmode!"); + args.Player.SendErrorMessage("You don't have permission to toggle godmode!"); return true; } break; @@ -3165,7 +3167,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_windstrength)) { - args.Player.SendErrorMessage("You have no permission to modify the wind strength of the server!"); + args.Player.SendErrorMessage("You don't have permission to modify the wind strength of the server!"); return true; } break; @@ -3174,7 +3176,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_rainstrength)) { - args.Player.SendErrorMessage("You have no permission to modify the rain strength of the server!"); + args.Player.SendErrorMessage("You don't have permission to modify the rain strength of the server!"); return true; } break; @@ -3183,7 +3185,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timespeed)) { - args.Player.SendErrorMessage("You have no permission to modify the time speed of the server!"); + args.Player.SendErrorMessage("You don't have permission to modify the time speed of the server!"); return true; } break; @@ -3192,7 +3194,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_rainfreeze)) { - args.Player.SendErrorMessage("You have no permission to freeze the rain strength of the server!"); + args.Player.SendErrorMessage("You don't have permission to freeze the rain strength of the server!"); return true; } break; @@ -3201,7 +3203,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_windfreeze)) { - args.Player.SendErrorMessage("You have no permission to freeze the wind strength of the server!"); + args.Player.SendErrorMessage("You don't have permission to freeze the wind strength of the server!"); return true; } break; @@ -3210,7 +3212,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_placementrange)) { - args.Player.SendErrorMessage("You have no permission to modify the tile placement range of your character!"); + args.Player.SendErrorMessage("You don't have permission to modify the tile placement range of your character!"); return true; } break; @@ -3219,7 +3221,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_setdifficulty)) { - args.Player.SendErrorMessage("You have no permission to modify the world dificulty of the server!"); + args.Player.SendErrorMessage("You don't have permission to modify the world dificulty of the server!"); return true; } break; @@ -3228,7 +3230,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_biomespreadfreeze)) { - args.Player.SendErrorMessage("You have no permission to freeze the biome spread of server!"); + args.Player.SendErrorMessage("You don't have permission to freeze the biome spread of server!"); return true; } break; @@ -3237,7 +3239,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_setspawnrate)) { - args.Player.SendErrorMessage("You have no permission to modify the NPC spawn rate of server!"); + args.Player.SendErrorMessage("You don't have permission to modify the NPC spawn rate of server!"); return true; } break; @@ -3247,6 +3249,20 @@ namespace TShockAPI return true; } } + } else if (moduleId == (int)NetModulesTypes.CreativeUnlocksPlayerReport) + { + var unknownField = args.Data.ReadByte(); + + if (unknownField == 0) //this is required or something??? + { + var itemId = args.Data.ReadUInt16(); + var amount = args.Data.ReadUInt16(); + + var totalSacrificed = TShock.ResearchDatastore.SacrificeItem(itemId, amount, args.Player); + + var response = NetCreativeUnlocksModule.SerializeItemSacrifice(itemId, totalSacrificed); + NetManager.Instance.Broadcast(response); + } } // As of 1.4.x.x, this is now used for more things: diff --git a/TShockAPI/PlayerData.cs b/TShockAPI/PlayerData.cs index 64a33676..6e649768 100644 --- a/TShockAPI/PlayerData.cs +++ b/TShockAPI/PlayerData.cs @@ -20,6 +20,9 @@ using Microsoft.Xna.Framework; using Terraria; using TShockAPI; using Terraria.Localization; +using Terraria.GameContent.NetModules; +using Terraria.Net; +using Terraria.ID; namespace TShockAPI { @@ -480,6 +483,20 @@ namespace TShockAPI NetMessage.SendData(76, -1, -1, NetworkText.Empty, player.Index); NetMessage.SendData(39, player.Index, -1, NetworkText.Empty, 400); + + + var sacrificedItems = TShock.ResearchDatastore.GetSacrificedItems(); + for(int i = 0; i < ItemID.Count; i++) + { + var amount = 0; + if (sacrificedItems.ContainsKey(i)) + { + amount = sacrificedItems[i]; + } + + var response = NetCreativeUnlocksModule.SerializeItemSacrifice(i, amount); + NetManager.Instance.SendToClient(response, player.TPlayer.whoAmI); + } } } } diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index c1032d43..aa1791d5 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -100,6 +100,8 @@ namespace TShockAPI public static RememberedPosManager RememberedPos; /// CharacterDB - Static reference to the SSC character manager. public static CharacterManager CharacterDB; + /// Contains the information about what research has been performed in Journey mode. + public static ResearchDatastore ResearchDatastore; /// Config - Static reference to the config system, for accessing values set in users' config files. public static ConfigFile Config { get; set; } /// ServerSideCharacterConfig - Static reference to the server side character config, for accessing values set by users to modify SSC. @@ -324,6 +326,7 @@ namespace TShockAPI TileBans = new TileManager(DB); RememberedPos = new RememberedPosManager(DB); CharacterDB = new CharacterManager(DB); + ResearchDatastore = new ResearchDatastore(DB); RestApi = new SecureRest(Netplay.ServerIP, Config.RestApiPort); RestManager = new RestManager(RestApi); RestManager.RegisterRestfulCommands(); diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 02b9e43e..c941ea0a 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -85,6 +85,7 @@ + @@ -220,4 +221,4 @@ --> - \ No newline at end of file +