Compare commits

..

56 Commits

Author SHA1 Message Date
Perttu Ahola
3bea1a944e Call this 0.4.dev-20120603 2012-06-04 00:37:51 +03:00
darkrose
3e419ffb38 Add InvRef:is_empty(listname) and make chests/furnaces not diggable if not empty in minimal game 2012-06-03 22:31:02 +03:00
darkrose
a149c6ecde Add can_dig callback 2012-06-03 22:31:02 +03:00
darkrose
3f18e9a2da fix locked chest to not destroy denied items (minimal game) 2012-06-03 22:31:02 +03:00
Perttu Ahola
4b0494062e Add fire visualization to minimal furnace menu 2012-06-03 22:31:02 +03:00
Perttu Ahola
a09d86dd3c Update inventory menu from formspec on-the-fly 2012-06-03 22:31:02 +03:00
Perttu Ahola
3ccb0f691b Add texture modification [lowpart:percent:filename 2012-06-03 22:31:02 +03:00
Perttu Ahola
1709fd854a Implement image[<X>,<Y>;<W>,<H>;<texture name>] and allow floating-point values in formspec 2012-06-03 22:31:02 +03:00
Perttu Ahola
7bf446f671 Reshape inventory menu code 2012-06-03 22:31:02 +03:00
Perttu Ahola
e4bc76f55c Add a bit of unit test for inventory 2012-06-03 22:31:02 +03:00
Perttu Ahola
81c5e5200b Fix loading of legacy chests 2012-06-03 22:31:01 +03:00
Perttu Ahola
9c3830dd35 Use proper furnace cook time 2012-06-03 22:31:01 +03:00
Perttu Ahola
bf8cfce50e Add ObjRef:is_player() and modify ObjRef:get_player_name() to always return a string to aid better inter-object compatibility of code that assumes objects to be players 2012-06-03 22:31:01 +03:00
Perttu Ahola
b235e4d290 Make GUIinventoryMenu::drawList more robust with invalid data 2012-06-03 22:31:01 +03:00
darkrose
b8cf6dee3e Lua implementation of furnace with visible active state 2012-06-03 22:31:01 +03:00
Perttu Ahola
35df1ee253 Implement locked chest; add after_place_node and after_dig_node node callbacks 2012-06-03 22:31:01 +03:00
Perttu Ahola
353f9fd43c minetest.get_craft_result 2012-06-03 22:31:01 +03:00
Perttu Ahola
3899f83da8 NodeMetaRef:{to,from}_table and lua_api.txt additions 2012-06-03 22:31:01 +03:00
Perttu Ahola
5f3c70061d Properly handle dropping of items from nodes, and disallow moving items directly between nodes 2012-06-03 22:31:01 +03:00
Perttu Ahola
aba7134301 on_metadata_inventory_{move,offer,take} 2012-06-03 22:31:01 +03:00
Perttu Ahola
d7447cdf9e Implement sign using form field protocol 2012-06-03 22:31:01 +03:00
Perttu Ahola
ff8d2bbc36 Convert legacy chest inventory list "0" to "main" 2012-06-03 22:31:00 +03:00
Perttu Ahola
70c98c9972 Properly create metadata inventories in minimal 2012-06-03 22:31:00 +03:00
Perttu Ahola
45f32838b9 Improve GUIInventoryMenu error messages more 2012-06-03 22:31:00 +03:00
Perttu Ahola
7d6e80a423 Add proper error messages to GUIInventoryMenu when inventory lists are invalid 2012-06-03 22:31:00 +03:00
Perttu Ahola
f3ec1452cd Random node metadata things 2012-06-03 22:31:00 +03:00
Perttu Ahola
29f03756bd Attempt to begin to implement chests and furnace in Lua (with problems) 2012-06-03 22:31:00 +03:00
Perttu Ahola
fe8c5546f0 Add node on_construct, on_destruct callbacks and update lua_api.txt 2012-06-03 22:31:00 +03:00
darkrose
c4315a7afa Strip unneeded variables from NodeMetadata 2012-06-03 22:31:00 +03:00
Kahrl
704782c95b WIP node metadata, node timers 2012-06-03 22:31:00 +03:00
Perttu Ahola
67059e1932 Catch VersionMismatchException while loading world data 2012-06-03 20:42:49 +03:00
Perttu Ahola
e2ea711136 Check password hash validity 2012-06-03 20:32:44 +03:00
Perttu Ahola
74aa598769 Fix MSVC build (pow type overload stuff) 2012-05-31 20:22:37 +03:00
Perttu Ahola
747ff7daf8 Add support for putting modpacks directly into mod directories 2012-05-21 19:49:35 +03:00
Perttu Ahola
1f670fc688 Add better trace peudo-loglevel support 2012-05-20 18:30:30 +03:00
Perttu Ahola
388906ac70 Allow using --worldname for local game too 2012-05-20 17:41:40 +03:00
Perttu Ahola
49d9745dc4 Comment out unnecessary debug output in Settings 2012-05-20 17:19:48 +03:00
Perttu Ahola
7c15d793ac --world list 2012-05-20 17:09:46 +03:00
Perttu Ahola
d90cb3511c Better description of static_spawnpoint in minetest.conf.example 2012-05-20 16:59:13 +03:00
Perttu Ahola
5100272dc3 Update minetest.conf.example a bit 2012-05-20 14:54:02 +03:00
Perttu Ahola
afda9810f1 Tune smooth lighting a bit 2012-05-20 14:12:17 +03:00
Perttu Ahola
22df8b81b3 Log more stuff in Client::afterContentReceived() 2012-05-19 17:11:41 +03:00
Perttu Ahola
7a427c9070 Take out the "Privileges of foo are hidden from you." response of /privs 2012-05-19 12:42:55 +03:00
Perttu Ahola
a35e4bdbda Add a hack to get rid of the rare "ERROR: StaticObjectList::insert(): id already exists" failure 2012-04-15 14:37:35 +03:00
Perttu Ahola
0aeb5adb8f Fix continuing to process TOSERVER_INIT2 even if player doesn't exist 2012-04-14 18:46:10 +03:00
Perttu Ahola
9c5cc217bf Add "Registered definitions of stuff" to doc/lua_api.txt 2012-04-14 11:24:18 +03:00
Perttu Ahola
24603d7ad3 Fix executing register_chatcommand-defined commands even in the middle of a chat line 2012-04-13 17:20:48 +03:00
Perttu Ahola
1eac6ff8ff Fix unhandled InvalidPositionException 2012-04-10 18:16:03 +03:00
Perttu Ahola
8742b3dab2 basic_privs to allow granting/revoking interact_extra too 2012-04-10 00:41:28 +03:00
Perttu Ahola
93cdc9b9dc Support static_spawnpoint setting 2012-04-09 23:32:13 +03:00
Perttu Ahola
8ed74a3429 Fix ABM handling on block activation 2012-04-09 23:06:24 +03:00
Perttu Ahola
07a5e59ae7 Allow redefining minetest.item_place and the like 2012-04-09 21:14:16 +03:00
Perttu Ahola
f8c2f1cf54 Make client know about reverted disallowed diggings 2012-04-09 21:13:22 +03:00
Perttu Ahola
b1f043d010 Add basic_privs privilege 2012-04-09 18:57:41 +03:00
Perttu Ahola
251c0c8508 Improve doc/lua_api.txt and add minetest.get_item_group(name, group) 2012-04-09 12:36:25 +03:00
Perttu Ahola
3214daca4c Fix unable to join server first time with a password 2012-04-09 11:24:35 +03:00
53 changed files with 2684 additions and 1904 deletions

View File

@@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH dev-20120408)
set(VERSION_PATCH dev-20120603)
if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif()

View File

@@ -84,12 +84,9 @@ minetest.builtin_auth_handler = {
-- always has an empty password, otherwise use default, which is
-- usually empty too)
local new_password_hash = ""
if not minetest.is_singleplayer() then
new_password_hash = minetest.get_password_hash(name, minetest.setting_get("default_password"))
end
-- Add player to authentication table if not there already
-- If not in authentication table, return nil
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
return nil
end
-- Figure out what privileges the player should have.
-- Take a copy of the privilege table

View File

@@ -19,4 +19,5 @@ dofile(minetest.get_modpath("__builtin").."/misc.lua")
dofile(minetest.get_modpath("__builtin").."/privileges.lua")
dofile(minetest.get_modpath("__builtin").."/auth.lua")
dofile(minetest.get_modpath("__builtin").."/chatcommands.lua")
dofile(minetest.get_modpath("__builtin").."/static_spawn.lua")

View File

@@ -14,7 +14,7 @@ function minetest.register_chatcommand(cmd, def)
end
minetest.register_on_chat_message(function(name, message)
local cmd, param = string.match(message, "/([^ ]+) *(.*)")
local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
if not param then
param = ""
end
@@ -101,9 +101,10 @@ minetest.register_chatcommand("privs", {
if param == "" then
param = name
else
if not minetest.check_player_privs(name, {privs=true}) then
--[[if not minetest.check_player_privs(name, {privs=true}) then
minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
end
return
end]]
end
minetest.chat_send_player(name, "Privileges of "..param..": "..minetest.privs_to_string(minetest.get_player_privs(param), ' '))
end,
@@ -111,8 +112,13 @@ minetest.register_chatcommand("privs", {
minetest.register_chatcommand("grant", {
params = "<name> <privilege>|all",
description = "Give privilege to player",
privs = {privs=true},
privs = {},
func = function(name, param)
if not minetest.check_player_privs(name, {privs=true}) and
not minetest.check_player_privs(name, {basic_privs=true}) then
minetest.chat_send_player(name, "Your privileges are insufficient.")
return
end
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
if not grantname or not grantprivstr then
minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
@@ -125,6 +131,10 @@ minetest.register_chatcommand("grant", {
local privs = minetest.get_player_privs(grantname)
local privs_known = true
for priv, _ in pairs(grantprivs) do
if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
minetest.chat_send_player(name, "Your privileges are insufficient.")
return
end
if not minetest.registered_privileges[priv] then
minetest.chat_send_player(name, "Unknown privilege: "..priv)
privs_known = false
@@ -144,8 +154,13 @@ minetest.register_chatcommand("grant", {
minetest.register_chatcommand("revoke", {
params = "<name> <privilege>|all",
description = "Remove privilege from player",
privs = {privs=true},
privs = {},
func = function(name, param)
if not minetest.check_player_privs(name, {privs=true}) and
not minetest.check_player_privs(name, {basic_privs=true}) then
minetest.chat_send_player(name, "Your privileges are insufficient.")
return
end
local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
if not revokename or not revokeprivstr then
minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
@@ -153,6 +168,12 @@ minetest.register_chatcommand("revoke", {
end
local revokeprivs = minetest.string_to_privs(revokeprivstr)
local privs = minetest.get_player_privs(revokename)
for priv, _ in pairs(revokeprivs) do
if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
minetest.chat_send_player(name, "Your privileges are insufficient.")
return
end
end
if revokeprivstr == "all" then
privs = {}
else
@@ -302,7 +323,7 @@ minetest.register_chatcommand("set", {
privs = {server=true},
func = function(name, param)
local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
if arg and arg == "n" and setname and setvalue then
if arg and arg == "-n" and setname and setvalue then
minetest.setting_set(setname, setvalue)
minetest.chat_send_player(name, setname.." = "..setvalue)
return

View File

@@ -158,12 +158,12 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
-- Add node and update
minetest.env:add_node(pos, newnode)
-- Set metadata owner
if def.metadata_name ~= "" then
minetest.env:get_meta(pos):set_owner(placer:get_player_name())
-- Run callback
if def.after_place_node then
def.after_place_node(pos, placer)
end
-- Run script hook
-- Run script hook (deprecated)
local _, callback
for _, callback in ipairs(minetest.registered_on_placenodes) do
callback(pos, newnode, placer)
@@ -229,7 +229,7 @@ function minetest.node_dig(pos, node, digger)
minetest.debug("node_dig")
local def = ItemStack({name=node.name}):get_definition()
if not def.diggable then
if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
minetest.debug("not diggable")
minetest.log("info", digger:get_player_name() .. " tried to dig "
.. node.name .. " which is not diggable "
@@ -237,15 +237,6 @@ function minetest.node_dig(pos, node, digger)
return
end
local meta = minetest.env:get_meta(pos)
if meta ~= nil and not meta:get_allow_removal() then
minetest.debug("dig prevented by metadata")
minetest.log("info", digger:get_player_name() .. " tried to dig "
.. node.name .. ", but removal is disabled by metadata "
.. minetest.pos_to_string(pos))
return
end
minetest.log('action', digger:get_player_name() .. " digs "
.. node.name .. " at " .. minetest.pos_to_string(pos))
@@ -265,17 +256,73 @@ function minetest.node_dig(pos, node, digger)
digger:get_inventory():add_item("main", dropped_item)
end
end
local oldnode = nil
local oldmetadata = nil
if def.after_dig_node then
oldnode = node;
oldmetadata = minetest.env:get_meta(pos):to_table()
end
-- Remove node and update
minetest.env:remove_node(pos)
-- Run callback
if def.after_dig_node then
def.after_dig_node(pos, oldnode, oldmetadata, digger)
end
-- Run script hook
-- Run script hook (deprecated)
local _, callback
for _, callback in ipairs(minetest.registered_on_dignodes) do
callback(pos, node, digger)
end
end
function minetest.node_metadata_inventory_move_allow_all(pos, from_list,
from_index, to_list, to_index, count, player)
minetest.log("verbose", "node_metadata_inventory_move_allow_all")
local meta = minetest.env:get_meta(pos)
local inv = meta:get_inventory()
local from_stack = inv:get_stack(from_list, from_index)
local taken_items = from_stack:take_item(count)
inv:set_stack(from_list, from_index, from_stack)
local to_stack = inv:get_stack(to_list, to_index)
to_stack:add_item(taken_items)
inv:set_stack(to_list, to_index, to_stack)
end
function minetest.node_metadata_inventory_offer_allow_all(pos, listname, index, stack, player)
minetest.log("verbose", "node_metadata_inventory_offer_allow_all")
local meta = minetest.env:get_meta(pos)
local inv = meta:get_inventory()
local the_stack = inv:get_stack(listname, index)
the_stack:add_item(stack)
inv:set_stack(listname, index, the_stack)
return ItemStack("")
end
function minetest.node_metadata_inventory_take_allow_all(pos, listname, index, count, player)
minetest.log("verbose", "node_metadata_inventory_take_allow_all")
local meta = minetest.env:get_meta(pos)
local inv = meta:get_inventory()
local the_stack = inv:get_stack(listname, index)
local taken_items = the_stack:take_item(count)
inv:set_stack(listname, index, the_stack)
return taken_items
end
-- This is used to allow mods to redefine minetest.item_place and so on
-- NOTE: This is not the preferred way. Preferred way is to provide enough
-- callbacks to not require redefining global functions. -celeron55
local function redef_wrapper(table, name)
return function(...)
return table[name](...)
end
end
--
-- Item definition defaults
--
@@ -295,12 +342,19 @@ minetest.nodedef_default = {
tool_capabilities = nil,
-- Interaction callbacks
on_place = minetest.item_place,
on_drop = minetest.item_drop,
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
on_use = nil,
can_dig = nil,
on_punch = minetest.node_punch,
on_dig = minetest.node_dig,
on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch
on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig
on_receive_fields = nil,
on_metadata_inventory_move = minetest.node_metadata_inventory_move_allow_all,
on_metadata_inventory_offer = minetest.node_metadata_inventory_offer_allow_all,
on_metadata_inventory_take = minetest.node_metadata_inventory_take_allow_all,
-- Node properties
drawtype = "normal",
@@ -321,7 +375,6 @@ minetest.nodedef_default = {
diggable = true,
climbable = false,
buildable_to = false,
metadata_name = "",
liquidtype = "none",
liquid_alternative_flowing = "",
liquid_alternative_source = "",
@@ -346,8 +399,8 @@ minetest.craftitemdef_default = {
tool_capabilities = nil,
-- Interaction callbacks
on_place = minetest.item_place,
on_drop = minetest.item_drop,
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
on_use = nil,
}
@@ -364,8 +417,8 @@ minetest.tooldef_default = {
tool_capabilities = nil,
-- Interaction callbacks
on_place = minetest.item_place,
on_drop = minetest.item_drop,
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
on_use = nil,
}

View File

@@ -44,7 +44,7 @@ function minetest.get_connected_players()
-- This could be optimized a bit, but leave that for later
local list = {}
for _, obj in pairs(minetest.env:get_objects_inside_radius({x=0,y=0,z=0}, 1000000)) do
if obj:get_player_name() then
if obj:is_player() then
table.insert(list, obj)
end
end
@@ -55,11 +55,47 @@ function minetest.hash_node_position(pos)
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
end
function minetest.get_node_group(name, group)
if not minetest.registered_nodes[name] or not
minetest.registered_nodes[name].groups[group] then
function minetest.get_item_group(name, group)
if not minetest.registered_items[name] or not
minetest.registered_items[name].groups[group] then
return 0
end
return minetest.registered_nodes[name].groups[group]
return minetest.registered_items[name].groups[group]
end
function minetest.get_node_group(name, group)
return minetest.get_item_group(name, group)
end
function minetest.string_to_pos(value)
local p = {}
p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
if p.x and p.y and p.z then
p.x = tonumber(p.x)
p.y = tonumber(p.y)
p.z = tonumber(p.z)
return p
end
local p = {}
p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
if p.x and p.y and p.z then
p.x = tonumber(p.x)
p.y = tonumber(p.y)
p.z = tonumber(p.z)
return p
end
return nil
end
assert(minetest.string_to_pos("10.0, 5, -2").x == 10)
assert(minetest.string_to_pos("( 10.0, 5, -2)").z == -2)
assert(minetest.string_to_pos("asd, 5, -2)") == nil)
function minetest.setting_get_pos(name)
local value = minetest.setting_get(name)
if not value then
return nil
end
return minetest.string_to_pos(value)
end

View File

@@ -301,8 +301,6 @@ end
minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()
minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration()
@@ -311,4 +309,16 @@ minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make
minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration()
minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
minetest.registered_on_placenodes = {}
minetest.register_on_placenode = function(callback)
minetest.log("info", debug.traceback())
minetest.log("info", "WARNING: minetest.register_on_placenode is deprecated. Use on_construct or after_place_node in node definition instead.")
table.insert(minetest.registered_on_placenodes, callback)
end
minetest.registered_on_dignodes = {}
minetest.register_on_dignode = function(callback)
minetest.log("info", debug.traceback())
minetest.log("info", "WARNING: minetest.register_on_dignode is deprecated. Use on_destruct or after_dig_node in node definition instead.")
table.insert(minetest.registered_on_dignodes, callback)
end

View File

@@ -30,6 +30,7 @@ minetest.register_privilege("teleport", "Can use /teleport command")
minetest.register_privilege("bring", "Can teleport other players")
minetest.register_privilege("settime", "Can use /time")
minetest.register_privilege("privs", "Can modify privileges")
minetest.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
minetest.register_privilege("server", "Can do server maintenance stuff")
minetest.register_privilege("shout", "Can speak in chat")
minetest.register_privilege("ban", "Can ban and unban players")

33
builtin/static_spawn.lua Normal file
View File

@@ -0,0 +1,33 @@
-- Minetest: builtin/static_spawn.lua
local function warn_invalid_static_spawnpoint()
if minetest.setting_get("static_spawnpoint") and
not minetest.setting_get_pos("static_spawnpoint") then
minetest.log('error', "The static_spawnpoint setting is invalid: \""..
minetest.setting_get("static_spawnpoint").."\"")
end
end
warn_invalid_static_spawnpoint()
local function put_player_in_spawn(obj)
warn_invalid_static_spawnpoint()
local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint")
if not static_spawnpoint then
return false
end
minetest.log('action', "Moving "..obj:get_player_name()..
" to static spawnpoint at "..
minetest.pos_to_string(static_spawnpoint))
obj:setpos(static_spawnpoint)
return true
end
minetest.register_on_newplayer(function(obj)
put_player_in_spawn(obj)
end)
minetest.register_on_respawnplayer(function(obj)
return put_player_in_spawn(obj)
end)

View File

@@ -25,7 +25,7 @@ If you have any difficulty in understanding this, please read:
Startup
--------
Mods are loaded during server startup from the mod load paths by running
the init.lua scripts.
the init.lua scripts in a shared environment.
Mod load path
-------------
@@ -59,6 +59,12 @@ This happens if the following directory exists:
Mods should be then be placed in:
$world/game/mods/
Modpack support
----------------
Mods can be put in a subdirectory, if the parent directory, which otherwise
should be a mod, contains a file named modpack.txt. This file shall be
empty, except for lines starting with #, which are comments.
Mod directory structure
------------------------
mods
@@ -180,11 +186,60 @@ Examples of sound parameter tables:
-- Play connected to an object, looped
{
object = <an ObjectRef>,
gain = 1.0, -- default
max_hear_distance = 32, -- default
gain = 1.0, -- default
max_hear_distance = 32, -- default
loop = true, -- only sounds connected to objects can be looped
}
SimpleSoundSpec:
eg. ""
eg. "default_place_node"
eg. {}
eg. {name="default_place_node"}
eg. {name="default_place_node", gain=1.0}
Registered definitions of stuff
--------------------------------
Anything added using certain minetest.register_* functions get added to
the global minetest.registered_* tables.
minetest.register_entity(name, prototype table)
-> minetest.registered_entities[name]
minetest.register_node(name, node definition)
-> minetest.registered_items[name]
-> minetest.registered_nodes[name]
minetest.register_tool(name, item definition)
-> minetest.registered_items[name]
minetest.register_craftitem(name, item definition)
-> minetest.registered_items[name]
Note that in some cases you will stumble upon things that are not contained
in these tables (eg. when a mod has been removed). Always check for
existence before trying to access the fields.
Example: If you want to check the drawtype of a node, you could do:
local function get_nodedef_field(nodename, fieldname)
if not minetest.registered_nodes[nodename] then
return nil
end
return minetest.registered_nodes[nodename][fieldname]
end
local drawtype = get_nodedef_field(nodename, "drawtype")
Example: minetest.get_item_group(name, group) has been implemented as:
function minetest.get_item_group(name, group)
if not minetest.registered_items[name] or not
minetest.registered_items[name].groups[group] then
return 0
end
return minetest.registered_items[name].groups[group]
end
Nodes
------
Nodes are the bulk data of the world: cubes and other things that take the
@@ -193,11 +248,10 @@ are quite static.
The definition of a node is stored and can be accessed by name in
minetest.registered_nodes[node.name]
See "Registered definitions of stuff".
Please note that for unknown nodes (eg. a node of an uninstalled mod) the
minetest.registered_nodes field for the node is nil.
Nodes are passed by value in Lua. They are represented by a table:
Nodes are passed by value between Lua and the engine.
They are represented by a table:
{name="name", param1=num, param2=num}
param1 and param2 are 8 bit and 4 bit integers, respectively. The engine
@@ -224,6 +278,8 @@ param2 is reserved for the engine when any of these are used:
^ The rotation of the node is stored in param2. Furnaces and chests are
rotated this way. Can be made by using minetest.dir_to_facedir().
Nodes can also contain extra data. See "Node Metadata".
Representations of simple things
--------------------------------
Position/vector:
@@ -231,29 +287,6 @@ Position/vector:
Currently the API does not provide any helper functions for addition,
subtraction and whatever; you can define those that you need yourself.
stackstring/itemstring: A stack of items in serialized format.
eg. 'default:dirt 5'
eg. 'default:pick_wood 21323'
eg. 'default:apple'
item: A stack of items in Lua table format.
eg. {name="default:dirt", count=5, wear=0, metadata=""}
^ 5 dirt nodes
eg. {name="default:pick_wood", count=1, wear=21323, metadata=""}
^ a wooden pick about 1/3 weared out
eg. {name="default:apple", count=1, wear=0, metadata=""}
^ an apple.
Any time an item must be passed to a function, it can be an
ItemStack (see below), an itemstring or a table in the above format.
SimpleSoundSpec:
eg. ""
eg. "default_place_node"
eg. {}
eg. {name="default_place_node"}
eg. {name="default_place_node", gain=1.0}
Items
------
Node (register_node):
@@ -263,6 +296,28 @@ Tool (register_tool):
Craftitem (register_craftitem):
A miscellaneous item
Items and item stacks can exist in three formats:
Serialized; This is called stackstring or itemstring:
eg. 'default:dirt 5'
eg. 'default:pick_wood 21323'
eg. 'default:apple'
Table format:
eg. {name="default:dirt", count=5, wear=0, metadata=""}
^ 5 dirt nodes
eg. {name="default:pick_wood", count=1, wear=21323, metadata=""}
^ a wooden pick about 1/3 weared out
eg. {name="default:apple", count=1, wear=0, metadata=""}
^ an apple.
ItemStack:
C++ native format with many helper methods. Useful for converting between
formats. See the Class reference section for details.
When an item must be passed to a function, it can usually be in any of
these formats.
Groups
-------
In a number of places, there is a group table. Groups define the
@@ -282,6 +337,9 @@ Usage:
- When not defined, the rating of a group defaults to 0. Thus when you
read groups, you must interpret nil and 0 as the same value, 0.
You can read the rating of a group for an item or a node by using
minetest.get_item_group(itemname, groupname)
Groups of items
----------------
Groups of items can define what kind of an item it is (eg. wool).
@@ -314,7 +372,7 @@ Groups in crafting recipes
{'group:water'},
{'group:bowl'},
},
preserve = {'group:bowl'},
preserve = {'group:bowl'},
}
Special groups
@@ -346,7 +404,7 @@ Valid ratings for these are 0, 1, 2 and 3, unless otherwise stated.
Can be added to nodes that shouldn't logically be breakable by the
hand but are. Somewhat similar to dig_immediate, but times are more
like {[1]=3.50,[2]=2.00,[3]=0.70} and this does not override the
speed of a tool if the tool can dig at a larger speed than this
speed of a tool if the tool can dig at a faster speed than this
suggests for the hand.
Examples of custom groups
@@ -402,9 +460,9 @@ it's useful item. (eg. iron ore to drop a lump of iron).
Determines how many uses the tool has when it is used for digging a node,
of this group, of the maximum level. For lower leveled nodes, the use count
is multiplied by 3^leveldiff.
- uses=10, leveldiff=0 -> actual_uses=10
- uses=10, leveldiff=1 -> actual_uses=30
- uses=10, leveldiff=2 -> actual_uses=90
- uses=10, leveldiff=0 -> actual uses: 10
- uses=10, leveldiff=1 -> actual uses: 30
- uses=10, leveldiff=2 -> actual uses: 90
**Maximum level**
Tells what is the maximum level of a node of this group that the tool will
@@ -414,7 +472,7 @@ be able to dig.
List of digging times for different ratings of the group, for nodes of the
maximum level.
* For example, as a lua table, ''times={2=2.00, 3=0.70}''. This would
result the tool to be able to dig nodes that have a rating of 2 or 3
result in the tool to be able to dig nodes that have a rating of 2 or 3
for this group, and unable to dig the rating 1, which is the toughest.
Unless there is a matching group that enables digging otherwise.
* For entities, damage equals the amount of nodes dug in the time spent
@@ -460,7 +518,7 @@ Entity damage mechanism
Damage calculation:
- Take the time spent after the last hit
- Limit time to full_punch_interval
- Take the damage groups, assume a node has them
- Take the damage groups and imagine a bunch of nodes that have them
- Damage in HP is the amount of nodes destroyed in this time.
Client predicts damage based on damage groups. Because of this, it is able to
@@ -469,28 +527,119 @@ pre-defined somehow (eg. by defining a sprite animation) (not implemented;
TODO).
- Currently a smoke puff will appear when an entity dies.
The group **immortal** will completely disable normal damage.
The group **immortal** completely disables normal damage.
Entities can define a special armor group, which is **punch_operable**. This
group will disable the regular damage mechanism for players punching it by hand
or a non-tool item.
group disables the regular damage mechanism for players punching it by hand or
a non-tool item, so that it can do something else than take damage.
On the Lua side, every punch calls ''entity:on_punch(puncher,
time_from_last_punch, tool_capabilities, direction)''. This should never be
called directly, because damage is usually not handled by the entity itself.
* ''puncher'' is the object performing the punch. Can be nil. Should never be
accessed unless absolutely required.
accessed unless absolutely required, to encourage interoperability.
* ''time_from_last_punch'' is time from last punch (by puncher) or nil.
* ''tool_capabilities'' can be nil.
* ''direction'' is a unit vector, pointing from the source of the punch to
the punched object.
To punch an entity/object in Lua, call ''object:punch(puncher, time_from_last_punch, tool_capabilities, direction)''.
To punch an entity/object in Lua, call ''object:punch(puncher,
time_from_last_punch, tool_capabilities, direction)''.
* Return value is tool wear.
* Parameters are equal to the above callback.
* If ''direction'' is nil and ''puncher'' is not nil, ''direction'' will be
automatically filled in based on the location of ''puncher''.
Node Metadata
-------------
The instance of a node in the world normally only contains the three values
mentioned in "Nodes". However, it is possible to insert extra data into a
node. It is called "node metadata"; See "NodeMetaRef".
Metadata contains two things:
- A key-value store
- An inventory
Some of the values in the key-value store are handled specially:
- formspec: Defines a right-click inventory menu. See "Formspec".
- infotext: Text shown on the screen when the node is pointed at
Example stuff:
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec",
"invsize[8,9;]"..
"list[current_name;main;0,0;8,4;]"..
"list[current_player;main;0,5;8,4;]")
meta:set_string("infotext", "Chest");
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
print(dump(meta:to_table()))
meta:from_table({
inventory = {
main = {[1] = "default:dirt", [2] = "", [3] = "", [4] = "", [5] = "", [6] = "", [7] = "", [8] = "", [9] = "", [10] = "", [11] = "", [12] = "", [13] = "", [14] = "default:cobble", [15] = "", [16] = "", [17] = "", [18] = "", [19] = "", [20] = "default:cobble", [21] = "", [22] = "", [23] = "", [24] = "", [25] = "", [26] = "", [27] = "", [28] = "", [29] = "", [30] = "", [31] = "", [32] = ""}
},
fields = {
formspec = "invsize[8,9;]list[current_name;main;0,0;8,4;]list[current_player;main;0,5;8,4;]",
infotext = "Chest"
}
})
Formspec
--------
Formspec defines a menu. Currently not much else than inventories are
supported. It is a string, with a somewhat strange format.
Spaces and newlines can be inserted between the blocks, as is used in the
examples.
Examples:
- Chest:
invsize[8,9;]
list[current_name;main;0,0;8,4;]
list[current_player;main;0,5;8,4;]
- Furnace:
invsize[8,9;]
list[current_name;fuel;2,3;1,1;]
list[current_name;src;2,1;1,1;]
list[current_name;dst;5,1;2,2;]
list[current_player;main;0,5;8,4;]
- Minecraft-like player inventory
invsize[8,7.5;]
image[1,0.6;1,2;player.png]
list[current_player;main;0,3.5;8,4;]
list[current_player;craft;3,0;3,3;]
list[current_player;craftpreview;7,1;1,1;]
Elements:
invsize[<W>,<H>;]
^ Define the size of the menu in inventory slots
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;]
^ Show an inventory list
image[<X>,<Y>;<W>,<H>;<texture name>]
^ Show an image
^ Position and size units are inventory slots
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
^ Textual field; will be sent to server when a button is clicked
^ Position and size units are inventory slots
^ Not implemented
button[<X>,<Y>;<W>,<H>;<name>;<label>]
^ Clickable button. When clicked, fields will be sent.
^ Button will be visible as a field, with the value "active".
^ Position and size units are inventory slots
^ Not implemented
Inventory location:
- "context": Selected node metadata (deprecated: "current_name")
- "current_player": Player to whom the menu is shown
- "player:<name>": Any player
- "nodemeta:<X>,<Y>,<Z>": Any node metadata
Helper functions
-----------------
dump2(obj, name="_", dumped={})
@@ -503,6 +652,7 @@ string:trim()
^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)"
^ Convert position to a printable string
minetest.string_to_pos(string) -> position
minetest namespace reference
-----------------------------
@@ -514,7 +664,7 @@ minetest.get_worldpath() -> eg. "/home/user/.minetest/world"
minetest.is_singleplayer()
minetest.debug(line)
^ Goes to dstream
^ Always printed to stderr and logfile (print() is redirected here)
minetest.log(line)
minetest.log(loglevel, line)
^ loglevel one of "error", "action", "info", "verbose"
@@ -527,17 +677,32 @@ minetest.register_tool(name, item definition)
minetest.register_craftitem(name, item definition)
minetest.register_alias(name, convert_to)
minetest.register_craft(recipe)
Global callback registration functions: (Call these only at load time)
minetest.register_globalstep(func(dtime))
^ Called every server step, usually interval of 0.05s
minetest.register_on_placenode(func(pos, newnode, placer))
^ Called when a node has been placed
^ Deprecated: Use on_construct or after_place_node in node definition instead
minetest.register_on_dignode(func(pos, oldnode, digger))
^ Called when a node has been dug. digger can be nil.
^ Deprecated: Use on_destruct or after_dig_node in node definition instead
minetest.register_on_punchnode(func(pos, node, puncher))
^ Called when a node is punched
minetest.register_on_generated(func(minp, maxp, blockseed))
^ Called after generating a piece of world. Modifying nodes inside the area
is a bit faster than usually.
minetest.register_on_newplayer(func(ObjectRef))
^ Called after a new player has been created
minetest.register_on_dieplayer(func(ObjectRef))
^ Called when a player dies
minetest.register_on_respawnplayer(func(ObjectRef))
^ Called when player is to be respawned
^ Called _before_ repositioning of player occurs
^ return true in func to disable regular player placement
^ currently called _before_ repositioning of player occurs
minetest.register_on_chat_message(func(name, message))
Other registration functions:
minetest.register_chatcommand(cmd, chatcommand definition)
minetest.register_privilege(name, definition)
^ definition: "description text"
@@ -552,6 +717,7 @@ Setting-related:
minetest.setting_set(name, value)
minetest.setting_get(name) -> string or nil
minetest.setting_getbool(name) -> boolean value or nil
minetest.setting_get_pos(name) -> position or nil
minetest.add_to_creative_inventory(itemstring)
Authentication:
@@ -592,6 +758,14 @@ minetest.dir_to_wallmounted(dir)
minetest.get_node_drops(nodename, toolname)
^ Returns list of item names.
^ Note: This will be removed or modified in a future version.
minetest.get_craft_result(input) -> output, decremented_input
^ input.method = 'normal' or 'cooking' or 'fuel'
^ input.width = for example 3
^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
stack 5, stack 6, stack 7, stack 8, stack 9 }
^ output.item = ItemStack, if unsuccessful: empty ItemStack
^ output.time = number, if unsuccessful: 0
^ decremented_input = like input
Defaults for the on_* item definition functions:
(These return the leftover itemstack)
@@ -628,11 +802,14 @@ Random:
minetest.get_connected_players() -> list of ObjectRefs
minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer
^ Gives a unique hash number for a node position (16+16+16=48bit)
minetest.get_item_group(name, group) -> rating
^ Get rating of a group of an item. (0 = not in group)
minetest.get_node_group(name, group) -> rating
^ Get rating of a group of a node. (0 = not in group)
^ Deprecated: An alias for the former.
Global objects:
minetest.env - environment reference
minetest.env - EnvRef of the server environment and world.
^ Using this you can access nodes and entities
Global tables:
minetest.registered_items
@@ -691,27 +868,20 @@ Deprecated:
- add_rat(pos): Add C++ rat object (no-op)
- add_firefly(pos): Add C++ firefly object (no-op)
NodeMetaRef (this stuff is subject to change in a future version)
NodeMetaRef: Node metadata - reference extra data and functionality stored
in a node
- Can be gotten via minetest.env:get_nodemeta(pos)
methods:
- get_type()
- allows_text_input()
- set_text(text) -- eg. set the text of a sign
- get_text()
- get_owner()
- set_owner(string)
Generic node metadata specific:
- set_infotext(infotext)
- get_inventory() -> InvRef
- set_inventory_draw_spec(string)
- set_allow_text_input(bool)
- set_allow_removal(bool)
- set_enforce_owner(bool)
- is_inventory_modified()
- reset_inventory_modified()
- is_text_modified()
- reset_text_modified()
- set_string(name, value)
- get_string(name)
- set_int(name, value)
- get_int(name)
- set_float(name, value)
- get_float(name)
- get_inventory() -> InvRef
- to_table() -> nil or {fields = {...}, inventory = {list1 = {}, ...}}
- from_table(nil or {})
^ See "Node Metadata"
ObjectRef: Moving things in the game are generally these
(basically reference to a C++ ServerActiveObject)
@@ -748,13 +918,15 @@ LuaEntitySAO-only: (no-op for other objects)
- get_entity_name() (DEPRECATED: Will be removed in a future version)
- get_luaentity()
Player-only: (no-op for other objects)
- get_player_name(): will return nil if is not a player
- is_player(): true for players, false for others
- get_player_name(): returns "" if is not a player
- get_look_dir(): get camera direction as a unit vector
- get_look_pitch(): pitch in radians
- get_look_yaw(): yaw in radians (wraps around pretty randomly as of now)
InvRef: Reference to an inventory
methods:
- is_empty(listname): return true if list is empty
- get_size(listname): get size of a list
- set_size(listname, size): set size of a list
- get_stack(listname, i): get a copy of stack index i in list
@@ -908,9 +1080,13 @@ Item definition (register_node, register_craftitem, register_tool)
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
}
}
on_drop = func(itemstack, dropper, pos),
on_place = func(itemstack, placer, pointed_thing),
^ default: minetest.item_place
on_drop = func(itemstack, dropper, pos),
^ default: minetest.item_drop
on_use = func(itemstack, user, pointed_thing),
^ default: nil
^ Function must return either nil if no item shall be removed from
inventory, or an itemstack to replace the original itemstack.
eg. itemstack:take_item(); return itemstack
@@ -942,7 +1118,6 @@ Node definition (register_node)
buildable_to = false,
drop = "",
-- alternatively drop = { max_items = ..., items = { ... } }
metadata_name = "",
liquidtype = "none",
liquid_alternative_flowing = "",
liquid_alternative_source = "",
@@ -957,6 +1132,69 @@ Node definition (register_node)
dig = <SimpleSoundSpec>, -- "__group" = group-based sound (default)
dug = <SimpleSoundSpec>,
},
on_construct = func(pos),
^ Node constructor; always called after adding node
^ Can set up metadata and stuff like that
^ default: nil
on_destruct = func(pos),
^ Node destructor; always called before removing node
^ default: nil
after_place_node = func(pos, placer),
^ Called after constructing node when node was placed using
minetest.item_place_node
^ default: nil
after_dig_node = func(pos, oldnode, oldmetadata, digger),
^ oldmetadata is in table format
^ Called after destructing node when node was dug using
minetest.node_dig
^ default: nil
can_dig = function(pos,player)
^ returns true if node can be dug, or false if not
^ default: nil
on_punch = func(pos, node, puncher),
^ default: minetest.node_punch
^ By default: does nothing
on_dig = func(pos, node, digger),
^ default: minetest.node_dig
^ By default: checks privileges, wears out tool and removes node
on_receive_fields = func(pos, formname, fields, sender),
^ fields = {name1 = value1, name2 = value2, ...}
^ Called when an UI form (eg. sign text input) returns data
^ default: nil
on_metadata_inventory_move = func(pos, from_list, from_index,
to_list, to_index, count, player),
^ Called when a player wants to move items inside the metadata
^ Should move items, or some items, if permitted. If not, should do
nothing.
^ The engine ensures the action is valid, i.e. the stack fits at the
given position
^ default: minetest.node_metadata_inventory_move_allow_all
on_metadata_inventory_offer = func(pos, listname, index, stack, player),
^ Called when a player wants to put something into the metadata
inventory
^ Should check if the action is permitted (the engine ensures the
action is valid, i.e. the stack fits at the given position)
^ If permitted, modify the metadata inventory and return the
"leftover" stack (normally nil).
^ If not permitted, return itemstack.
^ default: minetest.node_metadata_inventory_offer_allow_all
on_metadata_inventory_take = func(pos, listname, index, count, player),
^ Called when a player wants to take something out of the metadata
inventory
^ Should check if the action is permitted (the engine ensures the
action is valid, i.e. there's a stack of at least “count” items at
that position)
^ If permitted, modify the metadata inventory and return the
stack of items
^ If not permitted, return nil.
^ default: minetest.node_metadata_inventory_take_allow_all
}
Recipe: (register_craft)

View File

@@ -1112,6 +1112,21 @@ minetest.register_node("default:sign_wall", {
groups = {choppy=2,dig_immediate=2},
legacy_wallmounted = true,
sounds = default.node_sound_defaults(),
on_construct = function(pos)
--local n = minetest.env:get_node(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec", "hack:sign_text_input")
meta:set_string("infotext", "\"\"")
end,
on_receive_fields = function(pos, formname, fields, sender)
--print("Sign at "..minetest.pos_to_string(pos).." got "..dump(fields))
local meta = minetest.env:get_meta(pos)
fields.text = fields.text or ""
print((sender:get_player_name() or "").." wrote \""..fields.text..
"\" to sign at "..minetest.pos_to_string(pos))
meta:set_string("text", fields.text)
meta:set_string("infotext", '"'..fields.text..'"')
end,
})
minetest.register_node("default:chest", {
@@ -1119,32 +1134,312 @@ minetest.register_node("default:chest", {
tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
paramtype2 = "facedir",
metadata_name = "chest",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec",
"invsize[8,9;]"..
"list[current_name;main;0,0;8,4;]"..
"list[current_player;main;0,5;8,4;]")
meta:set_string("infotext", "Chest")
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
end,
can_dig = function(pos,player)
local meta = minetest.env:get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
on_metadata_inventory_move = function(pos, from_list, from_index,
to_list, to_index, count, player)
minetest.log("action", player:get_player_name()..
" moves stuff in chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_move_allow_all(
pos, from_list, from_index, to_list, to_index, count, player)
end,
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" moves stuff to chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_offer_allow_all(
pos, listname, index, stack, player)
end,
on_metadata_inventory_take = function(pos, listname, index, count, player)
minetest.log("action", player:get_player_name()..
" takes stuff from chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_take_allow_all(
pos, listname, index, count, player)
end,
})
local function has_locked_chest_privilege(meta, player)
if player:get_player_name() ~= meta:get_string("owner") then
return false
end
return true
end
minetest.register_node("default:chest_locked", {
description = "Locked Chest",
tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
"default_chest_side.png", "default_chest_side.png", "default_chest_lock.png"},
paramtype2 = "facedir",
metadata_name = "locked_chest",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
after_place_node = function(pos, placer)
local meta = minetest.env:get_meta(pos)
meta:set_string("owner", placer:get_player_name() or "")
meta:set_string("infotext", "Locked Chest (owned by "..
meta:get_string("owner")..")")
end,
on_construct = function(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec",
"invsize[8,9;]"..
"list[current_name;main;0,0;8,4;]"..
"list[current_player;main;0,5;8,4;]")
meta:set_string("infotext", "Locked Chest")
meta:set_string("owner", "")
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
end,
can_dig = function(pos,player)
local meta = minetest.env:get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
on_metadata_inventory_move = function(pos, from_list, from_index,
to_list, to_index, count, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return
end
minetest.log("action", player:get_player_name()..
" moves stuff in locked chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_move_allow_all(
pos, from_list, from_index, to_list, to_index, count, player)
end,
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return stack
end
minetest.log("action", player:get_player_name()..
" moves stuff to locked chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_offer_allow_all(
pos, listname, index, stack, player)
end,
on_metadata_inventory_take = function(pos, listname, index, count, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return
end
minetest.log("action", player:get_player_name()..
" takes stuff from locked chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_take_allow_all(
pos, listname, index, count, player)
end,
})
default.furnace_inactive_formspec =
"invsize[8,9;]"..
"image[2,2;1,1;default_furnace_fire_bg.png]"..
"list[current_name;fuel;2,3;1,1;]"..
"list[current_name;src;2,1;1,1;]"..
"list[current_name;dst;5,1;2,2;]"..
"list[current_player;main;0,5;8,4;]"
minetest.register_node("default:furnace", {
description = "Furnace",
tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
paramtype2 = "facedir",
metadata_name = "furnace",
groups = {cracky=2},
legacy_facedir_simple = true,
sounds = default.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec", default.furnace_inactive_formspec)
meta:set_string("infotext", "Furnace")
local inv = meta:get_inventory()
inv:set_size("fuel", 1)
inv:set_size("src", 1)
inv:set_size("dst", 4)
end,
can_dig = function(pos,player)
local meta = minetest.env:get_meta(pos);
local inv = meta:get_inventory()
if not inv:is_empty("fuel") then
return false
elseif not inv:is_empty("dst") then
return false
elseif not inv:is_empty("src") then
return false
end
return true
end,
})
minetest.register_node("default:furnace_active", {
description = "Furnace",
tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front_active.png"},
paramtype2 = "facedir",
light_source = 8,
drop = "default:furnace",
groups = {cracky=2},
legacy_facedir_simple = true,
sounds = default.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec", default.furnace_inactive_formspec)
meta:set_string("infotext", "Furnace");
local inv = meta:get_inventory()
inv:set_size("fuel", 1)
inv:set_size("src", 1)
inv:set_size("dst", 4)
end,
can_dig = function(pos,player)
local meta = minetest.env:get_meta(pos);
local inv = meta:get_inventory()
if not inv:is_empty("fuel") then
return false
elseif not inv:is_empty("dst") then
return false
elseif not inv:is_empty("src") then
return false
end
return true
end,
})
function hacky_swap_node(pos,name)
local node = minetest.env:get_node(pos)
local meta = minetest.env:get_meta(pos)
local meta0 = meta:to_table()
if node.name == name then
return
end
node.name = name
local meta0 = meta:to_table()
minetest.env:set_node(pos,node)
meta = minetest.env:get_meta(pos)
meta:from_table(meta0)
end
minetest.register_abm({
nodenames = {"default:furnace","default:furnace_active"},
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local meta = minetest.env:get_meta(pos)
for i, name in ipairs({
"fuel_totaltime",
"fuel_time",
"src_totaltime",
"src_time"
}) do
if meta:get_string(name) == "" then
meta:set_float(name, 0.0)
end
end
local inv = meta:get_inventory()
local srclist = inv:get_list("src")
local cooked = nil
if srclist then
cooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
end
local was_active = false
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
was_active = true
meta:set_float("fuel_time", meta:get_float("fuel_time") + 1)
meta:set_float("src_time", meta:get_float("src_time") + 1)
if cooked and cooked.item and meta:get_float("src_time") >= cooked.time then
-- check if there's room for output in "dst" list
if inv:room_for_item("dst",cooked.item) then
-- Put result in "dst" list
inv:add_item("dst", cooked.item)
-- take stuff from "src" list
srcstack = inv:get_stack("src", 1)
srcstack:take_item()
inv:set_stack("src", 1, srcstack)
else
print("Could not insert '"..cooked.item.."'")
end
meta:set_string("src_time", 0)
end
end
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
local percent = math.floor(meta:get_float("fuel_time") /
meta:get_float("fuel_totaltime") * 100)
meta:set_string("infotext","Furnace active: "..percent.."%")
hacky_swap_node(pos,"default:furnace_active")
meta:set_string("formspec",
"invsize[8,9;]"..
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100-percent)..":default_furnace_fire_fg.png]"..
"list[current_name;fuel;2,3;1,1;]"..
"list[current_name;src;2,1;1,1;]"..
"list[current_name;dst;5,1;2,2;]"..
"list[current_player;main;0,5;8,4;]")
return
end
local fuel = nil
local cooked = nil
local fuellist = inv:get_list("fuel")
local srclist = inv:get_list("src")
if srclist then
cooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
end
if fuellist then
fuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
end
if fuel.time <= 0 then
meta:set_string("infotext","Furnace out of fuel")
hacky_swap_node(pos,"default:furnace")
meta:set_string("formspec", default.furnace_inactive_formspec)
return
end
if cooked.item:is_empty() then
if was_active then
meta:set_string("infotext","Furnace is empty")
hacky_swap_node(pos,"default:furnace")
meta:set_string("formspec", default.furnace_inactive_formspec)
end
return
end
meta:set_string("fuel_totaltime", fuel.time)
meta:set_string("fuel_time", 0)
local stack = inv:get_stack("fuel", 1)
stack:take_item()
inv:set_stack("fuel", 1, stack)
end,
})
minetest.register_node("default:cobble", {
@@ -1526,6 +1821,57 @@ minetest.register_on_chat_message(function(name, message)
end
end)
--
-- Test some things
--
local function test_get_craft_result()
print("test_get_craft_result()")
-- normal
local input = {
method = "normal",
width = 2,
items = {"", "default:coal_lump", "", "default:stick"}
}
print("torch crafting input: "..dump(input))
local output, decremented_input = minetest.get_craft_result(input)
print("torch crafting output: "..dump(output))
print("torch crafting decremented input: "..dump(decremented_input))
assert(output.item)
print("torch crafting output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "default:torch")
assert(output.item:get_count() == 4)
-- fuel
local input = {
method = "fuel",
width = 1,
items = {"default:coal_lump"}
}
print("coal fuel input: "..dump(input))
local output, decremented_input = minetest.get_craft_result(input)
print("coal fuel output: "..dump(output))
print("coal fuel decremented input: "..dump(decremented_input))
assert(output.time)
assert(output.time > 0)
-- cook
local input = {
method = "cooking",
width = 1,
items = {"default:cobble"}
}
print("cobble cooking input: "..dump(output))
local output, decremented_input = minetest.get_craft_result(input)
print("cobble cooking output: "..dump(output))
print("cobble cooking decremented input: "..dump(decremented_input))
assert(output.time)
assert(output.time > 0)
assert(output.item)
print("cobble cooking output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "default:stone")
assert(output.item:get_count() == 1)
end
test_get_craft_result()
--
-- Done, print some random stuff
--

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -8,7 +8,7 @@
# Uncomment settings by removing the preceding #.
#
# Further documentation:
# http://celeron.55.lt/~celeron55/minetest/wiki/doku.php
# http://c55.me/minetest/wiki/doku.php
#
# NOTE: This file might not be up-to-date, refer to the
# defaultsettings.cpp file for an up-to-date list:
@@ -16,6 +16,7 @@
#
# A vim command to convert most of defaultsettings.cpp to conf file format:
# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# Note: Some of the settings are implemented in Lua
#
# Client and server
@@ -149,6 +150,8 @@
#unlimited_player_transfer_distance = true
# Whether to enable players killing each other
#enable_pvp = true
# If this is set, players will always (re)spawn at the given position
#static_spawnpoint = 0, 10, 0
# Profiler data print interval. #0 = disable.
#profiler_print_interval = 0
@@ -161,12 +164,16 @@
#max_simultaneous_block_sends_per_client = 2
# how many blocks are flying in the wire simultaneously per server
#max_simultaneous_block_sends_server_total = 8
# From how far blocks are sent to clients (value * 16 nodes)
#max_block_send_distance = 10
# From how far blocks are generated for clients (value * 16 nodes)
#max_block_generate_distance = 6
# Interval of sending time of day to clients
#time_send_interval = 5
# Length of day/night cycle. 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged
#time_speed = 96
#server_unload_unused_data_timeout = 29
# Interval of saving important changes in the world
#server_map_save_interval = 5.3
# To reduce lag, block transfers are slowed down when a player is building something.
# This determines how long they are slowed down after placing or removing a node.

View File

@@ -177,6 +177,7 @@ set(common_SRCS
content_mapnode.cpp
collision.cpp
nodemetadata.cpp
nodetimer.cpp
serverobject.cpp
noise.cpp
porting.cpp

View File

@@ -38,6 +38,13 @@ static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
bool base64_is_valid(std::string const& s)
{
for(int i=0; i<s.size(); i++)
if(!is_base64(s[i])) return false;
return true;
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;

View File

@@ -1,4 +1,5 @@
#include <string>
bool base64_is_valid(std::string const& s);
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

View File

@@ -1740,33 +1740,23 @@ void Client::interact(u8 action, const PointedThing& pointed)
Send(0, data, true);
}
void Client::sendSignNodeText(v3s16 p, std::string text)
void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
const std::map<std::string, std::string> &fields)
{
/*
u16 command
v3s16 p
u16 textlen
textdata
*/
std::ostringstream os(std::ios_base::binary);
u8 buf[12];
// Write command
writeU16(buf, TOSERVER_SIGNNODETEXT);
os.write((char*)buf, 2);
// Write p
writeV3S16(buf, p);
os.write((char*)buf, 6);
u16 textlen = text.size();
// Write text length
writeS16(buf, textlen);
os.write((char*)buf, 2);
writeU16(os, TOSERVER_NODEMETA_FIELDS);
writeV3S16(os, p);
os<<serializeString(formname);
writeU16(os, fields.size());
for(std::map<std::string, std::string>::const_iterator
i = fields.begin(); i != fields.end(); i++){
const std::string &name = i->first;
const std::string &value = i->second;
os<<serializeString(name);
os<<serializeLongString(value);
}
// Write text
os.write((char*)text.c_str(), textlen);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
@@ -2348,32 +2338,41 @@ ClientEvent Client::getClientEvent()
void Client::afterContentReceived()
{
verbosestream<<"Client::afterContentReceived() started"<<std::endl;
assert(m_itemdef_received);
assert(m_nodedef_received);
assert(m_media_received);
// remove the information about which checksum each texture
// ought to have
m_media_name_sha1_map.clear();
// Rebuild inherited images and recreate textures
verbosestream<<"Rebuilding images and textures"<<std::endl;
m_tsrc->rebuildImagesAndTextures();
// Update texture atlas
verbosestream<<"Updating texture atlas"<<std::endl;
if(g_settings->getBool("enable_texture_atlas"))
m_tsrc->buildMainAtlas(this);
// Update node aliases
verbosestream<<"Updating node aliases"<<std::endl;
m_nodedef->updateAliases(m_itemdef);
// Update node textures
verbosestream<<"Updating node textures"<<std::endl;
m_nodedef->updateTextures(m_tsrc);
// Update item textures and meshes
verbosestream<<"Updating item textures and meshes"<<std::endl;
m_itemdef->updateTexturesAndMeshes(this);
// Start mesh update thread after setting up content definitions
verbosestream<<"Starting mesh update thread"<<std::endl;
m_mesh_update_thread.Start();
verbosestream<<"Client::afterContentReceived() done"<<std::endl;
}
float Client::getRTT(void)

View File

@@ -210,11 +210,12 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
void interact(u8 action, const PointedThing& pointed);
void sendSignNodeText(v3s16 p, std::string text);
void sendNodemetaFields(v3s16 p, const std::string &formname,
const std::map<std::string, std::string> &fields);
void sendInventoryAction(InventoryAction *a);
void sendChatMessage(const std::wstring &message);
void sendChangePassword(const std::wstring oldpassword,
const std::wstring newpassword);
const std::wstring newpassword);
void sendDamage(u8 damage);
void sendRespawn();

View File

@@ -415,7 +415,7 @@ enum ToServerCommand
wstring message
*/
TOSERVER_SIGNNODETEXT = 0x33,
TOSERVER_SIGNNODETEXT = 0x33, // obsolete
/*
u16 command
v3s16 p
@@ -484,6 +484,20 @@ enum ToServerCommand
s32[len] sound_id
*/
TOSERVER_NODEMETA_FIELDS = 0x3b,
/*
u16 command
v3s16 p
u16 len
u8[len] form name (reserved for future use)
u16 number of fields
for each field:
u16 len
u8[len] field name
u32 len
u8[len] field value
*/
TOSERVER_REQUEST_MEDIA = 0x40,
/*
u16 command

View File

@@ -1,6 +1,6 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,15 +18,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "content_nodemeta.h"
#include <map>
#include "inventory.h"
#include "log.h"
#include "utility.h"
#include "craftdef.h"
#include "gamedef.h"
class Inventory;
#include <sstream>
#define NODEMETA_GENERIC 1
#define NODEMETA_SIGN 14
@@ -34,809 +29,167 @@ class Inventory;
#define NODEMETA_FURNACE 16
#define NODEMETA_LOCKABLE_CHEST 17
core::map<u16, NodeMetadata::Factory> NodeMetadata::m_types;
core::map<std::string, NodeMetadata::Factory2> NodeMetadata::m_names;
// Returns true if node timer must be set
static bool content_nodemeta_deserialize_legacy_body(
std::istream &is, s16 id, NodeMetadata *meta)
{
meta->clear();
class SignNodeMetadata : public NodeMetadata
{
public:
SignNodeMetadata(IGameDef *gamedef, std::string text);
//~SignNodeMetadata();
virtual u16 typeId() const;
virtual const char* typeName() const
{ return "sign"; }
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
static NodeMetadata* create(IGameDef *gamedef);
virtual NodeMetadata* clone(IGameDef *gamedef);
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual bool allowsTextInput(){ return true; }
virtual std::string getText(){ return m_text; }
virtual void setText(const std::string &t){ m_text = t; }
private:
std::string m_text;
};
class ChestNodeMetadata : public NodeMetadata
{
public:
ChestNodeMetadata(IGameDef *gamedef);
~ChestNodeMetadata();
virtual u16 typeId() const;
virtual const char* typeName() const
{ return "chest"; }
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
static NodeMetadata* create(IGameDef *gamedef);
virtual NodeMetadata* clone(IGameDef *gamedef);
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual bool nodeRemovalDisabled();
virtual std::string getInventoryDrawSpecString();
private:
Inventory *m_inventory;
};
class LockingChestNodeMetadata : public NodeMetadata
{
public:
LockingChestNodeMetadata(IGameDef *gamedef);
~LockingChestNodeMetadata();
virtual u16 typeId() const;
virtual const char* typeName() const
{ return "locked_chest"; }
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
static NodeMetadata* create(IGameDef *gamedef);
virtual NodeMetadata* clone(IGameDef *gamedef);
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual bool nodeRemovalDisabled();
virtual std::string getInventoryDrawSpecString();
virtual std::string getOwner(){ return m_text; }
virtual void setOwner(std::string t){ m_text = t; }
private:
Inventory *m_inventory;
std::string m_text;
};
class FurnaceNodeMetadata : public NodeMetadata
{
public:
FurnaceNodeMetadata(IGameDef *gamedef);
~FurnaceNodeMetadata();
virtual u16 typeId() const;
virtual const char* typeName() const
{ return "furnace"; }
virtual NodeMetadata* clone(IGameDef *gamedef);
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
static NodeMetadata* create(IGameDef *gamedef);
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual void inventoryModified();
virtual bool step(float dtime);
virtual bool nodeRemovalDisabled();
virtual std::string getInventoryDrawSpecString();
protected:
bool getCookResult(bool remove, std::string &cookresult, float &cooktime);
bool getBurnResult(bool remove, float &burntime);
private:
Inventory *m_inventory;
std::string m_infotext;
float m_step_accumulator;
float m_fuel_totaltime;
float m_fuel_time;
float m_src_totaltime;
float m_src_time;
};
/*
SignNodeMetadata
*/
// Prototype
SignNodeMetadata proto_SignNodeMetadata(NULL, "");
SignNodeMetadata::SignNodeMetadata(IGameDef *gamedef, std::string text):
NodeMetadata(gamedef),
m_text(text)
{
NodeMetadata::registerType(typeId(), typeName(), create, create);
}
u16 SignNodeMetadata::typeId() const
{
return NODEMETA_SIGN;
}
NodeMetadata* SignNodeMetadata::create(std::istream &is, IGameDef *gamedef)
{
std::string text = deSerializeString(is);
return new SignNodeMetadata(gamedef, text);
}
NodeMetadata* SignNodeMetadata::create(IGameDef *gamedef)
{
return new SignNodeMetadata(gamedef, "");
}
NodeMetadata* SignNodeMetadata::clone(IGameDef *gamedef)
{
return new SignNodeMetadata(gamedef, m_text);
}
void SignNodeMetadata::serializeBody(std::ostream &os)
{
os<<serializeString(m_text);
}
std::string SignNodeMetadata::infoText()
{
return std::string("\"")+m_text+"\"";
}
/*
ChestNodeMetadata
*/
// Prototype
ChestNodeMetadata proto_ChestNodeMetadata(NULL);
ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef):
NodeMetadata(gamedef)
{
NodeMetadata::registerType(typeId(), typeName(), create, create);
m_inventory = NULL;
}
ChestNodeMetadata::~ChestNodeMetadata()
{
delete m_inventory;
}
u16 ChestNodeMetadata::typeId() const
{
return NODEMETA_CHEST;
}
NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
{
ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->deSerialize(is);
return d;
}
NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef)
{
ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->addList("0", 8*4);
return d;
}
NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef)
{
ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
d->m_inventory = new Inventory(*m_inventory);
return d;
}
void ChestNodeMetadata::serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
}
std::string ChestNodeMetadata::infoText()
{
return "Chest";
}
bool ChestNodeMetadata::nodeRemovalDisabled()
{
/*
Disable removal if chest contains something
*/
InventoryList *list = m_inventory->getList("0");
if(list == NULL)
return false;
if(list->getUsedSlots() == 0)
return false;
return true;
}
std::string ChestNodeMetadata::getInventoryDrawSpecString()
{
return
"invsize[8,9;]"
"list[current_name;0;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]";
}
/*
LockingChestNodeMetadata
*/
// Prototype
LockingChestNodeMetadata proto_LockingChestNodeMetadata(NULL);
LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef):
NodeMetadata(gamedef)
{
NodeMetadata::registerType(typeId(), typeName(), create, create);
m_inventory = NULL;
}
LockingChestNodeMetadata::~LockingChestNodeMetadata()
{
delete m_inventory;
}
u16 LockingChestNodeMetadata::typeId() const
{
return NODEMETA_LOCKABLE_CHEST;
}
NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
{
LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
d->setOwner(deSerializeString(is));
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->deSerialize(is);
return d;
}
NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef)
{
LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->addList("0", 8*4);
return d;
}
NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef)
{
LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
d->m_inventory = new Inventory(*m_inventory);
return d;
}
void LockingChestNodeMetadata::serializeBody(std::ostream &os)
{
os<<serializeString(m_text);
m_inventory->serialize(os);
}
std::string LockingChestNodeMetadata::infoText()
{
return "Locking Chest";
}
bool LockingChestNodeMetadata::nodeRemovalDisabled()
{
/*
Disable removal if chest contains something
*/
InventoryList *list = m_inventory->getList("0");
if(list == NULL)
return false;
if(list->getUsedSlots() == 0)
return false;
return true;
}
std::string LockingChestNodeMetadata::getInventoryDrawSpecString()
{
return
"invsize[8,9;]"
"list[current_name;0;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]";
}
/*
FurnaceNodeMetadata
*/
// Prototype
FurnaceNodeMetadata proto_FurnaceNodeMetadata(NULL);
FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef):
NodeMetadata(gamedef)
{
NodeMetadata::registerType(typeId(), typeName(), create, create);
m_inventory = NULL;
m_infotext = "Furnace is inactive";
m_step_accumulator = 0;
m_fuel_totaltime = 0;
m_fuel_time = 0;
m_src_totaltime = 0;
m_src_time = 0;
}
FurnaceNodeMetadata::~FurnaceNodeMetadata()
{
delete m_inventory;
}
u16 FurnaceNodeMetadata::typeId() const
{
return NODEMETA_FURNACE;
}
NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef)
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef);
d->m_inventory = new Inventory(*m_inventory);
return d;
}
NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef)
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->deSerialize(is);
int temp = 0;
is>>temp;
d->m_fuel_totaltime = (float)temp/10;
temp = 0;
is>>temp;
d->m_fuel_time = (float)temp/10;
temp = 0;
is>>temp;
d->m_src_totaltime = (float)temp/10;
temp = 0;
is>>temp;
d->m_src_time = (float)temp/10;
if(is.eof())
if(id == NODEMETA_GENERIC) // GenericNodeMetadata (0.4-dev)
{
// Old furnaces didn't serialize src_totaltime and src_time
d->m_src_totaltime = 0;
d->m_src_time = 0;
d->m_infotext = "";
}
else
{
// New furnaces also serialize the infotext (so that the
// client doesn't need to have the list of cooking recipes).
d->m_infotext = deSerializeJsonString(is);
}
meta->getInventory()->deSerialize(is);
deSerializeLongString(is); // m_text
deSerializeString(is); // m_owner
return d;
}
NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef)
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->addList("fuel", 1);
d->m_inventory->addList("src", 1);
d->m_inventory->addList("dst", 4);
return d;
}
void FurnaceNodeMetadata::serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
os<<itos(m_fuel_totaltime*10)<<" ";
os<<itos(m_fuel_time*10)<<" ";
os<<itos(m_src_totaltime*10)<<" ";
os<<itos(m_src_time*10)<<" ";
os<<serializeJsonString(m_infotext);
}
std::string FurnaceNodeMetadata::infoText()
{
return m_infotext;
}
bool FurnaceNodeMetadata::nodeRemovalDisabled()
{
/*
Disable removal if furnace is not empty
*/
InventoryList *list[3] = {m_inventory->getList("src"),
m_inventory->getList("dst"), m_inventory->getList("fuel")};
for(int i = 0; i < 3; i++) {
if(list[i] == NULL)
continue;
if(list[i]->getUsedSlots() == 0)
continue;
return true;
}
return false;
}
void FurnaceNodeMetadata::inventoryModified()
{
infostream<<"Furnace inventory modification callback"<<std::endl;
}
bool FurnaceNodeMetadata::step(float dtime)
{
if(dtime > 60.0)
infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
InventoryList *dst_list = m_inventory->getList("dst");
assert(dst_list);
// Update at a fixed frequency
const float interval = 2.0;
m_step_accumulator += dtime;
bool changed = false;
while(m_step_accumulator > interval)
{
m_step_accumulator -= interval;
dtime = interval;
//infostream<<"Furnace step dtime="<<dtime<<std::endl;
bool changed_this_loop = false;
// Check
// 1. if the source item is cookable
// 2. if there is room for the cooked item
std::string cookresult;
float cooktime;
bool cookable = getCookResult(false, cookresult, cooktime);
ItemStack cookresult_item;
bool room_available = false;
if(cookable)
{
cookresult_item.deSerialize(cookresult, m_gamedef->idef());
room_available = dst_list->roomForItem(cookresult_item);
}
// Step fuel time
bool burning = (m_fuel_time < m_fuel_totaltime);
if(burning)
{
changed_this_loop = true;
m_fuel_time += dtime;
}
std::string infotext;
if(room_available)
{
float burntime;
if(burning)
{
changed_this_loop = true;
m_src_time += dtime;
m_src_totaltime = cooktime;
infotext = "Furnace is cooking";
}
else if(getBurnResult(true, burntime))
{
// Fuel inserted
changed_this_loop = true;
m_fuel_time = 0;
m_fuel_totaltime = burntime;
//m_src_time += dtime;
//m_src_totaltime = cooktime;
infotext = "Furnace is cooking";
}
else
{
m_src_time = 0;
m_src_totaltime = 0;
infotext = "Furnace is out of fuel";
}
if(m_src_totaltime > 0.001 && m_src_time >= m_src_totaltime)
{
// One item fully cooked
changed_this_loop = true;
dst_list->addItem(cookresult_item);
getCookResult(true, cookresult, cooktime); // decrement source
m_src_totaltime = 0;
m_src_time = 0;
}
}
else
{
// Not cookable or no room available
m_src_totaltime = 0;
m_src_time = 0;
if(cookable)
infotext = "Furnace is overloaded";
else if(burning)
infotext = "Furnace is active";
else
{
infotext = "Furnace is inactive";
m_fuel_totaltime = 0;
m_fuel_time = 0;
}
}
// Do this so it doesn't always show (0%) for weak fuel
if(m_fuel_totaltime > 3) {
infotext += " (";
infotext += itos(m_fuel_time/m_fuel_totaltime*100);
infotext += "%)";
}
if(infotext != m_infotext)
{
m_infotext = infotext;
changed_this_loop = true;
}
if(burning && m_fuel_time >= m_fuel_totaltime)
{
m_fuel_time = 0;
m_fuel_totaltime = 0;
}
if(changed_this_loop)
{
changed = true;
}
else
{
m_step_accumulator = 0;
break;
}
}
return changed;
}
std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
{
return
"invsize[8,9;]"
"list[current_name;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]"
"list[current_player;main;0,5;8,4;]";
}
bool FurnaceNodeMetadata::getCookResult(bool remove,
std::string &cookresult, float &cooktime)
{
std::vector<ItemStack> items;
InventoryList *src_list = m_inventory->getList("src");
assert(src_list);
items.push_back(src_list->getItem(0));
CraftInput ci(CRAFT_METHOD_COOKING, 1, items);
CraftOutput co;
bool found = m_gamedef->getCraftDefManager()->getCraftResult(
ci, co, remove, m_gamedef);
if(remove)
src_list->changeItem(0, ci.items[0]);
cookresult = co.item;
cooktime = co.time;
return found;
}
bool FurnaceNodeMetadata::getBurnResult(bool remove, float &burntime)
{
std::vector<ItemStack> items;
InventoryList *fuel_list = m_inventory->getList("fuel");
assert(fuel_list);
items.push_back(fuel_list->getItem(0));
CraftInput ci(CRAFT_METHOD_FUEL, 1, items);
CraftOutput co;
bool found = m_gamedef->getCraftDefManager()->getCraftResult(
ci, co, remove, m_gamedef);
if(remove)
fuel_list->changeItem(0, ci.items[0]);
burntime = co.time;
return found;
}
/*
GenericNodeMetadata
*/
class GenericNodeMetadata : public NodeMetadata
{
private:
Inventory *m_inventory;
std::string m_text;
std::string m_owner;
std::string m_infotext;
std::string m_inventorydrawspec;
bool m_allow_text_input;
bool m_removal_disabled;
bool m_enforce_owner;
bool m_inventory_modified;
bool m_text_modified;
std::map<std::string, std::string> m_stringvars;
public:
u16 typeId() const
{
return NODEMETA_GENERIC;
}
const char* typeName() const
{
return "generic";
}
GenericNodeMetadata(IGameDef *gamedef):
NodeMetadata(gamedef),
m_inventory(NULL),
m_text(""),
m_owner(""),
m_infotext("GenericNodeMetadata"),
m_inventorydrawspec(""),
m_allow_text_input(false),
m_removal_disabled(false),
m_enforce_owner(false),
m_inventory_modified(false),
m_text_modified(false)
{
NodeMetadata::registerType(typeId(), typeName(), create, create);
}
virtual ~GenericNodeMetadata()
{
delete m_inventory;
}
NodeMetadata* clone(IGameDef *gamedef)
{
GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef);
d->m_inventory = new Inventory(*m_inventory);
d->m_text = m_text;
d->m_owner = m_owner;
d->m_infotext = m_infotext;
d->m_inventorydrawspec = m_inventorydrawspec;
d->m_allow_text_input = m_allow_text_input;
d->m_removal_disabled = m_removal_disabled;
d->m_enforce_owner = m_enforce_owner;
d->m_inventory_modified = m_inventory_modified;
d->m_text_modified = m_text_modified;
return d;
}
static NodeMetadata* create(IGameDef *gamedef)
{
GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
return d;
}
static NodeMetadata* create(std::istream &is, IGameDef *gamedef)
{
GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
d->m_inventory = new Inventory(gamedef->idef());
d->m_inventory->deSerialize(is);
d->m_text = deSerializeLongString(is);
d->m_owner = deSerializeString(is);
d->m_infotext = deSerializeString(is);
d->m_inventorydrawspec = deSerializeString(is);
d->m_allow_text_input = readU8(is);
d->m_removal_disabled = readU8(is);
d->m_enforce_owner = readU8(is);
meta->setString("infotext",deSerializeString(is));
meta->setString("formspec",deSerializeString(is));
readU8(is); // m_allow_text_input
readU8(is); // m_allow_removal
readU8(is); // m_enforce_owner
int num_vars = readU32(is);
for(int i=0; i<num_vars; i++){
std::string name = deSerializeString(is);
std::string var = deSerializeLongString(is);
d->m_stringvars[name] = var;
meta->setString(name, var);
}
return d;
}
void serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
os<<serializeLongString(m_text);
os<<serializeString(m_owner);
os<<serializeString(m_infotext);
os<<serializeString(m_inventorydrawspec);
writeU8(os, m_allow_text_input);
writeU8(os, m_removal_disabled);
writeU8(os, m_enforce_owner);
int num_vars = m_stringvars.size();
writeU32(os, num_vars);
for(std::map<std::string, std::string>::iterator
i = m_stringvars.begin(); i != m_stringvars.end(); i++){
os<<serializeString(i->first);
os<<serializeLongString(i->second);
}
}
std::string infoText()
{
return m_infotext;
}
Inventory* getInventory()
{
return m_inventory;
}
void inventoryModified()
{
m_inventory_modified = true;
}
bool step(float dtime)
{
return false;
}
bool nodeRemovalDisabled()
else if(id == NODEMETA_SIGN) // SignNodeMetadata
{
return m_removal_disabled;
meta->setString("text", deSerializeLongString(is));
//meta->setString("infotext","\"${text}\"");
meta->setString("infotext",
std::string("\"") + meta->getString("text") + "\"");
meta->setString("formspec","field[text;;${text}]");
return false;
}
std::string getInventoryDrawSpecString()
else if(id == NODEMETA_CHEST) // ChestNodeMetadata
{
return m_inventorydrawspec;
}
bool allowsTextInput()
{
return m_allow_text_input;
}
std::string getText()
{
return m_text;
}
void setText(const std::string &t)
{
m_text = t;
m_text_modified = true;
}
std::string getOwner()
{
if(m_enforce_owner)
return m_owner;
else
return "";
}
void setOwner(std::string t)
{
m_owner = t;
}
/* Interface for GenericNodeMetadata */
meta->getInventory()->deSerialize(is);
void setInfoText(const std::string &text)
{
infostream<<"GenericNodeMetadata::setInfoText(\""
<<text<<"\")"<<std::endl;
m_infotext = text;
// Rename inventory list "0" to "main"
Inventory *inv = meta->getInventory();
if(!inv->getList("main") && inv->getList("0")){
inv->getList("0")->setName("main");
}
assert(inv->getList("main") && !inv->getList("0"));
meta->setString("formspec","invsize[8,9;]"
"list[current_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]");
return false;
}
void setInventoryDrawSpec(const std::string &text)
else if(id == NODEMETA_LOCKABLE_CHEST) // LockingChestNodeMetadata
{
m_inventorydrawspec = text;
}
void setAllowTextInput(bool b)
{
m_allow_text_input = b;
}
void setRemovalDisabled(bool b)
{
m_removal_disabled = b;
}
void setEnforceOwner(bool b)
{
m_enforce_owner = b;
}
bool isInventoryModified()
{
return m_inventory_modified;
}
void resetInventoryModified()
{
m_inventory_modified = false;
}
bool isTextModified()
{
return m_text_modified;
}
void resetTextModified()
{
m_text_modified = false;
}
void setString(const std::string &name, const std::string &var)
{
m_stringvars[name] = var;
}
std::string getString(const std::string &name)
{
std::map<std::string, std::string>::iterator i;
i = m_stringvars.find(name);
if(i == m_stringvars.end())
return "";
return i->second;
}
};
meta->setString("owner", deSerializeString(is));
meta->getInventory()->deSerialize(is);
// Prototype
GenericNodeMetadata proto_GenericNodeMetadata(NULL);
// Rename inventory list "0" to "main"
Inventory *inv = meta->getInventory();
if(!inv->getList("main") && inv->getList("0")){
inv->getList("0")->setName("main");
}
assert(inv->getList("main") && !inv->getList("0"));
meta->setString("formspec","invsize[8,9;]"
"list[current_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]");
return false;
}
else if(id == NODEMETA_FURNACE) // FurnaceNodeMetadata
{
meta->getInventory()->deSerialize(is);
int temp = 0;
is>>temp;
meta->setString("fuel_totaltime", ftos((float)temp/10));
temp = 0;
is>>temp;
meta->setString("fuel_time", ftos((float)temp/10));
temp = 0;
is>>temp;
//meta->setString("src_totaltime", ftos((float)temp/10));
temp = 0;
is>>temp;
meta->setString("src_time", ftos((float)temp/10));
meta->setString("formspec","invsize[8,9;]"
"list[current_name;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]"
"list[current_player;main;0,5;8,4;]");
return true;
}
else
{
throw SerializationError("Unknown legacy node metadata");
}
}
static bool content_nodemeta_deserialize_legacy_meta(
std::istream &is, NodeMetadata *meta)
{
// Read id
s16 id = readS16(is);
// Read data
std::string data = deSerializeString(is);
std::istringstream tmp_is(data, std::ios::binary);
return content_nodemeta_deserialize_legacy_body(tmp_is, id, meta);
}
void content_nodemeta_deserialize_legacy(std::istream &is,
NodeMetadataList *meta, NodeTimerList *timers,
IGameDef *gamedef)
{
meta->clear();
timers->clear();
u16 version = readU16(is);
if(version > 1)
{
infostream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
<<std::endl;
throw SerializationError(__FUNCTION_NAME);
}
u16 count = readU16(is);
for(u16 i=0; i<count; i++)
{
u16 p16 = readU16(is);
v3s16 p(0,0,0);
p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
p.Y += p16 / MAP_BLOCKSIZE;
p16 -= p.Y * MAP_BLOCKSIZE;
p.X += p16;
if(meta->get(p) != NULL)
{
infostream<<"WARNING: "<<__FUNCTION_NAME<<": "
<<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
continue;
}
NodeMetadata *data = new NodeMetadata(gamedef);
bool need_timer = content_nodemeta_deserialize_legacy_meta(is, data);
meta->set(p, data);
if(need_timer)
timers->set(p, NodeTimer(1., 0.));
}
}
void content_nodemeta_serialize_legacy(std::ostream &os, NodeMetadataList *meta)
{
// Sorry, I was too lazy to implement this. --kahrl
writeU16(os, 1); // version
writeU16(os, 0); // count
}
// END

View File

@@ -21,6 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CONTENT_NODEMETA_HEADER
#include "nodemetadata.h"
#include "nodetimer.h"
/*
Legacy nodemeta definitions
*/
void content_nodemeta_deserialize_legacy(std::istream &is,
NodeMetadataList *meta, NodeTimerList *timers,
IGameDef *gamedef);
void content_nodemeta_serialize_legacy(std::ostream &os, NodeMetadataList *meta);
#endif

View File

@@ -633,13 +633,15 @@ class ABMHandler
i->timer -= trigger_interval;
actual_interval = trigger_interval;
}
ActiveABM aabm;
aabm.abm = abm;
float intervals = actual_interval / trigger_interval;
if(intervals == 0)
continue;
float chance = abm->getTriggerChance();
if(chance == 0)
chance = 1;
aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
ActiveABM aabm;
aabm.abm = abm;
aabm.chance = chance / intervals;
if(aabm.chance == 0)
aabm.chance = 1;
// Trigger neighbors
@@ -771,18 +773,11 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
// Activate stored objects
activateObjects(block);
// Run node metadata
bool changed = block->m_node_metadata->step((float)dtime_s);
if(changed)
{
MapEditEvent event;
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
event.p = block->getPos();
m_map->dispatchEvent(&event);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"node metadata modified in activateBlock");
}
// Run node timers
std::map<v3s16, f32> elapsed_timers =
block->m_node_timers.step((float)dtime_s);
if(!elapsed_timers.empty())
errorstream<<"Node timers don't work yet!"<<std::endl;
/* Handle ActiveBlockModifiers */
ABMHandler abmhandler(m_abms, dtime_s, this, false);
@@ -1062,18 +1057,11 @@ void ServerEnvironment::step(float dtime)
block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
"Timestamp older than 60s (step)");
// Run node metadata
bool changed = block->m_node_metadata->step(dtime);
if(changed)
{
MapEditEvent event;
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
event.p = p;
m_map->dispatchEvent(&event);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"node metadata modified in step");
}
// Run node timers
std::map<v3s16, f32> elapsed_timers =
block->m_node_timers.step(dtime);
if(!elapsed_timers.empty())
errorstream<<"Node timers don't work yet!"<<std::endl;
}
}
@@ -1746,6 +1734,15 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
force_delete = true;
} else {
u16 new_id = pending_delete ? id : 0;
// If static counterpart already exists, remove it first.
// This shouldn't happen, but happens rarely for some
// unknown reason. Unsuccessful attempts have been made to
// find said reason.
if(new_id && block->m_static_objects.m_active.find(new_id)){
infostream<<"ServerEnv: WARNING: Performing hack #83274"
<<std::endl;
block->m_static_objects.remove(new_id);
}
block->m_static_objects.insert(new_id, s_obj);
// Only mark block as modified if data changed considerably

View File

@@ -103,7 +103,9 @@ struct TextDestNodeMetadata : public TextDest
std::string ntext = wide_to_narrow(text);
infostream<<"Changing text of a sign node: "
<<ntext<<std::endl;
m_client->sendSignNodeText(m_p, ntext);
std::map<std::string, std::string> fields;
fields["text"] = ntext;
m_client->sendNodemetaFields(m_p, "", fields);
}
v3s16 m_p;
@@ -130,6 +132,28 @@ class MainRespawnInitiator: public IRespawnInitiator
Client *m_client;
};
/* Form update callback */
class NodeMetadataFormSource: public IFormSource
{
public:
NodeMetadataFormSource(ClientMap *map, v3s16 p):
m_map(map),
m_p(p)
{
}
std::string getForm()
{
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
if(!meta)
return "";
return meta->getString("formspec");
}
ClientMap *m_map;
v3s16 m_p;
};
/*
Hotbar draw routine
*/
@@ -1567,24 +1591,19 @@ void the_game(
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
&g_menumgr, v2s16(8,7),
&g_menumgr,
&client, gamedef);
InventoryLocation inventoryloc;
inventoryloc.setCurrentPlayer();
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", inventoryloc, "main",
v2s32(0, 3), v2s32(8, 4)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", inventoryloc, "craft",
v2s32(3, 0), v2s32(3, 3)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", inventoryloc, "craftpreview",
v2s32(7, 1), v2s32(1, 1)));
menu->setDrawSpec(draw_spec);
menu->setFormSpec(
"invsize[8,7.5;]"
//"image[1,0.6;1,2;player.png]"
"list[current_player;main;0,3.5;8,4;]"
"list[current_player;craft;3,0;3,3;]"
"list[current_player;craftpreview;7,1;1,1;]"
, inventoryloc);
menu->drop();
}
@@ -2195,7 +2214,7 @@ void the_game(
ClientMap &map = client.getEnv().getClientMap();
NodeMetadata *meta = map.getNodeMetadata(nodepos);
if(meta){
infotext = narrow_to_wide(meta->infoText());
infotext = narrow_to_wide(meta->getString("infotext"));
} else {
MapNode n = map.getNode(nodepos);
if(nodedef->get(n).tname_tiles[0] == "unknown_block.png"){
@@ -2320,35 +2339,8 @@ void the_game(
{
infostream<<"Ground right-clicked"<<std::endl;
// If metadata provides an inventory view, activate it
if(meta && meta->getInventoryDrawSpecString() != "" && !random_input)
{
infostream<<"Launching custom inventory view"<<std::endl;
InventoryLocation inventoryloc;
inventoryloc.setNodeMeta(nodepos);
/*
Create menu
*/
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
v2s16 invsize =
GUIInventoryMenu::makeDrawSpecArrayFromString(
draw_spec,
meta->getInventoryDrawSpecString(),
inventoryloc);
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
&g_menumgr, invsize,
&client, gamedef);
menu->setDrawSpec(draw_spec);
menu->drop();
}
// If metadata provides text input, activate text input
else if(meta && meta->allowsTextInput() && !random_input)
// sign special case, at least until formspec is properly implemented
if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
{
infostream<<"Launching metadata text input"<<std::endl;
@@ -2356,12 +2348,32 @@ void the_game(
TextDest *dest = new TextDestNodeMetadata(nodepos, &client);
std::wstring wtext = narrow_to_wide(meta->getText());
std::wstring wtext = narrow_to_wide(meta->getString("text"));
(new GUITextInputMenu(guienv, guiroot, -1,
&g_menumgr, dest,
wtext))->drop();
}
// If metadata provides an inventory view, activate it
else if(meta && meta->getString("formspec") != "" && !random_input)
{
infostream<<"Launching custom inventory view"<<std::endl;
InventoryLocation inventoryloc;
inventoryloc.setNodeMeta(nodepos);
/* Create menu */
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
&g_menumgr,
&client, gamedef);
menu->setFormSpec(meta->getString("formspec"),
inventoryloc);
menu->setFormSource(new NodeMetadataFormSource(
&client.getEnv().getClientMap(), nodepos));
menu->drop();
}
// Otherwise report right click to server
else
{

View File

@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "log.h"
#include "tile.h" // ITextureSource
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
@@ -123,27 +124,26 @@ void drawItemStack(video::IVideoDriver *driver,
GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
v2s16 menu_size,
InventoryManager *invmgr,
IGameDef *gamedef
):
):
GUIModalMenu(env, parent, id, menumgr),
m_menu_size(menu_size),
m_invmgr(invmgr),
m_gamedef(gamedef)
m_gamedef(gamedef),
m_form_src(NULL),
m_selected_item(NULL),
m_selected_amount(0),
m_selected_dragging(false),
m_tooltip_element(NULL)
{
m_selected_item = NULL;
m_selected_amount = 0;
m_selected_dragging = false;
m_tooltip_element = NULL;
}
GUIInventoryMenu::~GUIInventoryMenu()
{
removeChildren();
if(m_selected_item)
delete m_selected_item;
delete m_selected_item;
delete m_form_src;
}
void GUIInventoryMenu::removeChildren()
@@ -178,60 +178,96 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
// Remove children
removeChildren();
/*padding = v2s32(24,24);
spacing = v2s32(60,56);
imgsize = v2s32(48,48);*/
padding = v2s32(screensize.Y/40, screensize.Y/40);
spacing = v2s32(screensize.Y/12, screensize.Y/13);
imgsize = v2s32(screensize.Y/15, screensize.Y/15);
v2s32 size(100,100);
s32 helptext_h = 15;
v2s32 size(
padding.X*2+spacing.X*(m_menu_size.X-1)+imgsize.X,
padding.Y*2+spacing.Y*(m_menu_size.Y-1)+imgsize.Y + helptext_h
);
core::rect<s32> rect(
screensize.X/2 - size.X/2,
screensize.Y/2 - size.Y/2,
screensize.X/2 + size.X/2,
screensize.Y/2 + size.Y/2
);
DesiredRect = rect;
recalculateAbsolutePosition(false);
core::rect<s32> rect;
v2s32 basepos = getBasePos();
m_draw_spec.clear();
for(u16 i=0; i<m_init_draw_spec.size(); i++)
/* Convert m_init_draw_spec to m_inventorylists */
m_inventorylists.clear();
m_images.clear();
Strfnd f(m_formspec_string);
while(f.atend() == false)
{
DrawSpec &s = m_init_draw_spec[i];
if(s.type == "list")
std::string type = trim(f.next("["));
if(type == "invsize")
{
m_draw_spec.push_back(ListDrawSpec(s.name, s.subname,
basepos + v2s32(spacing.X*s.pos.X, spacing.Y*s.pos.Y),
s.geom));
v2f invsize;
invsize.X = stof(f.next(","));
invsize.Y = stof(f.next(";"));
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
f.next("]");
padding = v2s32(screensize.Y/40, screensize.Y/40);
spacing = v2s32(screensize.Y/12, screensize.Y/13);
imgsize = v2s32(screensize.Y/15, screensize.Y/15);
size = v2s32(
padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
);
rect = core::rect<s32>(
screensize.X/2 - size.X/2,
screensize.Y/2 - size.Y/2,
screensize.X/2 + size.X/2,
screensize.Y/2 + size.Y/2
);
DesiredRect = rect;
recalculateAbsolutePosition(false);
basepos = getBasePos();
}
else if(type == "list")
{
std::string name = f.next(";");
InventoryLocation loc;
if(name == "context" || name == "current_name")
loc = m_current_inventory_location;
else
loc.deSerialize(name);
std::string listname = f.next(";");
v2s32 pos = basepos;
pos.X += stof(f.next(",")) * (float)spacing.X;
pos.Y += stof(f.next(";")) * (float)spacing.Y;
v2s32 geom;
geom.X = stoi(f.next(","));
geom.Y = stoi(f.next(";"));
infostream<<"list inv="<<name<<", listname="<<listname
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
f.next("]");
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
}
else if(type == "image")
{
v2s32 pos = basepos;
pos.X += stof(f.next(",")) * (float)spacing.X;
pos.Y += stof(f.next(";")) * (float)spacing.Y;
v2s32 geom;
geom.X = stof(f.next(",")) * (float)imgsize.X;
geom.Y = stof(f.next(";")) * (float)imgsize.Y;
std::string name = f.next("]");
infostream<<"image name="<<name
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos, geom));
}
else
{
// Ignore others
std::string ts = f.next("]");
infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
<<std::endl;
}
}
/*
m_draw_spec.clear();
m_draw_spec.push_back(ListDrawSpec("main",
basepos + v2s32(spacing.X*0, spacing.Y*3), v2s32(8, 4)));
m_draw_spec.push_back(ListDrawSpec("craft",
basepos + v2s32(spacing.X*3, spacing.Y*0), v2s32(3, 3)));
m_draw_spec.push_back(ListDrawSpec("craftresult",
basepos + v2s32(spacing.X*7, spacing.Y*1), v2s32(1, 1)));
*/
// Add children
{
core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
size.Y-rect.getHeight()-15);
size.Y-rect.getHeight()-5);
const wchar_t *text =
L"Left click: Move all items, Right click: Move single item";
Environment->addStaticText(text, rect, false, true, this, 256);
@@ -253,9 +289,9 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
{
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
for(u32 i=0; i<m_draw_spec.size(); i++)
for(u32 i=0; i<m_inventorylists.size(); i++)
{
const ListDrawSpec &s = m_draw_spec[i];
const ListDrawSpec &s = m_inventorylists[i];
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
{
@@ -284,8 +320,21 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
font = skin->getFont();
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
assert(inv);
if(!inv){
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
<<"The inventory location "
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
return;
}
InventoryList *ilist = inv->getList(s.listname);
if(!ilist){
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
<<"The inventory list \""<<s.listname<<"\" @ \""
<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
return;
}
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
@@ -380,6 +429,14 @@ void GUIInventoryMenu::drawSelectedItem()
void GUIInventoryMenu::drawMenu()
{
if(m_form_src){
std::string newform = m_form_src->getForm();
if(newform != m_formspec_string){
m_formspec_string = newform;
regenerateGui(m_screensize_old);
}
}
updateSelectedItem();
gui::IGUISkin* skin = Environment->getSkin();
@@ -399,9 +456,26 @@ void GUIInventoryMenu::drawMenu()
*/
for(int phase=0; phase<=1; phase++)
for(u32 i=0; i<m_draw_spec.size(); i++)
for(u32 i=0; i<m_inventorylists.size(); i++)
{
drawList(m_draw_spec[i], phase);
drawList(m_inventorylists[i], phase);
}
for(u32 i=0; i<m_images.size(); i++)
{
const ImageDrawSpec &spec = m_images[i];
video::ITexture *texture =
m_gamedef->tsrc()->getTextureRaw(spec.name);
// Image size on screen
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
}
/*
@@ -450,9 +524,9 @@ void GUIInventoryMenu::updateSelectedItem()
// If craftresult is nonempty and nothing else is selected, select it now.
if(!m_selected_item)
{
for(u32 i=0; i<m_draw_spec.size(); i++)
for(u32 i=0; i<m_inventorylists.size(); i++)
{
const ListDrawSpec &s = m_draw_spec[i];
const ListDrawSpec &s = m_inventorylists[i];
if(s.listname == "craftpreview")
{
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
@@ -526,16 +600,35 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
u32 s_count = 0;
if(s.isValid())
{
do{ // breakable
inv_s = m_invmgr->getInventory(s.inventoryloc);
assert(inv_s);
if(!inv_s){
errorstream<<"InventoryMenu: The selected inventory location "
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
s.i = -1; // make it invalid again
break;
}
InventoryList *list = inv_s->getList(s.listname);
if(list != NULL && (u32) s.i < list->getSize())
s_count = list->getItem(s.i).count;
else
if(list == NULL){
errorstream<<"InventoryMenu: The selected inventory list \""
<<s.listname<<"\" does not exist"<<std::endl;
s.i = -1; // make it invalid again
}
break;
}
if((u32)s.i >= list->getSize()){
errorstream<<"InventoryMenu: The selected inventory list \""
<<s.listname<<"\" is too small (i="<<s.i<<", size="
<<list->getSize()<<")"<<std::endl;
s.i = -1; // make it invalid again
break;
}
s_count = list->getItem(s.i).count;
}while(0);
bool identical = (m_selected_item != NULL) && s.isValid() &&
(inv_selected == inv_s) &&
@@ -788,88 +881,3 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false;
}
/*
Here is an example traditional set-up sequence for a DrawSpec list:
std::string furnace_inv_id = "nodemetadata:0,1,2";
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "fuel",
v2s32(2, 3), v2s32(1, 1)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "src",
v2s32(2, 1), v2s32(1, 1)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "dst",
v2s32(5, 1), v2s32(2, 2)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", "current_player", "main",
v2s32(0, 5), v2s32(8, 4)));
setDrawSpec(draw_spec);
Here is the string for creating the same DrawSpec list (a single line,
spread to multiple lines here):
GUIInventoryMenu::makeDrawSpecArrayFromString(
draw_spec,
"nodemetadata:0,1,2",
"invsize[8,9;]"
"list[current_name;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]"
"list[current_player;main;0,5;8,4;]");
Returns inventory menu size defined by invsize[].
*/
v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
const std::string &data,
const InventoryLocation &current_location)
{
v2s16 invsize(8,9);
Strfnd f(data);
while(f.atend() == false)
{
std::string type = trim(f.next("["));
//infostream<<"type="<<type<<std::endl;
if(type == "list")
{
std::string name = f.next(";");
InventoryLocation loc;
if(name == "current_name")
loc = current_location;
else
loc.deSerialize(name);
std::string subname = f.next(";");
s32 pos_x = stoi(f.next(","));
s32 pos_y = stoi(f.next(";"));
s32 geom_x = stoi(f.next(","));
s32 geom_y = stoi(f.next(";"));
infostream<<"list name="<<name<<", subname="<<subname
<<", pos=("<<pos_x<<","<<pos_y<<")"
<<", geom=("<<geom_x<<","<<geom_y<<")"
<<std::endl;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
type, loc, subname,
v2s32(pos_x,pos_y),v2s32(geom_x,geom_y)));
f.next("]");
}
else if(type == "invsize")
{
invsize.X = stoi(f.next(","));
invsize.Y = stoi(f.next(";"));
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
f.next("]");
}
else
{
// Ignore others
std::string ts = f.next("]");
infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
<<std::endl;
}
}
return invsize;
}

View File

@@ -30,6 +30,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IGameDef;
class InventoryManager;
class IFormSource
{
public:
virtual ~IFormSource(){}
virtual std::string getForm() = 0;
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
@@ -70,12 +77,12 @@ class GUIInventoryMenu : public GUIModalMenu
}
ListDrawSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
v2s32 a_pos, v2s32 a_geom)
v2s32 a_pos, v2s32 a_geom):
inventoryloc(a_inventoryloc),
listname(a_listname),
pos(a_pos),
geom(a_geom)
{
inventoryloc = a_inventoryloc;
listname = a_listname;
pos = a_pos;
geom = a_geom;
}
InventoryLocation inventoryloc;
@@ -83,50 +90,45 @@ class GUIInventoryMenu : public GUIModalMenu
v2s32 pos;
v2s32 geom;
};
public:
struct DrawSpec
{
DrawSpec()
{
}
DrawSpec(const std::string &a_type,
const InventoryLocation &a_name,
const std::string &a_subname,
v2s32 a_pos,
v2s32 a_geom)
{
type = a_type;
name = a_name;
subname = a_subname;
pos = a_pos;
geom = a_geom;
}
std::string type;
InventoryLocation name;
std::string subname;
struct ImageDrawSpec
{
ImageDrawSpec()
{
}
ImageDrawSpec(const std::string &a_name,
v2s32 a_pos, v2s32 a_geom):
name(a_name),
pos(a_pos),
geom(a_geom)
{
}
std::string name;
v2s32 pos;
v2s32 geom;
};
// See .cpp for format
static v2s16 makeDrawSpecArrayFromString(
core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
const std::string &data,
const InventoryLocation &current_location);
public:
GUIInventoryMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
v2s16 menu_size,
InventoryManager *invmgr,
IGameDef *gamedef
);
~GUIInventoryMenu();
void setDrawSpec(core::array<DrawSpec> &init_draw_spec)
void setFormSpec(const std::string &formspec_string,
InventoryLocation current_inventory_location)
{
m_init_draw_spec = init_draw_spec;
m_formspec_string = formspec_string;
m_current_inventory_location = current_inventory_location;
regenerateGui(m_screensize_old);
}
// form_src is deleted by this GUIInventoryMenu
void setFormSource(IFormSource *form_src)
{
m_form_src = form_src;
}
void removeChildren();
@@ -149,8 +151,6 @@ class GUIInventoryMenu : public GUIModalMenu
return padding + AbsoluteRect.UpperLeftCorner;
}
v2s16 m_menu_size;
v2s32 padding;
v2s32 spacing;
v2s32 imgsize;
@@ -158,8 +158,12 @@ class GUIInventoryMenu : public GUIModalMenu
InventoryManager *m_invmgr;
IGameDef *m_gamedef;
core::array<DrawSpec> m_init_draw_spec;
core::array<ListDrawSpec> m_draw_spec;
std::string m_formspec_string;
InventoryLocation m_current_inventory_location;
IFormSource *m_form_src;
core::array<ListDrawSpec> m_inventorylists;
core::array<ImageDrawSpec> m_images;
ItemSpec *m_selected_item;
u32 m_selected_amount;

View File

@@ -458,6 +458,11 @@ void InventoryList::setSize(u32 newsize)
m_size = newsize;
}
void InventoryList::setName(const std::string &name)
{
m_name = name;
}
void InventoryList::serialize(std::ostream &os) const
{
//os.imbue(std::locale("C"));
@@ -915,6 +920,17 @@ InventoryList * Inventory::getList(const std::string &name)
return m_lists[i];
}
std::vector<const InventoryList*> Inventory::getLists()
{
std::vector<const InventoryList*> lists;
for(u32 i=0; i<m_lists.size(); i++)
{
InventoryList *list = m_lists[i];
lists.push_back(list);
}
return lists;
}
bool Inventory::deleteList(const std::string &name)
{
s32 i = getListIndex(name);

View File

@@ -176,6 +176,7 @@ class InventoryList
~InventoryList();
void clearItems();
void setSize(u32 newsize);
void setName(const std::string &name);
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
@@ -262,6 +263,7 @@ class Inventory
InventoryList * addList(const std::string &name, u32 size);
InventoryList * getList(const std::string &name);
const InventoryList * getList(const std::string &name) const;
std::vector<const InventoryList*> getLists();
bool deleteList(const std::string &name);
// A shorthand for adding items. Returns leftover item (possibly empty).
ItemStack addItem(const std::string &listname, const ItemStack &newitem)

View File

@@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "utility.h"
#include "craftdef.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
/*
InventoryLocation
*/
@@ -197,27 +199,90 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
<<", to_list=\""<<to_list<<"\""<<std::endl;
return;
}
/*
This performs the actual movement
// Handle node metadata move
if(from_inv.type == InventoryLocation::NODEMETA &&
to_inv.type == InventoryLocation::NODEMETA &&
from_inv.p != to_inv.p)
{
errorstream<<"Directly moving items between two nodes is "
<<"disallowed."<<std::endl;
return;
}
else if(from_inv.type == InventoryLocation::NODEMETA &&
to_inv.type == InventoryLocation::NODEMETA &&
from_inv.p == to_inv.p)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
infostream<<player->getDescription()<<" moving "<<count0
<<" items inside node at "<<PP(from_inv.p)<<std::endl;
scriptapi_node_on_metadata_inventory_move(L, from_inv.p,
from_list, from_i, to_list, to_i, count0, player);
}
// Handle node metadata take
else if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
infostream<<player->getDescription()<<" taking "<<count0
<<" items from node at "<<PP(from_inv.p)<<std::endl;
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
L, from_inv.p, from_list, from_i, count0, player);
if(return_stack.count == 0)
infostream<<"Node metadata gave no items"<<std::endl;
return_stack = list_to->addItem(to_i, return_stack);
list_to->addItem(return_stack); // Force return of everything
}
// Handle node metadata offer
else if(to_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
ItemStack offer_stack = list_from->takeItem(from_i, count0);
infostream<<player->getDescription()<<" offering "
<<offer_stack.count<<" items to node at "
<<PP(to_inv.p)<<std::endl;
ItemStack reject_stack = scriptapi_node_on_metadata_inventory_offer(
L, to_inv.p, to_list, to_i, offer_stack, player);
if(reject_stack.count == offer_stack.count)
infostream<<"Node metadata rejected all items"<<std::endl;
else if(reject_stack.count != 0)
infostream<<"Node metadata rejected some items"<<std::endl;
reject_stack = list_from->addItem(from_i, reject_stack);
list_from->addItem(reject_stack); // Force return of everything
}
// Handle regular move
else
{
/*
This performs the actual movement
If something is wrong (source item is empty, destination is the
same as source), nothing happens
*/
list_from->moveItem(from_i, list_to, to_i, count);
If something is wrong (source item is empty, destination is the
same as source), nothing happens
*/
list_from->moveItem(from_i, list_to, to_i, count);
infostream<<"IMoveAction::apply(): moved "
<<" count="<<count
<<" from inv=\""<<from_inv.dump()<<"\""
<<" list=\""<<from_list<<"\""
<<" i="<<from_i
<<" to inv=\""<<to_inv.dump()<<"\""
<<" list=\""<<to_list<<"\""
<<" i="<<to_i
<<std::endl;
}
mgr->setInventoryModified(from_inv);
if(inv_from != inv_to)
mgr->setInventoryModified(to_inv);
infostream<<"IMoveAction::apply(): moved at "
<<" count="<<count<<"\""
<<" from inv=\""<<from_inv.dump()<<"\""
<<" list=\""<<from_list<<"\""
<<" i="<<from_i
<<" to inv=\""<<to_inv.dump()<<"\""
<<" list=\""<<to_list<<"\""
<<" i="<<to_i
<<std::endl;
}
void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
@@ -297,12 +362,31 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
return;
}
// Take item from source list
ItemStack item1;
if(count == 0)
item1 = list_from->changeItem(from_i, ItemStack());
// Handle node metadata take
if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
infostream<<player->getDescription()<<" dropping "<<count0
<<" items from node at "<<PP(from_inv.p)<<std::endl;
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
L, from_inv.p, from_list, from_i, count0, player);
if(return_stack.count == 0)
infostream<<"Node metadata gave no items"<<std::endl;
item1 = return_stack;
}
else
item1 = list_from->takeItem(from_i, count);
{
// Take item from source list
if(count == 0)
item1 = list_from->changeItem(from_i, ItemStack());
else
item1 = list_from->takeItem(from_i, count);
}
// Drop the item and apply the returned ItemStack
ItemStack item2 = item1;

View File

@@ -159,4 +159,5 @@ std::ostream actionstream(&actionbuf);
std::ostream infostream(&infobuf);
std::ostream verbosestream(&verbosebuf);
bool log_trace_level_enabled = false;

View File

@@ -69,5 +69,10 @@ extern std::ostream actionstream;
extern std::ostream infostream;
extern std::ostream verbosestream;
extern bool log_trace_level_enabled;
#define TRACESTREAM(x){ if(log_trace_level_enabled) verbosestream x; }
#define TRACEDO(x){ if(log_trace_level_enabled){ x ;} }
#endif

View File

@@ -790,7 +790,7 @@ int main(int argc, char *argv[])
allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING,
"Same as --world (deprecated)"));
allowed_options.insert("world", ValueSpec(VALUETYPE_STRING,
"Set world path (implies local game)"));
"Set world path (implies local game) ('list' lists all)"));
allowed_options.insert("worldname", ValueSpec(VALUETYPE_STRING,
"Set world by name (implies local game)"));
allowed_options.insert("info", ValueSpec(VALUETYPE_FLAG,
@@ -854,8 +854,9 @@ int main(int argc, char *argv[])
// If trace is enabled, enable logging of certain things
if(cmd_args.getFlag("trace")){
dstream<<"Enabling trace level debug output"<<std::endl;
dout_con_ptr = &verbosestream;
socket_enable_debug_output = true;
log_trace_level_enabled = true;
dout_con_ptr = &verbosestream; // this is somewhat old crap
socket_enable_debug_output = true; // socket doesn't use log.h
}
// In certain cases, output info level on stderr
if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
@@ -909,6 +910,14 @@ int main(int argc, char *argv[])
return 0;
}
// List worlds if requested
if(cmd_args.exists("world") && cmd_args.get("world") == "list"){
dstream<<"Available worlds:"<<std::endl;
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
print_worldspecs(worldspecs, dstream);
return 0;
}
// Print startup message
infostream<<PROJECT_NAME<<
" with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
@@ -1029,6 +1038,31 @@ int main(int argc, char *argv[])
}
}
// If a world name was specified, convert it to a path
if(commanded_worldname != ""){
// Get information about available worlds
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
bool found = false;
for(u32 i=0; i<worldspecs.size(); i++){
std::string name = worldspecs[i].name;
if(name == commanded_worldname){
if(commanded_world != ""){
dstream<<"--worldname takes precedence over previously "
<<"selected world."<<std::endl;
}
commanded_world = worldspecs[i].path;
found = true;
break;
}
}
if(!found){
dstream<<"World '"<<commanded_worldname<<"' not "
<<"available. Available worlds:"<<std::endl;
print_worldspecs(worldspecs, dstream);
return 1;
}
}
// Gamespec
SubgameSpec commanded_gamespec;
if(cmd_args.exists("gameid")){
@@ -1066,25 +1100,6 @@ int main(int argc, char *argv[])
infostream<<"Using commanded world path ["<<world_path<<"]"
<<std::endl;
}
// If a world name was specified, select it
else if(commanded_worldname != ""){
// Get information about available worlds
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
world_path = "";
for(u32 i=0; i<worldspecs.size(); i++){
std::string name = worldspecs[i].name;
if(name == commanded_worldname){
world_path = worldspecs[i].path;
break;
}
}
if(world_path == ""){
dstream<<"World '"<<commanded_worldname<<"' not "
<<"available. Available worlds:"<<std::endl;
print_worldspecs(worldspecs, dstream);
return 1;
}
}
// No world was specified; try to select it automatically
else
{
@@ -1199,8 +1214,6 @@ int main(int argc, char *argv[])
address = "";
else if(cmd_args.exists("address"))
address = cmd_args.get("address");
else if(cmd_args.exists("world"))
address = "";
std::string playername = g_settings->get("name");
if(cmd_args.exists("name"))

View File

@@ -1009,27 +1009,18 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
}
/*
Remove node metadata
*/
removeNodeMetadata(p);
/*
Set the node on the map
*/
setNode(p, n);
/*
Add intial metadata
*/
std::string metadata_name = nodemgr->get(n).metadata_name;
if(metadata_name != ""){
NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
if(!meta){
errorstream<<"Failed to create node metadata \""
<<metadata_name<<"\""<<std::endl;
} else {
setNodeMetadata(p, meta);
}
}
/*
If node is under sunlight and doesn't let sunlight through,
take all sunlighted nodes under it and clear light from them
@@ -1853,7 +1844,7 @@ NodeMetadata* Map::getNodeMetadata(v3s16 p)
<<std::endl;
return NULL;
}
NodeMetadata *meta = block->m_node_metadata->get(p_rel);
NodeMetadata *meta = block->m_node_metadata.get(p_rel);
return meta;
}
@@ -1873,7 +1864,7 @@ void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
<<std::endl;
return;
}
block->m_node_metadata->set(p_rel, meta);
block->m_node_metadata.set(p_rel, meta);
}
void Map::removeNodeMetadata(v3s16 p)
@@ -1887,36 +1878,7 @@ void Map::removeNodeMetadata(v3s16 p)
<<std::endl;
return;
}
block->m_node_metadata->remove(p_rel);
}
void Map::nodeMetadataStep(float dtime,
core::map<v3s16, MapBlock*> &changed_blocks)
{
/*
NOTE:
Currently there is no way to ensure that all the necessary
blocks are loaded when this is run. (They might get unloaded)
NOTE: ^- Actually, that might not be so. In a quick test it
reloaded a block with a furnace when I walked back to it from
a distance.
*/
core::map<v2s16, MapSector*>::Iterator si;
si = m_sectors.getIterator();
for(; si.atEnd() == false; si++)
{
MapSector *sector = si.getNode()->getValue();
core::list< MapBlock * > sectorblocks;
sector->getBlocks(sectorblocks);
core::list< MapBlock * >::Iterator i;
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
{
MapBlock *block = *i;
bool changed = block->m_node_metadata->step(dtime);
if(changed)
changed_blocks[block->getPos()] = block;
}
}
block->m_node_metadata.remove(p_rel);
}
/*
@@ -3495,15 +3457,15 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
}
catch(InvalidFilenameException &e)
{
return false;
return NULL;
}
catch(FileNotGoodException &e)
{
return false;
return NULL;
}
catch(std::exception &e)
{
return false;
return NULL;
}
}

View File

@@ -309,9 +309,7 @@ class Map /*: public NodeContainer*/
NodeMetadata* getNodeMetadata(v3s16 p);
void setNodeMetadata(v3s16 p, NodeMetadata *meta);
void removeNodeMetadata(v3s16 p);
void nodeMetadataStep(float dtime,
core::map<v3s16, MapBlock*> &changed_blocks);
/*
Misc.
*/

View File

@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "nameidmapping.h"
#include "content_mapnode.h" // For legacy name-id mapping
#include "content_nodemeta.h" // For legacy deserialization
#ifndef SERVER
#include "mapblock_mesh.h"
#endif
@@ -39,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_node_metadata(new NodeMetadataList),
m_parent(parent),
m_pos(pos),
m_gamedef(gamedef),
@@ -79,8 +79,6 @@ MapBlock::~MapBlock()
}
#endif
delete m_node_metadata;
if(data)
delete[] data;
}
@@ -605,7 +603,10 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
Node metadata
*/
std::ostringstream oss(std::ios_base::binary);
m_node_metadata->serialize(oss);
if(version >= 23)
m_node_metadata.serialize(oss);
else
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
compressZlib(oss.str(), os);
/*
@@ -613,6 +614,10 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
*/
if(disk)
{
// Node timers
if(version >= 23)
m_node_timers.serialize(os);
// Static objects
m_static_objects.serialize(os);
@@ -665,12 +670,17 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
std::ostringstream oss(std::ios_base::binary);
decompressZlib(is, oss);
std::istringstream iss(oss.str(), std::ios_base::binary);
m_node_metadata->deSerialize(iss, m_gamedef);
if(version >= 23)
m_node_metadata.deSerialize(iss, m_gamedef);
else
content_nodemeta_deserialize_legacy(iss,
&m_node_metadata, &m_node_timers,
m_gamedef);
}
catch(SerializationError &e)
{
errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
<<" while deserializing node metadata"<<std::endl;
<<" while deserializing node metadata: "<<e.what()<<std::endl;
}
/*
@@ -678,6 +688,10 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
*/
if(disk)
{
// Node timers
if(version >= 23)
m_node_timers.deSerialize(is);
// Static objects
m_static_objects.deSerialize(is);
@@ -857,7 +871,7 @@ void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
{
try{
std::ostringstream oss(std::ios_base::binary);
m_node_metadata->serialize(oss);
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
os<<serializeString(oss.str());
}
// This will happen if the string is longer than 65535
@@ -870,7 +884,7 @@ void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
else
{
std::ostringstream oss(std::ios_base::binary);
m_node_metadata->serialize(oss);
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
compressZlib(oss.str(), os);
//os<<serializeLongString(oss.str());
}
@@ -1024,7 +1038,9 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
{
std::string data = deSerializeString(is);
std::istringstream iss(data, std::ios_base::binary);
m_node_metadata->deSerialize(iss, m_gamedef);
content_nodemeta_deserialize_legacy(iss,
&m_node_metadata, &m_node_timers,
m_gamedef);
}
else
{
@@ -1032,7 +1048,9 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
std::ostringstream oss(std::ios_base::binary);
decompressZlib(is, oss);
std::istringstream iss(oss.str(), std::ios_base::binary);
m_node_metadata->deSerialize(iss, m_gamedef);
content_nodemeta_deserialize_legacy(iss,
&m_node_metadata, &m_node_timers,
m_gamedef);
}
}
catch(SerializationError &e)

View File

@@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "voxel.h"
#include "staticobject.h"
#include "nodemetadata.h"
#include "nodetimer.h"
#include "modifiedstate.h"
class Map;
@@ -473,7 +475,8 @@ class MapBlock /*: public NodeContainer*/
//JMutex mesh_mutex;
#endif
NodeMetadataList *m_node_metadata;
NodeMetadataList m_node_metadata;
NodeTimerList m_node_timers;
StaticObjectList m_static_objects;
private:

View File

@@ -274,8 +274,16 @@ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
if(ambient_occlusion > 4)
{
ambient_occlusion -= 4;
light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
//ambient_occlusion -= 4;
//light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
float light_amount = (8 - ambient_occlusion) / 4.0;
float light_f = (float)light / 255.0;
light_f = pow(light_f, 2.2f); // gamma -> linear space
light_f = light_f * light_amount;
light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
if(light_f > 1.0)
light_f = 1.0;
light = 255.0 * light_f + 0.5;
}
return light;

View File

@@ -26,6 +26,64 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "strfnd.h"
#include "log.h"
static void collectMods(const std::string &modspath,
std::queue<ModSpec> &mods_satisfied,
core::list<ModSpec> &mods_unsorted,
std::map<std::string, std::string> &mod_names,
std::string indentation)
{
TRACESTREAM(<<indentation<<"collectMods(\""<<modspath<<"\")"<<std::endl);
TRACEDO(indentation += " ");
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
for(u32 j=0; j<dirlist.size(); j++){
if(!dirlist[j].dir)
continue;
std::string modname = dirlist[j].name;
std::string modpath = modspath + DIR_DELIM + modname;
TRACESTREAM(<<indentation<<"collectMods: "<<modname<<" at \""<<modpath<<"\""<<std::endl);
// Handle modpacks (defined by containing modpack.txt)
{
std::ifstream is((modpath+DIR_DELIM+"modpack.txt").c_str(),
std::ios_base::binary);
if(is.good()){
// Is a modpack
is.close(); // We don't actually need the file
// Recurse into it
collectMods(modpath, mods_satisfied, mods_unsorted, mod_names, indentation);
continue;
}
}
// Detect mod name conflicts
{
std::map<std::string, std::string>::const_iterator i;
i = mod_names.find(modname);
if(i != mod_names.end()){
std::string s;
infostream<<indentation<<"WARNING: Mod name conflict detected: "
<<std::endl
<<"Already loaded: "<<i->second<<std::endl
<<"Will not load: "<<modpath<<std::endl;
continue;
}
}
std::set<std::string> depends;
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
std::ios_base::binary);
while(is.good()){
std::string dep;
std::getline(is, dep);
dep = trim(dep);
if(dep != "")
depends.insert(dep);
}
ModSpec spec(modname, modpath, depends);
mods_unsorted.push_back(spec);
if(depends.empty())
mods_satisfied.push(spec);
mod_names[modname] = modpath;
}
}
// Get a dependency-sorted list of ModSpecs
core::list<ModSpec> getMods(core::list<std::string> &modspaths)
throw(ModError)
@@ -38,41 +96,7 @@ core::list<ModSpec> getMods(core::list<std::string> &modspaths)
for(core::list<std::string>::Iterator i = modspaths.begin();
i != modspaths.end(); i++){
std::string modspath = *i;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
for(u32 j=0; j<dirlist.size(); j++){
if(!dirlist[j].dir)
continue;
std::string modname = dirlist[j].name;
std::string modpath = modspath + DIR_DELIM + modname;
// Detect mod name conflicts
{
std::map<std::string, std::string>::const_iterator i;
i = mod_names.find(modname);
if(i != mod_names.end()){
std::string s;
infostream<<"WARNING: Mod name conflict detected: "
<<std::endl
<<"Already loaded: "<<i->second<<std::endl
<<"Will not load: "<<modpath<<std::endl;
continue;
}
}
std::set<std::string> depends;
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
std::ios_base::binary);
while(is.good()){
std::string dep;
std::getline(is, dep);
dep = trim(dep);
if(dep != "")
depends.insert(dep);
}
ModSpec spec(modname, modpath, depends);
mods_unsorted.push_back(spec);
if(depends.empty())
mods_satisfied.push(spec);
mod_names[modname] = modpath;
}
collectMods(modspath, mods_satisfied, mods_unsorted, mod_names, "");
}
// Sort by depencencies
while(!mods_satisfied.empty()){
@@ -107,9 +131,26 @@ core::list<ModSpec> getMods(core::list<std::string> &modspaths)
errs<<"."<<std::endl;
mods_sorted.push_back(mod);
}
// If an error occurred, throw an exception
if(errs.str().size() != 0){
throw ModError(errs.str());
}
// Print out some debug info
TRACESTREAM(<<"Detected mods in load order:"<<std::endl);
for(core::list<ModSpec>::Iterator i = mods_sorted.begin();
i != mods_sorted.end(); i++){
ModSpec &mod = *i;
TRACESTREAM(<<"name=\""<<mod.name<<"\" path=\""<<mod.path<<"\"");
TRACESTREAM(<<" depends=[");
for(std::set<std::string>::iterator i = mod.depends.begin();
i != mod.depends.end(); i++)
TRACESTREAM(<<" "<<(*i));
TRACESTREAM(<<" ] unsatisfied_depends=[");
for(std::set<std::string>::iterator i = mod.unsatisfied_depends.begin();
i != mod.unsatisfied_depends.end(); i++)
TRACESTREAM(<<" "<<(*i));
TRACESTREAM(<<" ]"<<std::endl);
}
return mods_sorted;
}

View File

@@ -145,7 +145,6 @@ void ContentFeatures::reset()
diggable = true;
climbable = false;
buildable_to = false;
metadata_name = "";
liquid_type = LIQUID_NONE;
liquid_alternative_flowing = "";
liquid_alternative_source = "";
@@ -194,7 +193,7 @@ void ContentFeatures::serialize(std::ostream &os)
writeU8(os, diggable);
writeU8(os, climbable);
writeU8(os, buildable_to);
os<<serializeString(metadata_name);
os<<serializeString(""); // legacy: used to be metadata_name
writeU8(os, liquid_type);
os<<serializeString(liquid_alternative_flowing);
os<<serializeString(liquid_alternative_source);
@@ -248,7 +247,7 @@ void ContentFeatures::deSerialize(std::istream &is)
diggable = readU8(is);
climbable = readU8(is);
buildable_to = readU8(is);
metadata_name = deSerializeString(is);
deSerializeString(is); // legacy: used to be metadata_name
liquid_type = (enum LiquidType)readU8(is);
liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is);

View File

@@ -179,8 +179,6 @@ struct ContentFeatures
bool climbable;
// Player can build on these
bool buildable_to;
// Metadata name of node (eg. "furnace")
std::string metadata_name;
// Whether the node is non-liquid, source liquid or flowing liquid
enum LiquidType liquid_type;
// If the content is liquid, this is the flowing version of the liquid.

View File

@@ -19,11 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodemetadata.h"
#include "utility.h"
#include "mapnode.h"
#include "exceptions.h"
#include "gamedef.h"
#include "inventory.h"
#include <sstream>
#include "content_mapnode.h"
#include "log.h"
/*
@@ -31,161 +30,104 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
NodeMetadata::NodeMetadata(IGameDef *gamedef):
m_gamedef(gamedef)
m_stringvars(),
m_inventory(new Inventory(gamedef->idef()))
{
}
NodeMetadata::~NodeMetadata()
{
delete m_inventory;
}
NodeMetadata* NodeMetadata::create(const std::string &name, IGameDef *gamedef)
void NodeMetadata::serialize(std::ostream &os) const
{
// Find factory function
core::map<std::string, Factory2>::Node *n;
n = m_names.find(name);
if(n == NULL)
{
// If factory is not found, just return.
errorstream<<"WARNING: NodeMetadata: No factory for name=\""
<<name<<"\""<<std::endl;
return NULL;
}
// Try to load the metadata. If it fails, just return.
try
{
Factory2 f2 = n->getValue();
NodeMetadata *meta = (*f2)(gamedef);
return meta;
}
catch(SerializationError &e)
{
errorstream<<"NodeMetadata: SerializationError "
<<"while creating name=\""<<name<<"\""<<std::endl;
return NULL;
int num_vars = m_stringvars.size();
writeU32(os, num_vars);
for(std::map<std::string, std::string>::const_iterator
i = m_stringvars.begin(); i != m_stringvars.end(); i++){
os<<serializeString(i->first);
os<<serializeLongString(i->second);
}
m_inventory->serialize(os);
}
NodeMetadata* NodeMetadata::deSerialize(std::istream &is, IGameDef *gamedef)
void NodeMetadata::deSerialize(std::istream &is)
{
// Read id
u8 buf[2];
is.read((char*)buf, 2);
s16 id = readS16(buf);
// Read data
std::string data = deSerializeString(is);
// Find factory function
core::map<u16, Factory>::Node *n;
n = m_types.find(id);
if(n == NULL)
{
// If factory is not found, just return.
infostream<<"WARNING: NodeMetadata: No factory for typeId="
<<id<<std::endl;
return NULL;
}
// Try to load the metadata. If it fails, just return.
try
{
std::istringstream iss(data, std::ios_base::binary);
Factory f = n->getValue();
NodeMetadata *meta = (*f)(iss, gamedef);
return meta;
}
catch(SerializationError &e)
{
infostream<<"WARNING: NodeMetadata: ignoring SerializationError"<<std::endl;
return NULL;
m_stringvars.clear();
int num_vars = readU32(is);
for(int i=0; i<num_vars; i++){
std::string name = deSerializeString(is);
std::string var = deSerializeLongString(is);
m_stringvars[name] = var;
}
m_inventory->deSerialize(is);
}
void NodeMetadata::serialize(std::ostream &os)
void NodeMetadata::clear()
{
u8 buf[2];
writeU16(buf, typeId());
os.write((char*)buf, 2);
std::ostringstream oss(std::ios_base::binary);
serializeBody(oss);
os<<serializeString(oss.str());
}
void NodeMetadata::registerType(u16 id, const std::string &name, Factory f,
Factory2 f2)
{
{ // typeId
core::map<u16, Factory>::Node *n;
n = m_types.find(id);
if(!n)
m_types.insert(id, f);
}
{ // typeName
core::map<std::string, Factory2>::Node *n;
n = m_names.find(name);
if(!n)
m_names.insert(name, f2);
}
m_stringvars.clear();
m_inventory->clear();
}
/*
NodeMetadataList
*/
void NodeMetadataList::serialize(std::ostream &os)
void NodeMetadataList::serialize(std::ostream &os) const
{
u8 buf[6];
u16 version = 1;
writeU16(buf, version);
os.write((char*)buf, 2);
/*
Version 0 is a placeholder for "nothing to see here; go away."
*/
if(m_data.size() == 0){
writeU8(os, 0); // version
return;
}
writeU8(os, 1); // version
u16 count = m_data.size();
writeU16(buf, count);
os.write((char*)buf, 2);
writeU16(os, count);
for(core::map<v3s16, NodeMetadata*>::Iterator
i = m_data.getIterator();
i.atEnd()==false; i++)
for(std::map<v3s16, NodeMetadata*>::const_iterator
i = m_data.begin();
i != m_data.end(); i++)
{
v3s16 p = i.getNode()->getKey();
NodeMetadata *data = i.getNode()->getValue();
v3s16 p = i->first;
NodeMetadata *data = i->second;
u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
writeU16(buf, p16);
os.write((char*)buf, 2);
writeU16(os, p16);
data->serialize(os);
}
}
void NodeMetadataList::deSerialize(std::istream &is, IGameDef *gamedef)
{
m_data.clear();
u8 buf[6];
u8 version = readU8(is);
is.read((char*)buf, 2);
u16 version = readU16(buf);
if(version == 0){
// Nothing
return;
}
if(version > 1)
{
if(version != 1){
infostream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
<<std::endl;
throw SerializationError("NodeMetadataList::deSerialize");
}
is.read((char*)buf, 2);
u16 count = readU16(buf);
u16 count = readU16(is);
for(u16 i=0; i<count; i++)
{
is.read((char*)buf, 2);
u16 p16 = readU16(buf);
u16 p16 = readU16(is);
v3s16 p(0,0,0);
p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
@@ -193,43 +135,33 @@ void NodeMetadataList::deSerialize(std::istream &is, IGameDef *gamedef)
p.Y += p16 / MAP_BLOCKSIZE;
p16 -= p.Y * MAP_BLOCKSIZE;
p.X += p16;
NodeMetadata *data = NodeMetadata::deSerialize(is, gamedef);
if(data == NULL)
continue;
if(m_data.find(p))
if(m_data.find(p) != m_data.end())
{
infostream<<"WARNING: NodeMetadataList::deSerialize(): "
<<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
delete data;
continue;
}
m_data.insert(p, data);
NodeMetadata *data = new NodeMetadata(gamedef);
data->deSerialize(is);
m_data[p] = data;
}
}
NodeMetadataList::~NodeMetadataList()
{
for(core::map<v3s16, NodeMetadata*>::Iterator
i = m_data.getIterator();
i.atEnd()==false; i++)
{
delete i.getNode()->getValue();
}
clear();
}
NodeMetadata* NodeMetadataList::get(v3s16 p)
{
core::map<v3s16, NodeMetadata*>::Node *n;
n = m_data.find(p);
if(n == NULL)
std::map<v3s16, NodeMetadata*>::const_iterator n = m_data.find(p);
if(n == m_data.end())
return NULL;
return n->getValue();
return n->second;
}
void NodeMetadataList::remove(v3s16 p)
@@ -238,29 +170,23 @@ void NodeMetadataList::remove(v3s16 p)
if(olddata)
{
delete olddata;
m_data.remove(p);
m_data.erase(p);
}
}
void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
{
remove(p);
m_data.insert(p, d);
m_data.insert(std::make_pair(p, d));
}
bool NodeMetadataList::step(float dtime)
void NodeMetadataList::clear()
{
bool something_changed = false;
for(core::map<v3s16, NodeMetadata*>::Iterator
i = m_data.getIterator();
i.atEnd()==false; i++)
for(std::map<v3s16, NodeMetadata*>::iterator
i = m_data.begin();
i != m_data.end(); i++)
{
v3s16 p = i.getNode()->getKey();
NodeMetadata *meta = i.getNode()->getValue();
bool changed = meta->step(dtime);
if(changed)
something_changed = true;
delete i->second;
}
return something_changed;
m_data.clear();
}

View File

@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h"
#include <string>
#include <iostream>
#include <map>
/*
NodeMetadata stores arbitary amounts of data for special blocks.
@@ -39,77 +40,47 @@ class IGameDef;
class NodeMetadata
{
public:
typedef NodeMetadata* (*Factory)(std::istream&, IGameDef *gamedef);
typedef NodeMetadata* (*Factory2)(IGameDef *gamedef);
NodeMetadata(IGameDef *gamedef);
virtual ~NodeMetadata();
~NodeMetadata();
static NodeMetadata* create(const std::string &name, IGameDef *gamedef);
static NodeMetadata* deSerialize(std::istream &is, IGameDef *gamedef);
void serialize(std::ostream &os);
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
virtual u16 typeId() const = 0;
virtual const char* typeName() const = 0;
virtual NodeMetadata* clone(IGameDef *gamedef) = 0;
virtual void serializeBody(std::ostream &os) = 0;
void clear();
// Called on client-side; shown on screen when pointed at
virtual std::string infoText() {return "";}
//
virtual Inventory* getInventory() {return NULL;}
// Called always after the inventory is modified, before the changes
// are copied elsewhere
virtual void inventoryModified(){}
// Generic key/value store
std::string getString(const std::string &name) const
{
std::map<std::string, std::string>::const_iterator i;
i = m_stringvars.find(name);
if(i == m_stringvars.end())
return "";
return i->second;
}
void setString(const std::string &name, const std::string &var)
{
if(var.empty())
m_stringvars.erase(name);
else
m_stringvars[name] = var;
}
std::map<std::string, std::string> getStrings() const
{
return m_stringvars;
}
// A step in time. Shall return true if metadata changed.
virtual bool step(float dtime) {return false;}
// The inventory
Inventory* getInventory()
{
return m_inventory;
}
// Whether the related node and this metadata cannot be removed
virtual bool nodeRemovalDisabled(){return false;}
// If non-empty, player can interact by using an inventory view
// See format in guiInventoryMenu.cpp.
virtual std::string getInventoryDrawSpecString(){return "";}
// If true, player can interact by writing text
virtual bool allowsTextInput(){ return false; }
// Get old text for player interaction
virtual std::string getText(){ return ""; }
// Set player-written text
virtual void setText(const std::string &t){}
// If returns non-empty, only given player can modify text/inventory
virtual std::string getOwner(){ return std::string(""); }
// The name of the player who placed the node
virtual void setOwner(std::string t){}
/* Interface for GenericNodeMetadata */
virtual void setInfoText(const std::string &text){};
virtual void setInventoryDrawSpec(const std::string &text){};
virtual void setAllowTextInput(bool b){};
virtual void setRemovalDisabled(bool b){};
virtual void setEnforceOwner(bool b){};
virtual bool isInventoryModified(){return false;};
virtual void resetInventoryModified(){};
virtual bool isTextModified(){return false;};
virtual void resetTextModified(){};
virtual void setString(const std::string &name, const std::string &var){}
virtual std::string getString(const std::string &name){return "";}
protected:
static void registerType(u16 id, const std::string &name, Factory f,
Factory2 f2);
IGameDef *m_gamedef;
private:
static core::map<u16, Factory> m_types;
static core::map<std::string, Factory2> m_names;
std::map<std::string, std::string> m_stringvars;
Inventory *m_inventory;
};
/*
List of metadata of all the nodes of a block
*/
@@ -119,7 +90,7 @@ class NodeMetadataList
public:
~NodeMetadataList();
void serialize(std::ostream &os);
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is, IGameDef *gamedef);
// Get pointer to data
@@ -128,12 +99,11 @@ class NodeMetadataList
void remove(v3s16 p);
// Deletes old data and sets a new one
void set(v3s16 p, NodeMetadata *d);
// Deletes all
void clear();
// A step in time. Returns true if something changed.
bool step(float dtime);
private:
core::map<v3s16, NodeMetadata*> m_data;
std::map<v3s16, NodeMetadata*> m_data;
};
#endif

141
src/nodetimer.cpp Normal file
View File

@@ -0,0 +1,141 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "nodetimer.h"
#include "utility.h"
#include "log.h"
/*
NodeTimer
*/
void NodeTimer::serialize(std::ostream &os) const
{
writeF1000(os, duration);
writeF1000(os, elapsed);
}
void NodeTimer::deSerialize(std::istream &is)
{
duration = readF1000(is);
elapsed = readF1000(is);
}
/*
NodeTimerList
*/
void NodeTimerList::serialize(std::ostream &os) const
{
/*
Version 0 is a placeholder for "nothing to see here; go away."
*/
if(m_data.size() == 0){
writeU8(os, 0); // version
return;
}
writeU8(os, 1); // version
writeU16(os, m_data.size());
for(std::map<v3s16, NodeTimer>::const_iterator
i = m_data.begin();
i != m_data.end(); i++){
v3s16 p = i->first;
NodeTimer t = i->second;
u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
writeU16(os, p16);
t.serialize(os);
}
}
void NodeTimerList::deSerialize(std::istream &is)
{
m_data.clear();
u8 version = readU8(is);
if(version == 0)
return;
if(version != 1)
throw SerializationError("unsupported NodeTimerList version");
u16 count = readU16(is);
for(u16 i=0; i<count; i++)
{
u16 p16 = readU16(is);
v3s16 p(0,0,0);
p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
p.Y += p16 / MAP_BLOCKSIZE;
p16 -= p.Y * MAP_BLOCKSIZE;
p.X += p16;
NodeTimer t;
t.deSerialize(is);
if(t.duration <= 0)
{
infostream<<"WARNING: NodeTimerList::deSerialize(): "
<<"invalid data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
continue;
}
if(m_data.find(p) != m_data.end())
{
infostream<<"WARNING: NodeTimerList::deSerialize(): "
<<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
continue;
}
m_data.insert(std::make_pair(p, t));
}
}
std::map<v3s16, f32> NodeTimerList::step(float dtime)
{
std::map<v3s16, f32> elapsed_timers;
// Increment timers
for(std::map<v3s16, NodeTimer>::iterator
i = m_data.begin();
i != m_data.end(); i++){
v3s16 p = i->first;
NodeTimer t = i->second;
t.elapsed += dtime;
if(t.elapsed >= t.duration)
elapsed_timers.insert(std::make_pair(p, t.elapsed));
else
i->second = t;
}
// Delete elapsed timers
for(std::map<v3s16, f32>::const_iterator
i = elapsed_timers.begin();
i != elapsed_timers.end(); i++){
v3s16 p = i->first;
m_data.erase(p);
}
return elapsed_timers;
}

91
src/nodetimer.h Normal file
View File

@@ -0,0 +1,91 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef NODETIMER_HEADER
#define NODETIMER_HEADER
#include "irrlichttypes.h"
#include <iostream>
#include <map>
/*
NodeTimer provides per-node timed callback functionality.
Can be used for:
- Furnaces, to keep the fire burnin'
- "activated" nodes that snap back to their original state
after a fixed amount of time (mesecons buttons, for example)
*/
class NodeTimer
{
public:
NodeTimer(): duration(0.), elapsed(0.) {}
NodeTimer(f32 duration_, f32 elapsed_):
duration(duration_), elapsed(elapsed_) {}
~NodeTimer() {}
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
f32 duration;
f32 elapsed;
};
/*
List of timers of all the nodes of a block
*/
class NodeTimerList
{
public:
NodeTimerList() {}
~NodeTimerList() {}
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
// Get timer
NodeTimer get(v3s16 p){
std::map<v3s16, NodeTimer>::iterator n = m_data.find(p);
if(n == m_data.end())
return NodeTimer();
return n->second;
}
// Deletes timer
void remove(v3s16 p){
m_data.erase(p);
}
// Deletes old timer and sets a new one
void set(v3s16 p, NodeTimer t){
m_data[p] = t;
}
// Deletes all timers
void clear(){
m_data.clear();
}
// A step in time. Returns map of elapsed timers.
std::map<v3s16, f32> step(float dtime);
private:
std::map<v3s16, NodeTimer> m_data;
};
#endif

View File

@@ -141,14 +141,14 @@ static Server* get_server(lua_State *L)
return server;
}
/*static ServerEnvironment* get_env(lua_State *L)
static ServerEnvironment* get_env(lua_State *L)
{
// Get environment from registry
lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
lua_pop(L, 1);
return env;
}*/
}
static void objectref_get(lua_State *L, u16 id)
{
@@ -418,6 +418,14 @@ struct EnumString es_NodeBoxType[] =
{0, NULL},
};
struct EnumString es_CraftMethod[] =
{
{CRAFT_METHOD_NORMAL, "normal"},
{CRAFT_METHOD_COOKING, "cooking"},
{CRAFT_METHOD_FUEL, "fuel"},
{0, NULL},
};
/*
C struct <-> Lua table converter functions
*/
@@ -1080,6 +1088,8 @@ static ContentFeatures read_content_features(lua_State *L, int index)
"deprecated: use 'drop' field");
warn_if_field_exists(L, index, "extra_dug_item_rarity",
"deprecated: use 'drop' field");
warn_if_field_exists(L, index, "metadata_name",
"deprecated: use on_add and metadata callbacks");
// True for all ground-like things like stone and mud, false for eg. trees
getboolfield(L, index, "is_ground_content", f.is_ground_content);
@@ -1096,8 +1106,6 @@ static ContentFeatures read_content_features(lua_State *L, int index)
getboolfield(L, index, "climbable", f.climbable);
// Player can build on these
getboolfield(L, index, "buildable_to", f.buildable_to);
// Metadata name of node (eg. "furnace")
getstringfield(L, index, "metadata_name", f.metadata_name);
// Whether the node is non-liquid, source liquid or flowing liquid
f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype",
es_LiquidType, LIQUID_NONE);
@@ -1173,6 +1181,9 @@ static ContentFeatures read_content_features(lua_State *L, int index)
*/
static ItemStack read_item(lua_State *L, int index);
static std::vector<ItemStack> read_items(lua_State *L, int index);
// creates a table of ItemStacks
static void push_items(lua_State *L, const std::vector<ItemStack> &items);
static void inventory_set_list_from_lua(Inventory *inv, const char *name,
lua_State *L, int tableindex, int forcesize=-1)
@@ -1185,15 +1196,7 @@ static void inventory_set_list_from_lua(Inventory *inv, const char *name,
return;
}
// Otherwise set list
std::vector<ItemStack> items;
luaL_checktype(L, tableindex, LUA_TTABLE);
lua_pushnil(L);
while(lua_next(L, tableindex) != 0){
// key at index -2 and value at index -1
items.push_back(read_item(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
std::vector<ItemStack> items = read_items(L, tableindex);
int listsize = (forcesize != -1) ? forcesize : items.size();
InventoryList *invlist = inv->addList(name, listsize);
int index = 0;
@@ -1218,21 +1221,10 @@ static void inventory_get_list_to_lua(Inventory *inv, const char *name,
lua_pushnil(L);
return;
}
// Get the table insert function
lua_getglobal(L, "table");
lua_getfield(L, -1, "insert");
int table_insert = lua_gettop(L);
// Create and fill table
lua_newtable(L);
int table = lua_gettop(L);
for(u32 i=0; i<invlist->getSize(); i++){
ItemStack item = invlist->getItem(i);
lua_pushvalue(L, table_insert);
lua_pushvalue(L, table);
lua_pushstring(L, item.getItemString().c_str());
if(lua_pcall(L, 2, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
std::vector<ItemStack> items;
for(u32 i=0; i<invlist->getSize(); i++)
items.push_back(invlist->getItem(i));
push_items(L, items);
}
/*
@@ -1470,7 +1462,7 @@ class LuaItemStack
ItemStack &item = o->m_stack;
u32 takecount = 1;
if(!lua_isnone(L, 2))
takecount = lua_tointeger(L, 2);
takecount = luaL_checkinteger(L, 2);
ItemStack taken = item.takeItem(takecount);
create(L, taken);
return 1;
@@ -1638,6 +1630,45 @@ static ItemStack read_item(lua_State *L, int index)
}
}
static std::vector<ItemStack> read_items(lua_State *L, int index)
{
if(index < 0)
index = lua_gettop(L) + 1 + index;
std::vector<ItemStack> items;
luaL_checktype(L, index, LUA_TTABLE);
lua_pushnil(L);
while(lua_next(L, index) != 0){
// key at index -2 and value at index -1
items.push_back(read_item(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
return items;
}
// creates a table of ItemStacks
static void push_items(lua_State *L, const std::vector<ItemStack> &items)
{
// Get the table insert function
lua_getglobal(L, "table");
lua_getfield(L, -1, "insert");
int table_insert = lua_gettop(L);
// Create and fill table
lua_newtable(L);
int table = lua_gettop(L);
for(u32 i=0; i<items.size(); i++){
ItemStack item = items[i];
lua_pushvalue(L, table_insert);
lua_pushvalue(L, table);
LuaItemStack::create(L, item);
if(lua_pcall(L, 2, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
lua_remove(L, -2); // Remove table
lua_remove(L, -2); // Remove insert
}
/*
InvRef
*/
@@ -1687,6 +1718,20 @@ class InvRef
return 0;
}
// is_empty(self, listname) -> true/false
static int l_is_empty(lua_State *L)
{
InvRef *ref = checkobject(L, 1);
const char *listname = luaL_checkstring(L, 2);
InventoryList *list = getlist(L, ref, listname);
if(list && list->getUsedSlots() > 0){
lua_pushboolean(L, false);
} else {
lua_pushboolean(L, true);
}
return 1;
}
// get_size(self, listname)
static int l_get_size(lua_State *L)
{
@@ -1913,6 +1958,7 @@ class InvRef
};
const char InvRef::className[] = "InvRef";
const luaL_reg InvRef::methods[] = {
method(InvRef, is_empty),
method(InvRef, get_size),
method(InvRef, set_size),
method(InvRef, get_stack),
@@ -1947,22 +1993,17 @@ class NodeMetaRef
return *(NodeMetaRef**)ud; // unbox pointer
}
static NodeMetadata* getmeta(NodeMetaRef *ref)
static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create)
{
NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p);
if(meta == NULL && auto_create)
{
meta = new NodeMetadata(ref->m_env->getGameDef());
ref->m_env->getMap().setNodeMetadata(ref->m_p, meta);
}
return meta;
}
/*static IGenericNodeMetadata* getgenericmeta(NodeMetaRef *ref)
{
NodeMetadata *meta = getmeta(ref);
if(meta == NULL)
return NULL;
if(meta->typeId() != NODEMETA_GENERIC)
return NULL;
return (IGenericNodeMetadata*)meta;
}*/
static void reportMetadataChange(NodeMetaRef *ref)
{
// Inform other things that the metadata has changed
@@ -1987,106 +2028,99 @@ class NodeMetaRef
return 0;
}
// get_type(self)
static int l_get_type(lua_State *L)
// get_string(self, name)
static int l_get_string(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
std::string name = luaL_checkstring(L, 2);
NodeMetadata *meta = getmeta(ref, false);
if(meta == NULL){
lua_pushnil(L);
lua_pushlstring(L, "", 0);
return 1;
}
// Do it
lua_pushstring(L, meta->typeName());
std::string str = meta->getString(name);
lua_pushlstring(L, str.c_str(), str.size());
return 1;
}
// allows_text_input(self)
static int l_allows_text_input(lua_State *L)
// set_string(self, name, var)
static int l_set_string(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
lua_pushboolean(L, meta->allowsTextInput());
return 1;
}
std::string name = luaL_checkstring(L, 2);
size_t len = 0;
const char *s = lua_tolstring(L, 3, &len);
std::string str(s, len);
// set_text(self, text)
static int l_set_text(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string text = luaL_checkstring(L, 2);
meta->setText(text);
NodeMetadata *meta = getmeta(ref, !str.empty());
if(meta == NULL || str == meta->getString(name))
return 0;
meta->setString(name, str);
reportMetadataChange(ref);
return 0;
}
// get_text(self)
static int l_get_text(lua_State *L)
// get_int(self, name)
static int l_get_int(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string text = meta->getText();
lua_pushstring(L, text.c_str());
return 1;
}
std::string name = lua_tostring(L, 2);
// get_owner(self)
static int l_get_owner(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string owner = meta->getOwner();
lua_pushstring(L, owner.c_str());
return 1;
}
// set_owner(self, string)
static int l_set_owner(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string owner = luaL_checkstring(L, 2);
meta->setOwner(owner);
reportMetadataChange(ref);
return 1;
}
// get_allow_removal(self)
static int l_get_allow_removal(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
NodeMetadata *meta = getmeta(ref, false);
if(meta == NULL){
lua_pushboolean(L, true);
lua_pushnumber(L, 0);
return 1;
}
// Do it
lua_pushboolean(L, !meta->nodeRemovalDisabled());
std::string str = meta->getString(name);
lua_pushnumber(L, stoi(str));
return 1;
}
/* IGenericNodeMetadata interface */
// set_infotext(self, text)
static int l_set_infotext(lua_State *L)
// set_int(self, name, var)
static int l_set_int(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string text = luaL_checkstring(L, 2);
meta->setInfoText(text);
std::string name = lua_tostring(L, 2);
int a = lua_tointeger(L, 3);
std::string str = itos(a);
NodeMetadata *meta = getmeta(ref, true);
if(meta == NULL || str == meta->getString(name))
return 0;
meta->setString(name, str);
reportMetadataChange(ref);
return 0;
}
// get_float(self, name)
static int l_get_float(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
NodeMetadata *meta = getmeta(ref, false);
if(meta == NULL){
lua_pushnumber(L, 0);
return 1;
}
std::string str = meta->getString(name);
lua_pushnumber(L, stof(str));
return 1;
}
// set_float(self, name, var)
static int l_set_float(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
float a = lua_tonumber(L, 3);
std::string str = ftos(a);
NodeMetadata *meta = getmeta(ref, true);
if(meta == NULL || str == meta->getString(name))
return 0;
meta->setString(name, str);
reportMetadataChange(ref);
return 0;
}
@@ -2095,137 +2129,94 @@ class NodeMetaRef
static int l_get_inventory(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
getmeta(ref, true); // try to ensure the metadata exists
InvRef::createNodeMeta(L, ref->m_p);
return 1;
}
// set_inventory_draw_spec(self, text)
static int l_set_inventory_draw_spec(lua_State *L)
// to_table(self)
static int l_to_table(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string text = luaL_checkstring(L, 2);
meta->setInventoryDrawSpec(text);
reportMetadataChange(ref);
return 0;
}
// set_allow_text_input(self, text)
static int l_set_allow_text_input(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
bool b = lua_toboolean(L, 2);
meta->setAllowTextInput(b);
reportMetadataChange(ref);
return 0;
}
// set_allow_removal(self, text)
static int l_set_allow_removal(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
bool b = lua_toboolean(L, 2);
meta->setRemovalDisabled(!b);
reportMetadataChange(ref);
return 0;
}
// set_enforce_owner(self, text)
static int l_set_enforce_owner(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
bool b = lua_toboolean(L, 2);
meta->setEnforceOwner(b);
reportMetadataChange(ref);
return 0;
}
// is_inventory_modified(self)
static int l_is_inventory_modified(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
lua_pushboolean(L, meta->isInventoryModified());
NodeMetadata *meta = getmeta(ref, true);
if(meta == NULL){
lua_pushnil(L);
return 1;
}
lua_newtable(L);
// fields
lua_newtable(L);
{
std::map<std::string, std::string> fields = meta->getStrings();
for(std::map<std::string, std::string>::const_iterator
i = fields.begin(); i != fields.end(); i++){
const std::string &name = i->first;
const std::string &value = i->second;
lua_pushlstring(L, name.c_str(), name.size());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
}
lua_setfield(L, -2, "fields");
// inventory
lua_newtable(L);
Inventory *inv = meta->getInventory();
if(inv){
std::vector<const InventoryList*> lists = inv->getLists();
for(std::vector<const InventoryList*>::const_iterator
i = lists.begin(); i != lists.end(); i++){
inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L);
lua_setfield(L, -2, (*i)->getName().c_str());
}
}
lua_setfield(L, -2, "inventory");
return 1;
}
// reset_inventory_modified(self)
static int l_reset_inventory_modified(lua_State *L)
// from_table(self, table)
static int l_from_table(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
meta->resetInventoryModified();
int base = 2;
if(lua_isnil(L, base)){
// No metadata
ref->m_env->getMap().removeNodeMetadata(ref->m_p);
lua_pushboolean(L, true);
return 1;
}
// Has metadata; clear old one first
ref->m_env->getMap().removeNodeMetadata(ref->m_p);
// Create new metadata
NodeMetadata *meta = getmeta(ref, true);
// Set fields
lua_getfield(L, base, "fields");
int fieldstable = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, fieldstable) != 0){
// key at index -2 and value at index -1
std::string name = lua_tostring(L, -2);
size_t cl;
const char *cs = lua_tolstring(L, -1, &cl);
std::string value(cs, cl);
meta->setString(name, value);
lua_pop(L, 1); // removes value, keeps key for next iteration
}
// Set inventory
Inventory *inv = meta->getInventory();
lua_getfield(L, base, "inventory");
int inventorytable = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, inventorytable) != 0){
// key at index -2 and value at index -1
std::string name = lua_tostring(L, -2);
inventory_set_list_from_lua(inv, name.c_str(), L, -1);
lua_pop(L, 1); // removes value, keeps key for next iteration
}
reportMetadataChange(ref);
return 0;
}
// is_text_modified(self)
static int l_is_text_modified(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
lua_pushboolean(L, meta->isTextModified());
return 1;
}
// reset_text_modified(self)
static int l_reset_text_modified(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
meta->resetTextModified();
reportMetadataChange(ref);
return 0;
}
// set_string(self, name, var)
static int l_set_string(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string name = luaL_checkstring(L, 2);
size_t len = 0;
const char *s = lua_tolstring(L, 3, &len);
std::string str(s, len);
meta->setString(name, str);
reportMetadataChange(ref);
return 0;
}
// get_string(self, name)
static int l_get_string(lua_State *L)
{
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref);
if(meta == NULL) return 0;
// Do it
std::string name = luaL_checkstring(L, 2);
std::string str = meta->getString(name);
lua_pushlstring(L, str.c_str(), str.size());
lua_pushboolean(L, true);
return 1;
}
@@ -2281,25 +2272,15 @@ class NodeMetaRef
};
const char NodeMetaRef::className[] = "NodeMetaRef";
const luaL_reg NodeMetaRef::methods[] = {
method(NodeMetaRef, get_type),
method(NodeMetaRef, allows_text_input),
method(NodeMetaRef, set_text),
method(NodeMetaRef, get_text),
method(NodeMetaRef, get_owner),
method(NodeMetaRef, set_owner),
method(NodeMetaRef, get_allow_removal),
method(NodeMetaRef, set_infotext),
method(NodeMetaRef, get_inventory),
method(NodeMetaRef, set_inventory_draw_spec),
method(NodeMetaRef, set_allow_text_input),
method(NodeMetaRef, set_allow_removal),
method(NodeMetaRef, set_enforce_owner),
method(NodeMetaRef, is_inventory_modified),
method(NodeMetaRef, reset_inventory_modified),
method(NodeMetaRef, is_text_modified),
method(NodeMetaRef, reset_text_modified),
method(NodeMetaRef, set_string),
method(NodeMetaRef, get_string),
method(NodeMetaRef, set_string),
method(NodeMetaRef, get_int),
method(NodeMetaRef, set_int),
method(NodeMetaRef, get_float),
method(NodeMetaRef, set_float),
method(NodeMetaRef, get_inventory),
method(NodeMetaRef, to_table),
method(NodeMetaRef, from_table),
{0,0}
};
@@ -2717,6 +2698,15 @@ class ObjectRef
}
/* Player-only */
// is_player(self)
static int l_is_player(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
lua_pushboolean(L, (player != NULL));
return 1;
}
// get_player_name(self)
static int l_get_player_name(lua_State *L)
@@ -2724,7 +2714,7 @@ class ObjectRef
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if(player == NULL){
lua_pushnil(L);
lua_pushlstring(L, "", 0);
return 1;
}
// Do it
@@ -3032,8 +3022,15 @@ class EnvRef
// content
MapNode n = readnode(L, 3, env->getGameDef()->ndef());
// Do it
// Call destructor
MapNode n_old = env->getMap().getNodeNoEx(pos);
scriptapi_node_on_destruct(L, pos, n_old);
// Replace node
bool succeeded = env->getMap().addNodeWithEvent(pos, n);
lua_pushboolean(L, succeeded);
// Call constructor
if(succeeded)
scriptapi_node_on_construct(L, pos, n);
return 1;
}
@@ -3053,8 +3050,13 @@ class EnvRef
// pos
v3s16 pos = read_v3s16(L, 2);
// Do it
// Call destructor
MapNode n = env->getMap().getNodeNoEx(pos);
scriptapi_node_on_destruct(L, pos, n);
// Replace with air
bool succeeded = env->getMap().removeNodeWithEvent(pos);
lua_pushboolean(L, succeeded);
// Air doesn't require constructor
return 1;
}
@@ -4255,6 +4257,49 @@ static int l_notify_authentication_modified(lua_State *L)
return 0;
}
// get_craft_result(input)
static int l_get_craft_result(lua_State *L)
{
int input_i = 1;
std::string method_s = getstringfield_default(L, input_i, "method", "normal");
enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
es_CraftMethod, CRAFT_METHOD_NORMAL);
int width = 1;
lua_getfield(L, input_i, "width");
if(lua_isnumber(L, -1))
width = luaL_checkinteger(L, -1);
lua_pop(L, 1);
lua_getfield(L, input_i, "items");
std::vector<ItemStack> items = read_items(L, -1);
lua_pop(L, 1); // items
IGameDef *gdef = get_server(L);
ICraftDefManager *cdef = gdef->cdef();
CraftInput input(method, width, items);
CraftOutput output;
bool got = cdef->getCraftResult(input, output, true, gdef);
lua_newtable(L); // output table
if(got){
ItemStack item;
item.deSerialize(output.item, gdef->idef());
LuaItemStack::create(L, item);
lua_setfield(L, -2, "item");
setintfield(L, -1, "time", output.time);
} else {
LuaItemStack::create(L, ItemStack());
lua_setfield(L, -2, "item");
setintfield(L, -1, "time", 0);
}
lua_newtable(L); // decremented input table
lua_pushstring(L, method_s.c_str());
lua_setfield(L, -2, "method");
lua_pushinteger(L, width);
lua_setfield(L, -2, "width");
push_items(L, input.items);
lua_setfield(L, -2, "items");
return 2;
}
static const struct luaL_Reg minetest_f [] = {
{"debug", l_debug},
{"log", l_log},
@@ -4278,6 +4323,7 @@ static const struct luaL_Reg minetest_f [] = {
{"is_singleplayer", l_is_singleplayer},
{"get_password_hash", l_get_password_hash},
{"notify_authentication_modified", l_notify_authentication_modified},
{"get_craft_result", l_get_craft_result},
{NULL, NULL}
};
@@ -4935,7 +4981,7 @@ bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
return true;
}
bool scriptapi_node_on_punch(lua_State *L, v3s16 pos, MapNode node,
bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
ServerActiveObject *puncher)
{
realitycheck(L);
@@ -4949,7 +4995,7 @@ bool scriptapi_node_on_punch(lua_State *L, v3s16 pos, MapNode node,
return false;
// Call function
push_v3s16(L, pos);
push_v3s16(L, p);
pushnode(L, node, ndef);
objectref_get_or_create(L, puncher);
if(lua_pcall(L, 3, 0, 0))
@@ -4957,7 +5003,7 @@ bool scriptapi_node_on_punch(lua_State *L, v3s16 pos, MapNode node,
return true;
}
bool scriptapi_node_on_dig(lua_State *L, v3s16 pos, MapNode node,
bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
ServerActiveObject *digger)
{
realitycheck(L);
@@ -4971,7 +5017,7 @@ bool scriptapi_node_on_dig(lua_State *L, v3s16 pos, MapNode node,
return false;
// Call function
push_v3s16(L, pos);
push_v3s16(L, p);
pushnode(L, node, ndef);
objectref_get_or_create(L, digger);
if(lua_pcall(L, 3, 0, 0))
@@ -4979,6 +5025,178 @@ bool scriptapi_node_on_dig(lua_State *L, v3s16 pos, MapNode node,
return true;
}
void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
// Push callback function on stack
if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_construct"))
return;
// Call function
push_v3s16(L, p);
if(lua_pcall(L, 1, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
// Push callback function on stack
if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_destruct"))
return;
// Call function
push_v3s16(L, p);
if(lua_pcall(L, 1, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
const std::string &formname,
const std::map<std::string, std::string> &fields,
ServerActiveObject *sender)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = get_env(L)->getMap().getNodeNoEx(p);
if(node.getContent() == CONTENT_IGNORE)
return;
// Push callback function on stack
if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_receive_fields"))
return;
// Call function
// param 1
push_v3s16(L, p);
// param 2
lua_pushstring(L, formname.c_str());
// param 3
lua_newtable(L);
for(std::map<std::string, std::string>::const_iterator
i = fields.begin(); i != fields.end(); i++){
const std::string &name = i->first;
const std::string &value = i->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
// param 4
objectref_get_or_create(L, sender);
if(lua_pcall(L, 4, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p,
const std::string &from_list, int from_index,
const std::string &to_list, int to_index,
int count, ServerActiveObject *player)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = get_env(L)->getMap().getNodeNoEx(p);
if(node.getContent() == CONTENT_IGNORE)
return;
// Push callback function on stack
if(!get_item_callback(L, ndef->get(node).name.c_str(),
"on_metadata_inventory_move"))
return;
// function(pos, from_list, from_index, to_list, to_index, count, player)
push_v3s16(L, p);
lua_pushstring(L, from_list.c_str());
lua_pushinteger(L, from_index + 1);
lua_pushstring(L, to_list.c_str());
lua_pushinteger(L, to_index + 1);
lua_pushinteger(L, count);
objectref_get_or_create(L, player);
if(lua_pcall(L, 7, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p,
const std::string &listname, int index, ItemStack &stack,
ServerActiveObject *player)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = get_env(L)->getMap().getNodeNoEx(p);
if(node.getContent() == CONTENT_IGNORE)
return stack;
// Push callback function on stack
if(!get_item_callback(L, ndef->get(node).name.c_str(),
"on_metadata_inventory_offer"))
return stack;
// Call function(pos, listname, index, stack, player)
push_v3s16(L, p);
lua_pushstring(L, listname.c_str());
lua_pushinteger(L, index + 1);
LuaItemStack::create(L, stack);
objectref_get_or_create(L, player);
if(lua_pcall(L, 5, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
return read_item(L, -1);
}
ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p,
const std::string &listname, int index, int count,
ServerActiveObject *player)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = get_env(L)->getMap().getNodeNoEx(p);
if(node.getContent() == CONTENT_IGNORE)
return ItemStack();
// Push callback function on stack
if(!get_item_callback(L, ndef->get(node).name.c_str(),
"on_metadata_inventory_take"))
return ItemStack();
// Call function(pos, listname, index, count, player)
push_v3s16(L, p);
lua_pushstring(L, listname.c_str());
lua_pushinteger(L, index + 1);
lua_pushinteger(L, count);
objectref_get_or_create(L, player);
if(lua_pcall(L, 5, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
return read_item(L, -1);
}
/*
environment
*/

View File

@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include "mapnode.h"
#include <set>
#include <map>
class Server;
class ServerEnvironment;
@@ -81,6 +82,28 @@ bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
ServerActiveObject *puncher);
bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
ServerActiveObject *digger);
// Node constructor
void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node);
// Node destructor
void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node);
// Called when a metadata form returns values
void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
const std::string &formname,
const std::map<std::string, std::string> &fields,
ServerActiveObject *sender);
// Moves items
void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p,
const std::string &from_list, int from_index,
const std::string &to_list, int to_index,
int count, ServerActiveObject *player);
// Inserts items, returns rejected items
ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p,
const std::string &listname, int index, ItemStack &stack,
ServerActiveObject *player);
// Takes items, returns taken items
ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p,
const std::string &listname, int index, int count,
ServerActiveObject *player);
/* luaentity */
// Returns true if succesfully added into Lua; false otherwise.

View File

@@ -57,12 +57,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
19: new content type handling
20: many existing content types translated to extended ones
21: dynamic content type allocation
22: full 16-bit content types, minerals removed, facedir & wallmounted changed
22: minerals removed, facedir & wallmounted changed
23: NodeTimers, new node metadata format
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
#define SER_FMT_VER_HIGHEST 22
#define SER_FMT_VER_HIGHEST 23
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0

View File

@@ -172,7 +172,7 @@ void * EmergeThread::Thread()
After queue is empty, exit.
*/
while(getRun())
{
try{
QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
if(qptr == NULL)
break;
@@ -374,7 +374,11 @@ void * EmergeThread::Thread()
client->SetBlocksNotSent(modified_blocks);
}
}
}
catch(VersionMismatchException &e)
{
m_server->setAsyncFatalError(std::string(
"World data version mismatch (server-side) (world probably saved by a newer version of Minetest): ")+e.what());
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
@@ -2080,6 +2084,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
password[PASSWORD_SIZE-1] = 0;
}
if(!base64_is_valid(password)){
infostream<<"Server: "<<playername<<" supplied invalid password hash"<<std::endl;
SendAccessDenied(m_con, peer_id, L"Invalid password hash");
return;
}
std::string checkpwd;
bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
@@ -2176,6 +2186,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
verbosestream<<"Server: Got TOSERVER_INIT2 from "
<<peer_id<<std::endl;
Player *player = m_env->getPlayer(peer_id);
if(!player){
verbosestream<<"Server: TOSERVER_INIT2: "
<<"Player not found; ignoring."<<std::endl;
return;
}
getClient(peer_id)->serialization_version
= getClient(peer_id)->pending_serialization_version;
@@ -2203,8 +2219,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
UpdateCrafting(peer_id);
SendInventory(peer_id);
Player *player = m_env->getPlayer(peer_id);
// Send HP
SendPlayerHP(peer_id);
@@ -2403,47 +2417,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
else if(command == TOSERVER_SIGNNODETEXT)
{
if(!checkPriv(player->getName(), "interact"))
return;
/*
u16 command
v3s16 p
u16 textlen
textdata
*/
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u8 buf[6];
// Read stuff
is.read((char*)buf, 6);
v3s16 p = readV3S16(buf);
is.read((char*)buf, 2);
u16 textlen = readU16(buf);
std::string text;
for(u16 i=0; i<textlen; i++)
{
is.read((char*)buf, 1);
text += (char)buf[0];
}
NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
if(!meta)
return;
meta->setText(text);
actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
<<" at "<<PP(p)<<std::endl;
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
if(block)
{
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"sign node text");
}
setBlockNotSent(blockpos);
infostream<<"Server: SIGNNODETEXT not supported anymore"
<<std::endl;
return;
}
else if(command == TOSERVER_INVENTORY_ACTION)
{
@@ -2526,7 +2502,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
// If player is not an admin, check for ownership of src and dst
if(!checkPriv(player->getName(), "server"))
/*if(!checkPriv(player->getName(), "server"))
{
std::string owner_from = getInventoryOwner(ma->from_inv);
if(owner_from != "" && owner_from != player->getName())
@@ -2547,7 +2523,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
delete a;
return;
}
}
}*/
}
/*
Handle restrictions and special cases of the drop action
@@ -2567,7 +2543,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return;
}
// If player is not an admin, check for ownership
else if(!checkPriv(player->getName(), "server"))
/*else if(!checkPriv(player->getName(), "server"))
{
std::string owner_from = getInventoryOwner(da->from_inv);
if(owner_from != "" && owner_from != player->getName())
@@ -2578,7 +2554,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
delete a;
return;
}
}
}*/
}
/*
Handle restrictions and special cases of the craft action
@@ -2605,7 +2581,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
// If player is not an admin, check for ownership of inventory
if(!checkPriv(player->getName(), "server"))
/*if(!checkPriv(player->getName(), "server"))
{
std::string owner_craft = getInventoryOwner(ca->craft_inv);
if(owner_craft != "" && owner_craft != player->getName())
@@ -2616,7 +2592,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
delete a;
return;
}
}
}*/
}
// Do the action
@@ -2786,6 +2762,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
newpwd += c;
}
if(!base64_is_valid(newpwd)){
infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
// Wrong old password supplied!!
SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
return;
}
infostream<<"Server: Client requests a password change from "
<<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
@@ -2955,8 +2938,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
*/
if(!checkPriv(player->getName(), "interact"))
{
infostream<<"Ignoring interaction from player "<<player->getName()
<<" (no interact privilege)"<<std::endl;
actionstream<<player->getName()<<" attempted to interact with "
<<pointed.dump()<<" without 'interact' privilege"
<<std::endl;
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
client->SetBlockNotSent(blockpos);
return;
}
@@ -3041,6 +3029,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
if(n.getContent() != CONTENT_IGNORE)
scriptapi_node_on_dig(m_lua, p_under, n, playersao);
if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
{
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
client->SetBlockNotSent(blockpos);
}
}
} // action == 2
@@ -3130,6 +3126,24 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
m_playing_sounds.erase(i++);
}
}
else if(command == TOSERVER_NODEMETA_FIELDS)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
v3s16 p = readV3S16(is);
std::string formname = deSerializeString(is);
int num = readU16(is);
std::map<std::string, std::string> fields;
for(int k=0; k<num; k++){
std::string fieldname = deSerializeString(is);
std::string fieldvalue = deSerializeLongString(is);
fields[fieldname] = fieldvalue;
}
scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
playersao);
}
else
{
infostream<<"Server::ProcessData(): Ignoring "
@@ -3189,33 +3203,6 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
}
return NULL;
}
std::string Server::getInventoryOwner(const InventoryLocation &loc)
{
switch(loc.type){
case InventoryLocation::UNDEFINED:
{}
break;
case InventoryLocation::CURRENT_PLAYER:
{}
break;
case InventoryLocation::PLAYER:
{
return loc.name;
}
break;
case InventoryLocation::NODEMETA:
{
NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
if(!meta)
return "";
return meta->getOwner();
}
break;
default:
assert(0);
}
return "";
}
void Server::setInventoryModified(const InventoryLocation &loc)
{
switch(loc.type){
@@ -3238,10 +3225,6 @@ void Server::setInventoryModified(const InventoryLocation &loc)
{
v3s16 blockpos = getNodeBlockPos(loc.p);
NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
if(meta)
meta->inventoryModified();
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
if(block)
block->raiseModified(MOD_STATE_WRITE_NEEDED);

View File

@@ -483,7 +483,6 @@ class Server : public con::PeerHandler, public MapEventReceiver,
Shall be called with the environment and the connection locked.
*/
Inventory* getInventory(const InventoryLocation &loc);
std::string getInventoryOwner(const InventoryLocation &loc);
void setInventoryModified(const InventoryLocation &loc);
// Connection must be locked when called

View File

@@ -147,8 +147,8 @@ class Settings
if(is.good() == false)
return false;
infostream<<"Parsing configuration file: \""
<<filename<<"\""<<std::endl;
/*infostream<<"Parsing configuration file: \""
<<filename<<"\""<<std::endl;*/
while(parseConfigObject(is));

View File

@@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "utility_string.h"
#include "voxelalgorithms.h"
#include "inventory.h"
/*
Asserts that the exception occurs
@@ -650,6 +651,98 @@ struct TestVoxelAlgorithms
}
};
struct TestInventory
{
void Run(IItemDefManager *idef)
{
std::string serialized_inventory =
"List 0 32\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:cobble 61\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 71\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 99\n"
"Item default:cobble 38\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"EndInventoryList\n"
"EndInventory\n";
std::string serialized_inventory_2 =
"List main 32\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:cobble 61\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 71\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 99\n"
"Item default:cobble 38\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"EndInventoryList\n"
"EndInventory\n";
Inventory inv(idef);
std::istringstream is(serialized_inventory, std::ios::binary);
inv.deSerialize(is);
assert(inv.getList("0"));
assert(!inv.getList("main"));
inv.getList("0")->setName("main");
assert(!inv.getList("0"));
assert(inv.getList("main"));
std::ostringstream inv_os(std::ios::binary);
inv.serialize(inv_os);
assert(inv_os.str() == serialized_inventory_2);
}
};
/*
NOTE: These tests became non-working then NodeContainer was removed.
These should be redone, utilizing some kind of a virtual
@@ -1447,6 +1540,7 @@ void run_tests()
TESTPARAMS(TestMapNode, ndef);
TESTPARAMS(TestVoxelManipulator, ndef);
TESTPARAMS(TestVoxelAlgorithms, ndef);
TESTPARAMS(TestInventory, idef);
//TEST(TestMapBlock);
//TEST(TestMapSector);
if(INTERNET_SIMULATOR == false){

View File

@@ -1603,6 +1603,41 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
image->drop();
}
}
/*
[lowpart:percent:filename
Adds the lower part of a texture
*/
else if(part_of_name.substr(0,9) == "[lowpart:")
{
Strfnd sf(part_of_name);
sf.next(":");
u32 percent = stoi(sf.next(":"));
std::string filename = sf.next(":");
//infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
if(baseimg == NULL)
baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
video::IImage *img = sourcecache->getOrLoad(filename, device);
if(img)
{
core::dimension2d<u32> dim = img->getDimension();
core::position2d<s32> pos_base(0, 0);
video::IImage *img2 =
driver->createImage(video::ECF_A8R8G8B8, dim);
img->copyTo(img2);
img->drop();
core::position2d<s32> clippos(0, 0);
clippos.Y = dim.Height * (100-percent) / 100;
core::dimension2d<u32> clipdim = dim;
clipdim.Height = clipdim.Height * percent / 100 + 1;
core::rect<s32> cliprect(clippos, clipdim);
img2->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255),
&cliprect);
img2->drop();
}
}
else
{
errorstream<<"generate_image(): Invalid "