From df49eefadbf6825413fa9c3b4fbcf1712cc952c7 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Tue, 23 Feb 2021 15:39:46 -0500 Subject: [PATCH] Added recipe resolver --- Data/sql/gamedata_recipes.sql | 51 +++++++++++++ Map Server/DataObjects/Recipe.cs | 24 ++++++ Map Server/DataObjects/RecipeResolver.cs | 69 +++++++++++++++++ Map Server/Database.cs | 97 +++++++++++++++++++++++- Map Server/Lua/LuaEngine.cs | 2 + Map Server/Map Server.csproj | 2 + 6 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 Data/sql/gamedata_recipes.sql create mode 100644 Map Server/DataObjects/Recipe.cs create mode 100644 Map Server/DataObjects/RecipeResolver.cs diff --git a/Data/sql/gamedata_recipes.sql b/Data/sql/gamedata_recipes.sql new file mode 100644 index 00000000..4af0035f --- /dev/null +++ b/Data/sql/gamedata_recipes.sql @@ -0,0 +1,51 @@ +-- -------------------------------------------------------- +-- Host: 127.0.0.1 +-- Server version: 5.6.17 - MySQL Community Server (GPL) +-- Server OS: Win64 +-- HeidiSQL Version: 10.1.0.5464 +-- -------------------------------------------------------- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!50503 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + + +-- Dumping database structure for ffxiv_server +CREATE DATABASE IF NOT EXISTS `ffxiv_server` /*!40100 DEFAULT CHARACTER SET latin1 */; +USE `ffxiv_server`; + +-- Dumping structure for table ffxiv_server.gamedata_recipes +CREATE TABLE IF NOT EXISTS `gamedata_recipes` ( + `id` int(4) NOT NULL AUTO_INCREMENT, + `craftedItem` int(11) NOT NULL, + `craftedQuantity` int(11) NOT NULL, + `job` char(4) NOT NULL COMMENT 'A=CRP,B=BSM,C=ARM,D=GSM,E=LTW,F=WVR,G=ALC,H=CUL', + `level` tinyint(1) NOT NULL, + `dated` tinyint(1) DEFAULT NULL, + `kind` varchar(2) NOT NULL COMMENT 'AA=Material,BB=Parts,CC=Finished', + `crystal0ID` int(11) NOT NULL DEFAULT '0', + `crystal0Quantity` int(11) NOT NULL DEFAULT '0', + `crystal1ID` int(11) NOT NULL DEFAULT '0', + `crystal1Quantity` int(11) NOT NULL DEFAULT '0', + `facilities` int(11) DEFAULT NULL, + `material0` int(11) NOT NULL DEFAULT '0', + `material1` int(11) NOT NULL DEFAULT '0', + `material2` int(11) NOT NULL DEFAULT '0', + `material3` int(11) NOT NULL DEFAULT '0', + `material4` int(11) NOT NULL DEFAULT '0', + `material5` int(11) NOT NULL DEFAULT '0', + `material6` int(11) NOT NULL DEFAULT '0', + `material7` int(11) NOT NULL DEFAULT '0', + `subSkill0Job` int(11) DEFAULT NULL, + `subSkill0Level` varchar(5) DEFAULT NULL, + `subSkill1Job` int(11) DEFAULT NULL, + `subSkill1Level` varchar(5) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC; + +-- Data exporting was unselected. +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/Map Server/DataObjects/Recipe.cs b/Map Server/DataObjects/Recipe.cs new file mode 100644 index 00000000..67d3474a --- /dev/null +++ b/Map Server/DataObjects/Recipe.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Meteor.Map.DataObjects +{ + class Recipe + { + public readonly uint resultItemID; + public readonly uint resultQuantity; + public readonly byte[] allowedCrafters; + public readonly byte tier; + + public Recipe(uint resultItemID, uint resultQuantity, byte[] allowedCrafters, byte tier) + { + this.resultItemID = resultItemID; + this.resultQuantity = resultQuantity; + this.allowedCrafters = allowedCrafters; + this.tier = tier; + } + } +} diff --git a/Map Server/DataObjects/RecipeResolver.cs b/Map Server/DataObjects/RecipeResolver.cs new file mode 100644 index 00000000..ed8a42f4 --- /dev/null +++ b/Map Server/DataObjects/RecipeResolver.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; + +namespace Meteor.Map.DataObjects +{ + class RecipeResolver + { + Dictionary recipeList; + Dictionary> matsToRecipes; + + private RecipeResolver(Dictionary recipeList, Dictionary> matsToRecipes) + { + this.recipeList = recipeList; + this.matsToRecipes = matsToRecipes; + } + + public static RecipeResolver Init() + { + var recipeList = new Dictionary(); + var matToRecipes = new Dictionary>(); + + Database.GetRecipeGamedata(recipeList, matToRecipes); + + return new RecipeResolver(recipeList, matToRecipes); + } + + public List GetRecipeFromMats(uint mat1 = 0, uint mat2 = 0, uint mat3 = 0, uint mat4 = 0, uint mat5 = 0, uint mat6 = 0, uint mat7 = 0, uint mat8 = 0) + { + uint[] mats = new uint[8]; + mats[0] = mat1; + mats[1] = mat2; + mats[2] = mat3; + mats[3] = mat4; + mats[4] = mat5; + mats[5] = mat6; + mats[6] = mat7; + mats[7] = mat8; + + Array.Sort(mats); + + byte[] result = new byte[8 * sizeof(int)]; + Buffer.BlockCopy(mats, 0, result, 0, result.Length); + string hash; + using (MD5 md5 = MD5.Create()) + { + hash = BitConverter.ToString(md5.ComputeHash(result)); + } + + if (matsToRecipes.ContainsKey(hash)) + return matsToRecipes[hash]; + else + return null; + } + + public Recipe GetMatsForRecipe(uint recipeID) + { + if (recipeList.ContainsKey(recipeID)) + return recipeList[recipeID]; + else + return null; + } + + public int GetNumRecipes() + { + return recipeList.Count; + } + } +} diff --git a/Map Server/Database.cs b/Map Server/Database.cs index 75508b70..5e571deb 100644 --- a/Map Server/Database.cs +++ b/Map Server/Database.cs @@ -33,6 +33,8 @@ using Meteor.Map.packets.receive.supportdesk; using Meteor.Map.actors.chara.npc; using Meteor.Map.actors.chara.ai; using Meteor.Map.packets.send.actor.battle; +using Meteor.Map.DataObjects; +using System.Security.Cryptography; namespace Meteor.Map { @@ -168,6 +170,99 @@ namespace Meteor.Map } } + public static bool GetRecipeGamedata(Dictionary recipeList, Dictionary> matToRecipe) + { + using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + + string query = @" + SELECT + id, + craftedItem, + craftedQuantity, + + crystal0ID, + crystal0Quantity, + crystal1ID, + crystal1Quantity, + + material0, + material1, + material2, + material3, + material4, + material5, + material6, + material7 + FROM gamedata_recipes + ORDER BY craftedItem ASC + "; + + MySqlCommand cmd = new MySqlCommand(query, conn); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + uint recipeID = reader.GetUInt32("id"); + + uint craftedItem = reader.GetUInt32("craftedItem"); + uint craftedQuantity = reader.GetUInt32("craftedQuantity"); + + uint crystal0ID = reader.GetUInt32("crystal0ID"); + uint crystal0Quantity = reader.GetUInt32("crystal0Quantity"); + uint crystal1ID = reader.GetUInt32("crystal1ID"); + uint crystal1Quantity = reader.GetUInt32("crystal1Quantity"); + + uint[] mats = new uint[8]; + mats[0] = reader.GetUInt32("material0"); + mats[1] = reader.GetUInt32("material1"); + mats[2] = reader.GetUInt32("material2"); + mats[3] = reader.GetUInt32("material3"); + mats[4] = reader.GetUInt32("material4"); + mats[5] = reader.GetUInt32("material5"); + mats[6] = reader.GetUInt32("material6"); + mats[7] = reader.GetUInt32("material7"); + + Array.Sort(mats); + + Recipe recipe = new Recipe(craftedItem, craftedQuantity, new byte[] { }, 1); + + //Hash for the Mat -> Recipe Dictionary + byte[] result = new byte[8 * sizeof(int)]; + Buffer.BlockCopy(mats, 0, result, 0, result.Length); + string hash; + using (MD5 md5 = MD5.Create()) + { + hash = BitConverter.ToString(md5.ComputeHash(result)); + } + + if (!matToRecipe.ContainsKey(hash)) + matToRecipe.Add(hash, new List()); + + //Add to both Dictionaries + recipeList.Add(recipeID, new Recipe(craftedItem, craftedQuantity, new byte[] { }, 1)); + matToRecipe[hash].Add(new Recipe(craftedItem, craftedQuantity, new byte[] { }, 1)); + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + return false; + } + finally + { + conn.Dispose(); + } + + return true; + } + } + public static void SavePlayerAppearance(Player player) { string query; @@ -2777,6 +2872,6 @@ namespace Meteor.Map } } } - + } } diff --git a/Map Server/Lua/LuaEngine.cs b/Map Server/Lua/LuaEngine.cs index 236e4830..247eb440 100644 --- a/Map Server/Lua/LuaEngine.cs +++ b/Map Server/Lua/LuaEngine.cs @@ -37,6 +37,7 @@ using Meteor.Map.actors.area; using System.Threading; using Meteor.Map.actors.chara.ai; using Meteor.Map.actors.chara.ai.controllers; +using Meteor.Map.DataObjects; namespace Meteor.Map.lua { @@ -850,6 +851,7 @@ namespace Meteor.Map.lua script.Globals["GetWorldMaster"] = (Func)Server.GetWorldManager().GetActor; script.Globals["GetItemGamedata"] = (Func)Server.GetItemGamedata; script.Globals["GetGuildleveGamedata"] = (Func)Server.GetGuildleveGamedata; + script.Globals["GetRecipeResolver"] = (Func)Server.ResolveRecipe; script.Globals["GetLuaInstance"] = (Func)LuaEngine.GetInstance; script.Options.DebugPrint = s => { Program.Log.Debug(s); }; diff --git a/Map Server/Map Server.csproj b/Map Server/Map Server.csproj index 8ab70f72..5dcd478d 100644 --- a/Map Server/Map Server.csproj +++ b/Map Server/Map Server.csproj @@ -168,6 +168,8 @@ + +