itemdef.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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 "nodedef.h"
  19. #include "tool.h"
  20. #include "inventory.h"
  21. #ifndef SERVER
  22. #include "client/mapblock_mesh.h"
  23. #include "client/mesh.h"
  24. #include "client/wieldmesh.h"
  25. #include "client/client.h"
  26. #endif
  27. #include "log.h"
  28. #include "settings.h"
  29. #include "util/serialize.h"
  30. #include "util/container.h"
  31. #include "util/thread.h"
  32. #include "util/pointedthing.h"
  33. #include <map>
  34. #include <set>
  35. TouchInteraction::TouchInteraction()
  36. {
  37. pointed_nothing = TouchInteractionMode_USER;
  38. pointed_node = TouchInteractionMode_USER;
  39. pointed_object = TouchInteractionMode_USER;
  40. }
  41. TouchInteractionMode TouchInteraction::getMode(PointedThingType pointed_type) const
  42. {
  43. TouchInteractionMode result;
  44. switch (pointed_type) {
  45. case POINTEDTHING_NOTHING:
  46. result = pointed_nothing;
  47. break;
  48. case POINTEDTHING_NODE:
  49. result = pointed_node;
  50. break;
  51. case POINTEDTHING_OBJECT:
  52. result = pointed_object;
  53. break;
  54. default:
  55. FATAL_ERROR("Invalid PointedThingType given to TouchInteraction::getMode");
  56. }
  57. if (result == TouchInteractionMode_USER) {
  58. if (pointed_type == POINTEDTHING_OBJECT)
  59. result = g_settings->get("touch_punch_gesture") == "long_tap" ?
  60. LONG_DIG_SHORT_PLACE : SHORT_DIG_LONG_PLACE;
  61. else
  62. result = LONG_DIG_SHORT_PLACE;
  63. }
  64. return result;
  65. }
  66. void TouchInteraction::serialize(std::ostream &os) const
  67. {
  68. writeU8(os, pointed_nothing);
  69. writeU8(os, pointed_node);
  70. writeU8(os, pointed_object);
  71. }
  72. void TouchInteraction::deSerialize(std::istream &is)
  73. {
  74. u8 tmp = readU8(is);
  75. if (is.eof())
  76. throw SerializationError("");
  77. if (tmp < TouchInteractionMode_END)
  78. pointed_nothing = (TouchInteractionMode)tmp;
  79. tmp = readU8(is);
  80. if (is.eof())
  81. throw SerializationError("");
  82. if (tmp < TouchInteractionMode_END)
  83. pointed_node = (TouchInteractionMode)tmp;
  84. tmp = readU8(is);
  85. if (is.eof())
  86. throw SerializationError("");
  87. if (tmp < TouchInteractionMode_END)
  88. pointed_object = (TouchInteractionMode)tmp;
  89. }
  90. /*
  91. ItemDefinition
  92. */
  93. ItemDefinition::ItemDefinition()
  94. {
  95. resetInitial();
  96. }
  97. ItemDefinition::ItemDefinition(const ItemDefinition &def)
  98. {
  99. resetInitial();
  100. *this = def;
  101. }
  102. ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
  103. {
  104. if(this == &def)
  105. return *this;
  106. reset();
  107. type = def.type;
  108. name = def.name;
  109. description = def.description;
  110. short_description = def.short_description;
  111. inventory_image = def.inventory_image;
  112. inventory_overlay = def.inventory_overlay;
  113. wield_image = def.wield_image;
  114. wield_overlay = def.wield_overlay;
  115. wield_scale = def.wield_scale;
  116. stack_max = def.stack_max;
  117. usable = def.usable;
  118. liquids_pointable = def.liquids_pointable;
  119. pointabilities = def.pointabilities;
  120. if (def.tool_capabilities)
  121. tool_capabilities = new ToolCapabilities(*def.tool_capabilities);
  122. wear_bar_params = def.wear_bar_params;
  123. groups = def.groups;
  124. node_placement_prediction = def.node_placement_prediction;
  125. place_param2 = def.place_param2;
  126. wallmounted_rotate_vertical = def.wallmounted_rotate_vertical;
  127. sound_place = def.sound_place;
  128. sound_place_failed = def.sound_place_failed;
  129. sound_use = def.sound_use;
  130. sound_use_air = def.sound_use_air;
  131. range = def.range;
  132. palette_image = def.palette_image;
  133. color = def.color;
  134. touch_interaction = def.touch_interaction;
  135. return *this;
  136. }
  137. ItemDefinition::~ItemDefinition()
  138. {
  139. reset();
  140. }
  141. void ItemDefinition::resetInitial()
  142. {
  143. // Initialize pointers to NULL so reset() does not delete undefined pointers
  144. tool_capabilities = NULL;
  145. wear_bar_params = std::nullopt;
  146. reset();
  147. }
  148. void ItemDefinition::reset()
  149. {
  150. type = ITEM_NONE;
  151. name.clear();
  152. description.clear();
  153. short_description.clear();
  154. inventory_image.clear();
  155. inventory_overlay.clear();
  156. wield_image.clear();
  157. wield_overlay.clear();
  158. palette_image.clear();
  159. color = video::SColor(0xFFFFFFFF);
  160. wield_scale = v3f(1.0, 1.0, 1.0);
  161. stack_max = 99;
  162. usable = false;
  163. liquids_pointable = false;
  164. pointabilities = std::nullopt;
  165. delete tool_capabilities;
  166. tool_capabilities = NULL;
  167. wear_bar_params.reset();
  168. groups.clear();
  169. sound_place = SoundSpec();
  170. sound_place_failed = SoundSpec();
  171. sound_use = SoundSpec();
  172. sound_use_air = SoundSpec();
  173. range = -1;
  174. node_placement_prediction.clear();
  175. place_param2.reset();
  176. wallmounted_rotate_vertical = false;
  177. touch_interaction = TouchInteraction();
  178. }
  179. void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
  180. {
  181. // protocol_version >= 37
  182. u8 version = 6;
  183. writeU8(os, version);
  184. writeU8(os, type);
  185. os << serializeString16(name);
  186. os << serializeString16(description);
  187. os << serializeString16(inventory_image);
  188. os << serializeString16(wield_image);
  189. writeV3F32(os, wield_scale);
  190. writeS16(os, stack_max);
  191. writeU8(os, usable);
  192. writeU8(os, liquids_pointable);
  193. std::string tool_capabilities_s;
  194. if (tool_capabilities) {
  195. std::ostringstream tmp_os(std::ios::binary);
  196. tool_capabilities->serialize(tmp_os, protocol_version);
  197. tool_capabilities_s = tmp_os.str();
  198. }
  199. os << serializeString16(tool_capabilities_s);
  200. writeU16(os, groups.size());
  201. for (const auto &group : groups) {
  202. os << serializeString16(group.first);
  203. writeS16(os, group.second);
  204. }
  205. os << serializeString16(node_placement_prediction);
  206. // Version from ContentFeatures::serialize to keep in sync
  207. sound_place.serializeSimple(os, protocol_version);
  208. sound_place_failed.serializeSimple(os, protocol_version);
  209. writeF32(os, range);
  210. os << serializeString16(palette_image);
  211. writeARGB8(os, color);
  212. os << serializeString16(inventory_overlay);
  213. os << serializeString16(wield_overlay);
  214. os << serializeString16(short_description);
  215. if (protocol_version <= 43) {
  216. // Uncertainity whether 0 is the specified prediction or means disabled
  217. if (place_param2)
  218. os << *place_param2;
  219. else
  220. os << (u8)0;
  221. }
  222. sound_use.serializeSimple(os, protocol_version);
  223. sound_use_air.serializeSimple(os, protocol_version);
  224. os << (u8)place_param2.has_value(); // protocol_version >= 43
  225. if (place_param2)
  226. os << *place_param2;
  227. writeU8(os, wallmounted_rotate_vertical);
  228. touch_interaction.serialize(os);
  229. std::string pointabilities_s;
  230. if (pointabilities) {
  231. std::ostringstream tmp_os(std::ios::binary);
  232. pointabilities->serialize(tmp_os);
  233. pointabilities_s = tmp_os.str();
  234. }
  235. os << serializeString16(pointabilities_s);
  236. if (wear_bar_params.has_value()) {
  237. writeU8(os, 1);
  238. wear_bar_params->serialize(os);
  239. } else {
  240. writeU8(os, 0);
  241. }
  242. }
  243. void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
  244. {
  245. // Reset everything
  246. reset();
  247. // Deserialize
  248. int version = readU8(is);
  249. if (version < 6)
  250. throw SerializationError("unsupported ItemDefinition version");
  251. type = static_cast<ItemType>(readU8(is));
  252. if (type >= ItemType_END) {
  253. type = ITEM_NONE;
  254. }
  255. name = deSerializeString16(is);
  256. description = deSerializeString16(is);
  257. inventory_image = deSerializeString16(is);
  258. wield_image = deSerializeString16(is);
  259. wield_scale = readV3F32(is);
  260. stack_max = readS16(is);
  261. usable = readU8(is);
  262. liquids_pointable = readU8(is);
  263. std::string tool_capabilities_s = deSerializeString16(is);
  264. if (!tool_capabilities_s.empty()) {
  265. std::istringstream tmp_is(tool_capabilities_s, std::ios::binary);
  266. tool_capabilities = new ToolCapabilities;
  267. tool_capabilities->deSerialize(tmp_is);
  268. }
  269. groups.clear();
  270. u32 groups_size = readU16(is);
  271. for(u32 i=0; i<groups_size; i++){
  272. std::string name = deSerializeString16(is);
  273. int value = readS16(is);
  274. groups[name] = value;
  275. }
  276. node_placement_prediction = deSerializeString16(is);
  277. sound_place.deSerializeSimple(is, protocol_version);
  278. sound_place_failed.deSerializeSimple(is, protocol_version);
  279. range = readF32(is);
  280. palette_image = deSerializeString16(is);
  281. color = readARGB8(is);
  282. inventory_overlay = deSerializeString16(is);
  283. wield_overlay = deSerializeString16(is);
  284. // If you add anything here, insert it inside the try-catch
  285. // block to not need to increase the version.
  286. try {
  287. short_description = deSerializeString16(is);
  288. if (protocol_version <= 43) {
  289. place_param2 = readU8(is);
  290. // assume disabled prediction
  291. if (place_param2 == 0)
  292. place_param2.reset();
  293. }
  294. sound_use.deSerializeSimple(is, protocol_version);
  295. sound_use_air.deSerializeSimple(is, protocol_version);
  296. if (is.eof())
  297. throw SerializationError("");
  298. if (readU8(is)) // protocol_version >= 43
  299. place_param2 = readU8(is);
  300. wallmounted_rotate_vertical = readU8(is); // 0 if missing
  301. touch_interaction.deSerialize(is);
  302. std::string pointabilities_s = deSerializeString16(is);
  303. if (!pointabilities_s.empty()) {
  304. std::istringstream tmp_is(pointabilities_s, std::ios::binary);
  305. pointabilities = std::make_optional<Pointabilities>();
  306. pointabilities->deSerialize(tmp_is);
  307. }
  308. if (readU8(is)) {
  309. wear_bar_params = WearBarParams::deserialize(is);
  310. }
  311. } catch(SerializationError &e) {};
  312. }
  313. /*
  314. CItemDefManager
  315. */
  316. // SUGG: Support chains of aliases?
  317. class CItemDefManager: public IWritableItemDefManager
  318. {
  319. #ifndef SERVER
  320. struct ClientCached
  321. {
  322. video::ITexture *inventory_texture;
  323. ItemMesh wield_mesh;
  324. Palette *palette;
  325. ClientCached():
  326. inventory_texture(NULL),
  327. palette(NULL)
  328. {}
  329. ~ClientCached() {
  330. if (wield_mesh.mesh)
  331. wield_mesh.mesh->drop();
  332. }
  333. DISABLE_CLASS_COPY(ClientCached);
  334. };
  335. #endif
  336. public:
  337. CItemDefManager()
  338. {
  339. #ifndef SERVER
  340. m_main_thread = std::this_thread::get_id();
  341. #endif
  342. clear();
  343. }
  344. virtual ~CItemDefManager()
  345. {
  346. for (auto &item_definition : m_item_definitions) {
  347. delete item_definition.second;
  348. }
  349. m_item_definitions.clear();
  350. }
  351. virtual const ItemDefinition& get(const std::string &name_) const
  352. {
  353. // Convert name according to possible alias
  354. std::string name = getAlias(name_);
  355. // Get the definition
  356. auto i = m_item_definitions.find(name);
  357. if (i == m_item_definitions.cend())
  358. i = m_item_definitions.find("unknown");
  359. assert(i != m_item_definitions.cend());
  360. return *(i->second);
  361. }
  362. virtual const std::string &getAlias(const std::string &name) const
  363. {
  364. auto it = m_aliases.find(name);
  365. if (it != m_aliases.cend())
  366. return it->second;
  367. return name;
  368. }
  369. virtual void getAll(std::set<std::string> &result) const
  370. {
  371. result.clear();
  372. for (const auto &item_definition : m_item_definitions) {
  373. result.insert(item_definition.first);
  374. }
  375. for (const auto &alias : m_aliases) {
  376. result.insert(alias.first);
  377. }
  378. }
  379. virtual bool isKnown(const std::string &name_) const
  380. {
  381. // Convert name according to possible alias
  382. std::string name = getAlias(name_);
  383. // Get the definition
  384. return m_item_definitions.find(name) != m_item_definitions.cend();
  385. }
  386. #ifndef SERVER
  387. public:
  388. ClientCached* createClientCachedDirect(const ItemStack &item, Client *client) const
  389. {
  390. // This is not thread-safe
  391. sanity_check(std::this_thread::get_id() == m_main_thread);
  392. const ItemDefinition &def = item.getDefinition(this);
  393. std::string inventory_image = item.getInventoryImage(this);
  394. std::string inventory_overlay = item.getInventoryOverlay(this);
  395. std::string cache_key = def.name;
  396. if (!inventory_image.empty())
  397. cache_key += "/" + inventory_image;
  398. if (!inventory_overlay.empty())
  399. cache_key += ":" + inventory_overlay;
  400. // Skip if already in cache
  401. auto it = m_clientcached.find(cache_key);
  402. if (it != m_clientcached.end())
  403. return it->second.get();
  404. infostream << "Lazily creating item texture and mesh for \""
  405. << cache_key << "\"" << std::endl;
  406. ITextureSource *tsrc = client->getTextureSource();
  407. // Create new ClientCached
  408. auto cc = std::make_unique<ClientCached>();
  409. cc->inventory_texture = NULL;
  410. if (!inventory_image.empty())
  411. cc->inventory_texture = tsrc->getTexture(inventory_image);
  412. getItemMesh(client, item, &(cc->wield_mesh));
  413. cc->palette = tsrc->getPalette(def.palette_image);
  414. // Put in cache
  415. ClientCached *ptr = cc.get();
  416. m_clientcached[cache_key] = std::move(cc);
  417. return ptr;
  418. }
  419. // Get item inventory texture
  420. virtual video::ITexture* getInventoryTexture(const ItemStack &item,
  421. Client *client) const
  422. {
  423. ClientCached *cc = createClientCachedDirect(item, client);
  424. if (!cc)
  425. return nullptr;
  426. return cc->inventory_texture;
  427. }
  428. // Get item wield mesh
  429. virtual ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const
  430. {
  431. ClientCached *cc = createClientCachedDirect(item, client);
  432. if (!cc)
  433. return nullptr;
  434. return &(cc->wield_mesh);
  435. }
  436. // Get item palette
  437. virtual Palette* getPalette(const ItemStack &item, Client *client) const
  438. {
  439. ClientCached *cc = createClientCachedDirect(item, client);
  440. if (!cc)
  441. return nullptr;
  442. return cc->palette;
  443. }
  444. virtual video::SColor getItemstackColor(const ItemStack &stack,
  445. Client *client) const
  446. {
  447. // Look for direct color definition
  448. const std::string &colorstring = stack.metadata.getString("color", 0);
  449. video::SColor directcolor;
  450. if (!colorstring.empty() && parseColorString(colorstring, directcolor, true))
  451. return directcolor;
  452. // See if there is a palette
  453. Palette *palette = getPalette(stack, client);
  454. const std::string &index = stack.metadata.getString("palette_index", 0);
  455. if (palette && !index.empty())
  456. return (*palette)[mystoi(index, 0, 255)];
  457. // Fallback color
  458. return get(stack.name).color;
  459. }
  460. #endif
  461. void applyTextureOverrides(const std::vector<TextureOverride> &overrides)
  462. {
  463. infostream << "ItemDefManager::applyTextureOverrides(): Applying "
  464. "overrides to textures" << std::endl;
  465. for (const TextureOverride& texture_override : overrides) {
  466. if (m_item_definitions.find(texture_override.id) == m_item_definitions.end()) {
  467. continue; // Ignore unknown item
  468. }
  469. ItemDefinition* itemdef = m_item_definitions[texture_override.id];
  470. if (texture_override.hasTarget(OverrideTarget::INVENTORY))
  471. itemdef->inventory_image = texture_override.texture;
  472. if (texture_override.hasTarget(OverrideTarget::WIELD))
  473. itemdef->wield_image = texture_override.texture;
  474. }
  475. }
  476. void clear()
  477. {
  478. for (auto &i : m_item_definitions)
  479. {
  480. delete i.second;
  481. }
  482. m_item_definitions.clear();
  483. m_aliases.clear();
  484. // Add the four builtin items:
  485. // "" is the hand
  486. // "unknown" is returned whenever an undefined item
  487. // is accessed (is also the unknown node)
  488. // "air" is the air node
  489. // "ignore" is the ignore node
  490. ItemDefinition* hand_def = new ItemDefinition;
  491. hand_def->name.clear();
  492. hand_def->wield_image = "wieldhand.png";
  493. hand_def->tool_capabilities = new ToolCapabilities;
  494. m_item_definitions.insert(std::make_pair("", hand_def));
  495. ItemDefinition* unknown_def = new ItemDefinition;
  496. unknown_def->type = ITEM_NODE;
  497. unknown_def->name = "unknown";
  498. m_item_definitions.insert(std::make_pair("unknown", unknown_def));
  499. ItemDefinition* air_def = new ItemDefinition;
  500. air_def->type = ITEM_NODE;
  501. air_def->name = "air";
  502. m_item_definitions.insert(std::make_pair("air", air_def));
  503. ItemDefinition* ignore_def = new ItemDefinition;
  504. ignore_def->type = ITEM_NODE;
  505. ignore_def->name = "ignore";
  506. m_item_definitions.insert(std::make_pair("ignore", ignore_def));
  507. }
  508. virtual void registerItem(const ItemDefinition &def)
  509. {
  510. TRACESTREAM(<< "ItemDefManager: registering " << def.name << std::endl);
  511. // Ensure that the "" item (the hand) always has ToolCapabilities
  512. if (def.name.empty())
  513. FATAL_ERROR_IF(!def.tool_capabilities, "Hand does not have ToolCapabilities");
  514. if(m_item_definitions.count(def.name) == 0)
  515. m_item_definitions[def.name] = new ItemDefinition(def);
  516. else
  517. *(m_item_definitions[def.name]) = def;
  518. // Remove conflicting alias if it exists
  519. bool alias_removed = (m_aliases.erase(def.name) != 0);
  520. if(alias_removed)
  521. infostream<<"ItemDefManager: erased alias "<<def.name
  522. <<" because item was defined"<<std::endl;
  523. }
  524. virtual void unregisterItem(const std::string &name)
  525. {
  526. verbosestream<<"ItemDefManager: unregistering \""<<name<<"\""<<std::endl;
  527. delete m_item_definitions[name];
  528. m_item_definitions.erase(name);
  529. }
  530. virtual void registerAlias(const std::string &name,
  531. const std::string &convert_to)
  532. {
  533. if (m_item_definitions.find(name) == m_item_definitions.end()) {
  534. TRACESTREAM(<< "ItemDefManager: setting alias " << name
  535. << " -> " << convert_to << std::endl);
  536. m_aliases[name] = convert_to;
  537. }
  538. }
  539. void serialize(std::ostream &os, u16 protocol_version)
  540. {
  541. writeU8(os, 0); // version
  542. u16 count = m_item_definitions.size();
  543. writeU16(os, count);
  544. for (const auto &it : m_item_definitions) {
  545. ItemDefinition *def = it.second;
  546. // Serialize ItemDefinition and write wrapped in a string
  547. std::ostringstream tmp_os(std::ios::binary);
  548. def->serialize(tmp_os, protocol_version);
  549. os << serializeString16(tmp_os.str());
  550. }
  551. writeU16(os, m_aliases.size());
  552. for (const auto &it : m_aliases) {
  553. os << serializeString16(it.first);
  554. os << serializeString16(it.second);
  555. }
  556. }
  557. void deSerialize(std::istream &is, u16 protocol_version)
  558. {
  559. // Clear everything
  560. clear();
  561. if(readU8(is) != 0)
  562. throw SerializationError("unsupported ItemDefManager version");
  563. u16 count = readU16(is);
  564. for(u16 i=0; i<count; i++)
  565. {
  566. // Deserialize a string and grab an ItemDefinition from it
  567. std::istringstream tmp_is(deSerializeString16(is), std::ios::binary);
  568. ItemDefinition def;
  569. def.deSerialize(tmp_is, protocol_version);
  570. // Register
  571. registerItem(def);
  572. }
  573. u16 num_aliases = readU16(is);
  574. for(u16 i=0; i<num_aliases; i++)
  575. {
  576. std::string name = deSerializeString16(is);
  577. std::string convert_to = deSerializeString16(is);
  578. registerAlias(name, convert_to);
  579. }
  580. }
  581. private:
  582. // Key is name
  583. std::map<std::string, ItemDefinition*> m_item_definitions;
  584. // Aliases
  585. StringMap m_aliases;
  586. #ifndef SERVER
  587. // The id of the thread that is allowed to use irrlicht directly
  588. std::thread::id m_main_thread;
  589. // Cached textures and meshes
  590. mutable std::unordered_map<std::string, std::unique_ptr<ClientCached>> m_clientcached;
  591. #endif
  592. };
  593. IWritableItemDefManager* createItemDefManager()
  594. {
  595. return new CItemDefManager();
  596. }