Add ability to override item images using meta (#12614)

This commit is contained in:
rubenwardy 2023-04-17 19:44:41 +01:00 committed by GitHub
parent 8c2c7fadbf
commit 4158b72971
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 200 additions and 151 deletions

@ -2271,6 +2271,11 @@ Some of the values in the key-value store are handled specially:
See also: `get_description` in [`ItemStack`] See also: `get_description` in [`ItemStack`]
* `short_description`: Set the item stack's short description. * `short_description`: Set the item stack's short description.
See also: `get_short_description` in [`ItemStack`] See also: `get_short_description` in [`ItemStack`]
* `inventory_image`: Override inventory_image
* `inventory_overlay`: Override inventory_overlay
* `wield_image`: Override wield_image
* `wield_overlay`: Override wield_overlay
* `wield_scale`: Override wield_scale, use vector.to_string
* `color`: A `ColorString`, which sets the stack's color. * `color`: A `ColorString`, which sets the stack's color.
* `palette_index`: If the item has a palette, this is used to get the * `palette_index`: If the item has a palette, this is used to get the
current color from the palette. current color from the palette.

@ -40,6 +40,7 @@ minetest.register_craftitem("testitems:overlay_meta", {
on_place = overlay_on_place, on_place = overlay_on_place,
on_secondary_use = overlay_on_place, on_secondary_use = overlay_on_place,
}) })
minetest.register_craftitem("testitems:overlay_global", { minetest.register_craftitem("testitems:overlay_global", {
description = S("Texture Overlay Test Item, Global Color") .. "\n" .. description = S("Texture Overlay Test Item, Global Color") .. "\n" ..
S("Image must be an orange square with rainbow cross (inventory and wield)"), S("Image must be an orange square with rainbow cross (inventory and wield)"),
@ -51,3 +52,41 @@ minetest.register_craftitem("testitems:overlay_global", {
wield_overlay = "testitems_overlay_overlay.png", wield_overlay = "testitems_overlay_overlay.png",
color = GLOBAL_COLOR_ARG, color = GLOBAL_COLOR_ARG,
}) })
minetest.register_craftitem("testitems:image_meta", {
description = S("Image Override Meta Test Item"),
inventory_image = "default_apple.png",
wield_image = "basetools_icesword.png",
on_use = function(itemstack, player)
local meta = itemstack:get_meta()
local state = meta:get_int("state")
state = (state + 1) % 5
meta:set_int("state", state)
minetest.chat_send_player(player:get_player_name(), "State " .. state)
if state == 0 then
meta:set_string("inventory_image", "")
meta:set_string("wield_image", "")
meta:set_string("inventory_overlay", "")
meta:set_string("wield_overlay", "")
meta:set_string("wield_scale", "")
elseif state == 1 then
meta:set_string("inventory_image", "default_tree.png")
meta:set_string("wield_image", "basetools_firesword.png")
elseif state == 2 then
meta:set_string("inventory_image", "default_apple.png^testitems_overridden.png")
meta:set_string("wield_image", "basetools_icesword.png^testitems_overridden.png")
elseif state == 3 then
meta:set_string("inventory_image", "default_tree.png")
meta:set_string("wield_image", "basetools_firesword.png")
meta:set_string("inventory_overlay", "default_apple.png")
meta:set_string("wield_overlay", "default_apple.png")
elseif state == 4 then
local scale = vector.new(0.5, 0.5, 0.5)
meta:set_string("wield_scale", scale:to_string())
end
return itemstack
end,
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

@ -1840,7 +1840,6 @@ inline bool Game::handleCallbacks()
void Game::processQueues() void Game::processQueues()
{ {
texture_src->processQueue(); texture_src->processQueue();
itemdef_manager->processQueue(client);
shader_src->processQueue(); shader_src->processQueue();
} }

@ -1008,10 +1008,14 @@ void drawItemStack(
const static thread_local bool enable_animations = const static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations"); g_settings->getBool("inventory_items_animations");
const ItemDefinition &def = item.getDefinition(client->idef()); auto *idef = client->idef();
const ItemDefinition &def = item.getDefinition(idef);
bool draw_overlay = false; bool draw_overlay = false;
const std::string inventory_image = item.getInventoryImage(idef);
const std::string inventory_overlay = item.getInventoryOverlay(idef);
bool has_mesh = false; bool has_mesh = false;
ItemMesh *imesh; ItemMesh *imesh;
@ -1020,8 +1024,8 @@ void drawItemStack(
viewrect.clipAgainst(*clip); viewrect.clipAgainst(*clip);
// Render as mesh if animated or no inventory image // Render as mesh if animated or no inventory image
if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) { if ((enable_animations && rotation_kind < IT_ROT_NONE) || inventory_image.empty()) {
imesh = client->idef()->getWieldMesh(def.name, client); imesh = idef->getWieldMesh(item, client);
has_mesh = imesh && imesh->mesh; has_mesh = imesh && imesh->mesh;
} }
if (has_mesh) { if (has_mesh) {
@ -1110,9 +1114,9 @@ void drawItemStack(
driver->setTransform(video::ETS_PROJECTION, oldProjMat); driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort); driver->setViewPort(oldViewPort);
draw_overlay = def.type == ITEM_NODE && def.inventory_image.empty(); draw_overlay = def.type == ITEM_NODE && inventory_image.empty();
} else { // Otherwise just draw as 2D } else { // Otherwise just draw as 2D
video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client); video::ITexture *texture = client->idef()->getInventoryTexture(item, client);
video::SColor color; video::SColor color;
if (texture) { if (texture) {
color = client->idef()->getItemstackColor(item, client); color = client->idef()->getItemstackColor(item, client);
@ -1134,9 +1138,9 @@ void drawItemStack(
} }
// draw the inventory_overlay // draw the inventory_overlay
if (!def.inventory_overlay.empty() && draw_overlay) { if (!inventory_overlay.empty() && draw_overlay) {
ITextureSource *tsrc = client->getTextureSource(); ITextureSource *tsrc = client->getTextureSource();
video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay); video::ITexture *overlay_texture = tsrc->getTexture(inventory_overlay);
core::dimension2d<u32> dimens = overlay_texture->getOriginalSize(); core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height); core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true); draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);

@ -384,9 +384,13 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
m_colors.clear(); m_colors.clear();
m_base_color = idef->getItemstackColor(item, client); m_base_color = idef->getItemstackColor(item, client);
const std::string wield_image = item.getWieldImage(idef);
const std::string wield_overlay = item.getWieldOverlay(idef);
const v3f wield_scale = item.getWieldScale(idef);
// If wield_image needs to be checked and is defined, it overrides everything else // If wield_image needs to be checked and is defined, it overrides everything else
if (!def.wield_image.empty() && check_wield_image) { if (!wield_image.empty() && check_wield_image) {
setExtruded(def.wield_image, def.wield_overlay, def.wield_scale, tsrc, setExtruded(wield_image, wield_overlay, wield_scale, tsrc,
1); 1);
m_colors.emplace_back(); m_colors.emplace_back();
// overlay is white, if present // overlay is white, if present
@ -413,7 +417,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
case NDT_RAILLIKE: case NDT_RAILLIKE:
case NDT_PLANTLIKE: case NDT_PLANTLIKE:
case NDT_FLOWINGLIQUID: { case NDT_FLOWINGLIQUID: {
v3f wscale = def.wield_scale; v3f wscale = wield_scale;
if (f.drawtype == NDT_FLOWINGLIQUID) if (f.drawtype == NDT_FLOWINGLIQUID)
wscale.Z *= 0.1f; wscale.Z *= 0.1f;
setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
@ -429,7 +433,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
} }
case NDT_PLANTLIKE_ROOTED: { case NDT_PLANTLIKE_ROOTED: {
setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id),
"", def.wield_scale, tsrc, "", wield_scale, tsrc,
f.special_tiles[0].layers[0].animation_frame_count); f.special_tiles[0].layers[0].animation_frame_count);
// Add color // Add color
const TileLayer &l0 = f.special_tiles[0].layers[0]; const TileLayer &l0 = f.special_tiles[0].layers[0];
@ -439,7 +443,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
case NDT_NORMAL: case NDT_NORMAL:
case NDT_ALLFACES: case NDT_ALLFACES:
case NDT_LIQUID: case NDT_LIQUID:
setCube(f, def.wield_scale); setCube(f, wield_scale);
break; break;
default: { default: {
// Render non-trivial drawtypes like the actual node // Render non-trivial drawtypes like the actual node
@ -450,7 +454,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
changeToMesh(mesh); changeToMesh(mesh);
mesh->drop(); mesh->drop();
m_meshnode->setScale( m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale)); / (BS * f.visual_scale));
break; break;
} }
@ -471,9 +475,10 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
setColor(video::SColor(0xFFFFFFFF)); setColor(video::SColor(0xFFFFFFFF));
return; return;
} else { } else {
if (!def.inventory_image.empty()) { const std::string inventory_image = item.getInventoryImage(idef);
setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale, if (!inventory_image.empty()) {
tsrc, 1); const std::string inventory_overlay = item.getInventoryOverlay(idef);
setExtruded(inventory_image, inventory_overlay, def.wield_scale, tsrc, 1);
} else { } else {
setExtruded("no_texture.png", "", def.wield_scale, tsrc, 1); setExtruded("no_texture.png", "", def.wield_scale, tsrc, 1);
} }
@ -578,9 +583,10 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
bool cull_backface = f.needsBackfaceCulling(); bool cull_backface = f.needsBackfaceCulling();
// If inventory_image is defined, it overrides everything else // If inventory_image is defined, it overrides everything else
if (!def.inventory_image.empty()) { const std::string inventory_image = item.getInventoryImage(idef);
mesh = getExtrudedMesh(tsrc, def.inventory_image, const std::string inventory_overlay = item.getInventoryOverlay(idef);
def.inventory_overlay); if (!inventory_image.empty()) {
mesh = getExtrudedMesh(tsrc, inventory_image, inventory_overlay);
result->buffer_colors.emplace_back(); result->buffer_colors.emplace_back();
// overlay is white, if present // overlay is white, if present
result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
@ -588,8 +594,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
result->needs_shading = false; result->needs_shading = false;
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) { } else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
// Fallback image for airlike node // Fallback image for airlike node
mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", inventory_overlay);
def.inventory_overlay);
result->needs_shading = false; result->needs_shading = false;
} else if (def.type == ITEM_NODE) { } else if (def.type == ITEM_NODE) {
switch (f.drawtype) { switch (f.drawtype) {

@ -272,6 +272,50 @@ std::string ItemStack::getShortDescription(const IItemDefManager *itemdef) const
return desc; return desc;
} }
std::string ItemStack::getInventoryImage(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("inventory_image");
if (texture.empty())
texture = getDefinition(itemdef).inventory_image;
return texture;
}
std::string ItemStack::getInventoryOverlay(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("inventory_overlay");
if (texture.empty())
texture = getDefinition(itemdef).inventory_overlay;
return texture;
}
std::string ItemStack::getWieldImage(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("wield_image");
if (texture.empty())
texture = getDefinition(itemdef).wield_image;
return texture;
}
std::string ItemStack::getWieldOverlay(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("wield_overlay");
if (texture.empty())
texture = getDefinition(itemdef).wield_overlay;
return texture;
}
v3f ItemStack::getWieldScale(const IItemDefManager *itemdef) const
{
std::string scale = metadata.getString("wield_scale");
if (scale.empty())
return getDefinition(itemdef).wield_scale;
return str_to_v3f(scale);
}
ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
{ {

@ -51,6 +51,12 @@ struct ItemStack
std::string getDescription(const IItemDefManager *itemdef) const; std::string getDescription(const IItemDefManager *itemdef) const;
std::string getShortDescription(const IItemDefManager *itemdef) const; std::string getShortDescription(const IItemDefManager *itemdef) const;
std::string getInventoryImage(const IItemDefManager *itemdef) const;
std::string getInventoryOverlay(const IItemDefManager *itemdef) const;
std::string getWieldImage(const IItemDefManager *itemdef) const;
std::string getWieldOverlay(const IItemDefManager *itemdef) const;
v3f getWieldScale(const IItemDefManager *itemdef) const;
/* /*
Quantity methods Quantity methods
*/ */

@ -253,6 +253,13 @@ class CItemDefManager: public IWritableItemDefManager
inventory_texture(NULL), inventory_texture(NULL),
palette(NULL) palette(NULL)
{} {}
~ClientCached() {
if (wield_mesh.mesh)
wield_mesh.mesh->drop();
}
DISABLE_CLASS_COPY(ClientCached);
}; };
#endif #endif
@ -265,17 +272,9 @@ public:
#endif #endif
clear(); clear();
} }
virtual ~CItemDefManager() virtual ~CItemDefManager()
{ {
#ifndef SERVER
const std::vector<ClientCached*> &values = m_clientcached.getValues();
for (ClientCached *cc : values) {
if (cc->wield_mesh.mesh)
cc->wield_mesh.mesh->drop();
delete cc;
}
#endif
for (auto &item_definition : m_item_definitions) { for (auto &item_definition : m_item_definitions) {
delete item_definition.second; delete item_definition.second;
} }
@ -319,103 +318,72 @@ public:
} }
#ifndef SERVER #ifndef SERVER
public: public:
ClientCached* createClientCachedDirect(const std::string &name, ClientCached* createClientCachedDirect(const ItemStack &item, Client *client) const
Client *client) const
{ {
infostream<<"Lazily creating item texture and mesh for \""
<<name<<"\""<<std::endl;
// This is not thread-safe // This is not thread-safe
sanity_check(std::this_thread::get_id() == m_main_thread); sanity_check(std::this_thread::get_id() == m_main_thread);
const ItemDefinition &def = item.getDefinition(this);
std::string inventory_image = item.getInventoryImage(this);
std::string inventory_overlay = item.getInventoryOverlay(this);
std::string cache_key = def.name;
if (!inventory_image.empty())
cache_key += "/" + inventory_image;
if (!inventory_overlay.empty())
cache_key += ":" + inventory_overlay;
infostream << "Lazily creating item texture and mesh for \""
<< cache_key << "\""<<std::endl;
// Skip if already in cache // Skip if already in cache
ClientCached *cc = NULL; auto it = m_clientcached.find(cache_key);
m_clientcached.get(name, &cc); if (it != m_clientcached.end())
if(cc) return it->second.get();
return cc;
ITextureSource *tsrc = client->getTextureSource(); ITextureSource *tsrc = client->getTextureSource();
const ItemDefinition &def = get(name);
// Create new ClientCached // Create new ClientCached
cc = new ClientCached(); auto cc = std::make_unique<ClientCached>();
// Create an inventory texture // Create an inventory texture
cc->inventory_texture = NULL; cc->inventory_texture = NULL;
if (!def.inventory_image.empty()) if (!inventory_image.empty())
cc->inventory_texture = tsrc->getTexture(def.inventory_image); cc->inventory_texture = tsrc->getTexture(inventory_image);
ItemStack item = ItemStack();
item.name = def.name;
getItemMesh(client, item, &(cc->wield_mesh)); getItemMesh(client, item, &(cc->wield_mesh));
cc->palette = tsrc->getPalette(def.palette_image); cc->palette = tsrc->getPalette(def.palette_image);
// Put in cache // Put in cache
m_clientcached.set(name, cc); ClientCached *ptr = cc.get();
m_clientcached[cache_key] = std::move(cc);
return cc; return ptr;
} }
ClientCached* getClientCached(const std::string &name,
Client *client) const
{
ClientCached *cc = NULL;
m_clientcached.get(name, &cc);
if (cc)
return cc;
if (std::this_thread::get_id() == m_main_thread) {
return createClientCachedDirect(name, client);
}
// We're gonna ask the result to be put into here
static ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
// Throw a request in
m_get_clientcached_queue.add(name, 0, 0, &result_queue);
try {
while(true) {
// Wait result for a second
GetResult<std::string, ClientCached*, u8, u8>
result = result_queue.pop_front(1000);
if (result.key == name) {
return result.item;
}
}
} catch(ItemNotFoundException &e) {
errorstream << "Waiting for clientcached " << name
<< " timed out." << std::endl;
return &m_dummy_clientcached;
}
}
// Get item inventory texture // Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name, virtual video::ITexture* getInventoryTexture(const ItemStack &item,
Client *client) const Client *client) const
{ {
ClientCached *cc = getClientCached(name, client); ClientCached *cc = createClientCachedDirect(item, client);
if(!cc) if (!cc)
return NULL; return nullptr;
return cc->inventory_texture; return cc->inventory_texture;
} }
// Get item wield mesh // Get item wield mesh
virtual ItemMesh* getWieldMesh(const std::string &name, virtual ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const
Client *client) const
{ {
ClientCached *cc = getClientCached(name, client); ClientCached *cc = createClientCachedDirect(item, client);
if(!cc) if (!cc)
return NULL; return nullptr;
return &(cc->wield_mesh); return &(cc->wield_mesh);
} }
// Get item palette // Get item palette
virtual Palette* getPalette(const std::string &name, virtual Palette* getPalette(const ItemStack &item, Client *client) const
Client *client) const
{ {
ClientCached *cc = getClientCached(name, client); ClientCached *cc = createClientCachedDirect(item, client);
if(!cc) if (!cc)
return NULL; return nullptr;
return cc->palette; return cc->palette;
} }
@ -428,7 +396,7 @@ public:
if (!colorstring.empty() && parseColorString(colorstring, directcolor, true)) if (!colorstring.empty() && parseColorString(colorstring, directcolor, true))
return directcolor; return directcolor;
// See if there is a palette // See if there is a palette
Palette *palette = getPalette(stack.name, client); Palette *palette = getPalette(stack, client);
const std::string &index = stack.metadata.getString("palette_index", 0); const std::string &index = stack.metadata.getString("palette_index", 0);
if (palette && !index.empty()) if (palette && !index.empty())
return (*palette)[mystoi(index, 0, 255)]; return (*palette)[mystoi(index, 0, 255)];
@ -573,20 +541,7 @@ public:
registerAlias(name, convert_to); registerAlias(name, convert_to);
} }
} }
void processQueue(IGameDef *gamedef)
{
#ifndef SERVER
//NOTE this is only thread safe for ONE consumer thread!
while(!m_get_clientcached_queue.empty())
{
GetRequest<std::string, ClientCached*, u8, u8>
request = m_get_clientcached_queue.pop();
m_get_clientcached_queue.pushResult(request,
createClientCachedDirect(request.key, (Client *)gamedef));
}
#endif
}
private: private:
// Key is name // Key is name
std::map<std::string, ItemDefinition*> m_item_definitions; std::map<std::string, ItemDefinition*> m_item_definitions;
@ -595,12 +550,8 @@ private:
#ifndef SERVER #ifndef SERVER
// The id of the thread that is allowed to use irrlicht directly // The id of the thread that is allowed to use irrlicht directly
std::thread::id m_main_thread; std::thread::id m_main_thread;
// A reference to this can be returned when nothing is found, to avoid NULLs
mutable ClientCached m_dummy_clientcached;
// Cached textures and meshes // Cached textures and meshes
mutable MutexedMap<std::string, ClientCached*> m_clientcached; mutable std::unordered_map<std::string, std::unique_ptr<ClientCached>> m_clientcached;
// Queued clientcached fetches (to be processed by the main thread)
mutable RequestQueue<std::string, ClientCached*, u8, u8> m_get_clientcached_queue;
#endif #endif
}; };

@ -120,14 +120,16 @@ public:
virtual bool isKnown(const std::string &name) const=0; virtual bool isKnown(const std::string &name) const=0;
#ifndef SERVER #ifndef SERVER
// Get item inventory texture // Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name, virtual video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const=0;
Client *client) const=0;
// Get item wield mesh /**
virtual ItemMesh* getWieldMesh(const std::string &name, * Get wield mesh
Client *client) const=0; *
* Returns nullptr if there is an inventory image
*/
virtual ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const = 0;
// Get item palette // Get item palette
virtual Palette* getPalette(const std::string &name, virtual Palette* getPalette(const ItemStack &item, Client *client) const = 0;
Client *client) const = 0;
// Returns the base color of an item stack: the color of all // Returns the base color of an item stack: the color of all
// tiles that do not define their own color. // tiles that do not define their own color.
virtual video::SColor getItemstackColor(const ItemStack &stack, virtual video::SColor getItemstackColor(const ItemStack &stack,
@ -144,23 +146,6 @@ public:
virtual ~IWritableItemDefManager() = default; virtual ~IWritableItemDefManager() = default;
// Get item definition
virtual const ItemDefinition& get(const std::string &name) const=0;
// Get alias definition
virtual const std::string &getAlias(const std::string &name) const=0;
// Get set of all defined item names and aliases
virtual void getAll(std::set<std::string> &result) const=0;
// Check if item is known
virtual bool isKnown(const std::string &name) const=0;
#ifndef SERVER
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
Client *client) const=0;
// Get item wield mesh
virtual ItemMesh* getWieldMesh(const std::string &name,
Client *client) const=0;
#endif
// Replace the textures of registered nodes with the ones specified in // Replace the textures of registered nodes with the ones specified in
// the texture pack's override.txt files // the texture pack's override.txt files
virtual void applyTextureOverrides(const std::vector<TextureOverride> &overrides)=0; virtual void applyTextureOverrides(const std::vector<TextureOverride> &overrides)=0;
@ -177,11 +162,7 @@ public:
virtual void registerAlias(const std::string &name, virtual void registerAlias(const std::string &name,
const std::string &convert_to)=0; const std::string &convert_to)=0;
virtual void serialize(std::ostream &os, u16 protocol_version)=0;
virtual void deSerialize(std::istream &is, u16 protocol_version)=0; virtual void deSerialize(std::istream &is, u16 protocol_version)=0;
// Do stuff asked by threads that can only be done in the main thread
virtual void processQueue(IGameDef *gamedef)=0;
}; };
IWritableItemDefManager* createItemDefManager(); IWritableItemDefManager* createItemDefManager();

@ -562,13 +562,7 @@ v2f Settings::getV2F(const std::string &name) const
v3f Settings::getV3F(const std::string &name) const v3f Settings::getV3F(const std::string &name) const
{ {
v3f value; return str_to_v3f(get(name));
Strfnd f(get(name));
f.next("(");
value.X = stof(f.next(","));
value.Y = stof(f.next(","));
value.Z = stof(f.next(")"));
return value;
} }

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hex.h" #include "hex.h"
#include "porting.h" #include "porting.h"
#include "translation.h" #include "translation.h"
#include "strfnd.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@ -897,3 +898,15 @@ void safe_print_string(std::ostream &os, const std::string &str)
} }
os.setf(flags); os.setf(flags);
} }
v3f str_to_v3f(const std::string &str)
{
v3f value;
Strfnd f(str);
f.next("(");
value.X = stof(f.next(","));
value.Y = stof(f.next(","));
value.Z = stof(f.next(")"));
return value;
}

@ -765,3 +765,11 @@ std::string sanitizeDirName(const std::string &str, const std::string &optional_
* brackets (e.g. "a\x1eb" -> "a<1e>b"). * brackets (e.g. "a\x1eb" -> "a<1e>b").
*/ */
void safe_print_string(std::ostream &os, const std::string &str); void safe_print_string(std::ostream &os, const std::string &str);
/**
* Parses a string of form `(1, 2, 3)` to a v3f
*
* @param str String
* @return
*/
v3f str_to_v3f(const std::string &str);