3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013 Kahrl <kahrl@gmx.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "inventory.h"
27 #include "client/mapblock_mesh.h"
28 #include "client/mesh.h"
29 #include "client/wieldmesh.h"
30 #include "client/tile.h"
31 #include "client/client.h"
35 #include "util/serialize.h"
36 #include "util/container.h"
37 #include "util/thread.h"
44 ItemDefinition::ItemDefinition()
49 ItemDefinition::ItemDefinition(const ItemDefinition
&def
)
55 ItemDefinition
& ItemDefinition::operator=(const ItemDefinition
&def
)
64 description
= def
.description
;
65 short_description
= def
.short_description
;
66 inventory_image
= def
.inventory_image
;
67 inventory_overlay
= def
.inventory_overlay
;
68 wield_image
= def
.wield_image
;
69 wield_overlay
= def
.wield_overlay
;
70 wield_scale
= def
.wield_scale
;
71 stack_max
= def
.stack_max
;
73 liquids_pointable
= def
.liquids_pointable
;
74 if(def
.tool_capabilities
)
76 tool_capabilities
= new ToolCapabilities(
77 *def
.tool_capabilities
);
80 node_placement_prediction
= def
.node_placement_prediction
;
81 sound_place
= def
.sound_place
;
82 sound_place_failed
= def
.sound_place_failed
;
84 palette_image
= def
.palette_image
;
89 ItemDefinition::~ItemDefinition()
94 void ItemDefinition::resetInitial()
96 // Initialize pointers to NULL so reset() does not delete undefined pointers
97 tool_capabilities
= NULL
;
101 void ItemDefinition::reset()
106 short_description
= "";
107 inventory_image
= "";
108 inventory_overlay
= "";
112 color
= video::SColor(0xFFFFFFFF);
113 wield_scale
= v3f(1.0, 1.0, 1.0);
116 liquids_pointable
= false;
117 delete tool_capabilities
;
118 tool_capabilities
= NULL
;
120 sound_place
= SimpleSoundSpec();
121 sound_place_failed
= SimpleSoundSpec();
124 node_placement_prediction
= "";
127 void ItemDefinition::serialize(std::ostream
&os
, u16 protocol_version
) const
129 // protocol_version >= 37
131 writeU8(os
, version
);
133 os
<< serializeString16(name
);
134 os
<< serializeString16(description
);
135 os
<< serializeString16(inventory_image
);
136 os
<< serializeString16(wield_image
);
137 writeV3F32(os
, wield_scale
);
138 writeS16(os
, stack_max
);
140 writeU8(os
, liquids_pointable
);
142 std::string tool_capabilities_s
;
143 if (tool_capabilities
) {
144 std::ostringstream
tmp_os(std::ios::binary
);
145 tool_capabilities
->serialize(tmp_os
, protocol_version
);
146 tool_capabilities_s
= tmp_os
.str();
148 os
<< serializeString16(tool_capabilities_s
);
150 writeU16(os
, groups
.size());
151 for (const auto &group
: groups
) {
152 os
<< serializeString16(group
.first
);
153 writeS16(os
, group
.second
);
156 os
<< serializeString16(node_placement_prediction
);
158 // Version from ContentFeatures::serialize to keep in sync
159 sound_place
.serialize(os
, CONTENTFEATURES_VERSION
);
160 sound_place_failed
.serialize(os
, CONTENTFEATURES_VERSION
);
163 os
<< serializeString16(palette_image
);
164 writeARGB8(os
, color
);
165 os
<< serializeString16(inventory_overlay
);
166 os
<< serializeString16(wield_overlay
);
168 os
<< serializeString16(short_description
);
171 void ItemDefinition::deSerialize(std::istream
&is
)
177 int version
= readU8(is
);
179 throw SerializationError("unsupported ItemDefinition version");
181 type
= (enum ItemType
)readU8(is
);
182 name
= deSerializeString16(is
);
183 description
= deSerializeString16(is
);
184 inventory_image
= deSerializeString16(is
);
185 wield_image
= deSerializeString16(is
);
186 wield_scale
= readV3F32(is
);
187 stack_max
= readS16(is
);
189 liquids_pointable
= readU8(is
);
191 std::string tool_capabilities_s
= deSerializeString16(is
);
192 if (!tool_capabilities_s
.empty()) {
193 std::istringstream
tmp_is(tool_capabilities_s
, std::ios::binary
);
194 tool_capabilities
= new ToolCapabilities
;
195 tool_capabilities
->deSerialize(tmp_is
);
199 u32 groups_size
= readU16(is
);
200 for(u32 i
=0; i
<groups_size
; i
++){
201 std::string name
= deSerializeString16(is
);
202 int value
= readS16(is
);
203 groups
[name
] = value
;
206 node_placement_prediction
= deSerializeString16(is
);
208 // Version from ContentFeatures::serialize to keep in sync
209 sound_place
.deSerialize(is
, CONTENTFEATURES_VERSION
);
210 sound_place_failed
.deSerialize(is
, CONTENTFEATURES_VERSION
);
213 palette_image
= deSerializeString16(is
);
214 color
= readARGB8(is
);
215 inventory_overlay
= deSerializeString16(is
);
216 wield_overlay
= deSerializeString16(is
);
218 // If you add anything here, insert it primarily inside the try-catch
219 // block to not need to increase the version.
221 short_description
= deSerializeString16(is
);
222 } catch(SerializationError
&e
) {};
230 // SUGG: Support chains of aliases?
232 class CItemDefManager
: public IWritableItemDefManager
237 video::ITexture
*inventory_texture
;
242 inventory_texture(NULL
),
253 m_main_thread
= std::this_thread::get_id();
257 virtual ~CItemDefManager()
260 const std::vector
<ClientCached
*> &values
= m_clientcached
.getValues();
261 for (ClientCached
*cc
: values
) {
262 if (cc
->wield_mesh
.mesh
)
263 cc
->wield_mesh
.mesh
->drop();
268 for (auto &item_definition
: m_item_definitions
) {
269 delete item_definition
.second
;
271 m_item_definitions
.clear();
273 virtual const ItemDefinition
& get(const std::string
&name_
) const
275 // Convert name according to possible alias
276 std::string name
= getAlias(name_
);
277 // Get the definition
278 auto i
= m_item_definitions
.find(name
);
279 if (i
== m_item_definitions
.cend())
280 i
= m_item_definitions
.find("unknown");
281 assert(i
!= m_item_definitions
.cend());
284 virtual const std::string
&getAlias(const std::string
&name
) const
286 auto it
= m_aliases
.find(name
);
287 if (it
!= m_aliases
.cend())
291 virtual void getAll(std::set
<std::string
> &result
) const
294 for (const auto &item_definition
: m_item_definitions
) {
295 result
.insert(item_definition
.first
);
298 for (const auto &alias
: m_aliases
) {
299 result
.insert(alias
.first
);
302 virtual bool isKnown(const std::string
&name_
) const
304 // Convert name according to possible alias
305 std::string name
= getAlias(name_
);
306 // Get the definition
307 return m_item_definitions
.find(name
) != m_item_definitions
.cend();
311 ClientCached
* createClientCachedDirect(const std::string
&name
,
312 Client
*client
) const
314 infostream
<<"Lazily creating item texture and mesh for \""
315 <<name
<<"\""<<std::endl
;
317 // This is not thread-safe
318 sanity_check(std::this_thread::get_id() == m_main_thread
);
320 // Skip if already in cache
321 ClientCached
*cc
= NULL
;
322 m_clientcached
.get(name
, &cc
);
326 ITextureSource
*tsrc
= client
->getTextureSource();
327 const ItemDefinition
&def
= get(name
);
329 // Create new ClientCached
330 cc
= new ClientCached();
332 // Create an inventory texture
333 cc
->inventory_texture
= NULL
;
334 if (!def
.inventory_image
.empty())
335 cc
->inventory_texture
= tsrc
->getTexture(def
.inventory_image
);
337 ItemStack item
= ItemStack();
338 item
.name
= def
.name
;
340 getItemMesh(client
, item
, &(cc
->wield_mesh
));
342 cc
->palette
= tsrc
->getPalette(def
.palette_image
);
345 m_clientcached
.set(name
, cc
);
349 ClientCached
* getClientCached(const std::string
&name
,
350 Client
*client
) const
352 ClientCached
*cc
= NULL
;
353 m_clientcached
.get(name
, &cc
);
357 if (std::this_thread::get_id() == m_main_thread
) {
358 return createClientCachedDirect(name
, client
);
361 // We're gonna ask the result to be put into here
362 static ResultQueue
<std::string
, ClientCached
*, u8
, u8
> result_queue
;
364 // Throw a request in
365 m_get_clientcached_queue
.add(name
, 0, 0, &result_queue
);
368 // Wait result for a second
369 GetResult
<std::string
, ClientCached
*, u8
, u8
>
370 result
= result_queue
.pop_front(1000);
372 if (result
.key
== name
) {
376 } catch(ItemNotFoundException
&e
) {
377 errorstream
<< "Waiting for clientcached " << name
378 << " timed out." << std::endl
;
379 return &m_dummy_clientcached
;
382 // Get item inventory texture
383 virtual video::ITexture
* getInventoryTexture(const std::string
&name
,
384 Client
*client
) const
386 ClientCached
*cc
= getClientCached(name
, client
);
389 return cc
->inventory_texture
;
391 // Get item wield mesh
392 virtual ItemMesh
* getWieldMesh(const std::string
&name
,
393 Client
*client
) const
395 ClientCached
*cc
= getClientCached(name
, client
);
398 return &(cc
->wield_mesh
);
402 virtual Palette
* getPalette(const std::string
&name
,
403 Client
*client
) const
405 ClientCached
*cc
= getClientCached(name
, client
);
411 virtual video::SColor
getItemstackColor(const ItemStack
&stack
,
412 Client
*client
) const
414 // Look for direct color definition
415 const std::string
&colorstring
= stack
.metadata
.getString("color", 0);
416 video::SColor directcolor
;
417 if (!colorstring
.empty() && parseColorString(colorstring
, directcolor
, true))
419 // See if there is a palette
420 Palette
*palette
= getPalette(stack
.name
, client
);
421 const std::string
&index
= stack
.metadata
.getString("palette_index", 0);
422 if (palette
&& !index
.empty())
423 return (*palette
)[mystoi(index
, 0, 255)];
425 return get(stack
.name
).color
;
428 void applyTextureOverrides(const std::vector
<TextureOverride
> &overrides
)
430 infostream
<< "ItemDefManager::applyTextureOverrides(): Applying "
431 "overrides to textures" << std::endl
;
433 for (const TextureOverride
& texture_override
: overrides
) {
434 if (m_item_definitions
.find(texture_override
.id
) == m_item_definitions
.end()) {
435 continue; // Ignore unknown item
438 ItemDefinition
* itemdef
= m_item_definitions
[texture_override
.id
];
440 if (texture_override
.hasTarget(OverrideTarget::INVENTORY
))
441 itemdef
->inventory_image
= texture_override
.texture
;
443 if (texture_override
.hasTarget(OverrideTarget::WIELD
))
444 itemdef
->wield_image
= texture_override
.texture
;
449 for (auto &i
: m_item_definitions
)
453 m_item_definitions
.clear();
456 // Add the four builtin items:
458 // "unknown" is returned whenever an undefined item
459 // is accessed (is also the unknown node)
460 // "air" is the air node
461 // "ignore" is the ignore node
463 ItemDefinition
* hand_def
= new ItemDefinition
;
465 hand_def
->wield_image
= "wieldhand.png";
466 hand_def
->tool_capabilities
= new ToolCapabilities
;
467 m_item_definitions
.insert(std::make_pair("", hand_def
));
469 ItemDefinition
* unknown_def
= new ItemDefinition
;
470 unknown_def
->type
= ITEM_NODE
;
471 unknown_def
->name
= "unknown";
472 m_item_definitions
.insert(std::make_pair("unknown", unknown_def
));
474 ItemDefinition
* air_def
= new ItemDefinition
;
475 air_def
->type
= ITEM_NODE
;
476 air_def
->name
= "air";
477 m_item_definitions
.insert(std::make_pair("air", air_def
));
479 ItemDefinition
* ignore_def
= new ItemDefinition
;
480 ignore_def
->type
= ITEM_NODE
;
481 ignore_def
->name
= "ignore";
482 m_item_definitions
.insert(std::make_pair("ignore", ignore_def
));
484 virtual void registerItem(const ItemDefinition
&def
)
486 TRACESTREAM(<< "ItemDefManager: registering " << def
.name
<< std::endl
);
487 // Ensure that the "" item (the hand) always has ToolCapabilities
488 if (def
.name
.empty())
489 FATAL_ERROR_IF(!def
.tool_capabilities
, "Hand does not have ToolCapabilities");
491 if(m_item_definitions
.count(def
.name
) == 0)
492 m_item_definitions
[def
.name
] = new ItemDefinition(def
);
494 *(m_item_definitions
[def
.name
]) = def
;
496 // Remove conflicting alias if it exists
497 bool alias_removed
= (m_aliases
.erase(def
.name
) != 0);
499 infostream
<<"ItemDefManager: erased alias "<<def
.name
500 <<" because item was defined"<<std::endl
;
502 virtual void unregisterItem(const std::string
&name
)
504 verbosestream
<<"ItemDefManager: unregistering \""<<name
<<"\""<<std::endl
;
506 delete m_item_definitions
[name
];
507 m_item_definitions
.erase(name
);
509 virtual void registerAlias(const std::string
&name
,
510 const std::string
&convert_to
)
512 if (m_item_definitions
.find(name
) == m_item_definitions
.end()) {
513 TRACESTREAM(<< "ItemDefManager: setting alias " << name
514 << " -> " << convert_to
<< std::endl
);
515 m_aliases
[name
] = convert_to
;
518 void serialize(std::ostream
&os
, u16 protocol_version
)
520 writeU8(os
, 0); // version
521 u16 count
= m_item_definitions
.size();
524 for (const auto &it
: m_item_definitions
) {
525 ItemDefinition
*def
= it
.second
;
526 // Serialize ItemDefinition and write wrapped in a string
527 std::ostringstream
tmp_os(std::ios::binary
);
528 def
->serialize(tmp_os
, protocol_version
);
529 os
<< serializeString16(tmp_os
.str());
532 writeU16(os
, m_aliases
.size());
534 for (const auto &it
: m_aliases
) {
535 os
<< serializeString16(it
.first
);
536 os
<< serializeString16(it
.second
);
539 void deSerialize(std::istream
&is
)
544 int version
= readU8(is
);
546 throw SerializationError("unsupported ItemDefManager version");
547 u16 count
= readU16(is
);
548 for(u16 i
=0; i
<count
; i
++)
550 // Deserialize a string and grab an ItemDefinition from it
551 std::istringstream
tmp_is(deSerializeString16(is
), std::ios::binary
);
553 def
.deSerialize(tmp_is
);
557 u16 num_aliases
= readU16(is
);
558 for(u16 i
=0; i
<num_aliases
; i
++)
560 std::string name
= deSerializeString16(is
);
561 std::string convert_to
= deSerializeString16(is
);
562 registerAlias(name
, convert_to
);
565 void processQueue(IGameDef
*gamedef
)
568 //NOTE this is only thread safe for ONE consumer thread!
569 while(!m_get_clientcached_queue
.empty())
571 GetRequest
<std::string
, ClientCached
*, u8
, u8
>
572 request
= m_get_clientcached_queue
.pop();
574 m_get_clientcached_queue
.pushResult(request
,
575 createClientCachedDirect(request
.key
, (Client
*)gamedef
));
581 std::map
<std::string
, ItemDefinition
*> m_item_definitions
;
585 // The id of the thread that is allowed to use irrlicht directly
586 std::thread::id m_main_thread
;
587 // A reference to this can be returned when nothing is found, to avoid NULLs
588 mutable ClientCached m_dummy_clientcached
;
589 // Cached textures and meshes
590 mutable MutexedMap
<std::string
, ClientCached
*> m_clientcached
;
591 // Queued clientcached fetches (to be processed by the main thread)
592 mutable RequestQueue
<std::string
, ClientCached
*, u8
, u8
> m_get_clientcached_queue
;
596 IWritableItemDefManager
* createItemDefManager()
598 return new CItemDefManager();