itemdef.cpp 15 KB

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