itemdef.cpp 16 KB

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