itemdef.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. Copyright (C) 2013 Kahrl <kahrl@gmx.net>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU Lesser General Public License as published by
  7. the Free Software Foundation; either version 2.1 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public License along
  14. with this program; if not, write to the Free Software Foundation, Inc.,
  15. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. */
  17. #include "itemdef.h"
  18. #include "gamedef.h"
  19. #include "nodedef.h"
  20. #include "tool.h"
  21. #include "inventory.h"
  22. #ifndef SERVER
  23. #include "mapblock_mesh.h"
  24. #include "mesh.h"
  25. #include "tile.h"
  26. #endif
  27. #include "log.h"
  28. #include "main.h" // g_settings
  29. #include "settings.h"
  30. #include "util/serialize.h"
  31. #include "util/container.h"
  32. #include "util/thread.h"
  33. #include <map>
  34. #include <set>
  35. #ifdef __ANDROID__
  36. #include <GLES/gl.h>
  37. #endif
  38. /*
  39. ItemDefinition
  40. */
  41. ItemDefinition::ItemDefinition()
  42. {
  43. resetInitial();
  44. }
  45. ItemDefinition::ItemDefinition(const ItemDefinition &def)
  46. {
  47. resetInitial();
  48. *this = def;
  49. }
  50. ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
  51. {
  52. if(this == &def)
  53. return *this;
  54. reset();
  55. type = def.type;
  56. name = def.name;
  57. description = def.description;
  58. inventory_image = def.inventory_image;
  59. wield_image = def.wield_image;
  60. wield_scale = def.wield_scale;
  61. stack_max = def.stack_max;
  62. usable = def.usable;
  63. liquids_pointable = def.liquids_pointable;
  64. if(def.tool_capabilities)
  65. {
  66. tool_capabilities = new ToolCapabilities(
  67. *def.tool_capabilities);
  68. }
  69. groups = def.groups;
  70. node_placement_prediction = def.node_placement_prediction;
  71. sound_place = def.sound_place;
  72. range = def.range;
  73. return *this;
  74. }
  75. ItemDefinition::~ItemDefinition()
  76. {
  77. reset();
  78. }
  79. void ItemDefinition::resetInitial()
  80. {
  81. // Initialize pointers to NULL so reset() does not delete undefined pointers
  82. tool_capabilities = NULL;
  83. reset();
  84. }
  85. void ItemDefinition::reset()
  86. {
  87. type = ITEM_NONE;
  88. name = "";
  89. description = "";
  90. inventory_image = "";
  91. wield_image = "";
  92. wield_scale = v3f(1.0, 1.0, 1.0);
  93. stack_max = 99;
  94. usable = false;
  95. liquids_pointable = false;
  96. if(tool_capabilities)
  97. {
  98. delete tool_capabilities;
  99. tool_capabilities = NULL;
  100. }
  101. groups.clear();
  102. sound_place = SimpleSoundSpec();
  103. range = -1;
  104. node_placement_prediction = "";
  105. }
  106. void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
  107. {
  108. if(protocol_version <= 17)
  109. writeU8(os, 1); // version
  110. else if(protocol_version <= 20)
  111. writeU8(os, 2); // version
  112. else
  113. writeU8(os, 3); // version
  114. writeU8(os, type);
  115. os<<serializeString(name);
  116. os<<serializeString(description);
  117. os<<serializeString(inventory_image);
  118. os<<serializeString(wield_image);
  119. writeV3F1000(os, wield_scale);
  120. writeS16(os, stack_max);
  121. writeU8(os, usable);
  122. writeU8(os, liquids_pointable);
  123. std::string tool_capabilities_s = "";
  124. if(tool_capabilities){
  125. std::ostringstream tmp_os(std::ios::binary);
  126. tool_capabilities->serialize(tmp_os, protocol_version);
  127. tool_capabilities_s = tmp_os.str();
  128. }
  129. os<<serializeString(tool_capabilities_s);
  130. writeU16(os, groups.size());
  131. for(std::map<std::string, int>::const_iterator
  132. i = groups.begin(); i != groups.end(); i++){
  133. os<<serializeString(i->first);
  134. writeS16(os, i->second);
  135. }
  136. os<<serializeString(node_placement_prediction);
  137. if(protocol_version > 17){
  138. //serializeSimpleSoundSpec(sound_place, os);
  139. os<<serializeString(sound_place.name);
  140. writeF1000(os, sound_place.gain);
  141. }
  142. if(protocol_version > 20){
  143. writeF1000(os, range);
  144. }
  145. }
  146. void ItemDefinition::deSerialize(std::istream &is)
  147. {
  148. // Reset everything
  149. reset();
  150. // Deserialize
  151. int version = readU8(is);
  152. if(version < 1 || version > 3)
  153. throw SerializationError("unsupported ItemDefinition version");
  154. type = (enum ItemType)readU8(is);
  155. name = deSerializeString(is);
  156. description = deSerializeString(is);
  157. inventory_image = deSerializeString(is);
  158. wield_image = deSerializeString(is);
  159. wield_scale = readV3F1000(is);
  160. stack_max = readS16(is);
  161. usable = readU8(is);
  162. liquids_pointable = readU8(is);
  163. std::string tool_capabilities_s = deSerializeString(is);
  164. if(!tool_capabilities_s.empty())
  165. {
  166. std::istringstream tmp_is(tool_capabilities_s, std::ios::binary);
  167. tool_capabilities = new ToolCapabilities;
  168. tool_capabilities->deSerialize(tmp_is);
  169. }
  170. groups.clear();
  171. u32 groups_size = readU16(is);
  172. for(u32 i=0; i<groups_size; i++){
  173. std::string name = deSerializeString(is);
  174. int value = readS16(is);
  175. groups[name] = value;
  176. }
  177. if(version == 1){
  178. // We cant be sure that node_placement_prediction is send in version 1
  179. try{
  180. node_placement_prediction = deSerializeString(is);
  181. }catch(SerializationError &e) {};
  182. // Set the old default sound
  183. sound_place.name = "default_place_node";
  184. sound_place.gain = 0.5;
  185. } else if(version >= 2) {
  186. node_placement_prediction = deSerializeString(is);
  187. //deserializeSimpleSoundSpec(sound_place, is);
  188. sound_place.name = deSerializeString(is);
  189. sound_place.gain = readF1000(is);
  190. }
  191. if(version == 3) {
  192. range = readF1000(is);
  193. }
  194. // If you add anything here, insert it primarily inside the try-catch
  195. // block to not need to increase the version.
  196. try{
  197. }catch(SerializationError &e) {};
  198. }
  199. /*
  200. CItemDefManager
  201. */
  202. // SUGG: Support chains of aliases?
  203. class CItemDefManager: public IWritableItemDefManager
  204. {
  205. #ifndef SERVER
  206. struct ClientCached
  207. {
  208. video::ITexture *inventory_texture;
  209. scene::IMesh *wield_mesh;
  210. ClientCached():
  211. inventory_texture(NULL),
  212. wield_mesh(NULL)
  213. {}
  214. };
  215. #endif
  216. public:
  217. CItemDefManager()
  218. {
  219. #ifndef SERVER
  220. m_main_thread = get_current_thread_id();
  221. #endif
  222. clear();
  223. }
  224. virtual ~CItemDefManager()
  225. {
  226. #ifndef SERVER
  227. const std::list<ClientCached*> &values = m_clientcached.getValues();
  228. for(std::list<ClientCached*>::const_iterator
  229. i = values.begin(); i != values.end(); ++i)
  230. {
  231. ClientCached *cc = *i;
  232. if (cc->wield_mesh)
  233. cc->wield_mesh->drop();
  234. delete cc;
  235. }
  236. #endif
  237. for (std::map<std::string, ItemDefinition*>::iterator iter =
  238. m_item_definitions.begin(); iter != m_item_definitions.end();
  239. iter ++) {
  240. delete iter->second;
  241. }
  242. m_item_definitions.clear();
  243. }
  244. virtual const ItemDefinition& get(const std::string &name_) const
  245. {
  246. // Convert name according to possible alias
  247. std::string name = getAlias(name_);
  248. // Get the definition
  249. std::map<std::string, ItemDefinition*>::const_iterator i;
  250. i = m_item_definitions.find(name);
  251. if(i == m_item_definitions.end())
  252. i = m_item_definitions.find("unknown");
  253. assert(i != m_item_definitions.end());
  254. return *(i->second);
  255. }
  256. virtual std::string getAlias(const std::string &name) const
  257. {
  258. std::map<std::string, std::string>::const_iterator i;
  259. i = m_aliases.find(name);
  260. if(i != m_aliases.end())
  261. return i->second;
  262. return name;
  263. }
  264. virtual std::set<std::string> getAll() const
  265. {
  266. std::set<std::string> result;
  267. for(std::map<std::string, ItemDefinition*>::const_iterator
  268. i = m_item_definitions.begin();
  269. i != m_item_definitions.end(); i++)
  270. {
  271. result.insert(i->first);
  272. }
  273. for(std::map<std::string, std::string>::const_iterator
  274. i = m_aliases.begin();
  275. i != m_aliases.end(); i++)
  276. {
  277. result.insert(i->first);
  278. }
  279. return result;
  280. }
  281. virtual bool isKnown(const std::string &name_) const
  282. {
  283. // Convert name according to possible alias
  284. std::string name = getAlias(name_);
  285. // Get the definition
  286. std::map<std::string, ItemDefinition*>::const_iterator i;
  287. return m_item_definitions.find(name) != m_item_definitions.end();
  288. }
  289. #ifndef SERVER
  290. public:
  291. ClientCached* createClientCachedDirect(const std::string &name,
  292. IGameDef *gamedef) const
  293. {
  294. infostream<<"Lazily creating item texture and mesh for \""
  295. <<name<<"\""<<std::endl;
  296. // This is not thread-safe
  297. assert(get_current_thread_id() == m_main_thread);
  298. // Skip if already in cache
  299. ClientCached *cc = NULL;
  300. m_clientcached.get(name, &cc);
  301. if(cc)
  302. return cc;
  303. ITextureSource *tsrc = gamedef->getTextureSource();
  304. INodeDefManager *nodedef = gamedef->getNodeDefManager();
  305. IrrlichtDevice *device = tsrc->getDevice();
  306. video::IVideoDriver *driver = device->getVideoDriver();
  307. const ItemDefinition *def = &get(name);
  308. // Create new ClientCached
  309. cc = new ClientCached();
  310. bool need_node_mesh = false;
  311. // Create an inventory texture
  312. cc->inventory_texture = NULL;
  313. if(def->inventory_image != "")
  314. {
  315. cc->inventory_texture = tsrc->getTexture(def->inventory_image);
  316. }
  317. else if(def->type == ITEM_NODE)
  318. {
  319. need_node_mesh = true;
  320. }
  321. // Create a wield mesh
  322. assert(cc->wield_mesh == NULL);
  323. if(def->type == ITEM_NODE && def->wield_image == "")
  324. {
  325. need_node_mesh = true;
  326. }
  327. else if(def->wield_image != "" || def->inventory_image != "")
  328. {
  329. // Extrude the wield image into a mesh
  330. std::string imagename;
  331. if(def->wield_image != "")
  332. imagename = def->wield_image;
  333. else
  334. imagename = def->inventory_image;
  335. cc->wield_mesh = createExtrudedMesh(
  336. tsrc->getTexture(imagename),
  337. driver,
  338. def->wield_scale * v3f(40.0, 40.0, 4.0));
  339. if(cc->wield_mesh == NULL)
  340. {
  341. infostream<<"ItemDefManager: WARNING: "
  342. <<"updateTexturesAndMeshes(): "
  343. <<"Unable to create extruded mesh for item "
  344. <<def->name<<std::endl;
  345. }
  346. }
  347. if(need_node_mesh)
  348. {
  349. /*
  350. Get node properties
  351. */
  352. content_t id = nodedef->getId(def->name);
  353. const ContentFeatures &f = nodedef->get(id);
  354. u8 param1 = 0;
  355. if(f.param_type == CPT_LIGHT)
  356. param1 = 0xee;
  357. /*
  358. Make a mesh from the node
  359. */
  360. bool reenable_shaders = false;
  361. if(g_settings->getBool("enable_shaders")){
  362. reenable_shaders = true;
  363. g_settings->setBool("enable_shaders",false);
  364. }
  365. MeshMakeData mesh_make_data(gamedef);
  366. MapNode mesh_make_node(id, param1, 0);
  367. mesh_make_data.fillSingleNode(&mesh_make_node);
  368. MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
  369. scene::IMesh *node_mesh = mapblock_mesh.getMesh();
  370. assert(node_mesh);
  371. video::SColor c(255, 255, 255, 255);
  372. setMeshColor(node_mesh, c);
  373. /*
  374. Scale and translate the mesh so it's a unit cube
  375. centered on the origin
  376. */
  377. scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
  378. translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
  379. /*
  380. Draw node mesh into a render target texture
  381. */
  382. if(cc->inventory_texture == NULL)
  383. {
  384. TextureFromMeshParams params;
  385. params.mesh = node_mesh;
  386. params.dim.set(64, 64);
  387. params.rtt_texture_name = "INVENTORY_"
  388. + def->name + "_RTT";
  389. params.delete_texture_on_shutdown = true;
  390. params.camera_position.set(0, 1.0, -1.5);
  391. params.camera_position.rotateXZBy(45);
  392. params.camera_lookat.set(0, 0, 0);
  393. // Set orthogonal projection
  394. params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
  395. 1.65, 1.65, 0, 100);
  396. params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
  397. params.light_position.set(10, 100, -50);
  398. params.light_color.set(1.0, 0.5, 0.5, 0.5);
  399. params.light_radius = 1000;
  400. #ifdef __ANDROID__
  401. params.camera_position.set(0, -1.0, -1.5);
  402. params.camera_position.rotateXZBy(45);
  403. params.light_position.set(10, -100, -50);
  404. #endif
  405. cc->inventory_texture =
  406. tsrc->generateTextureFromMesh(params);
  407. // render-to-target didn't work
  408. if(cc->inventory_texture == NULL)
  409. {
  410. cc->inventory_texture =
  411. tsrc->getTexture(f.tiledef[0].name);
  412. }
  413. }
  414. /*
  415. Use the node mesh as the wield mesh
  416. */
  417. // Scale to proper wield mesh proportions
  418. scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
  419. * def->wield_scale);
  420. cc->wield_mesh = node_mesh;
  421. cc->wield_mesh->grab();
  422. //no way reference count can be smaller than 2 in this place!
  423. assert(cc->wield_mesh->getReferenceCount() >= 2);
  424. if (reenable_shaders)
  425. g_settings->setBool("enable_shaders",true);
  426. }
  427. // Put in cache
  428. m_clientcached.set(name, cc);
  429. return cc;
  430. }
  431. ClientCached* getClientCached(const std::string &name,
  432. IGameDef *gamedef) const
  433. {
  434. ClientCached *cc = NULL;
  435. m_clientcached.get(name, &cc);
  436. if(cc)
  437. return cc;
  438. if(get_current_thread_id() == m_main_thread)
  439. {
  440. return createClientCachedDirect(name, gamedef);
  441. }
  442. else
  443. {
  444. // We're gonna ask the result to be put into here
  445. static ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
  446. // Throw a request in
  447. m_get_clientcached_queue.add(name, 0, 0, &result_queue);
  448. try{
  449. while(true) {
  450. // Wait result for a second
  451. GetResult<std::string, ClientCached*, u8, u8>
  452. result = result_queue.pop_front(1000);
  453. if (result.key == name) {
  454. return result.item;
  455. }
  456. }
  457. }
  458. catch(ItemNotFoundException &e)
  459. {
  460. errorstream<<"Waiting for clientcached " << name << " timed out."<<std::endl;
  461. return &m_dummy_clientcached;
  462. }
  463. }
  464. }
  465. // Get item inventory texture
  466. virtual video::ITexture* getInventoryTexture(const std::string &name,
  467. IGameDef *gamedef) const
  468. {
  469. ClientCached *cc = getClientCached(name, gamedef);
  470. if(!cc)
  471. return NULL;
  472. return cc->inventory_texture;
  473. }
  474. // Get item wield mesh
  475. virtual scene::IMesh* getWieldMesh(const std::string &name,
  476. IGameDef *gamedef) const
  477. {
  478. ClientCached *cc = getClientCached(name, gamedef);
  479. if(!cc)
  480. return NULL;
  481. return cc->wield_mesh;
  482. }
  483. #endif
  484. void clear()
  485. {
  486. for(std::map<std::string, ItemDefinition*>::const_iterator
  487. i = m_item_definitions.begin();
  488. i != m_item_definitions.end(); i++)
  489. {
  490. delete i->second;
  491. }
  492. m_item_definitions.clear();
  493. m_aliases.clear();
  494. // Add the four builtin items:
  495. // "" is the hand
  496. // "unknown" is returned whenever an undefined item
  497. // is accessed (is also the unknown node)
  498. // "air" is the air node
  499. // "ignore" is the ignore node
  500. ItemDefinition* hand_def = new ItemDefinition;
  501. hand_def->name = "";
  502. hand_def->wield_image = "wieldhand.png";
  503. hand_def->tool_capabilities = new ToolCapabilities;
  504. m_item_definitions.insert(std::make_pair("", hand_def));
  505. ItemDefinition* unknown_def = new ItemDefinition;
  506. unknown_def->type = ITEM_NODE;
  507. unknown_def->name = "unknown";
  508. m_item_definitions.insert(std::make_pair("unknown", unknown_def));
  509. ItemDefinition* air_def = new ItemDefinition;
  510. air_def->type = ITEM_NODE;
  511. air_def->name = "air";
  512. m_item_definitions.insert(std::make_pair("air", air_def));
  513. ItemDefinition* ignore_def = new ItemDefinition;
  514. ignore_def->type = ITEM_NODE;
  515. ignore_def->name = "ignore";
  516. m_item_definitions.insert(std::make_pair("ignore", ignore_def));
  517. }
  518. virtual void registerItem(const ItemDefinition &def)
  519. {
  520. verbosestream<<"ItemDefManager: registering \""<<def.name<<"\""<<std::endl;
  521. // Ensure that the "" item (the hand) always has ToolCapabilities
  522. if(def.name == "")
  523. assert(def.tool_capabilities != NULL);
  524. if(m_item_definitions.count(def.name) == 0)
  525. m_item_definitions[def.name] = new ItemDefinition(def);
  526. else
  527. *(m_item_definitions[def.name]) = def;
  528. // Remove conflicting alias if it exists
  529. bool alias_removed = (m_aliases.erase(def.name) != 0);
  530. if(alias_removed)
  531. infostream<<"ItemDefManager: erased alias "<<def.name
  532. <<" because item was defined"<<std::endl;
  533. }
  534. virtual void registerAlias(const std::string &name,
  535. const std::string &convert_to)
  536. {
  537. if(m_item_definitions.find(name) == m_item_definitions.end())
  538. {
  539. verbosestream<<"ItemDefManager: setting alias "<<name
  540. <<" -> "<<convert_to<<std::endl;
  541. m_aliases[name] = convert_to;
  542. }
  543. }
  544. void serialize(std::ostream &os, u16 protocol_version)
  545. {
  546. writeU8(os, 0); // version
  547. u16 count = m_item_definitions.size();
  548. writeU16(os, count);
  549. for(std::map<std::string, ItemDefinition*>::const_iterator
  550. i = m_item_definitions.begin();
  551. i != m_item_definitions.end(); i++)
  552. {
  553. ItemDefinition *def = i->second;
  554. // Serialize ItemDefinition and write wrapped in a string
  555. std::ostringstream tmp_os(std::ios::binary);
  556. def->serialize(tmp_os, protocol_version);
  557. os<<serializeString(tmp_os.str());
  558. }
  559. writeU16(os, m_aliases.size());
  560. for(std::map<std::string, std::string>::const_iterator
  561. i = m_aliases.begin(); i != m_aliases.end(); i++)
  562. {
  563. os<<serializeString(i->first);
  564. os<<serializeString(i->second);
  565. }
  566. }
  567. void deSerialize(std::istream &is)
  568. {
  569. // Clear everything
  570. clear();
  571. // Deserialize
  572. int version = readU8(is);
  573. if(version != 0)
  574. throw SerializationError("unsupported ItemDefManager version");
  575. u16 count = readU16(is);
  576. for(u16 i=0; i<count; i++)
  577. {
  578. // Deserialize a string and grab an ItemDefinition from it
  579. std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
  580. ItemDefinition def;
  581. def.deSerialize(tmp_is);
  582. // Register
  583. registerItem(def);
  584. }
  585. u16 num_aliases = readU16(is);
  586. for(u16 i=0; i<num_aliases; i++)
  587. {
  588. std::string name = deSerializeString(is);
  589. std::string convert_to = deSerializeString(is);
  590. registerAlias(name, convert_to);
  591. }
  592. }
  593. void processQueue(IGameDef *gamedef)
  594. {
  595. #ifndef SERVER
  596. //NOTE this is only thread safe for ONE consumer thread!
  597. while(!m_get_clientcached_queue.empty())
  598. {
  599. GetRequest<std::string, ClientCached*, u8, u8>
  600. request = m_get_clientcached_queue.pop();
  601. m_get_clientcached_queue.pushResult(request,
  602. createClientCachedDirect(request.key, gamedef));
  603. }
  604. #endif
  605. }
  606. private:
  607. // Key is name
  608. std::map<std::string, ItemDefinition*> m_item_definitions;
  609. // Aliases
  610. std::map<std::string, std::string> m_aliases;
  611. #ifndef SERVER
  612. // The id of the thread that is allowed to use irrlicht directly
  613. threadid_t m_main_thread;
  614. // A reference to this can be returned when nothing is found, to avoid NULLs
  615. mutable ClientCached m_dummy_clientcached;
  616. // Cached textures and meshes
  617. mutable MutexedMap<std::string, ClientCached*> m_clientcached;
  618. // Queued clientcached fetches (to be processed by the main thread)
  619. mutable RequestQueue<std::string, ClientCached*, u8, u8> m_get_clientcached_queue;
  620. #endif
  621. };
  622. IWritableItemDefManager* createItemDefManager()
  623. {
  624. return new CItemDefManager();
  625. }