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
+