itemdef.cpp 16 KB

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