forked from Mirrorlandia_minetest/minetest
Crafting speedup
This greatly increases crafting performance, especially in worlds with many mods. Approved by @kwolekr. Introduces a hash-type-layered fall-through mechanism, where every layer specifies one hash algorithm, and the "deeper the fall", the more collisions to expect for the algorithm. One Craft definition only resides at one layer, which improves speed for lower layers (and a complete fail), due to most craft definitions residing at high layers. Due to the fall-through design, the undocumented behaviour that later craft recipes override older ones had to be weaked up a bit, but craft recipes with the same hash and layer will still override.
This commit is contained in:
parent
fedbbc8883
commit
334e70455b
330
src/craftdef.cpp
330
src/craftdef.cpp
@ -27,10 +27,48 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
#include "inventory.h"
|
#include "inventory.h"
|
||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
|
#include "util/string.h"
|
||||||
#include "util/numeric.h"
|
#include "util/numeric.h"
|
||||||
#include "strfnd.h"
|
#include "strfnd.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
|
||||||
|
inline bool isGroupRecipeStr(const std::string &rec_name)
|
||||||
|
{
|
||||||
|
return str_starts_with(rec_name, std::string("group:"));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 getHashForString(const std::string &recipe_str)
|
||||||
|
{
|
||||||
|
/*errorstream << "Hashing craft string \"" << recipe_str << "\"";*/
|
||||||
|
return murmur_hash_64_ua(recipe_str.data(), recipe_str.length(), 0xdeadbeef);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 getHashForGrid(CraftHashType type, const std::vector<std::string> &grid_names)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case CRAFT_HASH_TYPE_ITEM_NAMES: {
|
||||||
|
std::ostringstream os;
|
||||||
|
bool is_first = true;
|
||||||
|
for (size_t i = 0; i < grid_names.size(); i++) {
|
||||||
|
if (grid_names[i] != "") {
|
||||||
|
os << (is_first ? "" : "\n") << grid_names[i];
|
||||||
|
is_first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getHashForString(os.str());
|
||||||
|
} case CRAFT_HASH_TYPE_COUNT: {
|
||||||
|
u64 cnt = 0;
|
||||||
|
for (size_t i = 0; i < grid_names.size(); i++)
|
||||||
|
if (grid_names[i] != "")
|
||||||
|
cnt++;
|
||||||
|
return cnt;
|
||||||
|
} case CRAFT_HASH_TYPE_UNHASHED:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// invalid CraftHashType
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if input matches recipe
|
// Check if input matches recipe
|
||||||
// Takes recipe groups into account
|
// Takes recipe groups into account
|
||||||
static bool inputItemMatchesRecipe(const std::string &inp_name,
|
static bool inputItemMatchesRecipe(const std::string &inp_name,
|
||||||
@ -41,7 +79,7 @@ static bool inputItemMatchesRecipe(const std::string &inp_name,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Group
|
// Group
|
||||||
if(rec_name.substr(0,6) == "group:" && idef->isKnown(inp_name)){
|
if (isGroupRecipeStr(rec_name) && idef->isKnown(inp_name)) {
|
||||||
const struct ItemDefinition &def = idef->get(inp_name);
|
const struct ItemDefinition &def = idef->get(inp_name);
|
||||||
Strfnd f(rec_name.substr(6));
|
Strfnd f(rec_name.substr(6));
|
||||||
bool all_groups_match = true;
|
bool all_groups_match = true;
|
||||||
@ -405,8 +443,13 @@ bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) co
|
|||||||
if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
|
if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
|
||||||
return false; // it was empty
|
return false; // it was empty
|
||||||
|
|
||||||
|
std::vector<std::string> rec_names;
|
||||||
|
if (hash_inited)
|
||||||
|
rec_names = recipe_names;
|
||||||
|
else
|
||||||
|
rec_names = craftGetItemNames(recipe, gamedef);
|
||||||
|
|
||||||
// Get recipe item matrix
|
// Get recipe item matrix
|
||||||
std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
|
|
||||||
unsigned int rec_width = width;
|
unsigned int rec_width = width;
|
||||||
if(rec_width == 0)
|
if(rec_width == 0)
|
||||||
return false;
|
return false;
|
||||||
@ -462,6 +505,43 @@ void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef)
|
|||||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CraftHashType CraftDefinitionShaped::getHashType() const
|
||||||
|
{
|
||||||
|
assert(hash_inited); //pre-condition
|
||||||
|
bool has_group = false;
|
||||||
|
for (size_t i = 0; i < recipe_names.size(); i++) {
|
||||||
|
if (isGroupRecipeStr(recipe_names[i])) {
|
||||||
|
has_group = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_group)
|
||||||
|
return CRAFT_HASH_TYPE_COUNT;
|
||||||
|
else
|
||||||
|
return CRAFT_HASH_TYPE_ITEM_NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 CraftDefinitionShaped::getHash(CraftHashType type) const
|
||||||
|
{
|
||||||
|
assert(hash_inited); //pre-condition
|
||||||
|
if ((type == CRAFT_HASH_TYPE_ITEM_NAMES) || (type == CRAFT_HASH_TYPE_COUNT)) {
|
||||||
|
std::vector<std::string> rec_names = recipe_names;
|
||||||
|
std::sort(rec_names.begin(), rec_names.end());
|
||||||
|
return getHashForGrid(type, rec_names);
|
||||||
|
} else {
|
||||||
|
//illegal hash type for this CraftDefinition (pre-condition)
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CraftDefinitionShaped::initHash(IGameDef *gamedef)
|
||||||
|
{
|
||||||
|
if (hash_inited)
|
||||||
|
return;
|
||||||
|
hash_inited = true;
|
||||||
|
recipe_names = craftGetItemNames(recipe, gamedef);
|
||||||
|
}
|
||||||
|
|
||||||
std::string CraftDefinitionShaped::dump() const
|
std::string CraftDefinitionShaped::dump() const
|
||||||
{
|
{
|
||||||
std::ostringstream os(std::ios::binary);
|
std::ostringstream os(std::ios::binary);
|
||||||
@ -526,12 +606,18 @@ bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try with all permutations of the recipe
|
std::vector<std::string> recipe_copy;
|
||||||
std::vector<std::string> recipe_copy = craftGetItemNames(recipe, gamedef);
|
if (hash_inited)
|
||||||
// Start from the lexicographically first permutation (=sorted)
|
recipe_copy = recipe_names;
|
||||||
|
else {
|
||||||
|
recipe_copy = craftGetItemNames(recipe, gamedef);
|
||||||
std::sort(recipe_copy.begin(), recipe_copy.end());
|
std::sort(recipe_copy.begin(), recipe_copy.end());
|
||||||
//while(std::prev_permutation(recipe_copy.begin(), recipe_copy.end())){}
|
}
|
||||||
do{
|
|
||||||
|
// Try with all permutations of the recipe,
|
||||||
|
// start from the lexicographically first permutation (=sorted),
|
||||||
|
// recipe_names is pre-sorted
|
||||||
|
do {
|
||||||
// If all items match, the recipe matches
|
// If all items match, the recipe matches
|
||||||
bool all_match = true;
|
bool all_match = true;
|
||||||
//dstream<<"Testing recipe (output="<<output<<"):";
|
//dstream<<"Testing recipe (output="<<output<<"):";
|
||||||
@ -566,6 +652,42 @@ void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamed
|
|||||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CraftHashType CraftDefinitionShapeless::getHashType() const
|
||||||
|
{
|
||||||
|
assert(hash_inited); //pre-condition
|
||||||
|
bool has_group = false;
|
||||||
|
for (size_t i = 0; i < recipe_names.size(); i++) {
|
||||||
|
if (isGroupRecipeStr(recipe_names[i])) {
|
||||||
|
has_group = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_group)
|
||||||
|
return CRAFT_HASH_TYPE_COUNT;
|
||||||
|
else
|
||||||
|
return CRAFT_HASH_TYPE_ITEM_NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
|
||||||
|
{
|
||||||
|
assert(hash_inited); //pre-condition
|
||||||
|
if (type == CRAFT_HASH_TYPE_ITEM_NAMES || type == CRAFT_HASH_TYPE_COUNT) {
|
||||||
|
return getHashForGrid(type, recipe_names);
|
||||||
|
} else {
|
||||||
|
//illegal hash type for this CraftDefinition (pre-condition)
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
|
||||||
|
{
|
||||||
|
if (hash_inited)
|
||||||
|
return;
|
||||||
|
hash_inited = true;
|
||||||
|
recipe_names = craftGetItemNames(recipe, gamedef);
|
||||||
|
std::sort(recipe_names.begin(), recipe_names.end());
|
||||||
|
}
|
||||||
|
|
||||||
std::string CraftDefinitionShapeless::dump() const
|
std::string CraftDefinitionShapeless::dump() const
|
||||||
{
|
{
|
||||||
std::ostringstream os(std::ios::binary);
|
std::ostringstream os(std::ios::binary);
|
||||||
@ -763,6 +885,34 @@ void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef
|
|||||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CraftHashType CraftDefinitionCooking::getHashType() const
|
||||||
|
{
|
||||||
|
if (isGroupRecipeStr(recipe_name))
|
||||||
|
return CRAFT_HASH_TYPE_COUNT;
|
||||||
|
else
|
||||||
|
return CRAFT_HASH_TYPE_ITEM_NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 CraftDefinitionCooking::getHash(CraftHashType type) const
|
||||||
|
{
|
||||||
|
if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
|
||||||
|
return getHashForString(recipe_name);
|
||||||
|
} else if (type == CRAFT_HASH_TYPE_COUNT) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
//illegal hash type for this CraftDefinition (pre-condition)
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CraftDefinitionCooking::initHash(IGameDef *gamedef)
|
||||||
|
{
|
||||||
|
if (hash_inited)
|
||||||
|
return;
|
||||||
|
hash_inited = true;
|
||||||
|
recipe_name = craftGetItemName(recipe, gamedef);
|
||||||
|
}
|
||||||
|
|
||||||
std::string CraftDefinitionCooking::dump() const
|
std::string CraftDefinitionCooking::dump() const
|
||||||
{
|
{
|
||||||
std::ostringstream os(std::ios::binary);
|
std::ostringstream os(std::ios::binary);
|
||||||
@ -844,6 +994,33 @@ void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) c
|
|||||||
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
craftDecrementOrReplaceInput(input, replacements, gamedef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CraftHashType CraftDefinitionFuel::getHashType() const
|
||||||
|
{
|
||||||
|
if (isGroupRecipeStr(recipe_name))
|
||||||
|
return CRAFT_HASH_TYPE_COUNT;
|
||||||
|
else
|
||||||
|
return CRAFT_HASH_TYPE_ITEM_NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 CraftDefinitionFuel::getHash(CraftHashType type) const
|
||||||
|
{
|
||||||
|
if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
|
||||||
|
return getHashForString(recipe_name);
|
||||||
|
} else if (type == CRAFT_HASH_TYPE_COUNT) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
//illegal hash type for this CraftDefinition (pre-condition)
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CraftDefinitionFuel::initHash(IGameDef *gamedef)
|
||||||
|
{
|
||||||
|
if (hash_inited)
|
||||||
|
return;
|
||||||
|
hash_inited = true;
|
||||||
|
recipe_name = craftGetItemName(recipe, gamedef);
|
||||||
|
}
|
||||||
std::string CraftDefinitionFuel::dump() const
|
std::string CraftDefinitionFuel::dump() const
|
||||||
{
|
{
|
||||||
std::ostringstream os(std::ios::binary);
|
std::ostringstream os(std::ios::binary);
|
||||||
@ -876,10 +1053,16 @@ void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
|
|||||||
class CCraftDefManager: public IWritableCraftDefManager
|
class CCraftDefManager: public IWritableCraftDefManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
CCraftDefManager()
|
||||||
|
{
|
||||||
|
m_craft_defs.resize(craft_hash_type_max + 1);
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~CCraftDefManager()
|
virtual ~CCraftDefManager()
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
|
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
|
||||||
bool decrementInput, IGameDef *gamedef) const
|
bool decrementInput, IGameDef *gamedef) const
|
||||||
{
|
{
|
||||||
@ -888,47 +1071,55 @@ public:
|
|||||||
|
|
||||||
// If all input items are empty, abort.
|
// If all input items are empty, abort.
|
||||||
bool all_empty = true;
|
bool all_empty = true;
|
||||||
for(std::vector<ItemStack>::const_iterator
|
for (std::vector<ItemStack>::const_iterator
|
||||||
i = input.items.begin();
|
i = input.items.begin();
|
||||||
i != input.items.end(); i++)
|
i != input.items.end(); i++) {
|
||||||
{
|
if (!i->empty()) {
|
||||||
if(!i->empty())
|
|
||||||
{
|
|
||||||
all_empty = false;
|
all_empty = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(all_empty)
|
if (all_empty)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
std::vector<std::string> input_names;
|
||||||
|
input_names = craftGetItemNames(input.items, gamedef);
|
||||||
|
std::sort(input_names.begin(), input_names.end());
|
||||||
|
|
||||||
|
// Try hash types with increasing collision rate, and return if found.
|
||||||
|
for (int type = 0; type <= craft_hash_type_max; type++) {
|
||||||
|
u64 hash = getHashForGrid((CraftHashType) type, input_names);
|
||||||
|
|
||||||
|
/*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/
|
||||||
|
|
||||||
|
// We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];"
|
||||||
|
// but that doesn't compile for some reason. This does.
|
||||||
|
std::map<u64, std::vector<CraftDefinition*> >::const_iterator
|
||||||
|
col_iter = (m_craft_defs[type]).find(hash);
|
||||||
|
|
||||||
|
if (col_iter == (m_craft_defs[type]).end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const std::vector<CraftDefinition*> &hash_collisions = col_iter->second;
|
||||||
// Walk crafting definitions from back to front, so that later
|
// Walk crafting definitions from back to front, so that later
|
||||||
// definitions can override earlier ones.
|
// definitions can override earlier ones.
|
||||||
for(std::vector<CraftDefinition*>::const_reverse_iterator
|
for (std::vector<CraftDefinition*>::const_reverse_iterator
|
||||||
i = m_craft_definitions.rbegin();
|
i = hash_collisions.rbegin();
|
||||||
i != m_craft_definitions.rend(); i++)
|
i != hash_collisions.rend(); i++) {
|
||||||
{
|
|
||||||
CraftDefinition *def = *i;
|
CraftDefinition *def = *i;
|
||||||
|
|
||||||
/*infostream<<"Checking "<<input.dump()<<std::endl
|
/*errorstream << "Checking " << input.dump() << std::endl
|
||||||
<<" against "<<def->dump()<<std::endl;*/
|
<< " against " << def->dump() << std::endl;*/
|
||||||
|
|
||||||
try {
|
if (def->check(input, gamedef)) {
|
||||||
if(def->check(input, gamedef))
|
|
||||||
{
|
|
||||||
// Get output, then decrement input (if requested)
|
// Get output, then decrement input (if requested)
|
||||||
output = def->getOutput(input, gamedef);
|
output = def->getOutput(input, gamedef);
|
||||||
if(decrementInput)
|
if (decrementInput)
|
||||||
def->decrementInput(input, gamedef);
|
def->decrementInput(input, gamedef);
|
||||||
|
/*errorstream << "Check RETURNS TRUE" << std::endl;*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(SerializationError &e)
|
|
||||||
{
|
|
||||||
errorstream<<"getCraftResult: ERROR: "
|
|
||||||
<<"Serialization error in recipe "
|
|
||||||
<<def->dump()<<std::endl;
|
|
||||||
// then go on with the next craft definition
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -960,12 +1151,16 @@ public:
|
|||||||
virtual std::string dump() const
|
virtual std::string dump() const
|
||||||
{
|
{
|
||||||
std::ostringstream os(std::ios::binary);
|
std::ostringstream os(std::ios::binary);
|
||||||
os<<"Crafting definitions:\n";
|
os << "Crafting definitions:\n";
|
||||||
for(std::vector<CraftDefinition*>::const_iterator
|
for (int type = 0; type <= craft_hash_type_max; type++) {
|
||||||
i = m_craft_definitions.begin();
|
for (std::map<u64, std::vector<CraftDefinition*> >::const_iterator
|
||||||
i != m_craft_definitions.end(); i++)
|
i = (m_craft_defs[type]).begin();
|
||||||
{
|
i != (m_craft_defs[type]).end(); i++) {
|
||||||
os<<(*i)->dump()<<"\n";
|
for (std::vector<CraftDefinition*>::const_iterator
|
||||||
|
ii = i->second.begin(); ii != i->second.end(); ii++) {
|
||||||
|
os << "type " << type << " hash " << i->first << (*ii)->dump() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
@ -973,7 +1168,7 @@ public:
|
|||||||
{
|
{
|
||||||
verbosestream<<"registerCraft: registering craft definition: "
|
verbosestream<<"registerCraft: registering craft definition: "
|
||||||
<<def->dump()<<std::endl;
|
<<def->dump()<<std::endl;
|
||||||
m_craft_definitions.push_back(def);
|
m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);
|
||||||
|
|
||||||
CraftInput input;
|
CraftInput input;
|
||||||
std::string output_name = craftGetItemName(
|
std::string output_name = craftGetItemName(
|
||||||
@ -982,48 +1177,41 @@ public:
|
|||||||
}
|
}
|
||||||
virtual void clear()
|
virtual void clear()
|
||||||
{
|
{
|
||||||
for(std::vector<CraftDefinition*>::iterator
|
for (int type = 0; type <= craft_hash_type_max; type++) {
|
||||||
i = m_craft_definitions.begin();
|
for (std::map<u64, std::vector<CraftDefinition*> >::iterator
|
||||||
i != m_craft_definitions.end(); i++){
|
i = m_craft_defs[type].begin();
|
||||||
delete *i;
|
i != m_craft_defs[type].end(); i++) {
|
||||||
|
for (std::vector<CraftDefinition*>::iterator
|
||||||
|
ii = i->second.begin(); ii != i->second.end(); ii++) {
|
||||||
|
delete *ii;
|
||||||
|
}
|
||||||
|
i->second.clear();
|
||||||
|
}
|
||||||
|
m_craft_defs[type].clear();
|
||||||
}
|
}
|
||||||
m_craft_definitions.clear();
|
|
||||||
m_output_craft_definitions.clear();
|
m_output_craft_definitions.clear();
|
||||||
}
|
}
|
||||||
virtual void serialize(std::ostream &os) const
|
virtual void initHashes(IGameDef *gamedef)
|
||||||
{
|
{
|
||||||
writeU8(os, 0); // version
|
// Move the CraftDefs from the unhashed layer into layers higher up.
|
||||||
u16 count = m_craft_definitions.size();
|
for (std::vector<CraftDefinition*>::iterator
|
||||||
writeU16(os, count);
|
i = (m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]).begin();
|
||||||
for(std::vector<CraftDefinition*>::const_iterator
|
i != (m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]).end(); i++) {
|
||||||
i = m_craft_definitions.begin();
|
|
||||||
i != m_craft_definitions.end(); i++){
|
|
||||||
CraftDefinition *def = *i;
|
CraftDefinition *def = *i;
|
||||||
// Serialize wrapped in a string
|
|
||||||
std::ostringstream tmp_os(std::ios::binary);
|
// Initialize and get the definition's hash
|
||||||
def->serialize(tmp_os);
|
def->initHash(gamedef);
|
||||||
os<<serializeString(tmp_os.str());
|
CraftHashType type = def->getHashType();
|
||||||
}
|
u64 hash = def->getHash(type);
|
||||||
}
|
|
||||||
virtual void deSerialize(std::istream &is, IGameDef *gamedef)
|
// Enter the definition
|
||||||
{
|
m_craft_defs[type][hash].push_back(def);
|
||||||
// Clear everything
|
|
||||||
clear();
|
|
||||||
// Deserialize
|
|
||||||
int version = readU8(is);
|
|
||||||
if(version != 0) throw SerializationError(
|
|
||||||
"unsupported CraftDefManager version");
|
|
||||||
u16 count = readU16(is);
|
|
||||||
for(u16 i=0; i<count; i++){
|
|
||||||
// Deserialize a string and grab a CraftDefinition from it
|
|
||||||
std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
|
|
||||||
CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
|
|
||||||
// Register
|
|
||||||
registerCraft(def, gamedef);
|
|
||||||
}
|
}
|
||||||
|
m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].clear();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::vector<CraftDefinition*> m_craft_definitions;
|
//TODO: change both maps to unordered_map when c++11 can be used
|
||||||
|
std::vector<std::map<u64, std::vector<CraftDefinition*> > > m_craft_defs;
|
||||||
std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
|
std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,6 +43,28 @@ enum CraftMethod
|
|||||||
CRAFT_METHOD_FUEL,
|
CRAFT_METHOD_FUEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
The type a hash can be. The earlier a type is mentioned in this enum,
|
||||||
|
the earlier it is tried at crafting, and the less likely is a collision.
|
||||||
|
Changing order causes changes in behaviour, so know what you do.
|
||||||
|
*/
|
||||||
|
enum CraftHashType
|
||||||
|
{
|
||||||
|
// Hashes the normalized names of the recipe's elements.
|
||||||
|
// Only recipes without group usage can be found here,
|
||||||
|
// because groups can't be guessed efficiently.
|
||||||
|
CRAFT_HASH_TYPE_ITEM_NAMES,
|
||||||
|
|
||||||
|
// Counts the non-empty slots.
|
||||||
|
CRAFT_HASH_TYPE_COUNT,
|
||||||
|
|
||||||
|
// This layer both spares an extra variable, and helps to retain (albeit rarely used) functionality. Maps to 0.
|
||||||
|
// Before hashes are "initialized", all hashes reside here, after initialisation, none are.
|
||||||
|
CRAFT_HASH_TYPE_UNHASHED
|
||||||
|
|
||||||
|
};
|
||||||
|
const int craft_hash_type_max = (int) CRAFT_HASH_TYPE_UNHASHED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Input: The contents of the crafting slots, arranged in matrix form
|
Input: The contents of the crafting slots, arranged in matrix form
|
||||||
*/
|
*/
|
||||||
@ -135,8 +157,13 @@ public:
|
|||||||
// Decreases count of every input item
|
// Decreases count of every input item
|
||||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0;
|
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0;
|
||||||
|
|
||||||
virtual std::string dump() const=0;
|
virtual CraftHashType getHashType() const = 0;
|
||||||
|
virtual u64 getHash(CraftHashType type) const = 0;
|
||||||
|
|
||||||
|
// to be called after all mods are loaded, so that we catch all aliases
|
||||||
|
virtual void initHash(IGameDef *gamedef) = 0;
|
||||||
|
|
||||||
|
virtual std::string dump() const=0;
|
||||||
protected:
|
protected:
|
||||||
virtual void serializeBody(std::ostream &os) const=0;
|
virtual void serializeBody(std::ostream &os) const=0;
|
||||||
virtual void deSerializeBody(std::istream &is, int version)=0;
|
virtual void deSerializeBody(std::istream &is, int version)=0;
|
||||||
@ -152,14 +179,15 @@ class CraftDefinitionShaped: public CraftDefinition
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CraftDefinitionShaped():
|
CraftDefinitionShaped():
|
||||||
output(""), width(1), recipe(), replacements()
|
output(""), width(1), recipe(), hash_inited(false), replacements()
|
||||||
{}
|
{}
|
||||||
CraftDefinitionShaped(
|
CraftDefinitionShaped(
|
||||||
const std::string &output_,
|
const std::string &output_,
|
||||||
unsigned int width_,
|
unsigned int width_,
|
||||||
const std::vector<std::string> &recipe_,
|
const std::vector<std::string> &recipe_,
|
||||||
const CraftReplacements &replacements_):
|
const CraftReplacements &replacements_):
|
||||||
output(output_), width(width_), recipe(recipe_), replacements(replacements_)
|
output(output_), width(width_), recipe(recipe_),
|
||||||
|
hash_inited(false), replacements(replacements_)
|
||||||
{}
|
{}
|
||||||
virtual ~CraftDefinitionShaped(){}
|
virtual ~CraftDefinitionShaped(){}
|
||||||
|
|
||||||
@ -169,6 +197,11 @@ public:
|
|||||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||||
|
|
||||||
|
virtual CraftHashType getHashType() const;
|
||||||
|
virtual u64 getHash(CraftHashType type) const;
|
||||||
|
|
||||||
|
virtual void initHash(IGameDef *gamedef);
|
||||||
|
|
||||||
virtual std::string dump() const;
|
virtual std::string dump() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -182,6 +215,10 @@ private:
|
|||||||
unsigned int width;
|
unsigned int width;
|
||||||
// Recipe matrix (itemstrings)
|
// Recipe matrix (itemstrings)
|
||||||
std::vector<std::string> recipe;
|
std::vector<std::string> recipe;
|
||||||
|
// Recipe matrix (item names)
|
||||||
|
std::vector<std::string> recipe_names;
|
||||||
|
// bool indicating if initHash has been called already
|
||||||
|
bool hash_inited;
|
||||||
// Replacement items for decrementInput()
|
// Replacement items for decrementInput()
|
||||||
CraftReplacements replacements;
|
CraftReplacements replacements;
|
||||||
};
|
};
|
||||||
@ -195,13 +232,14 @@ class CraftDefinitionShapeless: public CraftDefinition
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CraftDefinitionShapeless():
|
CraftDefinitionShapeless():
|
||||||
output(""), recipe(), replacements()
|
output(""), recipe(), hash_inited(false), replacements()
|
||||||
{}
|
{}
|
||||||
CraftDefinitionShapeless(
|
CraftDefinitionShapeless(
|
||||||
const std::string &output_,
|
const std::string &output_,
|
||||||
const std::vector<std::string> &recipe_,
|
const std::vector<std::string> &recipe_,
|
||||||
const CraftReplacements &replacements_):
|
const CraftReplacements &replacements_):
|
||||||
output(output_), recipe(recipe_), replacements(replacements_)
|
output(output_), recipe(recipe_),
|
||||||
|
hash_inited(false), replacements(replacements_)
|
||||||
{}
|
{}
|
||||||
virtual ~CraftDefinitionShapeless(){}
|
virtual ~CraftDefinitionShapeless(){}
|
||||||
|
|
||||||
@ -211,6 +249,11 @@ public:
|
|||||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||||
|
|
||||||
|
virtual CraftHashType getHashType() const;
|
||||||
|
virtual u64 getHash(CraftHashType type) const;
|
||||||
|
|
||||||
|
virtual void initHash(IGameDef *gamedef);
|
||||||
|
|
||||||
virtual std::string dump() const;
|
virtual std::string dump() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -222,6 +265,10 @@ private:
|
|||||||
std::string output;
|
std::string output;
|
||||||
// Recipe list (itemstrings)
|
// Recipe list (itemstrings)
|
||||||
std::vector<std::string> recipe;
|
std::vector<std::string> recipe;
|
||||||
|
// Recipe list (item names)
|
||||||
|
std::vector<std::string> recipe_names;
|
||||||
|
// bool indicating if initHash has been called already
|
||||||
|
bool hash_inited;
|
||||||
// Replacement items for decrementInput()
|
// Replacement items for decrementInput()
|
||||||
CraftReplacements replacements;
|
CraftReplacements replacements;
|
||||||
};
|
};
|
||||||
@ -249,6 +296,11 @@ public:
|
|||||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||||
|
|
||||||
|
virtual CraftHashType getHashType() const { return CRAFT_HASH_TYPE_COUNT; }
|
||||||
|
virtual u64 getHash(CraftHashType type) const { return 2; }
|
||||||
|
|
||||||
|
virtual void initHash(IGameDef *gamedef) {}
|
||||||
|
|
||||||
virtual std::string dump() const;
|
virtual std::string dump() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -272,14 +324,15 @@ class CraftDefinitionCooking: public CraftDefinition
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CraftDefinitionCooking():
|
CraftDefinitionCooking():
|
||||||
output(""), recipe(""), cooktime()
|
output(""), recipe(""), hash_inited(false), cooktime()
|
||||||
{}
|
{}
|
||||||
CraftDefinitionCooking(
|
CraftDefinitionCooking(
|
||||||
const std::string &output_,
|
const std::string &output_,
|
||||||
const std::string &recipe_,
|
const std::string &recipe_,
|
||||||
float cooktime_,
|
float cooktime_,
|
||||||
const CraftReplacements &replacements_):
|
const CraftReplacements &replacements_):
|
||||||
output(output_), recipe(recipe_), cooktime(cooktime_), replacements(replacements_)
|
output(output_), recipe(recipe_), hash_inited(false),
|
||||||
|
cooktime(cooktime_), replacements(replacements_)
|
||||||
{}
|
{}
|
||||||
virtual ~CraftDefinitionCooking(){}
|
virtual ~CraftDefinitionCooking(){}
|
||||||
|
|
||||||
@ -289,6 +342,11 @@ public:
|
|||||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||||
|
|
||||||
|
virtual CraftHashType getHashType() const;
|
||||||
|
virtual u64 getHash(CraftHashType type) const;
|
||||||
|
|
||||||
|
virtual void initHash(IGameDef *gamedef);
|
||||||
|
|
||||||
virtual std::string dump() const;
|
virtual std::string dump() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -300,6 +358,10 @@ private:
|
|||||||
std::string output;
|
std::string output;
|
||||||
// Recipe itemstring
|
// Recipe itemstring
|
||||||
std::string recipe;
|
std::string recipe;
|
||||||
|
// Recipe item name
|
||||||
|
std::string recipe_name;
|
||||||
|
// bool indicating if initHash has been called already
|
||||||
|
bool hash_inited;
|
||||||
// Time in seconds
|
// Time in seconds
|
||||||
float cooktime;
|
float cooktime;
|
||||||
// Replacement items for decrementInput()
|
// Replacement items for decrementInput()
|
||||||
@ -314,12 +376,12 @@ class CraftDefinitionFuel: public CraftDefinition
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CraftDefinitionFuel():
|
CraftDefinitionFuel():
|
||||||
recipe(""), burntime()
|
recipe(""), hash_inited(false), burntime()
|
||||||
{}
|
{}
|
||||||
CraftDefinitionFuel(std::string recipe_,
|
CraftDefinitionFuel(std::string recipe_,
|
||||||
float burntime_,
|
float burntime_,
|
||||||
const CraftReplacements &replacements_):
|
const CraftReplacements &replacements_):
|
||||||
recipe(recipe_), burntime(burntime_), replacements(replacements_)
|
recipe(recipe_), hash_inited(false), burntime(burntime_), replacements(replacements_)
|
||||||
{}
|
{}
|
||||||
virtual ~CraftDefinitionFuel(){}
|
virtual ~CraftDefinitionFuel(){}
|
||||||
|
|
||||||
@ -329,6 +391,11 @@ public:
|
|||||||
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
|
||||||
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
|
||||||
|
|
||||||
|
virtual CraftHashType getHashType() const;
|
||||||
|
virtual u64 getHash(CraftHashType type) const;
|
||||||
|
|
||||||
|
virtual void initHash(IGameDef *gamedef);
|
||||||
|
|
||||||
virtual std::string dump() const;
|
virtual std::string dump() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -338,6 +405,10 @@ protected:
|
|||||||
private:
|
private:
|
||||||
// Recipe itemstring
|
// Recipe itemstring
|
||||||
std::string recipe;
|
std::string recipe;
|
||||||
|
// Recipe item name
|
||||||
|
std::string recipe_name;
|
||||||
|
// bool indicating if initHash has been called already
|
||||||
|
bool hash_inited;
|
||||||
// Time in seconds
|
// Time in seconds
|
||||||
float burntime;
|
float burntime;
|
||||||
// Replacement items for decrementInput()
|
// Replacement items for decrementInput()
|
||||||
@ -361,8 +432,6 @@ public:
|
|||||||
|
|
||||||
// Print crafting recipes for debugging
|
// Print crafting recipes for debugging
|
||||||
virtual std::string dump() const=0;
|
virtual std::string dump() const=0;
|
||||||
|
|
||||||
virtual void serialize(std::ostream &os) const=0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IWritableCraftDefManager : public ICraftDefManager
|
class IWritableCraftDefManager : public ICraftDefManager
|
||||||
@ -383,11 +452,12 @@ public:
|
|||||||
// Add a crafting definition.
|
// Add a crafting definition.
|
||||||
// After calling this, the pointer belongs to the manager.
|
// After calling this, the pointer belongs to the manager.
|
||||||
virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;
|
virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;
|
||||||
|
|
||||||
// Delete all crafting definitions
|
// Delete all crafting definitions
|
||||||
virtual void clear()=0;
|
virtual void clear()=0;
|
||||||
|
|
||||||
virtual void serialize(std::ostream &os) const=0;
|
// To be called after all mods are loaded, so that we catch all aliases
|
||||||
virtual void deSerialize(std::istream &is, IGameDef *gamedef) = 0;
|
virtual void initHashes(IGameDef *gamedef) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
IWritableCraftDefManager* createCraftDefManager();
|
IWritableCraftDefManager* createCraftDefManager();
|
||||||
|
@ -334,6 +334,9 @@ Server::Server(
|
|||||||
// Perform pending node name resolutions
|
// Perform pending node name resolutions
|
||||||
m_nodedef->runNodeResolverCallbacks();
|
m_nodedef->runNodeResolverCallbacks();
|
||||||
|
|
||||||
|
// init the recipe hashes to speed up crafting
|
||||||
|
m_craftdef->initHashes(this);
|
||||||
|
|
||||||
// Initialize Environment
|
// Initialize Environment
|
||||||
m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
|
m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user