]> git.lizzy.rs Git - minetest.git/blob - src/script/lua_api/l_metadata.cpp
Support packing arbitrary graphs (#12289)
[minetest.git] / src / script / lua_api / l_metadata.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
5
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.
10
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.
15
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.
19 */
20
21 #include "lua_api/l_metadata.h"
22 #include "lua_api/l_internal.h"
23 #include "common/c_content.h"
24 #include "serverenvironment.h"
25 #include "map.h"
26 #include "server.h"
27
28 // LUALIB_API
29 void *luaL_checkudata_is_metadataref(lua_State *L, int ud) {
30         void *p = lua_touserdata(L, ud);
31         if (p != NULL &&  // value is a userdata?
32                         lua_getmetatable(L, ud)) {  // does it have a metatable?
33                 lua_getfield(L, -1, "metadata_class");
34                 if (lua_type(L, -1) == LUA_TSTRING) { // does it have a metadata_class field?
35                         return p;
36                 }
37         }
38         luaL_typerror(L, ud, "MetaDataRef");
39         return NULL;
40 }
41
42 MetaDataRef* MetaDataRef::checkobject(lua_State *L, int narg)
43 {
44         luaL_checktype(L, narg, LUA_TUSERDATA);
45         void *ud = luaL_checkudata_is_metadataref(L, narg);
46         if (!ud)
47                 luaL_typerror(L, narg, "MetaDataRef");
48
49         return *(MetaDataRef**)ud;  // unbox pointer
50 }
51
52 // Exported functions
53
54 // contains(self, name)
55 int MetaDataRef::l_contains(lua_State *L)
56 {
57         MAP_LOCK_REQUIRED;
58
59         MetaDataRef *ref = checkobject(L, 1);
60         std::string name = luaL_checkstring(L, 2);
61
62         Metadata *meta = ref->getmeta(false);
63         if (meta == NULL)
64                 return 0;
65
66         lua_pushboolean(L, meta->contains(name));
67         return 1;
68 }
69
70 // get(self, name)
71 int MetaDataRef::l_get(lua_State *L)
72 {
73         MAP_LOCK_REQUIRED;
74
75         MetaDataRef *ref = checkobject(L, 1);
76         std::string name = luaL_checkstring(L, 2);
77
78         Metadata *meta = ref->getmeta(false);
79         if (meta == NULL)
80                 return 0;
81
82         std::string str;
83         if (meta->getStringToRef(name, str)) {
84                 lua_pushlstring(L, str.c_str(), str.size());
85         } else {
86                 lua_pushnil(L);
87         }
88         return 1;
89 }
90
91 // get_string(self, name)
92 int MetaDataRef::l_get_string(lua_State *L)
93 {
94         MAP_LOCK_REQUIRED;
95
96         MetaDataRef *ref = checkobject(L, 1);
97         std::string name = luaL_checkstring(L, 2);
98
99         Metadata *meta = ref->getmeta(false);
100         if (meta == NULL) {
101                 lua_pushlstring(L, "", 0);
102                 return 1;
103         }
104
105         const std::string &str = meta->getString(name);
106         lua_pushlstring(L, str.c_str(), str.size());
107         return 1;
108 }
109
110 // set_string(self, name, var)
111 int MetaDataRef::l_set_string(lua_State *L)
112 {
113         MAP_LOCK_REQUIRED;
114
115         MetaDataRef *ref = checkobject(L, 1);
116         std::string name = luaL_checkstring(L, 2);
117         size_t len = 0;
118         const char *s = lua_tolstring(L, 3, &len);
119         std::string str(s, len);
120
121         Metadata *meta = ref->getmeta(!str.empty());
122         if (meta == NULL || str == meta->getString(name))
123                 return 0;
124
125         meta->setString(name, str);
126         ref->reportMetadataChange(&name);
127         return 0;
128 }
129
130 // get_int(self, name)
131 int MetaDataRef::l_get_int(lua_State *L)
132 {
133         MAP_LOCK_REQUIRED;
134
135         MetaDataRef *ref = checkobject(L, 1);
136         std::string name = luaL_checkstring(L, 2);
137
138         Metadata *meta = ref->getmeta(false);
139         if (meta == NULL) {
140                 lua_pushnumber(L, 0);
141                 return 1;
142         }
143
144         const std::string &str = meta->getString(name);
145         lua_pushnumber(L, stoi(str));
146         return 1;
147 }
148
149 // set_int(self, name, var)
150 int MetaDataRef::l_set_int(lua_State *L)
151 {
152         MAP_LOCK_REQUIRED;
153
154         MetaDataRef *ref = checkobject(L, 1);
155         std::string name = luaL_checkstring(L, 2);
156         int a = luaL_checkint(L, 3);
157         std::string str = itos(a);
158
159         Metadata *meta = ref->getmeta(true);
160         if (meta == NULL || str == meta->getString(name))
161                 return 0;
162
163         meta->setString(name, str);
164         ref->reportMetadataChange(&name);
165         return 0;
166 }
167
168 // get_float(self, name)
169 int MetaDataRef::l_get_float(lua_State *L)
170 {
171         MAP_LOCK_REQUIRED;
172
173         MetaDataRef *ref = checkobject(L, 1);
174         std::string name = luaL_checkstring(L, 2);
175
176         Metadata *meta = ref->getmeta(false);
177         if (meta == NULL) {
178                 lua_pushnumber(L, 0);
179                 return 1;
180         }
181
182         const std::string &str = meta->getString(name);
183         lua_pushnumber(L, stof(str));
184         return 1;
185 }
186
187 // set_float(self, name, var)
188 int MetaDataRef::l_set_float(lua_State *L)
189 {
190         MAP_LOCK_REQUIRED;
191
192         MetaDataRef *ref = checkobject(L, 1);
193         std::string name = luaL_checkstring(L, 2);
194         float a = readParam<float>(L, 3);
195         std::string str = ftos(a);
196
197         Metadata *meta = ref->getmeta(true);
198         if (meta == NULL || str == meta->getString(name))
199                 return 0;
200
201         meta->setString(name, str);
202         ref->reportMetadataChange(&name);
203         return 0;
204 }
205
206 // to_table(self)
207 int MetaDataRef::l_to_table(lua_State *L)
208 {
209         MAP_LOCK_REQUIRED;
210
211         MetaDataRef *ref = checkobject(L, 1);
212
213         Metadata *meta = ref->getmeta(true);
214         if (meta == NULL) {
215                 lua_pushnil(L);
216                 return 1;
217         }
218         lua_newtable(L);
219
220         ref->handleToTable(L, meta);
221
222         return 1;
223 }
224
225 // from_table(self, table)
226 int MetaDataRef::l_from_table(lua_State *L)
227 {
228         MAP_LOCK_REQUIRED;
229
230         MetaDataRef *ref = checkobject(L, 1);
231         int base = 2;
232
233         ref->clearMeta();
234
235         if (!lua_istable(L, base)) {
236                 // No metadata
237                 lua_pushboolean(L, true);
238                 return 1;
239         }
240
241         // Create new metadata
242         Metadata *meta = ref->getmeta(true);
243         if (meta == NULL) {
244                 lua_pushboolean(L, false);
245                 return 1;
246         }
247
248         bool was_successful = ref->handleFromTable(L, base, meta);
249         ref->reportMetadataChange();
250         lua_pushboolean(L, was_successful);
251         return 1;
252 }
253
254 void MetaDataRef::handleToTable(lua_State *L, Metadata *meta)
255 {
256         lua_newtable(L);
257         {
258                 const StringMap &fields = meta->getStrings();
259                 for (const auto &field : fields) {
260                         const std::string &name = field.first;
261                         const std::string &value = field.second;
262                         lua_pushlstring(L, name.c_str(), name.size());
263                         lua_pushlstring(L, value.c_str(), value.size());
264                         lua_settable(L, -3);
265                 }
266         }
267         lua_setfield(L, -2, "fields");
268 }
269
270 bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta)
271 {
272         // Set fields
273         lua_getfield(L, table, "fields");
274         if (lua_istable(L, -1)) {
275                 int fieldstable = lua_gettop(L);
276                 lua_pushnil(L);
277                 while (lua_next(L, fieldstable) != 0) {
278                         // key at index -2 and value at index -1
279                         std::string name = readParam<std::string>(L, -2);
280                         size_t cl;
281                         const char *cs = lua_tolstring(L, -1, &cl);
282                         meta->setString(name, std::string(cs, cl));
283                         lua_pop(L, 1); // Remove value, keep key for next iteration
284                 }
285                 lua_pop(L, 1);
286         }
287
288         return true;
289 }
290
291 // equals(self, other)
292 int MetaDataRef::l_equals(lua_State *L)
293 {
294         MetaDataRef *ref1 = checkobject(L, 1);
295         Metadata *data1 = ref1->getmeta(false);
296         MetaDataRef *ref2 = checkobject(L, 2);
297         Metadata *data2 = ref2->getmeta(false);
298         if (data1 == NULL || data2 == NULL)
299                 lua_pushboolean(L, data1 == data2);
300         else
301                 lua_pushboolean(L, *data1 == *data2);
302         return 1;
303 }