aboutsummaryrefslogtreecommitdiff
path: root/src/util/srp.cpp
Commit message (Expand)AuthorAge
* Don't compile pcgrandom on Windowsest312015-11-08
* Fix some SRP issuesest312015-09-30
* Initialize random for verification key generation tooest312015-08-06
* Check output of mpz_set_str and fix leak on error conditionest312015-07-24
* Remove some old dead code. Fix some Clang warnings in SRP (ng->N... willLoic Blot2015-07-24
* Make early protocol auth mechanism generic, and add SRPest312015-05-11
='#n51'>51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
local S = minetest.get_translator("testtools")
local F = minetest.formspec_escape

-- TODO: Add a Node Metadata tool

-- Param 2 Tool: Set param2 value of tools
-- Punch: +1
-- Punch+Shift:	+8
-- Place: -1
-- Place+Shift:	-8
minetest.register_tool("testtools:param2tool", {
	description = S("Param2 Tool"),
	inventory_image = "testtools_param2tool.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = function(itemstack, user, pointed_thing)
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		if pointed_thing.type ~= "node" or (not pos) then
			return
		end
		local add = 1
		if user then
			local ctrl = user:get_player_control()
			if ctrl.sneak then
				add = 8
			end
		end
		local node = minetest.get_node(pos)
		node.param2 = node.param2 + add
		minetest.swap_node(pos, node)
	end,
	on_place = function(itemstack, user, pointed_thing)
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		if pointed_thing.type ~= "node" or (not pos) then
			return
		end
		local add = -1
		if user then
			local ctrl = user:get_player_control()
			if ctrl.sneak then
				add = -8
			end
		end
		local node = minetest.get_node(pos)
		node.param2 = node.param2 + add
		minetest.swap_node(pos, node)
	end,
})

minetest.register_tool("testtools:node_setter", {
	description = S("Node Setter"),
	inventory_image = "testtools_node_setter.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = function(itemstack, user, pointed_thing)
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		if pointed_thing.type == "nothing" then
			local meta = itemstack:get_meta()
			meta:set_string("node", "air")
			meta:set_int("node_param2", 0)
			if user and user:is_player() then
				minetest.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", "air", 0))
			end
			return itemstack
		elseif pointed_thing.type ~= "node" or (not pos) then
			return
		end
		local node = minetest.get_node(pos)
		local meta = itemstack:get_meta()
		meta:set_string("node", node.name)
		meta:set_int("node_param2", node.param2)
		if user and user:is_player() then
			minetest.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", node.name, node.param2))
		end
		return itemstack
	end,
	on_secondary_use = function(itemstack, user, pointed_thing)
		local meta = itemstack:get_meta()
		local nodename = meta:get_string("node") or ""
		local param2 = meta:get_int("node_param2") or 0

		minetest.show_formspec(user:get_player_name(), "testtools:node_setter",
			"size[4,4]"..
			"field[0.5,1;3,1;nodename;"..F(S("Node name (itemstring):"))..";"..F(nodename).."]"..
			"field[0.5,2;3,1;param2;"..F(S("param2:"))..";"..F(tostring(param2)).."]"..
			"button_exit[0.5,3;3,1;submit;"..F(S("Submit")).."]"
		)
	end,
	on_place = function(itemstack, user, pointed_thing)
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		local meta = itemstack:get_meta()
		local nodename = meta:get_string("node")
		if nodename == "" and user and user:is_player() then
			minetest.chat_send_player(user:get_player_name(), S("Punch a node first!"))
			return
		end
		local param2 = meta:get_int("node_param2")
		if not param2 then
			param2 = 0
		end
		local node = { name = nodename, param2 = param2 }
		if not minetest.registered_nodes[nodename] then
			minetest.chat_send_player(user:get_player_name(), S("Cannot set unknown node: @1", nodename))
			return
		end
		minetest.set_node(pos, node)
	end,
})

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if formname == "testtools:node_setter" then
		local playername = player:get_player_name()
		local witem = player:get_wielded_item()
		if witem:get_name() == "testtools:node_setter" then
			if fields.nodename and fields.param2 then
				local param2 = tonumber(fields.param2)
				if not param2 then
					return
				end
				local meta = witem:get_meta()
				meta:set_string("node", fields.nodename)
				meta:set_int("node_param2", param2)
				player:set_wielded_item(witem)
			end
		end
	end
end)

minetest.register_tool("testtools:remover", {
	description = S("Remover"),
	inventory_image = "testtools_remover.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = function(itemstack, user, pointed_thing)
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		if pointed_thing.type == "node" and pos ~= nil then
			minetest.remove_node(pos)
		elseif pointed_thing.type == "object" then
			local obj = pointed_thing.ref
			if not obj:is_player() then
				obj:remove()
			end
		end
	end,
})

minetest.register_tool("testtools:falling_node_tool", {
	description = S("Falling Node Tool"),
	inventory_image = "testtools_falling_node_tool.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_place = function(itemstack, user, pointed_thing)
		-- Teleport node 1-2 units upwards (if possible) and make it fall
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		if pointed_thing.type ~= "node" or (not pos) then
			return
		end
		local ok = false
		local highest
		for i=1,2 do
			local above = {x=pos.x,y=pos.y+i,z=pos.z}
			local n2 = minetest.get_node(above)
			local def2 = minetest.registered_nodes[n2.name]
			if def2 and (not def2.walkable) then
				highest = above
			else
				break
			end
		end
		if highest then
			local node = minetest.get_node(pos)
			local metatable = minetest.get_meta(pos):to_table()
			minetest.remove_node(pos)
			minetest.set_node(highest, node)
			local meta_highest = minetest.get_meta(highest)
			meta_highest:from_table(metatable)
			ok = minetest.spawn_falling_node(highest)
		else
			ok = minetest.spawn_falling_node(pos)
		end
		if not ok and user and user:is_player() then
			minetest.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!"))
		end
	end,
	on_use = function(itemstack, user, pointed_thing)
		local pos = minetest.get_pointed_thing_position(pointed_thing)
		if pointed_thing.type ~= "node" or (not pos) then
			return
		end
		local ok = minetest.spawn_falling_node(pos)
		if not ok and user and user:is_player() then
			minetest.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!"))
		end
	end,
})

minetest.register_tool("testtools:rotator", {
	description = S("Entity Rotator"),
	inventory_image = "testtools_entity_rotator.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type ~= "object" then
			return
		end
		local obj = pointed_thing.ref
		if obj:is_player() then
			-- No player rotation
			return
		else
			local axis = "y"
			if user and user:is_player() then
				local ctrl = user:get_player_control()
				if ctrl.sneak then
					axis = "x"
				elseif ctrl.aux1 then
					axis = "z"
				end
			end
			local rot = obj:get_rotation()
			rot[axis] = rot[axis] + math.pi/8
			if rot[axis] > math.pi*2 then
				rot[axis] = rot[axis] - math.pi*2
			end
			obj:set_rotation(rot)
		end
	end,
})

local mover_config = function(itemstack, user, pointed_thing)
	if not (user and user:is_player()) then
		return
	end
	local name = user:get_player_name()
	local ctrl = user:get_player_control()
	local meta = itemstack:get_meta()
	local dist = 1.0
	if meta:contains("distance") then
		dist = meta:get_int("distance")
	end
	if ctrl.sneak then
		dist = dist - 1
	else
		dist = dist + 1
	end
	meta:set_int("distance", dist)
	minetest.chat_send_player(user:get_player_name(), S("distance=@1/10", dist*2))
	return itemstack
end

minetest.register_tool("testtools:object_mover", {
	description = S("Object Mover"),
	inventory_image = "testtools_object_mover.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_place = mover_config,
	on_secondary_use = mover_config,
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type ~= "object" then
			return
		end
		local obj = pointed_thing.ref
		if not (user and user:is_player()) then
			return
		end
		local yaw = user:get_look_horizontal()
		local dir = minetest.yaw_to_dir(yaw)
		local pos = obj:get_pos()
		local pitch = user:get_look_vertical()
		if pitch > 0.25 * math.pi then
			dir.y = -1
			dir.x = 0
			dir.z = 0
		elseif pitch < -0.25 * math.pi then
			dir.y = 1
			dir.x = 0
			dir.z = 0
		end
		local ctrl = user:get_player_control()
		if ctrl.sneak then
			dir = vector.multiply(dir, -1)
		end
		local meta = itemstack:get_meta()
		if meta:contains("distance") then
			local dist = meta:get_int("distance")
			dir = vector.multiply(dir, dist*0.2)
		end
		pos = vector.add(pos, dir)
		obj:set_pos(pos)
	end,
})



minetest.register_tool("testtools:entity_scaler", {
	description = S("Entity Visual Scaler"),
	inventory_image = "testtools_entity_scaler.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type ~= "object" then
			return
		end
		local obj = pointed_thing.ref
		if obj:is_player() then
			-- No player scaling
			return
		else
			local diff = 0.1
			if user and user:is_player() then
				local ctrl = user:get_player_control()
				if ctrl.sneak then
					diff = -0.1
				end
			end
			local prop = obj:get_properties()
			if not prop.visual_size then
				prop.visual_size = { x=1, y=1, z=1 }
			else
				prop.visual_size = { x=prop.visual_size.x+diff, y=prop.visual_size.y+diff, z=prop.visual_size.z+diff }
				if prop.visual_size.x <= 0.1 then
					prop.visual_size.x = 0.1
				end
				if prop.visual_size.y <= 0.1 then
					prop.visual_size.y = 0.1
				end
				if prop.visual_size.z <= 0.1 then
					prop.visual_size.z = 0.1
				end
			end
			obj:set_properties(prop)
		end
	end,
})

local selections = {}
local entity_list
local function get_entity_list()
	if entity_list then
		return entity_list
	end
	local ents = minetest.registered_entities
	local list = {}
	for k,_ in pairs(ents) do
		table.insert(list, k)
	end
	table.sort(list)
	entity_list = list
	return entity_list
end
minetest.register_tool("testtools:entity_spawner", {
	description = S("Entity Spawner"),
	inventory_image = "testtools_entity_spawner.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_place = function(itemstack, user, pointed_thing)
		local name = user:get_player_name()
		if selections[name] and pointed_thing.type == "node" then
			local pos = pointed_thing.above
			minetest.add_entity(pos, get_entity_list()[selections[name]])
		end
	end,
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type == "object" then
			return
		end
		if user and user:is_player() then
			local list = table.concat(get_entity_list(), ",")
			local name = user:get_player_name()
			local sel = selections[name] or ""
			minetest.show_formspec(name, "testtools:entity_list",
				"size[9,9]"..
				"textlist[0,0;9,8;entity_list;"..list..";"..sel..";false]"..
				"button[0,8;4,1;spawn;Spawn entity]"
			)
		end
	end,
})

local function prop_to_string(property)
	if type(property) == "string" then
		return "\"" .. property .. "\""
	elseif type(property) == "table" then
		return tostring(dump(property)):gsub("\n", "")
	else
		return tostring(property)
	end
end

local property_formspec_data = {}
local property_formspec_index = {}
local selected_objects = {}
local function get_object_properties_form(obj, playername)
	if not playername then return "" end
	local props = obj:get_properties()
	local str = ""
	property_formspec_data[playername] = {}
	local proplist = {}
	for k,_ in pairs(props) do
		table.insert(proplist, k)
	end
	table.sort(proplist)
	for p=1, #proplist do
		local k = proplist[p]
		local v = props[k]
		local newline = ""
		newline = k .. " = "
		newline = newline .. prop_to_string(v)
		str = str .. F(newline)
		if p < #proplist then
			str = str .. ","
		end
		table.insert(property_formspec_data[playername], k)
	end
	return str
end

local editor_formspec_selindex = {}

local editor_formspec = function(playername, obj, value, sel)
	if not value then
		value = ""
	end
	if not sel then
		sel = ""
	end
	local list = get_object_properties_form(obj, playername)
	local title
	if obj:is_player() then
		title = S("Object properties of player “@1”", obj:get_player_name())
	else
		local ent = obj:get_luaentity()
		title = S("Object properties of @1", ent.name)
	end
	minetest.show_formspec(playername, "testtools:object_editor",
		"size[9,9]"..
		"label[0,0;"..F(title).."]"..
		"textlist[0,0.5;9,7.5;object_props;"..list..";"..sel..";false]"..
		"field[0.2,8.75;8,1;value;"..F(S("Value"))..";"..F(value).."]"..
		"field_close_on_enter[value;false]"..
		"button[8,8.5;1,1;submit;"..F(S("Submit")).."]"
	)
end

minetest.register_tool("testtools:object_editor", {
	description = S("Object Property Editor"),
	inventory_image = "testtools_object_editor.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = function(itemstack, user, pointed_thing)
		if user and user:is_player() then
			local name = user:get_player_name()

			if pointed_thing.type == "object" then
				selected_objects[name] = pointed_thing.ref
			elseif pointed_thing.type == "nothing" then
				-- Use on yourself if pointing nothing
				selected_objects[name] = user
			else
				-- Unsupported pointed thing
				return
			end

			local sel = editor_formspec_selindex[name]
			local val
			if selected_objects[name] and selected_objects[name]:get_properties() then
				local props = selected_objects[name]:get_properties()
				local keys = property_formspec_data[name]
				if property_formspec_index[name] and props then
					local key = keys[property_formspec_index[name]]
					val = prop_to_string(props[key])
				end
			end

			editor_formspec(name, selected_objects[name], val, sel)
		end
	end,
})

local ent_parent = {}
local ent_child = {}
local DEFAULT_ATTACH_OFFSET_Y = 11

local attacher_config = function(itemstack, user, pointed_thing)
	if not (user and user:is_player()) then
		return
	end
	if pointed_thing.type == "object" then
		return
	end
	local name = user:get_player_name()
	local ctrl = user:get_player_control()
	local meta = itemstack:get_meta()
	if ctrl.aux1 then
		local rot_x = meta:get_float("rot_x")
		if ctrl.sneak then
			rot_x = rot_x - math.pi/8
		else
			rot_x = rot_x + math.pi/8
		end
		if rot_x > 6.2 then
			rot_x = 0
		elseif rot_x < 0 then
			rot_x = math.pi * (15/8)
		end
		minetest.chat_send_player(name, S("rotation=@1", minetest.pos_to_string({x=rot_x,y=0,z=0})))
		meta:set_float("rot_x", rot_x)
	else
		local pos_y
		if meta:contains("pos_y") then
			pos_y = meta:get_int("pos_y")
		else
			pos_y = DEFAULT_ATTACH_OFFSET_Y
		end
		if ctrl.sneak then
			pos_y = pos_y - 1
		else
			pos_y = pos_y + 1
		end
		minetest.chat_send_player(name, S("position=@1", minetest.pos_to_string({x=0,y=pos_y,z=0})))
		meta:set_int("pos_y", pos_y)
	end
	return itemstack
end

minetest.register_tool("testtools:object_attacher", {
	description = S("Object Attacher"),
	inventory_image = "testtools_object_attacher.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_place = attacher_config,
	on_secondary_use = attacher_config,
	on_use = function(itemstack, user, pointed_thing)
		if user and user:is_player() then
			local name = user:get_player_name()
			local selected_object
			if pointed_thing.type == "object" then
				selected_object = pointed_thing.ref
			elseif pointed_thing.type == "nothing" then
				selected_object = user
			else
				return
			end
			local ctrl = user:get_player_control()
			if ctrl.sneak then
				if selected_object:get_attach() then
					selected_object:set_detach()
					minetest.chat_send_player(name, S("Object detached!"))
				else
					minetest.chat_send_player(name, S("Object is not attached!"))
				end
				return
			end
			local parent = ent_parent[name]
			local child = ent_child[name]
			local ename = S("<unknown>")
			if not parent then
				parent = selected_object
				ent_parent[name] = parent
			elseif not child then
				child = selected_object
				ent_child[name] = child
			end
			local entity = selected_object:get_luaentity()
			if entity then
				ename = entity.name
			elseif selected_object:is_player() then
				ename = selected_object:get_player_name()
			end
			if selected_object == parent then
				minetest.chat_send_player(name, S("Parent object selected: @1", ename))
			elseif selected_object == child then
				minetest.chat_send_player(name, S("Child object selected: @1", ename))
			end
			if parent and child then
				if parent == child then
					minetest.chat_send_player(name, S("Can't attach an object to itself!"))
					ent_parent[name] = nil
					ent_child[name] = nil
					return
				end
				local meta = itemstack:get_meta()
				local y
				if meta:contains("pos_y") then
					y = meta:get_int("pos_y")
				else
					y = DEFAULT_ATTACH_OFFSET_Y
				end
				local rx = meta:get_float("rot_x") or 0
				local offset = {x=0,y=y,z=0}
				local angle = {x=rx,y=0,z=0}
				child:set_attach(parent, "", offset, angle)
				local check_parent = child:get_attach()
				if check_parent then
					minetest.chat_send_player(name, S("Object attached! position=@1, rotation=@2",
						minetest.pos_to_string(offset), minetest.pos_to_string(angle)))
				else
					minetest.chat_send_player(name, S("Attachment failed!"))
				end
				ent_parent[name] = nil
				ent_child[name] = nil
			end
		end
	end,
})

-- Use loadstring to parse param as a Lua value
local function use_loadstring(param, player)
	-- For security reasons, require 'server' priv, just in case
	-- someone is actually crazy enough to run this on a public server.
	local privs = minetest.get_player_privs(player:get_player_name())
	if not privs.server then
		return false, "You need 'server' privilege to change object properties!"
	end
	if not param then
		return false, "Failed: parameter is nil"
	end
	--[[ DANGER ZONE ]]
	-- Interpret string as Lua value
	local func, errormsg = loadstring("return (" .. param .. ")")
	if not func then
		return false, "Failed: " .. errormsg
	end

	-- Apply sandbox here using setfenv
	setfenv(func, {})

	-- Run it
	local good, errOrResult = pcall(func)
	if not good then
		-- A Lua error was thrown
		return false, "Failed: " .. errOrResult
	end

	-- errOrResult will be the value
	return true, errOrResult
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if not (player and player:is_player()) then
		return
	end
	if formname == "testtools:entity_list" then
		local name = player:get_player_name()
		if fields.entity_list then
			local expl = minetest.explode_textlist_event(fields.entity_list)
			if expl.type == "DCL" then
				local pos = vector.add(player:get_pos(), {x=0,y=1,z=0})
				selections[name] = expl.index
				minetest.add_entity(pos, get_entity_list()[expl.index])
				return
			elseif expl.type == "CHG" then
				selections[name] = expl.index
				return
			end
		elseif fields.spawn and selections[name] then
			local pos = vector.add(player:get_pos(), {x=0,y=1,z=0})
			minetest.add_entity(pos, get_entity_list()[selections[name]])
			return
		end
	elseif formname == "testtools:object_editor" then
		local name = player:get_player_name()
		if fields.object_props then
			local expl = minetest.explode_textlist_event(fields.object_props)
			if expl.type == "DCL" or expl.type == "CHG" then
				property_formspec_index[name] = expl.index

				local props = selected_objects[name]:get_properties()
				local keys = property_formspec_data[name]
				if (not property_formspec_index[name]) or (not props) then
					return
				end
				local key = keys[property_formspec_index[name]]
				editor_formspec_selindex[name] = expl.index
				editor_formspec(name, selected_objects[name], prop_to_string(props[key]), expl.index)
				return
			end
		end
		if fields.key_enter_field == "value" or fields.submit then
			local props = selected_objects[name]:get_properties()
			local keys = property_formspec_data[name]
			if (not property_formspec_index[name]) or (not props) then
				return
			end
			local key = keys[property_formspec_index[name]]
			if not key then
				return
			end
			local success, str = use_loadstring(fields.value, player)
			if success then
				props[key] = str
			else
				minetest.chat_send_player(name, str)
				return
			end
			selected_objects[name]:set_properties(props)
			local sel = editor_formspec_selindex[name]
			editor_formspec(name, selected_objects[name], prop_to_string(props[key]), sel)
			return
		end
	end
end)