itemdef.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  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/tile.h"
  26. #include "client/client.h"
  27. #endif
  28. #include "log.h"
  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. /*
  36. ItemDefinition
  37. */
  38. ItemDefinition::ItemDefinition()
  39. {
  40. resetInitial();
  41. }
  42. ItemDefinition::ItemDefinition(const ItemDefinition &def)
  43. {
  44. resetInitial();
  45. *this = def;
  46. }
  47. ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
  48. {
  49. if(this == &def)
  50. return *this;
  51. reset();
  52. type = def.type;
  53. name = def.name;
  54. description = def.description;
  55. short_description = def.short_description;
  56. inventory_image = def.inventory_image;
  57. inventory_overlay = def.inventory_overlay;
  58. wield_image = def.wield_image;
  59. wield_overlay = def.wield_overlay;
  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. tool_capabilities = new ToolCapabilities(*def.tool_capabilities);
  66. groups = def.groups;
  67. node_placement_prediction = def.node_placement_prediction;
  68. place_param2 = def.place_param2;
  69. sound_place = def.sound_place;
  70. sound_place_failed = def.sound_place_failed;
  71. sound_use = def.sound_use;
  72. sound_use_air = def.sound_use_air;
  73. range = def.range;
  74. palette_image = def.palette_image;
  75. color = def.color;
  76. return *this;
  77. }
  78. ItemDefinition::~ItemDefinition()
  79. {
  80. reset();
  81. }
  82. void ItemDefinition::resetInitial()
  83. {
  84. // Initialize pointers to NULL so reset() does not delete undefined pointers
  85. tool_capabilities = NULL;
  86. reset();
  87. }
  88. void ItemDefinition::reset()
  89. {
  90. type = ITEM_NONE;
  91. name.clear();
  92. description.clear();
  93. short_description.clear();
  94. inventory_image.clear();
  95. inventory_overlay.clear();
  96. wield_image.clear();
  97. wield_overlay.clear();
  98. palette_image.clear();
  99. color = video::SColor(0xFFFFFFFF);
  100. wield_scale = v3f(1.0, 1.0, 1.0);
  101. stack_max = 99;
  102. usable = false;
  103. liquids_pointable = false;
  104. delete tool_capabilities;
  105. tool_capabilities = NULL;
  106. groups.clear();
  107. sound_place = SimpleSoundSpec();
  108. sound_place_failed = SimpleSoundSpec();
  109. sound_use = SimpleSoundSpec();
  110. sound_use_air = SimpleSoundSpec();
  111. range = -1;
  112. node_placement_prediction.clear();
  113. place_param2 = 0;
  114. }
  115. void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
  116. {
  117. // protocol_version >= 37
  118. u8 version = 6;
  119. writeU8(os, version);
  120. writeU8(os, type);
  121. os << serializeString16(name);
  122. os << serializeString16(description);
  123. os << serializeString16(inventory_image);
  124. os << serializeString16(wield_image);
  125. writeV3F32(os, wield_scale);
  126. writeS16(os, stack_max);
  127. writeU8(os, usable);
  128. writeU8(os, liquids_pointable);
  129. std::string tool_capabilities_s;
  130. if (tool_capabilities) {
  131. std::ostringstream tmp_os(std::ios::binary);
  132. tool_capabilities->serialize(tmp_os, protocol_version);
  133. tool_capabilities_s = tmp_os.str();
  134. }
  135. os << serializeString16(tool_capabilities_s);
  136. writeU16(os, groups.size());
  137. for (const auto &group : groups) {
  138. os << serializeString16(group.first);
  139. writeS16(os, group.second);
  140. }
  141. os << serializeString16(node_placement_prediction);
  142. // Version from ContentFeatures::serialize to keep in sync
  143. sound_place.serialize(os, protocol_version);
  144. sound_place_failed.serialize(os, protocol_version);
  145. writeF32(os, range);
  146. os << serializeString16(palette_image);
  147. writeARGB8(os, color);
  148. os << serializeString16(inventory_overlay);
  149. os << serializeString16(wield_overlay);
  150. os << serializeString16(short_description);
  151. os << place_param2;
  152. sound_use.serialize(os, protocol_version);
  153. sound_use_air.serialize(os, protocol_version);
  154. }
  155. void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
  156. {
  157. // Reset everything
  158. reset();
  159. // Deserialize
  160. int version = readU8(is);
  161. if (version < 6)
  162. throw SerializationError("unsupported ItemDefinition version");
  163. type = (enum ItemType)readU8(is);
  164. name = deSerializeString16(is);
  165. description = deSerializeString16(is);
  166. inventory_image = deSerializeString16(is);
  167. wield_image = deSerializeString16(is);
  168. wield_scale = readV3F32(is);
  169. stack_max = readS16(is);
  170. usable = readU8(is);
  171. liquids_pointable = readU8(is);
  172. std::string tool_capabilities_s = deSerializeString16(is);
  173. if (!tool_capabilities_s.empty()) {
  174. std::istringstream tmp_is(tool_capabilities_s, std::ios::binary);
  175. tool_capabilities = new ToolCapabilities;
  176. tool_capabilities->deSerialize(tmp_is);
  177. }
  178. groups.clear();
  179. u32 groups_size = readU16(is);
  180. for(u32 i=0; i<groups_size; i++){
  181. std::string name = deSerializeString16(is);
  182. int value = readS16(is);
  183. groups[name] = value;
  184. }
  185. node_placement_prediction = deSerializeString16(is);
  186. sound_place.deSerialize(is, protocol_version);
  187. sound_place_failed.deSerialize(is, protocol_version);
  188. range = readF32(is);
  189. palette_image = deSerializeString16(is);
  190. color = readARGB8(is);
  191. inventory_overlay = deSerializeString16(is);
  192. wield_overlay = deSerializeString16(is);
  193. // If you add anything here, insert it inside the try-catch
  194. // block to not need to increase the version.
  195. try {
  196. short_description = deSerializeString16(is);
  197. place_param2 = readU8(is); // 0 if missing
  198. sound_use.deSerialize(is, protocol_version);
  199. sound_use_air.deSerialize(is, protocol_version);
  200. } catch(SerializationError &e) {};
  201. }
  202. /*
  203. CItemDefManager
  204. */
  205. // SUGG: Support chains of aliases?
  206. class CItemDefManager: public IWritableItemDefManager
  207. {
  208. #ifndef SERVER
  209. struct ClientCached
  210. {
  211. video::ITexture *inventory_texture;
  212. ItemMesh wield_mesh;
  213. Palette *palette;
  214. ClientCached():
  215. inventory_texture(NULL),
  216. palette(NULL)
  217. {}
  218. };
  219. #endif
  220. public:
  221. CItemDefManager()
  222. {
  223. #ifndef SERVER
  224. m_main_thread = std::this_thread::get_id();
  225. #endif
  226. clear();
  227. }
  228. virtual ~CItemDefManager()
  229. {
  230. #ifndef SERVER
  231. const std::vector<ClientCached*> &values = m_clientcached.getValues();
  232. for (ClientCached *cc : values) {
  233. if (cc->wield_mesh.mesh)
  234. cc->wield_mesh.mesh->drop();
  235. delete cc;
  236. }
  237. #endif
  238. for (auto &item_definition : m_item_definitions) {
  239. delete item_definition.second;
  240. }
  241. m_item_definitions.clear();
  242. }
  243. virtual const ItemDefinition& get(const std::string &name_) const
  244. {
  245. // Convert name according to possible alias
  246. std::string name = getAlias(name_);
  247. // Get the definition
  248. auto i = m_item_definitions.find(name);
  249. if (i == m_item_definitions.cend())
  250. i = m_item_definitions.find("unknown");
  251. assert(i != m_item_definitions.cend());
  252. return *(i->second);
  253. }
  254. virtual const std::string &getAlias(const std::string &name) const
  255. {
  256. auto it = m_aliases.find(name);
  257. if (it != m_aliases.cend())
  258. return it->second;
  259. return name;
  260. }
  261. virtual void getAll(std::set<std::string> &result) const
  262. {
  263. result.clear();
  264. for (const auto &item_definition : m_item_definitions) {
  265. result.insert(item_definition.first);
  266. }
  267. for (const auto &alias : m_aliases) {
  268. result.insert(alias.first);
  269. }
  270. }
  271. virtual bool isKnown(const std::string &name_) const
  272. {
  273. // Convert name according to possible alias
  274. std::string name = getAlias(name_);
  275. // Get the definition
  276. return m_item_definitions.find(name) != m_item_definitions.cend();
  277. }
  278. #ifndef SERVER
  279. public:
  280. ClientCached* createClientCachedDirect(const std::string &name,
  281. Client *client) const
  282. {
  283. infostream<<"Lazily creating item texture and mesh for \""
  284. <<name<<"\""<<std::endl;
  285. // This is not thread-safe
  286. sanity_check(std::this_thread::get_id() == m_main_thread);
  287. // Skip if already in cache
  288. ClientCached *cc = NULL;
  289. m_clientcached.get(name, &cc);
  290. if(cc)
  291. return cc;
  292. ITextureSource *tsrc = client->getTextureSource();
  293. const ItemDefinition &def = get(name);
  294. // Create new ClientCached
  295. cc = new ClientCached();
  296. // Create an inventory texture
  297. cc->inventory_texture = NULL;
  298. if (!def.inventory_image.empty())
  299. cc->inventory_texture = tsrc->getTexture(def.inventory_image);
  300. ItemStack item = ItemStack();
  301. item.name = def.name;
  302. getItemMesh(client, item, &(cc->wield_mesh));
  303. cc->palette = tsrc->getPalette(def.palette_image);
  304. // Put in cache
  305. m_clientcached.set(name, cc);
  306. return cc;
  307. }
  308. ClientCached* getClientCached(const std::string &name,
  309. Client *client) const
  310. {
  311. ClientCached *cc = NULL;
  312. m_clientcached.get(name, &cc);
  313. if (cc)
  314. return cc;
  315. if (std::this_thread::get_id() == m_main_thread) {
  316. return createClientCachedDirect(name, client);
  317. }
  318. // We're gonna ask the result to be put into here
  319. static ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
  320. // Throw a request in
  321. m_get_clientcached_queue.add(name, 0, 0, &result_queue);
  322. try {
  323. while(true) {
  324. // Wait result for a second
  325. GetResult<std::string, ClientCached*, u8, u8>
  326. result = result_queue.pop_front(1000);
  327. if (result.key == name) {
  328. return result.item;
  329. }
  330. }
  331. } catch(ItemNotFoundException &e) {
  332. errorstream << "Waiting for clientcached " << name
  333. << " timed out." << std::endl;
  334. return &m_dummy_clientcached;
  335. }
  336. }
  337. // Get item inventory texture
  338. virtual video::ITexture* getInventoryTexture(const std::string &name,
  339. Client *client) const
  340. {
  341. ClientCached *cc = getClientCached(name, client);
  342. if(!cc)
  343. return NULL;
  344. return cc->inventory_texture;
  345. }
  346. // Get item wield mesh
  347. virtual ItemMesh* getWieldMesh(const std::string &name,
  348. Client *client) const
  349. {
  350. ClientCached *cc = getClientCached(name, client);
  351. if(!cc)
  352. return NULL;
  353. return &(cc->wield_mesh);
  354. }
  355. // Get item palette
  356. virtual Palette* getPalette(const std::string &name,
  357. Client *client) const
  358. {
  359. ClientCached *cc = getClientCached(name, client);
  360. if(!cc)
  361. return NULL;
  362. return cc->palette;
  363. }
  364. virtual video::SColor getItemstackColor(const ItemStack &stack,
  365. Client *client) const
  366. {
  367. // Look for direct color definition
  368. const std::string &colorstring = stack.metadata.getString("color", 0);
  369. video::SColor directcolor;
  370. if (!colorstring.empty() && parseColorString(colorstring, directcolor, true))
  371. return directcolor;
  372. // See if there is a palette
  373. Palette *palette = getPalette(stack.name, client);
  374. const std::string &index = stack.metadata.getString("palette_index", 0);
  375. if (palette && !index.empty())
  376. return (*palette)[mystoi(index, 0, 255)];
  377. // Fallback color
  378. return get(stack.name).color;
  379. }
  380. #endif
  381. void applyTextureOverrides(const std::vector<TextureOverride> &overrides)
  382. {
  383. infostream << "ItemDefManager::applyTextureOverrides(): Applying "
  384. "overrides to textures" << std::endl;
  385. for (const TextureOverride& texture_override : overrides) {
  386. if (m_item_definitions.find(texture_override.id) == m_item_definitions.end()) {
  387. continue; // Ignore unknown item
  388. }
  389. ItemDefinition* itemdef = m_item_definitions[texture_override.id];
  390. if (texture_override.hasTarget(OverrideTarget::INVENTORY))
  391. itemdef->inventory_image = texture_override.texture;
  392. if (texture_override.hasTarget(OverrideTarget::WIELD))
  393. itemdef->wield_image = texture_override.texture;
  394. }
  395. }
  396. void clear()
  397. {
  398. for (auto &i : m_item_definitions)
  399. {
  400. delete i.second;
  401. }
  402. m_item_definitions.clear();
  403. m_aliases.clear();
  404. // Add the four builtin items:
  405. // "" is the hand
  406. // "unknown" is returned whenever an undefined item
  407. // is accessed (is also the unknown node)
  408. // "air" is the air node
  409. // "ignore" is the ignore node
  410. ItemDefinition* hand_def = new ItemDefinition;
  411. hand_def->name.clear();
  412. hand_def->wield_image = "wieldhand.png";
  413. hand_def->tool_capabilities = new ToolCapabilities;
  414. m_item_definitions.insert(std::make_pair("", hand_def));
  415. ItemDefinition* unknown_def = new ItemDefinition;
  416. unknown_def->type = ITEM_NODE;
  417. unknown_def->name = "unknown";
  418. m_item_definitions.insert(std::make_pair("unknown", unknown_def));
  419. ItemDefinition* air_def = new ItemDefinition;
  420. air_def->type = ITEM_NODE;
  421. air_def->name = "air";
  422. m_item_definitions.insert(std::make_pair("air", air_def));
  423. ItemDefinition* ignore_def = new ItemDefinition;
  424. ignore_def->type = ITEM_NODE;
  425. ignore_def->name = "ignore";
  426. m_item_definitions.insert(std::make_pair("ignore", ignore_def));
  427. }
  428. virtual void registerItem(const ItemDefinition &def)
  429. {
  430. TRACESTREAM(<< "ItemDefManager: registering " << def.name << std::endl);
  431. // Ensure that the "" item (the hand) always has ToolCapabilities
  432. if (def.name.empty())
  433. FATAL_ERROR_IF(!def.tool_capabilities, "Hand does not have ToolCapabilities");
  434. if(m_item_definitions.count(def.name) == 0)
  435. m_item_definitions[def.name] = new ItemDefinition(def);
  436. else
  437. *(m_item_definitions[def.name]) = def;
  438. // Remove conflicting alias if it exists
  439. bool alias_removed = (m_aliases.erase(def.name) != 0);
  440. if(alias_removed)
  441. infostream<<"ItemDefManager: erased alias "<<def.name
  442. <<" because item was defined"<<std::endl;
  443. }
  444. virtual void unregisterItem(const std::string &name)
  445. {
  446. verbosestream<<"ItemDefManager: unregistering \""<<name<<"\""<<std::endl;
  447. delete m_item_definitions[name];
  448. m_item_definitions.erase(name);
  449. }
  450. virtual void registerAlias(const std::string &name,
  451. const std::string &convert_to)
  452. {
  453. if (m_item_definitions.find(name) == m_item_definitions.end()) {
  454. TRACESTREAM(<< "ItemDefManager: setting alias " << name
  455. << " -> " << convert_to << std::endl);
  456. m_aliases[name] = convert_to;
  457. }
  458. }
  459. void serialize(std::ostream &os, u16 protocol_version)
  460. {
  461. writeU8(os, 0); // version
  462. u16 count = m_item_definitions.size();
  463. writeU16(os, count);
  464. for (const auto &it : m_item_definitions) {
  465. ItemDefinition *def = it.second;
  466. // Serialize ItemDefinition and write wrapped in a string
  467. std::ostringstream tmp_os(std::ios::binary);
  468. def->serialize(tmp_os, protocol_version);
  469. os << serializeString16(tmp_os.str());
  470. }
  471. writeU16(os, m_aliases.size());
  472. for (const auto &it : m_aliases) {
  473. os << serializeString16(it.first);
  474. os << serializeString16(it.second);
  475. }
  476. }
  477. void deSerialize(std::istream &is, u16 protocol_version)
  478. {
  479. // Clear everything
  480. clear();
  481. if(readU8(is) != 0)
  482. throw SerializationError("unsupported ItemDefManager version");
  483. u16 count = readU16(is);
  484. for(u16 i=0; i<count; i++)
  485. {
  486. // Deserialize a string and grab an ItemDefinition from it
  487. std::istringstream tmp_is(deSerializeString16(is), std::ios::binary);
  488. ItemDefinition def;
  489. def.deSerialize(tmp_is, protocol_version);
  490. // Register
  491. registerItem(def);
  492. }
  493. u16 num_aliases = readU16(is);
  494. for(u16 i=0; i<num_aliases; i++)
  495. {
  496. std::string name = deSerializeString16(is);
  497. std::string convert_to = deSerializeString16(is);
  498. registerAlias(name, convert_to);
  499. }
  500. }
  501. void processQueue(IGameDef *gamedef)
  502. {
  503. #ifndef SERVER
  504. //NOTE this is only thread safe for ONE consumer thread!
  505. while(!m_get_clientcached_queue.empty())
  506. {
  507. GetRequest<std::string, ClientCached*, u8, u8>
  508. request = m_get_clientcached_queue.pop();
  509. m_get_clientcached_queue.pushResult(request,
  510. createClientCachedDirect(request.key, (Client *)gamedef));
  511. }
  512. #endif
  513. }
  514. private:
  515. // Key is name
  516. std::map<std::string, ItemDefinition*> m_item_definitions;
  517. // Aliases
  518. StringMap m_aliases;
  519. #ifndef SERVER
  520. // The id of the thread that is allowed to use irrlicht directly
  521. std::thread::id m_main_thread;
  522. // A reference to this can be returned when nothing is found, to avoid NULLs
  523. mutable ClientCached m_dummy_clientcached;
  524. // Cached textures and meshes
  525. mutable MutexedMap<std::string, ClientCached*> m_clientcached;
  526. // Queued clientcached fetches (to be processed by the main thread)
  527. mutable RequestQueue<std::string, ClientCached*, u8, u8> m_get_clientcached_queue;
  528. #endif
  529. };
  530. IWritableItemDefManager* createItemDefManager()
  531. {
  532. return new CItemDefManager();
  533. }