Crafting definition in scripts

This commit is contained in:
Perttu Ahola 2011-11-17 02:28:46 +02:00
parent 8dd3622c6d
commit 9d5b458479
12 changed files with 634 additions and 57 deletions

View File

@ -321,6 +321,29 @@ minetest.register_node("somenode", {
inventory_image = "treeprop.png"
})
minetest.register_node("TNT", {
tile_images = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png", "tnt_side.png", "tnt_side.png", "tnt_side.png"},
inventory_image = "tnt_side.png"
})
minetest.register_craft({
output = 'ToolItem "STPick" 4',
recipe = {
{'NodeItem "cobble" 1', 'NodeItem "cobble" 1', 'NodeItem "cobble" 1'},
{'', 'CraftItem "Stick"', ''},
{'', 'CraftItem "Stick"', ''},
}
})
minetest.register_craft({
output = 'NodeItem "TNT" 4',
recipe = {
{'NodeItem "wood" 1'},
{'CraftItem "lump_of_coal" 1'},
{'NodeItem "wood" 1'}
}
})
local TNT = {
-- Maybe handle gravity and collision this way? dunno
physical = true,

View File

@ -94,6 +94,7 @@ configure_file(
)
set(common_SRCS
craftdef.cpp
nameidmapping.cpp
tooldef.cpp
nodedef.cpp

View File

@ -2317,6 +2317,11 @@ INodeDefManager* Client::getNodeDefManager()
{
return m_nodedef;
}
ICraftDefManager* Client::getCraftDefManager()
{
return NULL;
//return m_craftdef;
}
ITextureSource* Client::getTextureSource()
{
return m_tsrc;

View File

@ -36,6 +36,7 @@ class IGameDef;
class IWritableTextureSource;
class IWritableToolDefManager;
class IWritableNodeDefManager;
//class IWritableCraftDefManager;
class ClientNotReadyException : public BaseException
{
@ -326,6 +327,7 @@ public:
// IGameDef interface
virtual IToolDefManager* getToolDefManager();
virtual INodeDefManager* getNodeDefManager();
virtual ICraftDefManager* getCraftDefManager();
virtual ITextureSource* getTextureSource();
virtual u16 allocateUnknownNodeId(const std::string &name);

229
src/craftdef.cpp Normal file
View File

@ -0,0 +1,229 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "craftdef.h"
#include "irrlichttypes.h"
#include "log.h"
#include <sstream>
#include "utility.h"
#include "gamedef.h"
#include "inventory.h"
CraftPointerInput::~CraftPointerInput()
{
for(u32 i=0; i<items.size(); i++)
delete items[i];
}
CraftPointerInput createPointerInput(const CraftInput &ci, IGameDef *gamedef)
{
std::vector<InventoryItem*> items;
for(u32 i=0; i<ci.items.size(); i++){
InventoryItem *item = NULL;
if(ci.items[i] != ""){
std::istringstream iss(ci.items[i], std::ios::binary);
item = InventoryItem::deSerialize(iss, gamedef);
}
items.push_back(item);
}
return CraftPointerInput(ci.width, items);
}
CraftInput createInput(const CraftPointerInput &cpi)
{
std::vector<std::string> items;
for(u32 i=0; i<cpi.items.size(); i++){
if(cpi.items[i] == NULL)
items.push_back("");
else{
std::ostringstream oss(std::ios::binary);
cpi.items[i]->serialize(oss);
items.push_back(oss.str());
}
}
return CraftInput(cpi.width, items);
}
std::string CraftInput::dump() const
{
std::ostringstream os(std::ios::binary);
os<<"(width="<<width<<"){";
for(u32 i=0; i<items.size(); i++)
os<<"\""<<items[i]<<"\",";
os<<"}";
return os.str();
}
std::string CraftDefinition::dump() const
{
std::ostringstream os(std::ios::binary);
os<<"{output=\""<<output<<"\", input={";
for(u32 i=0; i<input.items.size(); i++)
os<<"\""<<input.items[i]<<"\",";
os<<"}, (input.width="<<input.width<<")}";
return os.str();
}
void CraftDefinition::serialize(std::ostream &os) const
{
writeU8(os, 0); // version
os<<serializeString(output);
writeU8(os, input.width);
writeU16(os, input.items.size());
for(u32 i=0; i<input.items.size(); i++)
os<<serializeString(input.items[i]);
}
void CraftDefinition::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 0) throw SerializationError(
"unsupported CraftDefinition version");
output = deSerializeString(is);
input.width = readU8(is);
u32 count = readU16(is);
for(u32 i=0; i<count; i++)
input.items.push_back(deSerializeString(is));
}
class CCraftDefManager: public IWritableCraftDefManager
{
public:
virtual ~CCraftDefManager()
{
clear();
}
virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi,
IGameDef *gamedef) const
{
if(input_cpi.width > 3){
errorstream<<"getCraftResult: IGNORING ERROR: "
<<"input_cpi.width > 3"<<std::endl;
return NULL;
}
InventoryItem *input_items[9];
for(u32 y=0; y<3; y++)
for(u32 x=0; x<3; x++)
{
u32 i=y*3+x;
if(x >= input_cpi.width || y >= input_cpi.height())
input_items[i] = NULL;
else
input_items[i] = input_cpi.items[y*input_cpi.width+x];
}
for(core::list<CraftDefinition*>::ConstIterator
i = m_craft_definitions.begin();
i != m_craft_definitions.end(); i++)
{
CraftDefinition *def = *i;
infostream<<"Checking "<<createInput(input_cpi).dump()<<std::endl
<<" against "<<def->input.dump()
<<" (output=\""<<def->output<<"\")"<<std::endl;
CraftPointerInput spec_cpi = createPointerInput(def->input, gamedef);
if(spec_cpi.width > 3){
errorstream<<"getCraftResult: IGNORING ERROR: "
<<"spec_cpi.width > 3"<<std::endl;
continue;
}
InventoryItem *spec_items[9];
for(u32 y=0; y<3; y++)
for(u32 x=0; x<3; x++)
{
u32 i=y*3+x;
if(x >= spec_cpi.width || y >= spec_cpi.height())
spec_items[i] = NULL;
else
spec_items[i] = spec_cpi.items[y*spec_cpi.width+x];
infostream<<"spec_items["<<i<<"] = "<<spec_items[i]<<std::endl;
}
bool match = checkItemCombination(input_items, spec_items);
if(match){
std::istringstream iss(def->output, std::ios::binary);
return InventoryItem::deSerialize(iss, gamedef);
}
}
return NULL;
}
virtual void registerCraft(const CraftDefinition &def)
{
infostream<<"registerCraft: registering craft definition: "
<<def.dump()<<std::endl;
if(def.input.width > 3 || def.input.height() > 3){
errorstream<<"registerCraft: input size is larger than 3x3,"
<<" ignoring"<<std::endl;
return;
}
m_craft_definitions.push_back(new CraftDefinition(def));
}
virtual void clear()
{
for(core::list<CraftDefinition*>::Iterator
i = m_craft_definitions.begin();
i != m_craft_definitions.end(); i++){
delete *i;
}
m_craft_definitions.clear();
}
virtual void serialize(std::ostream &os)
{
writeU8(os, 0); // version
u16 count = m_craft_definitions.size();
writeU16(os, count);
for(core::list<CraftDefinition*>::Iterator
i = m_craft_definitions.begin();
i != m_craft_definitions.end(); i++){
CraftDefinition *def = *i;
// Serialize wrapped in a string
std::ostringstream tmp_os(std::ios::binary);
def->serialize(tmp_os);
os<<serializeString(tmp_os.str());
}
}
virtual void deSerialize(std::istream &is)
{
// 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;
def.deSerialize(tmp_is);
// Register
registerCraft(def);
}
}
private:
core::list<CraftDefinition*> m_craft_definitions;
};
IWritableCraftDefManager* createCraftDefManager()
{
return new CCraftDefManager();
}

111
src/craftdef.h Normal file
View File

@ -0,0 +1,111 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CRAFTDEF_HEADER
#define CRAFTDEF_HEADER
#include <string>
#include <iostream>
#include <vector>
class IGameDef;
class InventoryItem;
struct CraftPointerInput
{
unsigned int width;
std::vector<InventoryItem*> items;
CraftPointerInput(unsigned int width_, const std::vector<InventoryItem*> &items_):
width(width_),
items(items_)
{}
CraftPointerInput():
width(0)
{}
~CraftPointerInput();
unsigned int height() const{
return (items.size() + width - 1) / width;
}
};
struct CraftInput
{
unsigned int width;
std::vector<std::string> items;
CraftInput(unsigned int width_, const std::vector<std::string> &items_):
width(width_),
items(items_)
{}
CraftInput():
width(0)
{}
unsigned int height() const{
return (items.size() + width - 1) / width;
}
std::string dump() const;
};
struct CraftDefinition
{
std::string output;
CraftInput input;
CraftDefinition(){}
CraftDefinition(const std::string &output_, unsigned int width_,
const std::vector<std::string> &input_):
output(output_),
input(width_, input_)
{}
std::string dump() const;
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
};
class ICraftDefManager
{
public:
ICraftDefManager(){}
virtual ~ICraftDefManager(){}
virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi,
IGameDef *gamedef) const=0;
virtual void serialize(std::ostream &os)=0;
};
class IWritableCraftDefManager : public ICraftDefManager
{
public:
IWritableCraftDefManager(){}
virtual ~IWritableCraftDefManager(){}
virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi,
IGameDef *gamedef) const=0;
virtual void registerCraft(const CraftDefinition &def)=0;
virtual void clear()=0;
virtual void serialize(std::ostream &os)=0;
virtual void deSerialize(std::istream &is)=0;
};
IWritableCraftDefManager* createCraftDefManager();
#endif

View File

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IToolDefManager;
class INodeDefManager;
class ICraftDefManager;
//class IItemDefManager; //TODO
// Mineral too?
class ITextureSource;
@ -40,6 +41,7 @@ public:
// Thus, first they are set up and then they are only read.
virtual IToolDefManager* getToolDefManager()=0;
virtual INodeDefManager* getNodeDefManager()=0;
virtual ICraftDefManager* getCraftDefManager()=0;
//virtual IItemDefManager* getItemDefManager()=0;
// This is always thread-safe, but referencing the irrlicht texture
@ -52,6 +54,7 @@ public:
// Shorthands
IToolDefManager* tdef(){return getToolDefManager();}
INodeDefManager* ndef(){return getNodeDefManager();}
ICraftDefManager* cdef(){return getCraftDefManager();}
ITextureSource* tsrc(){return getTextureSource();}
};

View File

@ -1153,5 +1153,85 @@ bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *sp
return true;
}
bool checkItemCombination(const InventoryItem * const * items,
const InventoryItem * const * specs)
{
u16 items_min_x = 100;
u16 items_max_x = 100;
u16 items_min_y = 100;
u16 items_max_y = 100;
for(u16 y=0; y<3; y++)
for(u16 x=0; x<3; x++)
{
if(items[y*3 + x] == NULL)
continue;
if(items_min_x == 100 || x < items_min_x)
items_min_x = x;
if(items_min_y == 100 || y < items_min_y)
items_min_y = y;
if(items_max_x == 100 || x > items_max_x)
items_max_x = x;
if(items_max_y == 100 || y > items_max_y)
items_max_y = y;
}
// No items at all, just return false
if(items_min_x == 100)
return false;
u16 items_w = items_max_x - items_min_x + 1;
u16 items_h = items_max_y - items_min_y + 1;
u16 specs_min_x = 100;
u16 specs_max_x = 100;
u16 specs_min_y = 100;
u16 specs_max_y = 100;
for(u16 y=0; y<3; y++)
for(u16 x=0; x<3; x++)
{
if(specs[y*3 + x] == NULL)
continue;
if(specs_min_x == 100 || x < specs_min_x)
specs_min_x = x;
if(specs_min_y == 100 || y < specs_min_y)
specs_min_y = y;
if(specs_max_x == 100 || x > specs_max_x)
specs_max_x = x;
if(specs_max_y == 100 || y > specs_max_y)
specs_max_y = y;
}
// No specs at all, just return false
if(specs_min_x == 100)
return false;
u16 specs_w = specs_max_x - specs_min_x + 1;
u16 specs_h = specs_max_y - specs_min_y + 1;
// Different sizes
if(items_w != specs_w || items_h != specs_h)
return false;
for(u16 y=0; y<specs_h; y++)
for(u16 x=0; x<specs_w; x++)
{
u16 items_x = items_min_x + x;
u16 items_y = items_min_y + y;
u16 specs_x = specs_min_x + x;
u16 specs_y = specs_min_y + y;
const InventoryItem *item = items[items_y * 3 + items_x];
const InventoryItem *spec = specs[specs_y * 3 + specs_x];
if(item == NULL && spec == NULL)
continue;
if(item == NULL && spec != NULL)
return false;
if(item != NULL && spec == NULL)
return false;
if(!spec->isSubsetOf(item))
return false;
}
return true;
}
//END

View File

@ -70,25 +70,26 @@ public:
Quantity methods
*/
// Shall return true if the item can be add()ed to the other
// Return true if the item can be add()ed to the other
virtual bool addableTo(const InventoryItem *other) const
{
return false;
}
{ return false; }
// Return true if the other item contains this item
virtual bool isSubsetOf(const InventoryItem *other) const
{ return false; }
// Remove the other item from this one if possible and return true
// Return false if not possible
virtual bool removeOther(const InventoryItem *other)
{ return false; }
u16 getCount() const
{
return m_count;
}
{ return m_count; }
void setCount(u16 count)
{
m_count = count;
}
{ m_count = count; }
// This should return something else for stackable items
virtual u16 freeSpace() const
{
return 0;
}
{ return 0; }
void add(u16 count)
{
assert(m_count + count <= QUANTITY_ITEM_MAX_COUNT);
@ -168,6 +169,24 @@ public:
return false;
return true;
}
virtual bool isSubsetOf(const InventoryItem *other) const
{
if(std::string(other->getName()) != "MaterialItem")
return false;
MaterialItem *m = (MaterialItem*)other;
if(m->m_nodename != m_nodename)
return false;
return m_count <= m->m_count;
}
virtual bool removeOther(const InventoryItem *other)
{
if(!other->isSubsetOf(this))
return false;
MaterialItem *m = (MaterialItem*)other;
m_count += m->m_count;
return true;
}
u16 freeSpace() const
{
if(m_count > QUANTITY_ITEM_MAX_COUNT)
@ -245,6 +264,24 @@ public:
return false;
return true;
}
virtual bool isSubsetOf(const InventoryItem *other) const
{
if(std::string(other->getName()) != "CraftItem")
return false;
CraftItem *m = (CraftItem*)other;
if(m->m_subname != m_subname)
return false;
return m_count <= m->m_count;
}
virtual bool removeOther(const InventoryItem *other)
{
if(!other->isSubsetOf(this))
return false;
CraftItem *m = (CraftItem*)other;
m_count += m->m_count;
return true;
}
u16 freeSpace() const
{
if(m_count > QUANTITY_ITEM_MAX_COUNT)
@ -312,23 +349,26 @@ public:
std::string getText()
{
return "";
/*std::ostringstream os;
u16 f = 4;
u16 d = 65535/f;
u16 i;
for(i=0; i<(65535-m_wear)/d; i++)
os<<'X';
for(; i<f; i++)
os<<'-';
return os.str();*/
/*std::ostringstream os;
os<<m_toolname;
os<<" ";
os<<(m_wear/655);
return os.str();*/
}
virtual bool isSubsetOf(const InventoryItem *other) const
{
if(std::string(other->getName()) != "ToolItem")
return false;
ToolItem *m = (ToolItem*)other;
if(m->m_toolname != m_toolname)
return false;
return m_wear <= m->m_wear;
}
virtual bool removeOther(const InventoryItem *other)
{
if(!other->isSubsetOf(this))
return false;
ToolItem *m = (ToolItem*)other;
m_wear -= m->m_wear;
return true;
}
/*
Special methods
*/
@ -591,5 +631,12 @@ struct ItemSpec
*/
bool checkItemCombination(const InventoryItem * const*items, const ItemSpec *specs);
/*
items: a pointer to an array of 9 pointers to items
specs: a pointer to an array of 9 pointers to items
*/
bool checkItemCombination(const InventoryItem * const * items,
const InventoryItem * const * specs);
#endif

View File

@ -37,6 +37,7 @@ extern "C" {
#include "content_sao.h" // For LuaEntitySAO
#include "tooldef.h"
#include "nodedef.h"
#include "craftdef.h"
/*
TODO:
@ -205,26 +206,6 @@ static int l_register_globalstep(lua_State *L)
return 0; /* number of results */
}
#if 0
// Clear all registered tools
// deregister_tools()
static int l_deregister_tools(lua_State *L)
{
infostream<<"deregister_tools"<<std::endl;
// Get server from registry
lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
Server *server = (Server*)lua_touserdata(L, -1);
// And get the writable tool definition manager from the server
IWritableToolDefManager *tooldef =
server->getWritableToolDefManager();
tooldef->clear();
return 0; /* number of results */
}
#endif
// register_tool(name, {lots of stuff})
static int l_register_tool(lua_State *L)
{
@ -336,16 +317,90 @@ static int l_register_node(lua_State *L)
f.tname_inventory = lua_tostring(L, -1);
lua_pop(L, 1);
// TODO: Replace with actual parameter reading
// Temporarily set some sane parameters to allow digging
f.material.diggability = DIGGABLE_NORMAL;
f.material.weight = 0;
f.material.crackiness = 0;
f.material.crumbliness = 0;
f.material.cuttability = 0;
nodedef->set(name, f);
return 0; /* number of results */
}
// register_craft({output=item, recipe={{item00,item10},{item01,item11}})
static int l_register_craft(lua_State *L)
{
infostream<<"register_craft"<<std::endl;
luaL_checktype(L, 1, LUA_TTABLE);
int table0 = 1;
// Get server from registry
lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
Server *server = (Server*)lua_touserdata(L, -1);
// And get the writable craft definition manager from the server
IWritableCraftDefManager *craftdef =
server->getWritableCraftDefManager();
std::string output;
int width = 0;
std::vector<std::string> input;
lua_getfield(L, table0, "output");
luaL_checktype(L, -1, LUA_TSTRING);
if(lua_isstring(L, -1))
output = lua_tostring(L, -1);
lua_pop(L, 1);
lua_getfield(L, table0, "recipe");
luaL_checktype(L, -1, LUA_TTABLE);
if(lua_istable(L, -1)){
int table1 = lua_gettop(L);
lua_pushnil(L);
int rowcount = 0;
while(lua_next(L, table1) != 0){
int colcount = 0;
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TTABLE);
if(lua_istable(L, -1)){
int table2 = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table2) != 0){
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
input.push_back(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
colcount++;
}
}
if(rowcount == 0){
width = colcount;
} else {
if(colcount != width){
script_error(L, "error: %s\n", "Invalid crafting recipe");
}
}
// removes value, keeps key for next iteration
lua_pop(L, 1);
rowcount++;
}
}
lua_pop(L, 1);
CraftDefinition def(output, width, input);
craftdef->registerCraft(def);
return 0; /* number of results */
}
static const struct luaL_Reg minetest_f [] = {
{"register_entity", l_register_entity},
{"register_globalstep", l_register_globalstep},
//{"deregister_tools", l_deregister_tools},
{"register_tool", l_register_tool},
{"register_node", l_register_node},
{"register_craft", l_register_craft},
{NULL, NULL}
};

View File

@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "scriptapi.h"
#include "nodedef.h"
#include "tooldef.h"
#include "craftdef.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -988,6 +989,7 @@ Server::Server(
m_lua(NULL),
m_toolmgr(createToolDefManager()),
m_nodedef(createNodeDefManager()),
m_craftdef(createCraftDefManager()),
m_thread(this),
m_emergethread(this),
m_time_counter(0),
@ -4332,14 +4334,19 @@ void Server::UpdateCrafting(u16 peer_id)
}
if(clist && rlist && player->craftresult_is_preview)
{
InventoryItem *items[9];
for(u16 i=0; i<9; i++)
{
items[i] = clist->getItem(i);
}
// Get result of crafting grid
InventoryItem *result = craft_get_result(items, this);
std::vector<InventoryItem*> items;
for(u16 i=0; i<9; i++){
if(clist->getItem(i) == NULL)
items.push_back(NULL);
else
items.push_back(clist->getItem(i)->clone());
}
CraftPointerInput cpi(3, items);
InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
//InventoryItem *result = craft_get_result(items, this);
if(result)
rlist->addItem(result);
}
@ -4424,6 +4431,10 @@ INodeDefManager* Server::getNodeDefManager()
{
return m_nodedef;
}
ICraftDefManager* Server::getCraftDefManager()
{
return m_craftdef;
}
ITextureSource* Server::getTextureSource()
{
return NULL;
@ -4441,6 +4452,10 @@ IWritableNodeDefManager* Server::getWritableNodeDefManager()
{
return m_nodedef;
}
IWritableCraftDefManager* Server::getWritableCraftDefManager()
{
return m_craftdef;
}
v3f findSpawnPos(ServerMap &map)
{

View File

@ -35,6 +35,7 @@ struct LuaState;
typedef struct lua_State lua_State;
class IWritableToolDefManager;
class IWritableNodeDefManager;
class IWritableCraftDefManager;
/*
Some random functions
@ -490,11 +491,13 @@ public:
// Under envlock
virtual IToolDefManager* getToolDefManager();
virtual INodeDefManager* getNodeDefManager();
virtual ICraftDefManager* getCraftDefManager();
virtual ITextureSource* getTextureSource();
virtual u16 allocateUnknownNodeId(const std::string &name);
IWritableToolDefManager* getWritableToolDefManager();
IWritableNodeDefManager* getWritableNodeDefManager();
IWritableCraftDefManager* getWritableCraftDefManager();
private:
@ -635,6 +638,9 @@ private:
// Node definition manager
IWritableNodeDefManager *m_nodedef;
// Craft definition manager
IWritableCraftDefManager *m_craftdef;
/*
Threads
*/