9b2bed33f0b416a9e22600002c1dc4d670d5e828
[oweals/minetest.git] / share / builtin / builtin.lua
1 --
2 -- This file contains built-in stuff in Minetest implemented in Lua.
3 --
4 -- It is always loaded and executed after registration of the C API,
5 -- before loading and running any mods.
6 --
7
8 --
9 -- Override some Lua library functions
10 --
11
12 print = minetest.debug
13
14 --
15 --
16 --
17
18 function basic_dump2(o)
19         if type(o) == "number" then
20                 return tostring(o)
21         elseif type(o) == "string" then
22                 return string.format("%q", o)
23         elseif type(o) == "boolean" then
24                 return tostring(o)
25         elseif type(o) == "function" then
26                 return "<function>"
27         elseif type(o) == "userdata" then
28                 return "<userdata>"
29         elseif type(o) == "nil" then
30                 return "nil"
31         else
32                 error("cannot dump a " .. type(o))
33                 return nil
34         end
35 end
36
37 function dump2(o, name, dumped)
38         name = name or "_"
39         dumped = dumped or {}
40         io.write(name, " = ")
41         if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
42                         or type(o) == "function" or type(o) == "nil"
43                         or type(o) == "userdata" then
44                 io.write(basic_dump2(o), "\n")
45         elseif type(o) == "table" then
46                 if dumped[o] then
47                         io.write(dumped[o], "\n")
48                 else
49                         dumped[o] = name
50                         io.write("{}\n") -- new table
51                         for k,v in pairs(o) do
52                                 local fieldname = string.format("%s[%s]", name, basic_dump2(k))
53                                 dump2(v, fieldname, dumped)
54                         end
55                 end
56         else
57                 error("cannot dump a " .. type(o))
58                 return nil
59         end
60 end
61
62 function dump(o, dumped)
63         dumped = dumped or {}
64         if type(o) == "number" then
65                 return tostring(o)
66         elseif type(o) == "string" then
67                 return string.format("%q", o)
68         elseif type(o) == "table" then
69                 if dumped[o] then
70                         return "<circular reference>"
71                 end
72                 dumped[o] = true
73                 local t = {}
74                 for k,v in pairs(o) do
75                         t[#t+1] = "" .. k .. " = " .. dump(v, dumped)
76                 end
77                 return "{" .. table.concat(t, ", ") .. "}"
78         elseif type(o) == "boolean" then
79                 return tostring(o)
80         elseif type(o) == "function" then
81                 return "<function>"
82         elseif type(o) == "userdata" then
83                 return "<userdata>"
84         elseif type(o) == "nil" then
85                 return "nil"
86         else
87                 error("cannot dump a " .. type(o))
88                 return nil
89         end
90 end
91
92 --
93 -- Item definition helpers
94 --
95
96 function minetest.inventorycube(img1, img2, img3)
97         img2 = img2 or img1
98         img3 = img3 or img1
99         return "[inventorycube"
100                         .. "{" .. img1:gsub("%^", "&")
101                         .. "{" .. img2:gsub("%^", "&")
102                         .. "{" .. img3:gsub("%^", "&")
103 end
104
105 function minetest.pos_to_string(pos)
106         return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
107 end
108
109 function minetest.get_pointed_thing_position(pointed_thing, above)
110         if pointed_thing.type == "node" then
111                 if above then
112                         -- The position where a node would be placed
113                         return pointed_thing.above
114                 else
115                         -- The position where a node would be dug
116                         return pointed_thing.under
117                 end
118         elseif pointed_thing.type == "object" then
119                 obj = pointed_thing.ref
120                 if obj ~= nil then
121                         return obj:getpos()
122                 else
123                         return nil
124                 end
125         else
126                 return nil
127         end
128 end
129
130 function minetest.dir_to_facedir(dir)
131         if math.abs(dir.x) > math.abs(dir.z) then
132                 if dir.x < 0 then
133                         return 3
134                 else
135                         return 1
136                 end
137         else
138                 if dir.z < 0 then
139                         return 2
140                 else
141                         return 0
142                 end
143         end
144 end
145
146 function minetest.dir_to_wallmounted(dir)
147         if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
148                 if dir.y < 0 then
149                         return 1
150                 else
151                         return 0
152                 end
153         elseif math.abs(dir.x) > math.abs(dir.z) then
154                 if dir.x < 0 then
155                         return 3
156                 else
157                         return 2
158                 end
159         else
160                 if dir.z < 0 then
161                         return 5
162                 else
163                         return 4
164                 end
165         end
166 end
167
168 function minetest.get_node_drops(nodename, toolname)
169         local drop = ItemStack({name=nodename}):get_definition().drop
170         if drop == nil then
171                 -- default drop
172                 return {ItemStack({name=nodename})}
173         elseif type(drop) == "string" then
174                 -- itemstring drop
175                 return {ItemStack(drop)}
176         elseif drop.items == nil then
177                 -- drop = {} to disable default drop
178                 return {}
179         end
180
181         -- Extended drop table
182         local got_items = {}
183         local got_count = 0
184         local _, item, tool
185         for _, item in ipairs(drop.items) do
186                 local good_rarity = true
187                 local good_tool = true
188                 if item.rarity ~= nil then
189                         good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
190                 end
191                 if item.tools ~= nil then
192                         good_tool = false
193                         for _, tool in ipairs(item.tools) do
194                                 if tool:sub(1, 1) == '~' then
195                                         good_tool = toolname:find(tool:sub(2)) ~= nil
196                                 else
197                                         good_tool = toolname == tool
198                                 end
199                                 if good_tool then
200                                         break
201                                 end
202                         end
203                 end
204                 if good_rarity and good_tool then
205                         got_count = got_count + 1
206                         for _, add_item in ipairs(item.items) do
207                                 got_items[#got_items+1] = add_item
208                         end
209                         if drop.max_items ~= nil and got_count == drop.max_items then
210                                 break
211                         end
212                 end
213         end
214         return got_items
215 end
216
217 function minetest.item_place_node(itemstack, placer, pointed_thing)
218         local item = itemstack:peek_item()
219         local def = itemstack:get_definition()
220         if def.type == "node" and pointed_thing.type == "node" then
221                 local pos = pointed_thing.above
222                 local oldnode = minetest.env:get_node(pos)
223                 local olddef = ItemStack({name=oldnode.name}):get_definition()
224
225                 if not olddef.buildable_to then
226                         minetest.log("info", placer:get_player_name() .. " tried to place"
227                                 .. " node in invalid position " .. minetest.pos_to_string(pos)
228                                 .. ", replacing " .. oldnode.name)
229                         return
230                 end
231
232                 minetest.log("action", placer:get_player_name() .. " places node "
233                         .. def.name .. " at " .. minetest.pos_to_string(pos))
234
235                 local newnode = {name = def.name, param1 = 0, param2 = 0}
236
237                 -- Calculate direction for wall mounted stuff like torches and signs
238                 if def.paramtype2 == 'wallmounted' then
239                         local under = pointed_thing.under
240                         local above = pointed_thing.above
241                         local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
242                         newnode.param2 = minetest.dir_to_wallmounted(dir)
243                 -- Calculate the direction for furnaces and chests and stuff
244                 elseif def.paramtype2 == 'facedir' then
245                         local playerpos = placer:getpos()
246                         local dir = {x = pos.x - playerpos.x, y = pos.y - playerpos.y, z = pos.z - playerpos.z}
247                         newnode.param2 = minetest.dir_to_facedir(dir)
248                         minetest.log("action", "facedir: " .. newnode.param2)
249                 end
250
251                 -- Add node and update
252                 minetest.env:add_node(pos, newnode)
253
254                 -- Set metadata owner
255                 if def.metadata_name ~= "" then
256                         minetest.env:get_meta(pos):set_owner(placer:get_player_name())
257                 end
258
259                 -- Run script hook
260                 local _, callback
261                 for _, callback in ipairs(minetest.registered_on_placenodes) do
262                         callback(pos, newnode, placer)
263                 end
264
265                 itemstack:take_item()
266         end
267         return itemstack
268 end
269
270 function minetest.item_place_object(itemstack, placer, pointed_thing)
271         local pos = minetest.get_pointed_thing_position(pointed_thing, true)
272         if pos ~= nil then
273                 local item = itemstack:take_item()
274                 minetest.env:add_item(pos, item)
275         end
276         return itemstack
277 end
278
279 function minetest.item_place(itemstack, placer, pointed_thing)
280         if itemstack:get_definition().type == "node" then
281                 return minetest.item_place_node(itemstack, placer, pointed_thing)
282         else
283                 return minetest.item_place_object(itemstack, placer, pointed_thing)
284         end
285 end
286
287 function minetest.item_drop(itemstack, dropper, pos)
288         minetest.env:add_item(pos, itemstack)
289         return ""
290 end
291
292 function minetest.item_eat(hp_change, replace_with_item)
293         return function(itemstack, user, pointed_thing)  -- closure
294                 if itemstack:take_item() ~= nil then
295                         user:set_hp(user:get_hp() + hp_change)
296                         itemstack:add_item(replace_with_item) -- note: replace_with_item is optional
297                 end
298                 return itemstack
299         end
300 end
301
302 function minetest.node_punch(pos, node, puncher)
303         -- Run script hook
304         local _, callback
305         for _, callback in ipairs(minetest.registered_on_punchnodes) do
306                 callback(pos, node, puncher)
307         end
308
309 end
310
311 function minetest.node_dig(pos, node, digger)
312         minetest.debug("node_dig")
313
314         local def = ItemStack({name=node.name}):get_definition()
315         if not def.diggable then
316                 minetest.debug("not diggable")
317                 minetest.log("info", digger:get_player_name() .. " tried to dig "
318                         .. node.name .. " which is not diggable "
319                         .. minetest.pos_to_string(pos))
320                 return
321         end
322
323         local meta = minetest.env:get_meta(pos)
324         if meta ~= nil and not meta:get_allow_removal() then
325                 minetest.debug("dig prevented by metadata")
326                 minetest.log("info", digger:get_player_name() .. " tried to dig "
327                         .. node.name .. ", but removal is disabled by metadata "
328                         .. minetest.pos_to_string(pos))
329                 return
330         end
331
332         minetest.log('action', digger:get_player_name() .. " digs "
333                 .. node.name .. " at " .. minetest.pos_to_string(pos))
334
335         if not minetest.setting_getbool("creative_mode") then
336                 local wielded = digger:get_wielded_item()
337                 local drops = minetest.get_node_drops(node.name, wielded:get_name())
338
339                 -- Wear out tool
340                 tp = wielded:get_tool_capabilities()
341                 dp = minetest.get_dig_params(def.groups, tp)
342                 wielded:add_wear(dp.wear)
343                 digger:set_wielded_item(wielded)
344
345                 -- Add dropped items
346                 local _, dropped_item
347                 for _, dropped_item in ipairs(drops) do
348                         digger:get_inventory():add_item("main", dropped_item)
349                 end
350         end
351
352         -- Remove node and update
353         minetest.env:remove_node(pos)
354
355         -- Run script hook
356         local _, callback
357         for _, callback in ipairs(minetest.registered_on_dignodes) do
358                 callback(pos, node, digger)
359         end
360 end
361
362 --
363 -- Item definition defaults
364 --
365
366 minetest.nodedef_default = {
367         -- Item properties
368         type="node",
369         -- name intentionally not defined here
370         description = "",
371         groups = {},
372         inventory_image = "",
373         wield_image = "",
374         wield_scale = {x=1,y=1,z=1},
375         stack_max = 99,
376         usable = false,
377         liquids_pointable = false,
378         tool_capabilities = nil,
379
380         -- Interaction callbacks
381         on_place = minetest.item_place,
382         on_drop = minetest.item_drop,
383         on_use = nil,
384
385         on_punch = minetest.node_punch,
386         on_dig = minetest.node_dig,
387
388         -- Node properties
389         drawtype = "normal",
390         visual_scale = 1.0,
391         tile_images = {""},
392         special_materials = {
393                 {image="", backface_culling=true},
394                 {image="", backface_culling=true},
395         },
396         alpha = 255,
397         post_effect_color = {a=0, r=0, g=0, b=0},
398         paramtype = "none",
399         paramtype2 = "none",
400         is_ground_content = false,
401         sunlight_propagates = false,
402         walkable = true,
403         pointable = true,
404         diggable = true,
405         climbable = false,
406         buildable_to = false,
407         metadata_name = "",
408         liquidtype = "none",
409         liquid_alternative_flowing = "",
410         liquid_alternative_source = "",
411         liquid_viscosity = 0,
412         light_source = 0,
413         damage_per_second = 0,
414         selection_box = {type="regular"},
415         legacy_facedir_simple = false,
416         legacy_wallmounted = false,
417 }
418
419 minetest.craftitemdef_default = {
420         type="craft",
421         -- name intentionally not defined here
422         description = "",
423         groups = {},
424         inventory_image = "",
425         wield_image = "",
426         wield_scale = {x=1,y=1,z=1},
427         stack_max = 99,
428         liquids_pointable = false,
429         tool_capabilities = nil,
430
431         -- Interaction callbacks
432         on_place = minetest.item_place,
433         on_drop = minetest.item_drop,
434         on_use = nil,
435 }
436
437 minetest.tooldef_default = {
438         type="tool",
439         -- name intentionally not defined here
440         description = "",
441         groups = {},
442         inventory_image = "",
443         wield_image = "",
444         wield_scale = {x=1,y=1,z=1},
445         stack_max = 1,
446         liquids_pointable = false,
447         tool_capabilities = nil,
448
449         -- Interaction callbacks
450         on_place = minetest.item_place,
451         on_drop = minetest.item_drop,
452         on_use = nil,
453 }
454
455 minetest.noneitemdef_default = {  -- This is used for the hand and unknown items
456         type="none",
457         -- name intentionally not defined here
458         description = "",
459         groups = {},
460         inventory_image = "",
461         wield_image = "",
462         wield_scale = {x=1,y=1,z=1},
463         stack_max = 99,
464         liquids_pointable = false,
465         tool_capabilities = nil,
466
467         -- Interaction callbacks
468         on_place = nil,
469         on_drop = nil,
470         on_use = nil,
471 }
472
473 --
474 -- Make raw registration functions inaccessible to anyone except builtin.lua
475 --
476
477 local register_item_raw = minetest.register_item_raw
478 minetest.register_item_raw = nil
479
480 local register_alias_raw = minetest.register_alias_raw
481 minetest.register_item_raw = nil
482
483 --
484 -- Item / entity / ABM registration functions
485 --
486
487 minetest.registered_abms = {}
488 minetest.registered_entities = {}
489 minetest.registered_items = {}
490 minetest.registered_nodes = {}
491 minetest.registered_craftitems = {}
492 minetest.registered_tools = {}
493 minetest.registered_aliases = {}
494
495 -- For tables that are indexed by item name:
496 -- If table[X] does not exist, default to table[minetest.registered_aliases[X]]
497 local function set_alias_metatable(table)
498         setmetatable(table, {
499                 __index = function(name)
500                         return rawget(table, minetest.registered_aliases[name])
501                 end
502         })
503 end
504 set_alias_metatable(minetest.registered_items)
505 set_alias_metatable(minetest.registered_nodes)
506 set_alias_metatable(minetest.registered_craftitems)
507 set_alias_metatable(minetest.registered_tools)
508
509 -- These item names may not be used because they would interfere
510 -- with legacy itemstrings
511 local forbidden_item_names = {
512         MaterialItem = true,
513         MaterialItem2 = true,
514         MaterialItem3 = true,
515         NodeItem = true,
516         node = true,
517         CraftItem = true,
518         craft = true,
519         MBOItem = true,
520         ToolItem = true,
521         tool = true,
522 }
523
524 local function check_modname_prefix(name)
525         if name:sub(1,1) == ":" then
526                 -- Escape the modname prefix enforcement mechanism
527                 return name:sub(2)
528         else
529                 -- Modname prefix enforcement
530                 local expected_prefix = minetest.get_current_modname() .. ":"
531                 if name:sub(1, #expected_prefix) ~= expected_prefix then
532                         error("Name " .. name .. " does not follow naming conventions: " ..
533                                 "\"modname:\" or \":\" prefix required")
534                 end
535                 local subname = name:sub(#expected_prefix+1)
536                 if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
537                         error("Name " .. name .. " does not follow naming conventions: " ..
538                                 "contains unallowed characters")
539                 end
540                 return name
541         end
542 end
543
544 function minetest.register_abm(spec)
545         -- Add to minetest.registered_abms
546         minetest.registered_abms[#minetest.registered_abms+1] = spec
547 end
548
549 function minetest.register_entity(name, prototype)
550         -- Check name
551         if name == nil then
552                 error("Unable to register entity: Name is nil")
553         end
554         name = check_modname_prefix(tostring(name))
555
556         prototype.name = name
557         prototype.__index = prototype  -- so that it can be used as a metatable
558
559         -- Add to minetest.registered_entities
560         minetest.registered_entities[name] = prototype
561 end
562
563 function minetest.register_item(name, itemdef)
564         -- Check name
565         if name == nil then
566                 error("Unable to register item: Name is nil")
567         end
568         name = check_modname_prefix(tostring(name))
569         if forbidden_item_names[name] then
570                 error("Unable to register item: Name is forbidden: " .. name)
571         end
572         itemdef.name = name
573
574         -- Apply defaults and add to registered_* table
575         if itemdef.type == "node" then
576                 setmetatable(itemdef, {__index = minetest.nodedef_default})
577                 minetest.registered_nodes[itemdef.name] = itemdef
578         elseif itemdef.type == "craft" then
579                 setmetatable(itemdef, {__index = minetest.craftitemdef_default})
580                 minetest.registered_craftitems[itemdef.name] = itemdef
581         elseif itemdef.type == "tool" then
582                 setmetatable(itemdef, {__index = minetest.tooldef_default})
583                 minetest.registered_tools[itemdef.name] = itemdef
584         elseif itemdef.type == "none" then
585                 setmetatable(itemdef, {__index = minetest.noneitemdef_default})
586         else
587                 error("Unable to register item: Type is invalid: " .. dump(itemdef))
588         end
589
590         -- Flowing liquid uses param2
591         if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
592                 itemdef.paramtype2 = "flowingliquid"
593         end
594
595         -- BEGIN Legacy stuff
596         if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
597                 minetest.register_craft({
598                         type="cooking",
599                         output=itemdef.cookresult_itemstring,
600                         recipe=itemdef.name,
601                         cooktime=itemdef.furnace_cooktime
602                 })
603         end
604         if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
605                 minetest.register_craft({
606                         type="fuel",
607                         recipe=itemdef.name,
608                         burntime=itemdef.furnace_burntime
609                 })
610         end
611         -- END Legacy stuff
612
613         -- Disable all further modifications
614         getmetatable(itemdef).__newindex = {}
615
616         --minetest.log("Registering item: " .. itemdef.name)
617         minetest.registered_items[itemdef.name] = itemdef
618         minetest.registered_aliases[itemdef.name] = nil
619         register_item_raw(itemdef)
620 end
621
622 function minetest.register_node(name, nodedef)
623         nodedef.type = "node"
624         minetest.register_item(name, nodedef)
625 end
626
627 function minetest.register_craftitem(name, craftitemdef)
628         craftitemdef.type = "craft"
629
630         -- BEGIN Legacy stuff
631         if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
632                 craftitemdef.inventory_image = craftitemdef.image
633         end
634         -- END Legacy stuff
635
636         minetest.register_item(name, craftitemdef)
637 end
638
639 function minetest.register_tool(name, tooldef)
640         tooldef.type = "tool"
641         tooldef.stack_max = 1
642
643         -- BEGIN Legacy stuff
644         if tooldef.inventory_image == nil and tooldef.image ~= nil then
645                 tooldef.inventory_image = tooldef.image
646         end
647         if tooldef.tool_capabilities == nil and
648            (tooldef.full_punch_interval ~= nil or
649             tooldef.basetime ~= nil or
650             tooldef.dt_weight ~= nil or
651             tooldef.dt_crackiness ~= nil or
652             tooldef.dt_crumbliness ~= nil or
653             tooldef.dt_cuttability ~= nil or
654             tooldef.basedurability ~= nil or
655             tooldef.dd_weight ~= nil or
656             tooldef.dd_crackiness ~= nil or
657             tooldef.dd_crumbliness ~= nil or
658             tooldef.dd_cuttability ~= nil) then
659                 tooldef.tool_capabilities = {
660                         full_punch_interval = tooldef.full_punch_interval,
661                         basetime = tooldef.basetime,
662                         dt_weight = tooldef.dt_weight,
663                         dt_crackiness = tooldef.dt_crackiness,
664                         dt_crumbliness = tooldef.dt_crumbliness,
665                         dt_cuttability = tooldef.dt_cuttability,
666                         basedurability = tooldef.basedurability,
667                         dd_weight = tooldef.dd_weight,
668                         dd_crackiness = tooldef.dd_crackiness,
669                         dd_crumbliness = tooldef.dd_crumbliness,
670                         dd_cuttability = tooldef.dd_cuttability,
671                 }
672         end
673         -- END Legacy stuff
674
675         minetest.register_item(name, tooldef)
676 end
677
678 function minetest.register_alias(name, convert_to)
679         if forbidden_item_names[name] then
680                 error("Unable to register alias: Name is forbidden: " .. name)
681         end
682         if minetest.registered_items[name] ~= nil then
683                 minetest.log("WARNING: Not registering alias, item with same name" ..
684                         " is already defined: " .. name .. " -> " .. convert_to)
685         else
686                 --minetest.log("Registering alias: " .. name .. " -> " .. convert_to)
687                 minetest.registered_aliases[name] = convert_to
688                 register_alias_raw(name, convert_to)
689         end
690 end
691
692 -- Alias the forbidden item names to "" so they can't be
693 -- created via itemstrings (e.g. /give)
694 local name
695 for name in pairs(forbidden_item_names) do
696         minetest.registered_aliases[name] = ""
697         register_alias_raw(name, "")
698 end
699
700
701 -- Deprecated:
702 -- Aliases for minetest.register_alias (how ironic...)
703 --minetest.alias_node = minetest.register_alias
704 --minetest.alias_tool = minetest.register_alias
705 --minetest.alias_craftitem = minetest.register_alias
706
707 --
708 -- Built-in node definitions. Also defined in C.
709 --
710
711 minetest.register_item(":unknown", {
712         type = "none",
713         description = "Unknown Item",
714         inventory_image = "unknown_item.png",
715         on_place = minetest.item_place,
716         on_drop = minetest.item_drop,
717 })
718
719 minetest.register_node(":air", {
720         description = "Air (you hacker you!)",
721         inventory_image = "unknown_block.png",
722         wield_image = "unknown_block.png",
723         drawtype = "airlike",
724         paramtype = "light",
725         sunlight_propagates = true,
726         walkable = false,
727         pointable = false,
728         diggable = false,
729         buildable_to = true,
730         air_equivalent = true,
731 })
732
733 minetest.register_node(":ignore", {
734         description = "Ignore (you hacker you!)",
735         inventory_image = "unknown_block.png",
736         wield_image = "unknown_block.png",
737         drawtype = "airlike",
738         paramtype = "none",
739         sunlight_propagates = false,
740         walkable = false,
741         pointable = false,
742         diggable = false,
743         buildable_to = true, -- A way to remove accidentally placed ignores
744         air_equivalent = true,
745 })
746
747 -- The hand (bare definition)
748 minetest.register_item(":", {
749         type = "none",
750 })
751
752 --
753 -- Default material types
754 --
755 function digprop_err()
756         minetest.log("info", debug.traceback())
757         minetest.log("info", "WARNING: The minetest.digprop_* functions are obsolete and need to be replaced by item groups.")
758 end
759
760 minetest.digprop_constanttime = digprop_err
761 minetest.digprop_stonelike = digprop_err
762 minetest.digprop_dirtlike = digprop_err
763 minetest.digprop_gravellike = digprop_err
764 minetest.digprop_woodlike = digprop_err
765 minetest.digprop_leaveslike = digprop_err
766 minetest.digprop_glasslike = digprop_err
767
768 --
769 -- Creative inventory
770 --
771
772 minetest.creative_inventory = {}
773
774 minetest.add_to_creative_inventory = function(itemstring)
775         table.insert(minetest.creative_inventory, itemstring)
776 end
777
778 --
779 -- Callback registration
780 --
781
782 local function make_registration()
783         local t = {}
784         local registerfunc = function(func) table.insert(t, func) end
785         return t, registerfunc
786 end
787
788 minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
789 minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
790 minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
791 minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()
792 minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
793 minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
794 minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration()
795 minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration()
796 minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration()
797
798 --
799 -- Set random seed
800 --
801
802 math.randomseed(os.time())
803
804 -- END