Merge branch 'NALC2' into NALC

This commit is contained in:
sys4-fr 2017-09-24 15:51:50 +02:00
commit 8c2d33c84e
244 changed files with 1180 additions and 14557 deletions

View File

@ -13,14 +13,23 @@ minetest.register_craftitem("default:paper", {
groups = {flammable = 3},
})
local lpp = 14 -- Lines per book's page
local function book_on_use(itemstack, user)
local player_name = user:get_player_name()
local data = minetest.deserialize(itemstack:get_metadata())
local meta = itemstack:get_meta()
local title, text, owner = "", "", player_name
local page, page_max, lines, string = 1, 1, {}, ""
if data then
-- Backwards compatibility
local old_data = minetest.deserialize(itemstack:get_metadata())
if old_data then
meta:from_table({ fields = old_data })
end
local data = meta:to_table().fields
if data.owner then
title = data.title
text = data.text
owner = data.owner
@ -66,12 +75,16 @@ local function book_on_use(itemstack, user)
minetest.show_formspec(player_name, "default:book", formspec)
end
local max_text_size = 10000
local max_title_size = 80
local short_title_size = 35
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "default:book" then return end
local inv = player:get_inventory()
local stack = player:get_wielded_item()
if fields.save and fields.title ~= "" and fields.text ~= "" then
if fields.save and fields.title and fields.text
and fields.title ~= "" and fields.text ~= "" then
local new_stack, data
if stack:get_name() ~= "default:book_written" then
local count = stack:get_count()
@ -82,35 +95,46 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
new_stack = ItemStack("default:book_written")
end
else
data = minetest.deserialize(stack:get_metadata())
data = stack:get_meta():to_table().fields
end
if data and data.owner and data.owner ~= player:get_player_name() then
return
end
if not data then data = {} end
data.title = fields.title
data.text = fields.text
data.text_len = #data.text
data.title = fields.title:sub(1, max_title_size)
data.owner = player:get_player_name()
local short_title = data.title
-- Don't bother triming the title if the trailing dots would make it longer
if #short_title > short_title_size + 3 then
short_title = short_title:sub(1, short_title_size) .. "..."
end
data.description = "\""..short_title.."\" by "..data.owner
data.text = fields.text:sub(1, max_text_size)
data.page = 1
data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp)
data.owner = player:get_player_name()
local data_str = minetest.serialize(data)
if new_stack then
new_stack:set_metadata(data_str)
new_stack:get_meta():from_table({ fields = data })
if inv:room_for_item("main", new_stack) then
inv:add_item("main", new_stack)
else
minetest.add_item(player:getpos(), new_stack)
end
else
stack:set_metadata(data_str)
stack:get_meta():from_table({ fields = data })
end
elseif fields.book_next or fields.book_prev then
local data = minetest.deserialize(stack:get_metadata())
local data = stack:get_meta():to_table().fields
if not data or not data.page then
return
end
data.page = tonumber(data.page)
data.page_max = tonumber(data.page_max)
if fields.book_next then
data.page = data.page + 1
if data.page > data.page_max then
@ -123,11 +147,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
end
local data_str = minetest.serialize(data)
stack:set_metadata(data_str)
book_on_use(stack, player)
stack:get_meta():from_table({fields = data})
stack = book_on_use(stack, player)
end
-- Update stack
player:set_wielded_item(stack)
end)
@ -168,13 +192,69 @@ minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv
if not original then
return
end
local copymeta = original:get_metadata()
local copymeta = original:get_meta():to_table()
-- copy of the book held by player's mouse cursor
itemstack:set_metadata(copymeta)
itemstack:get_meta():from_table(copymeta)
-- put the book with metadata back in the craft grid
craft_inv:set_stack("craft", index, original)
end)
minetest.register_craftitem("default:skeleton_key", {
description = "Skeleton Key",
inventory_image = "default_key_skeleton.png",
groups = {key = 1},
on_use = function(itemstack, user, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
local pos = pointed_thing.under
local node = minetest.get_node(pos)
if not node then
return itemstack
end
local on_skeleton_key_use = minetest.registered_nodes[node.name].on_skeleton_key_use
if not on_skeleton_key_use then
return itemstack
end
-- make a new key secret in case the node callback needs it
local random = math.random
local newsecret = string.format(
"%04x%04x%04x%04x",
random(2^16) - 1, random(2^16) - 1,
random(2^16) - 1, random(2^16) - 1)
local secret, _, _ = on_skeleton_key_use(pos, user, newsecret)
if secret then
local inv = minetest.get_inventory({type="player", name=user:get_player_name()})
-- update original itemstack
itemstack:take_item()
-- finish and return the new key
local new_stack = ItemStack("default:key")
local meta = new_stack:get_meta()
meta:set_string("secret", secret)
meta:set_string("description", "Key to "..user:get_player_name().."'s "
..minetest.registered_nodes[node.name].description)
if itemstack:get_count() == 0 then
itemstack = new_stack
else
if inv:add_item("main", new_stack):get_count() > 0 then
minetest.add_item(user:getpos(), new_stack)
end -- else: added to inventory successfully
end
return itemstack -- FIXME: See if this return is a problem
end
end
})
minetest.register_craftitem("default:coal_lump", {
description = "Coal Lump",
inventory_image = "default_coal_lump.png",
@ -293,4 +373,3 @@ minetest.register_craftitem("default:flint", {
description = "Flint",
inventory_image = "default_flint.png"
})

55
mods/WorldEdit/ChatCommands.md Executable file → Normal file
View File

@ -22,6 +22,9 @@ Many commands also have shorter names that can be typed faster. For example, if
| `//hdo` | `//hollowdome` |
| `//do` | `//dome` |
| `//hcyl` | `//hollowcylinder` |
| `//cyl` | `//cylinder` |
| `//hpyr` | `//hollowpyramid` |
| `//pyr` | `//pyramid` |
### `//about`
@ -100,7 +103,7 @@ Display the volume of the current WorldEdit region.
### `//deleteblocks`
Delete the MapBlocks (16x16x16 units) that contain the selected region. This means that mapgen will be invoked for that area. As only whole MapBlocks get removed, the deleted area is usually larger than the selected one. Also, mapgen can trigger mechanisms like mud reflow or cavegen, which affects nodes (up to 112 nodes away) outside the MapBlock, so dont use this near buildings.
Delete the MapBlocks (16x16x16 units) that contain the selected region. This means that mapgen will be invoked for that area. As only whole MapBlocks get removed, the deleted area is usually larger than the selected one. Also, mapgen can trigger mechanisms like mud reflow or cavegen, which affects nodes (up to 112 nodes away) outside the MapBlock, so dont use this near buildings. Note that active entities are not part of a MapBlock and do not get deleted.
//deleteblocks
@ -190,6 +193,15 @@ Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length `<length
//cylinder z -12 3 mesecons:wire_00000000_off
//cylinder ? 2 4 default:stone
### `//hollowpyramid x/y/z? <height> <node>`
Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height `<height>`, composed of `<node>`.
//hollowpyramid x 8 Diamond Block
//hollowpyramid y -5 glass
//hollowpyramid z 2 mesecons:wire_00000000_off
//hollowpyramid ? 12 mesecons:wire_00000000_off
### `//pyramid x/y/z? <height> <node>`
Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height `<height>`, composed of `<node>`.
@ -291,6 +303,12 @@ Fixes the lighting in the current WorldEdit region.
//fixlight
### `//drain`
Removes any fluid node within the current WorldEdit region.
//drain
### `//hide`
Hide all nodes in the current WorldEdit region non-destructively.
@ -380,3 +398,38 @@ This mode can be left with `//mtschemprob finish`. `//mtschemprob get` will disp
Clears all objects within the WorldEdit region.
//clearobjects
### `//shift x/y/z/?/up/down/left/right/front/back [+|-]<amount>`
Shifts the selection area by `[+|-]<amount>` without touching its contents. The shifting axis can be absolute (`x/y/z`) or
relative (`up/down/left/right/front/back`).
//shift left 5
### `//expand [+|-]x/y/z/?/up/down/left/right/front/back <amount> [reverse-amount]`
Expands the selection by `<amount>` in the selected absolute or relative axis. If specified, the selection can be expanded in the
opposite direction over the same axis by `[reverse-amount]`.
//expand right 7 5
### `//contract [+|-]x/y/z/?/up/down/left/right/front/back <amount> [reverse-amount]`
Contracts the selection by `<amount>` in the selected absolute or relative axis. If specified, the selection can be contracted in the
opposite direction over the same axis by `[reverse-amount]`.
//expand right 7 5
### `//outset [hv] <amount>`
Expands the selection in all directions by `<amount>`. If specified, the selection can be expanded horizontally in the x and z axes `[h]`
or vertically in the y axis `[v]`.
//outset v 5
### `//inset [hv] <amount>`
Contracts the selection in all directions by `<amount>`. If specified, the selection can be contracted horizontally in the x and z axes `[h]`
or vertically in the y axis `[v]`.
//outset v 5

10
mods/WorldEdit/WorldEdit API.md Executable file → Normal file
View File

@ -45,6 +45,12 @@ Copies the region defined by positions `pos1` and `pos2` along the `axis` axis (
Returns the number of nodes copied.
### count = worldedit.copy2(pos1, pos2, off)
Copies the region defined by positions `pos1` and `pos2` by the offset vector `off`.
Returns the number of nodes copied.
### count = worldedit.move(pos1, pos2, axis, amount)
Moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.
@ -127,9 +133,9 @@ Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `
Returns the number of nodes added.
### count = worldedit.pyramid(pos, axis, height, node_name)
### count = worldedit.pyramid(pos, axis, height, node_name, hollow)
Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`.
Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`, composed of `node_name`.
Returns the number of nodes added.

View File

@ -0,0 +1,258 @@
-- Expands or contracts the cuboid in all axes by amount (positive or negative)
worldedit.cuboid_volumetric_expand = function(name, amount)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false, "Undefined cuboid"
end
local delta1 = vector.new()
local delta2 = vector.new()
local delta_dir1
local delta_dir2
delta1 = vector.add(delta1, amount)
delta2 = vector.add(delta2, amount)
delta_dir1, delta_dir2 = worldedit.get_expansion_directions(pos1, pos2)
delta1 = vector.multiply(delta1, delta_dir1)
delta2 = vector.multiply(delta2, delta_dir2)
worldedit.pos1[name] = vector.add(pos1, delta1)
worldedit.pos2[name] = vector.add(pos2, delta2)
return true
end
-- Expands or contracts the cuboid in a single axis by amount (positive or negative)
worldedit.cuboid_linear_expand = function(name, axis, direction, amount)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false, "undefined cuboid"
end
if direction ~= 1 and direction ~= -1 then
return false, "invalid marker"
end
local marker = worldedit.marker_get_closest_to_axis(name, axis, direction)
local deltavect = vector.new()
if axis == 'x' then
deltavect.x = amount * direction
elseif axis == 'y' then
deltavect.y = amount * direction
elseif axis == 'z' then
deltavect.z = amount * direction
else
return false, "invalid axis"
end
worldedit.marker_move(name, marker, deltavect)
return true
end
-- Shifts the cuboid by '+-amount' in axis 'axis'
worldedit.cuboid_shift = function(name, axis, amount)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false, "undefined cuboid"
end
if axis == 'x' then
worldedit.pos1[name].x = pos1.x + amount
worldedit.pos2[name].x = pos2.x + amount
elseif axis == 'y' then
worldedit.pos1[name].y = pos1.y + amount
worldedit.pos2[name].y = pos2.y + amount
elseif axis == 'z' then
worldedit.pos1[name].z = pos1.z + amount
worldedit.pos2[name].z = pos2.z + amount
else
return false, "invalid axis"
end
return true
end
-- Moves the location of a single marker by adding deltavector
worldedit.marker_move = function(name, marker, deltavector)
if marker ~= 1 and marker ~= 2 then
return false
end
if marker == 1 then
local pos = worldedit.pos1[name]
worldedit.pos1[name] = vector.add(deltavector, pos)
else
local pos = worldedit.pos2[name]
worldedit.pos2[name] = vector.add(deltavector, pos)
end
return true
end
-- Updates the location ingame of the markers
worldedit.marker_update = function(name, marker)
if marker == nil then
worldedit.mark_pos1(name)
worldedit.mark_pos2(name)
elseif marker == 1 then
worldedit.mark_pos1(name)
elseif marker == 2 then
worldedit.mark_pos2(name)
else
minetest.debug(
"worldedit: Invalid execution of function update_markers")
end
end
-- Returns two vectors with the directions for volumetric expansion
worldedit.get_expansion_directions = function(mark1, mark2)
if mark1 == nil or mark2 == nil then
return
end
local dir1 = vector.new()
local dir2 = vector.new()
if mark1.x < mark2.x then
dir1.x = -1
dir2.x = 1
else
dir1.x = 1
dir2.x = -1
end
if mark1.y < mark2.y then
dir1.y = -1
dir2.y = 1
else
dir1.y = 1
dir2.y = -1
end
if mark1.z < mark2.z then
dir1.z = -1
dir2.z = 1
else
dir1.z = 1
dir2.z = -1
end
return dir1, dir2
end
-- Return the marker that is closest to the player
worldedit.marker_get_closest_to_player = function(name)
local playerpos = minetest.get_player_by_name(name):getpos()
local dist1 = vector.distance(playerpos, worldedit.pos1[name])
local dist2 = vector.distance(playerpos, worldedit.pos2[name])
if dist1 < dist2 then
return 1
else
return 2
end
end
-- Returns the closest marker to the specified axis and direction
worldedit.marker_get_closest_to_axis = function(name, axis, direction)
local pos1 = vector.new()
local pos2 = vector.new()
if direction ~= 1 and direction ~= -1 then
return nil
end
if axis == 'x' then
pos1.x = worldedit.pos1[name].x * direction
pos2.x = worldedit.pos2[name].x * direction
if pos1.x > pos2.x then
return 1
else
return 2
end
elseif axis == 'y' then
pos1.y = worldedit.pos1[name].y * direction
pos2.y = worldedit.pos2[name].y * direction
if pos1.y > pos2.y then
return 1
else
return 2
end
elseif axis == 'z' then
pos1.z = worldedit.pos1[name].z * direction
pos2.z = worldedit.pos2[name].z * direction
if pos1.z > pos2.z then
return 1
else
return 2
end
else
return nil
end
end
-- Translates up, down, left, right, front, back to their corresponding axes and
-- directions according to faced direction
worldedit.translate_direction = function(name, direction)
local axis, dir = worldedit.player_axis(name)
local resaxis, resdir
if direction == "up" then
return 'y', 1
end
if direction == "down" then
return 'y', -1
end
if direction == "front" then
if axis == "y" then
resaxis = nil
resdir = nil
else
resaxis = axis
resdir = dir
end
end
if direction == "back" then
if axis == "y" then
resaxis = nil
resdir = nil
else
resaxis = axis
resdir = -dir
end
end
if direction == "left" then
if axis == 'x' then
resaxis = 'z'
resdir = dir
elseif axis == 'z' then
resaxis = 'x'
resdir = -dir
end
end
if direction == "right" then
if axis == 'x' then
resaxis = 'z'
resdir = -dir
elseif axis == 'z' then
resaxis = 'x'
resdir = dir
end
end
return resaxis, resdir
end

View File

@ -36,6 +36,7 @@ load_module(path .. "/visualization.lua")
load_module(path .. "/serialization.lua")
load_module(path .. "/code.lua")
load_module(path .. "/compatibility.lua")
load_module(path .. "/cuboid.lua")
if minetest.setting_getbool("log_mods") then

45
mods/WorldEdit/worldedit/manipulations.lua Executable file → Normal file
View File

@ -90,7 +90,7 @@ function worldedit.stack2(pos1, pos2, direction, amount, finished)
translated.x = translated.x + direction.x
translated.y = translated.y + direction.y
translated.z = translated.z + direction.z
worldedit.copy2(pos1, pos2, translated, volume)
worldedit.copy2(pos1, pos2, translated)
minetest.after(0, next_one)
else
if finished then
@ -164,6 +164,38 @@ function worldedit.copy(pos1, pos2, axis, amount)
return worldedit.volume(pos1, pos2)
end
--- Copies a region by offset vector `off`.
-- @param pos1
-- @param pos2
-- @param off
-- @return The number of nodes copied.
function worldedit.copy2(pos1, pos2, off)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
worldedit.keep_loaded(pos1, pos2)
local get_node, get_meta, set_node = minetest.get_node,
minetest.get_meta, minetest.set_node
local pos = {}
pos.x = pos2.x
while pos.x >= pos1.x do
pos.y = pos2.y
while pos.y >= pos1.y do
pos.z = pos2.z
while pos.z >= pos1.z do
local node = get_node(pos) -- Obtain current node
local meta = get_meta(pos):to_table() -- Get meta of current node
local newpos = vector.add(pos, off) -- Calculate new position
set_node(newpos, node) -- Copy node to new position
get_meta(newpos):from_table(meta) -- Set metadata of new node
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
return worldedit.volume(pos1, pos2)
end
--- Moves a region along `axis` by `amount` nodes.
-- @return The number of nodes moved.
@ -543,14 +575,11 @@ end
function worldedit.fixlight(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
worldedit.keep_loaded(pos1, pos2)
local vmanip = minetest.get_voxel_manip(pos1, pos2)
vmanip:write_to_map()
vmanip:update_map() -- this updates the lighting
local nodes = minetest.find_nodes_in_area(pos1, pos2, "air")
local dig_node = minetest.dig_node
for _, pos in ipairs(nodes) do
dig_node(pos)
end
return #nodes
return worldedit.volume(pos1, pos2)
end

13
mods/WorldEdit/worldedit/primitives.lua Executable file → Normal file
View File

@ -150,14 +150,15 @@ end
-- @param axis Axis ("x", "y", or "z")
-- @param height Pyramid height.
-- @param node_name Name of node to make pyramid of.
-- @param hollow Whether the pyramid should be hollow.
-- @return The number of nodes added.
function worldedit.pyramid(pos, axis, height, node_name)
function worldedit.pyramid(pos, axis, height, node_name, hollow)
local other1, other2 = worldedit.get_axis_others(axis)
-- Set up voxel manipulator
local manip, area = mh.init_axis_radius(pos, axis,
height >= 0 and height or -height)
local data = mh.get_empty_data()
local data = mh.get_empty_data(area)
-- Handle inverted pyramids
local start_axis, end_axis, step
@ -177,7 +178,7 @@ function worldedit.pyramid(pos, axis, height, node_name)
y = pos.y - area.MinEdge.y,
z = pos.z - area.MinEdge.z,
}
local size = height * step
local size = math.abs(height * step)
local count = 0
-- For each level of the pyramid
for index1 = 0, height, step do
@ -187,10 +188,12 @@ function worldedit.pyramid(pos, axis, height, node_name)
local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1]
for index3 = -size, size do
local i = new_index2 + (index3 + offset[other2]) * stride[other2]
data[i] = node_id
if (not hollow or size - math.abs(index2) < 2 or size - math.abs(index3) < 2) then
data[i] = node_id
count = count + 1
end
end
end
count = count + (size * 2 + 1) ^ 2
size = size - 1
end

6
mods/WorldEdit/worldedit/serialization.lua Executable file → Normal file
View File

@ -144,9 +144,9 @@ local function load_schematic(value)
"([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
param1, param2 = tonumber(param1), tonumber(param2)
table.insert(nodes, {
x = originx + tonumber(x),
y = originy + tonumber(y),
z = originz + tonumber(z),
x = tonumber(x),
y = tonumber(y),
z = tonumber(z),
name = name,
param1 = param1 ~= 0 and param1 or nil,
param2 = param2 ~= 0 and param2 or nil,

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

View File

@ -0,0 +1 @@
*~

View File

@ -0,0 +1,240 @@
minetest.register_chatcommand("/outset", {
params = "[h|v] <amount>",
description = "outset the selection",
privs = {worldedit=true},
func = function(name, param)
local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)")
if find == nil then
return false, "invalid usage: " .. param
end
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false,
"Undefined region. Region must be defined beforehand."
end
local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then
return false, "Invalid direction."
end
if dir == "" or dir == "hv" or dir == "vh" then
assert(worldedit.cuboid_volumetric_expand(name, amount))
elseif dir == "h" then
assert(worldedit.cuboid_linear_expand(name, 'x', 1, amount))
assert(worldedit.cuboid_linear_expand(name, 'x', -1, amount))
assert(worldedit.cuboid_linear_expand(name, 'z', 1, amount))
assert(worldedit.cuboid_linear_expand(name, 'z', -1, amount))
elseif dir == "v" then
assert(worldedit.cuboid_linear_expand(name, 'y', 1, amount))
assert(worldedit.cuboid_linear_expand(name, 'y', -1, amount))
else
return false, "Invalid number of arguments"
end
worldedit.marker_update(name)
return true, "Region outset by " .. amount .. " blocks"
end,
}
)
minetest.register_chatcommand("/inset", {
params = "[h|v] <amount>",
description = "inset the selection",
privs = {worldedit=true},
func = function(name, param)
local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)")
if find == nil then
return false, "invalid usage: " .. param
end
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false,
"Undefined region. Region must be defined beforehand."
end
local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then
return false, "Invalid direction."
end
if dir == "" or dir == "vh" or dir == "hv" then
assert(worldedit.cuboid_volumetric_expand(name, -amount))
elseif dir == "h" then
assert(worldedit.cuboid_linear_expand(name, 'x', 1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'x', -1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'z', 1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'z', -1, -amount))
elseif dir == "v" then
assert(worldedit.cuboid_linear_expand(name, 'y', 1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'y', -1, -amount))
else
return false, "Invalid number of arguments"
end
worldedit.marker_update(name)
return true, "Region inset by " .. amount .. " blocks"
end,
}
)
minetest.register_chatcommand("/shift", {
params = "[x|y|z|?|up|down|left|right|front|back] [+|-]<amount>",
description = "Moves the selection region. Does not move contents.",
privs = {worldedit=true},
func = function(name, param)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
local find, _, direction, amount = param:find("([%?%l]+)%s*([+-]?%d+)")
if find == nil then
worldedit.player_notify(name, "invalid usage: " .. param)
return
end
if pos1 == nil or pos2 == nil then
worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end
local axis, dir
if direction == "x" or direction == "y" or direction == "z" then
axis, dir = direction, 1
elseif direction == "?" then
axis, dir = worldedit.player_axis(name)
else
axis, dir = worldedit.translate_direction(name, direction)
end
if axis == nil or dir == nil then
return false, "Invalid if looking straight up or down"
end
assert(worldedit.cuboid_shift(name, axis, amount * dir))
worldedit.marker_update(name)
return true, "Region shifted by " .. amount .. " nodes"
end,
}
)
minetest.register_chatcommand("/expand", {
params = "[+|-]<x|y|z|?|up|down|left|right|front|back> <amount> [reverse-amount]",
description = "expand the selection in one or two directions at once",
privs = {worldedit=true},
func = function(name, param)
local find, _, sign, direction, amount,
rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)")
if find == nil then
worldedit.player_notify(name, "invalid use: " .. param)
return
end
if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then
worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end
local absolute = direction:find("[xyz?]")
local dir, axis
if rev_amount == "" then
rev_amount = 0
end
if absolute == nil then
axis, dir = worldedit.translate_direction(name, direction)
if axis == nil or dir == nil then
return false, "Invalid if looking straight up or down"
end
else
if direction == "?" then
axis, dir = worldedit.player_axis(name)
else
axis = direction
dir = 1
end
end
if sign == "-" then
dir = -dir
end
worldedit.cuboid_linear_expand(name, axis, dir, amount)
worldedit.cuboid_linear_expand(name, axis, -dir, rev_amount)
worldedit.marker_update(name)
return true, "Region expanded by " .. (amount + rev_amount) .. " nodes"
end,
}
)
minetest.register_chatcommand("/contract", {
params = "[+|-]<x|y|z|?|up|down|left|right|front|back> <amount> [reverse-amount]",
description = "contract the selection in one or two directions at once",
privs = {worldedit=true},
func = function(name, param)
local find, _, sign, direction, amount,
rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)")
if find == nil then
worldedit.player_notify(name, "invalid use: " .. param)
return
end
if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then
worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end
local absolute = direction:find("[xyz?]")
local dir, axis
if rev_amount == "" then
rev_amount = 0
end
if absolute == nil then
axis, dir = worldedit.translate_direction(name, direction)
if axis == nil or dir == nil then
return false, "Invalid if looking straight up or down"
end
else
if direction == "?" then
axis, dir = worldedit.player_axis(name)
else
axis = direction
dir = 1
end
end
if sign == "-" then
dir = -dir
end
worldedit.cuboid_linear_expand(name, axis, dir, -amount)
worldedit.cuboid_linear_expand(name, axis, -dir, -rev_amount)
worldedit.marker_update(name)
return true, "Region contracted by " .. (amount + rev_amount) .. " nodes"
end,
}
)

189
mods/WorldEdit/worldedit_commands/init.lua Executable file → Normal file
View File

@ -10,8 +10,10 @@ if minetest.place_schematic then
worldedit.prob_list = {}
end
dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")
dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")
local safe_region, check_region = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")
dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua")
local safe_region, check_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")
local function get_position(name) --position 1 retrieval function for when not using `safe_region`
local pos1 = worldedit.pos1[name]
@ -91,6 +93,56 @@ minetest.register_chatcommand("/about", {
end,
})
-- mostly copied from builtin/chatcommands.lua with minor modifications
minetest.register_chatcommand("/help", {
privs = {},
params = "[all/<cmd>]",
description = "Get help for WorldEdit commands",
func = function(name, param)
local function is_we_command(cmd)
return cmd:sub(0, 1) == "/"
end
local function format_help_line(cmd, def)
local msg = minetest.colorize("#00ffff", "/"..cmd)
if def.params and def.params ~= "" then
msg = msg .. " " .. def.params
end
if def.description and def.description ~= "" then
msg = msg .. ": " .. def.description
end
return msg
end
if not minetest.check_player_privs(name, "worldedit") then
return false, "You are not allowed to use any WorldEdit commands."
end
if param == "" then
local msg = ""
local cmds = {}
for cmd, def in pairs(minetest.chatcommands) do
if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
cmds[#cmds + 1] = cmd:sub(2) -- strip the /
end
end
table.sort(cmds)
return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
.. "Use '//help <cmd>' to get more information,"
.. " or '//help all' to list everything."
elseif param == "all" then
local cmds = {}
for cmd, def in pairs(minetest.chatcommands) do
if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
cmds[#cmds + 1] = format_help_line(cmd, def)
end
end
table.sort(cmds)
return true, "Available commands:\n"..table.concat(cmds, "\n")
else
return minetest.chatcommands["help"].func(name, "/" .. param)
end
end,
})
minetest.register_chatcommand("/inspect", {
params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
description = "Enable or disable node inspection",
@ -110,16 +162,28 @@ minetest.register_chatcommand("/inspect", {
end,
})
local function get_node_rlight(pos)
local vecs = { -- neighboring nodes
{x= 1, y= 0, z= 0},
{x=-1, y= 0, z= 0},
{x= 0, y= 1, z= 0},
{x= 0, y=-1, z= 0},
{x= 0, y= 0, z= 1},
{x= 0, y= 0, z=-1},
}
local ret = 0
for _, v in ipairs(vecs) do
ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
end
return ret
end
minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
if worldedit.inspect[name] then
if minetest.check_player_privs(name, {worldedit=true}) then
local axis, sign = worldedit.player_axis(name)
message = string.format("inspector: %s at %s (param1=%d, param2=%d) punched by %s facing the %s axis",
node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-"))
else
message = "inspector: worldedit privileges required"
end
local axis, sign = worldedit.player_axis(name)
message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",
node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
worldedit.player_notify(name, message)
end
end)
@ -134,6 +198,8 @@ minetest.register_chatcommand("/reset", {
worldedit.mark_pos1(name)
worldedit.mark_pos2(name)
worldedit.set_pos[name] = nil
--make sure the user does not try to confirm an operation after resetting pos:
reset_pending(name)
worldedit.player_notify(name, "region reset")
end,
})
@ -516,6 +582,39 @@ minetest.register_chatcommand("/cylinder", {
end, check_cylinder),
})
local check_pyramid = function(name, param)
if worldedit.pos1[name] == nil then
worldedit.player_notify(name, "no position 1 selected")
return nil
end
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param)
return nil
end
local node = get_node(name, nodename)
if not node then return nil end
height = tonumber(height)
return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
end
minetest.register_chatcommand("/hollowpyramid", {
params = "x/y/z/? <height> <node>",
description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
privs = {worldedit=true},
func = safe_region(function(name, param)
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
height = tonumber(height)
if axis == "?" then
axis, sign = worldedit.player_axis(name)
height = height * sign
end
local node = get_node(name, nodename)
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
worldedit.player_notify(name, count .. " nodes added")
end, check_pyramid),
})
minetest.register_chatcommand("/pyramid", {
params = "x/y/z/? <height> <node>",
description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
@ -530,22 +629,7 @@ minetest.register_chatcommand("/pyramid", {
local node = get_node(name, nodename)
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
worldedit.player_notify(name, count .. " nodes added")
end,
function(name, param)
if worldedit.pos1[name] == nil then
worldedit.player_notify(name, "no position 1 selected")
return nil
end
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param)
return nil
end
local node = get_node(name, nodename)
if not node then return nil end
height = tonumber(height)
return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
end),
end, check_pyramid),
})
minetest.register_chatcommand("/spiral", {
@ -570,7 +654,7 @@ minetest.register_chatcommand("/spiral", {
end
local node = get_node(name, nodename)
if not node then return nil end
return check_region(name, param)
return 1 -- TODO: return an useful value
end),
})
@ -838,6 +922,30 @@ minetest.register_chatcommand("/fixlight", {
end),
})
minetest.register_chatcommand("/drain", {
params = "",
description = "Remove any fluid node within the current WorldEdit region",
privs = {worldedit=true},
func = safe_region(function(name, param)
-- TODO: make an API function for this
local count = 0
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
for x = pos1.x, pos2.x do
for y = pos1.y, pos2.y do
for z = pos1.z, pos2.z do
local n = minetest.get_node({x=x, y=y, z=z}).name
local d = minetest.registered_nodes[n]
if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
minetest.remove_node({x=x, y=y, z=z})
count = count + 1
end
end
end
end
worldedit.player_notify(name, count .. " nodes updated")
end),
})
minetest.register_chatcommand("/hide", {
params = "",
description = "Hide all nodes in the current WorldEdit region non-destructively",
@ -1015,11 +1123,6 @@ minetest.register_chatcommand("/lua", {
description = "Executes <code> as a Lua chunk in the global namespace",
privs = {worldedit=true, server=true},
func = function(name, param)
local admin = minetest.setting_get("name")
if not admin or not name == admin then
worldedit.player_notify(name, "this command can only be run by the server administrator")
return
end
local err = worldedit.lua(param)
if err then
worldedit.player_notify(name, "code error: " .. err)
@ -1034,12 +1137,6 @@ minetest.register_chatcommand("/luatransform", {
description = "Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region",
privs = {worldedit=true, server=true},
func = safe_region(function(name, param)
local admin = minetest.setting_get("name")
if not admin or not name == admin then
worldedit.player_notify(name, "this command can only be run by the server administrator")
return
end
local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
if err then
worldedit.player_notify(name, "code error: " .. err, false)
@ -1127,8 +1224,8 @@ minetest.register_chatcommand("/mtschemprob", {
return
end
for k,v in pairs(problist) do
local prob = math.floor(((v["prob"] / 256) * 100) * 100 + 0.5) / 100
text = text .. minetest.pos_to_string(v["pos"]) .. ": " .. prob .. "% | "
local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
end
worldedit.player_notify(name, "currently set node probabilities:")
worldedit.player_notify(name, text)
@ -1138,16 +1235,14 @@ minetest.register_chatcommand("/mtschemprob", {
end,
})
minetest.register_on_player_receive_fields(
function(player, formname, fields)
if (formname == "prob_val_enter") and (fields.text ~= "") then
local name = player:get_player_name()
local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
local index = table.getn(worldedit.prob_list[name]) + 1
worldedit.prob_list[name][index] = prob_entry
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
local name = player:get_player_name()
local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
local index = table.getn(worldedit.prob_list[name]) + 1
worldedit.prob_list[name][index] = prob_entry
end
)
end)
minetest.register_chatcommand("/clearobjects", {
params = "",

47
mods/WorldEdit/worldedit_commands/mark.lua Executable file → Normal file
View File

@ -58,8 +58,19 @@ worldedit.mark_region = function(name)
end
worldedit.marker_region[name] = nil
end
if pos1 ~= nil and pos2 ~= nil then
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local vec = vector.subtract(pos2, pos1)
local maxside = math.max(vec.x, math.max(vec.y, vec.z))
local limit = tonumber(minetest.setting_get("active_object_send_range_blocks")) * 16
if maxside > limit * 1.5 then
-- The client likely won't be able to see the plane markers as intended anyway,
-- thus don't place them and also don't load the area into memory
return
end
local thickness = 0.2
local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2
@ -72,24 +83,28 @@ worldedit.mark_region = function(name)
--XY plane markers
for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
local marker = minetest.add_entity({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "worldedit:region_cube")
marker:set_properties({
visual_size={x=sizex * 2, y=sizey * 2},
collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
})
marker:get_luaentity().player_name = name
table.insert(markers, marker)
if marker ~= nil then
marker:set_properties({
visual_size={x=sizex * 2, y=sizey * 2},
collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
})
marker:get_luaentity().player_name = name
table.insert(markers, marker)
end
end
--YZ plane markers
for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
local marker = minetest.add_entity({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "worldedit:region_cube")
marker:set_properties({
visual_size={x=sizez * 2, y=sizey * 2},
collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
})
marker:setyaw(math.pi / 2)
marker:get_luaentity().player_name = name
table.insert(markers, marker)
if marker ~= nil then
marker:set_properties({
visual_size={x=sizez * 2, y=sizey * 2},
collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
})
marker:setyaw(math.pi / 2)
marker:get_luaentity().player_name = name
table.insert(markers, marker)
end
end
worldedit.marker_region[name] = markers
@ -153,7 +168,11 @@ minetest.register_entity(":worldedit:region_cube", {
end
end,
on_punch = function(self, hitter)
for _, entity in ipairs(worldedit.marker_region[self.player_name]) do
local markers = worldedit.marker_region[self.player_name]
if not markers then
return
end
for _, entity in ipairs(markers) do
entity:remove()
end
worldedit.marker_region[self.player_name] = nil

15
mods/WorldEdit/worldedit_commands/safe.lua Executable file → Normal file
View File

@ -30,6 +30,10 @@ local function safe_region(callback, nodes_needed)
end
end
local function reset_pending(name)
safe_region_callback[name], safe_region_param[name] = nil, nil
end
minetest.register_chatcommand("/y", {
params = "",
description = "Confirm a pending operation",
@ -40,15 +44,8 @@ minetest.register_chatcommand("/y", {
return
end
--obtain positions
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected")
return
end
safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation
callback(name, param, pos1, pos2)
callback(name, param)
end,
})
@ -64,5 +61,5 @@ minetest.register_chatcommand("/n", {
end,
})
return safe_region, check_region
return safe_region, check_region, reset_pending

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 B

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

After

Width:  |  Height:  |  Size: 157 B

View File

@ -0,0 +1,24 @@
minetest.register_tool(":worldedit:wand", {
description = "WorldEdit Wand tool, Left-click to set 1st position, right-click to set 2nd",
inventory_image = "worldedit_wand.png",
stack_max = 1, -- there is no need to have more than one
liquids_pointable = true, -- ground with only water on can be selected as well
on_use = function(itemstack, placer, pointed_thing)
if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then
local name = placer:get_player_name()
worldedit.pos1[name] = pointed_thing.under
worldedit.mark_pos1(name)
end
return itemstack -- nothing consumed, nothing changed
end,
on_place = function(itemstack, placer, pointed_thing) -- Left Click
if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then
local name = placer:get_player_name()
worldedit.pos2[name] = pointed_thing.under
worldedit.mark_pos2(name)
end
return itemstack -- nothing consumed, nothing changed
end,
})

3
mods/WorldEdit/worldedit_gui/depends.txt Executable file → Normal file
View File

@ -2,4 +2,5 @@ worldedit
worldedit_commands
unified_inventory?
inventory_plus?
creative?
sfinv?
creative?

View File

@ -40,13 +40,13 @@ local angle_values = {90, 180, 270}
setmetatable(angle_indices, {__index = function () return 1 end})
setmetatable(angle_values, {__index = function () return 90 end})
--given multiple sets of privileges, produces a single set of privs that would have the same effect as requiring all of them at the same time
-- given multiple sets of privileges, produces a single set of privs that would have the same effect as requiring all of them at the same time
local combine_privs = function(...)
local result = {}
for i, privs in ipairs({...}) do
for name, value in pairs(privs) do
if result[name] ~= nil and result[name] ~= value then --the priv must be both true and false, which can never happen
return {__fake_priv_that_nobody_has__=true} --priviledge table that can never be satisfied
return {__fake_priv_that_nobody_has__=true} --privilege table that can never be satisfied
end
result[name] = value
end
@ -54,22 +54,44 @@ local combine_privs = function(...)
return result
end
-- display node (or unknown_node image otherwise) at specified pos in formspec
local formspec_node = function(pos, nodename)
return nodename and string.format("item_image[%s;1,1;%s]", pos, nodename)
or string.format("image[%s;1,1;worldedit_gui_unknown.png]", pos)
end
-- two further priv helpers
local function we_privs(command)
return minetest.chatcommands["/" .. command].privs
end
local function combine_we_privs(list)
local args = {}
for _, t in ipairs(list) do
table.insert(args, we_privs(t))
end
return combine_privs(unpack(args))
end
worldedit.register_gui_function("worldedit_gui_about", {
name = "About", privs = minetest.chatcommands["/about"].privs,
name = "About",
privs = {interact=true},
on_select = function(name)
minetest.chatcommands["/about"].func(name, "")
end,
})
worldedit.register_gui_function("worldedit_gui_inspect", {
name = "Toggle Inspect", privs = minetest.chatcommands["/inspect"].privs,
name = "Toggle Inspect",
privs = we_privs("inspect"),
on_select = function(name)
minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable")
end,
})
worldedit.register_gui_function("worldedit_gui_region", {
name = "Get/Set Region", privs = combine_privs(minetest.chatcommands["/p"].privs, minetest.chatcommands["/pos1"].privs, minetest.chatcommands["/pos2"].privs, minetest.chatcommands["/reset"].privs, minetest.chatcommands["/mark"].privs, minetest.chatcommands["/unmark"].privs, minetest.chatcommands["/volume"].privs, minetest.chatcommands["/fixedpos"].privs),
name = "Get/Set Region",
privs = combine_we_privs({"p", "pos1", "pos2", "reset", "mark", "unmark", "volume", "fixedpos"}),
get_formspec = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
return "size[9,7]" .. worldedit.get_formspec_header("worldedit_gui_region") ..
@ -148,15 +170,15 @@ worldedit.register_gui_handler("worldedit_gui_region", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_set", {
name = "Set Nodes", privs = minetest.chatcommands["/set"].privs,
name = "Set Nodes",
privs = we_privs("set"),
get_formspec = function(name)
local node = gui_nodename1[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_set") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_set_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_set_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_set_submit;Set Nodes]"
end,
})
@ -166,7 +188,10 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_set_node)
worldedit.show_page(name, "worldedit_gui_set")
if fields.worldedit_gui_set_submit then
minetest.chatcommands["/set"].func(name, gui_nodename1[name])
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/set"].func(name, n)
end
end
return true
end
@ -174,19 +199,18 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_replace", {
name = "Replace Nodes", privs = combine_privs(minetest.chatcommands["/replace"].privs, minetest.chatcommands["/replaceinverse"].privs),
name = "Replace Nodes",
privs = combine_we_privs({"replace", "replaceinverse"}),
get_formspec = function(name)
local search, replace = gui_nodename1[name], gui_nodename2[name]
local search_nodename, replace_nodename = worldedit.normalize_nodename(search), worldedit.normalize_nodename(replace)
return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_replace") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_replace_search;Name;%s]", minetest.formspec_escape(search)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_replace_search_search;Search]" ..
(search_nodename and string.format("item_image[5.5,1.1;1,1;%s]", search_nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", search_nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_replace_replace;Name;%s]", minetest.formspec_escape(replace)) ..
"button[4,2.18;1.5,0.8;worldedit_gui_replace_replace_search;Search]" ..
(replace_nodename and string.format("item_image[5.5,2.1;1,1;%s]", replace_nodename)
or "image[5.5,2.1;1,1;unknown_node.png]") ..
formspec_node("5.5,2.1", replace_nodename) ..
"button_exit[0,3.5;3,0.8;worldedit_gui_replace_submit;Replace Nodes]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_replace_submit_inverse;Replace Inverse]"
end,
@ -198,10 +222,19 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_replace_search)
gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace)
worldedit.show_page(name, "worldedit_gui_replace")
local submit = nil
if fields.worldedit_gui_replace_submit then
minetest.chatcommands["/replace"].func(name, string.format("%s %s", gui_nodename1[name], gui_nodename2[name]))
submit = "replace"
elseif fields.worldedit_gui_replace_submit_inverse then
minetest.chatcommands["/replaceinverse"].func(name, string.format("%s %s", gui_nodename1[name], gui_nodename2[name]))
submit = "replaceinverse"
end
if submit then
local n1 = worldedit.normalize_nodename(gui_nodename1[name])
local n2 = worldedit.normalize_nodename(gui_nodename2[name])
if n1 and n2 then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s", n1, n2))
end
end
return true
end
@ -209,15 +242,15 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_sphere_dome", {
name = "Sphere/Dome", privs = combine_privs(minetest.chatcommands["/hollowsphere"].privs, minetest.chatcommands["/sphere"].privs, minetest.chatcommands["/hollowdome"].privs, minetest.chatcommands["/dome"].privs),
name = "Sphere/Dome",
privs = combine_we_privs({"hollowsphere", "sphere", "hollowdome", "dome"}),
get_formspec = function(name)
local node, radius = gui_nodename1[name], gui_distance2[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_sphere_dome") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_sphere_dome_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_sphere_dome_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_sphere_dome_radius;Radius;%s]", minetest.formspec_escape(radius)) ..
"button_exit[0,3.5;3,0.8;worldedit_gui_sphere_dome_submit_hollow;Hollow Sphere]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_sphere_dome_submit_solid;Solid Sphere]" ..
@ -233,14 +266,22 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field
gui_nodename1[name] = tostring(fields.worldedit_gui_sphere_dome_node)
gui_distance2[name] = tostring(fields.worldedit_gui_sphere_dome_radius)
worldedit.show_page(name, "worldedit_gui_sphere_dome")
local submit = nil
if fields.worldedit_gui_sphere_dome_submit_hollow then
minetest.chatcommands["/hollowsphere"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name]))
submit = "hollowsphere"
elseif fields.worldedit_gui_sphere_dome_submit_solid then
minetest.chatcommands["/sphere"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name]))
submit = "sphere"
elseif fields.worldedit_gui_sphere_dome_submit_hollow_dome then
minetest.chatcommands["/hollowdome"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name]))
submit = "hollowdome"
elseif fields.worldedit_gui_sphere_dome_submit_solid_dome then
minetest.chatcommands["/dome"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name]))
submit = "dome"
end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s", gui_distance2[name], n))
end
end
return true
end
@ -248,15 +289,15 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field
end)
worldedit.register_gui_function("worldedit_gui_cylinder", {
name = "Cylinder", privs = combine_privs(minetest.chatcommands["/hollowcylinder"].privs, minetest.chatcommands["/cylinder"].privs),
name = "Cylinder",
privs = combine_we_privs({"hollowcylinder", "cylinder"}),
get_formspec = function(name)
local node, axis, length, radius = gui_nodename1[name], gui_axis1[name], gui_distance1[name], gui_distance2[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_cylinder") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_cylinder_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_cylinder_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_cylinder_length;Length;%s]", minetest.formspec_escape(length)) ..
string.format("dropdown[4,2.18;2.5;worldedit_gui_cylinder_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
string.format("field[0.5,3.5;4,0.8;worldedit_gui_cylinder_radius;Radius;%s]", minetest.formspec_escape(radius)) ..
@ -273,56 +314,85 @@ worldedit.register_gui_handler("worldedit_gui_cylinder", function(name, fields)
gui_distance1[name] = tostring(fields.worldedit_gui_cylinder_length)
gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius)
worldedit.show_page(name, "worldedit_gui_cylinder")
local submit = nil
if fields.worldedit_gui_cylinder_submit_hollow then
minetest.chatcommands["/hollowcylinder"].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_nodename1[name]))
submit = "hollowcylinder"
elseif fields.worldedit_gui_cylinder_submit_solid then
minetest.chatcommands["/cylinder"].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_nodename1[name]))
submit = "cylinder"
end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], n))
end
end
return true
end
if fields.worldedit_gui_cylinder_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_cylinder_axis]
worldedit.show_page(name, "worldedit_gui_cylinder")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_pyramid", {
name = "Pyramid", privs = minetest.chatcommands["/pyramid"].privs,
name = "Pyramid",
privs = we_privs("pyramid"),
get_formspec = function(name)
local node, axis, length = gui_nodename1[name], gui_axis1[name], gui_distance1[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_pyramid") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_pyramid_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_pyramid_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_pyramid_length;Length;%s]", minetest.formspec_escape(length)) ..
string.format("dropdown[4,2.18;2.5;worldedit_gui_pyramid_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
"button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit;Pyramid]"
"button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit_hollow;Hollow Pyramid]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_pyramid_submit_solid;Solid Pyramid]"
end,
})
worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields)
if fields.worldedit_gui_pyramid_search or fields.worldedit_gui_pyramid_submit then
if fields.worldedit_gui_pyramid_search or fields.worldedit_gui_pyramid_submit_solid or fields.worldedit_gui_pyramid_submit_hollow or fields.worldedit_gui_pyramid_axis then
gui_nodename1[name] = tostring(fields.worldedit_gui_pyramid_node)
gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length)
worldedit.show_page(name, "worldedit_gui_pyramid")
if fields.worldedit_gui_pyramid_submit then
minetest.chatcommands["/pyramid"].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_nodename1[name]))
local submit = nil
if fields.worldedit_gui_pyramid_submit_solid then
submit = "pyramid"
elseif fields.worldedit_gui_pyramid_submit_hollow then
submit = "hollowpyramid"
end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], n))
end
end
return true
end
if fields.worldedit_gui_pyramid_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
worldedit.show_page(name, "worldedit_gui_pyramid")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_spiral", {
name = "Spiral", privs = minetest.chatcommands["/spiral"].privs,
name = "Spiral",
privs = we_privs("spiral"),
get_formspec = function(name)
local node, length, height, space = gui_nodename1[name], gui_distance1[name], gui_distance2[name], gui_distance3[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,6]" .. worldedit.get_formspec_header("worldedit_gui_spiral") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_spiral_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_spiral_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_spiral_length;Side Length;%s]", minetest.formspec_escape(length)) ..
string.format("field[0.5,3.5;4,0.8;worldedit_gui_spiral_height;Height;%s]", minetest.formspec_escape(height)) ..
string.format("field[0.5,4.5;4,0.8;worldedit_gui_spiral_space;Wall Spacing;%s]", minetest.formspec_escape(space)) ..
@ -338,7 +408,10 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields)
gui_distance3[name] = tostring(fields.worldedit_gui_spiral_space)
worldedit.show_page(name, "worldedit_gui_spiral")
if fields.worldedit_gui_spiral_submit then
minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], gui_nodename1[name]))
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], n))
end
end
return true
end
@ -346,7 +419,8 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_copy_move", {
name = "Copy/Move", privs = combine_privs(minetest.chatcommands["/copy"].privs, minetest.chatcommands["/move"].privs),
name = "Copy/Move",
privs = combine_we_privs({"copy", "move"}),
get_formspec = function(name)
local axis = gui_axis1[name] or 4
local amount = gui_distance1[name] or "10"
@ -370,11 +444,17 @@ worldedit.register_gui_handler("worldedit_gui_copy_move", function(name, fields)
end
return true
end
if fields.worldedit_gui_copy_move_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_copy_move_axis] or 4
worldedit.show_page(name, "worldedit_gui_copy_move")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_stack", {
name = "Stack", privs = minetest.chatcommands["/stack"].privs,
name = "Stack",
privs = we_privs("stack"),
get_formspec = function(name)
local axis, count = gui_axis1[name], gui_count1[name]
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") ..
@ -392,11 +472,17 @@ worldedit.register_gui_handler("worldedit_gui_stack", function(name, fields)
minetest.chatcommands["/stack"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_count1[name]))
return true
end
if fields.worldedit_gui_stack_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_stack_axis]
worldedit.show_page(name, "worldedit_gui_stack")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_stretch", {
name = "Stretch", privs = minetest.chatcommands["/stretch"].privs,
name = "Stretch",
privs = we_privs("stretch"),
get_formspec = function(name)
local stretchx, stretchy, stretchz = gui_count1[name], gui_count2[name], gui_count3[name]
return "size[5,5]" .. worldedit.get_formspec_header("worldedit_gui_stretch") ..
@ -420,7 +506,8 @@ worldedit.register_gui_handler("worldedit_gui_stretch", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_transpose", {
name = "Transpose", privs = minetest.chatcommands["/transpose"].privs,
name = "Transpose",
privs = we_privs("transpose"),
get_formspec = function(name)
local axis1, axis2 = gui_axis1[name], gui_axis2[name]
return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_transpose") ..
@ -433,18 +520,28 @@ worldedit.register_gui_function("worldedit_gui_transpose", {
worldedit.register_gui_handler("worldedit_gui_transpose", function(name, fields)
if fields.worldedit_gui_transpose_submit then
gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1]
gui_axis2[name] = axis_indices[fields.worldedit_gui_transpose_axis2]
worldedit.show_page(name, "worldedit_gui_transpose")
minetest.chatcommands["/transpose"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], axis_values[gui_axis2[name]]))
return true
end
if fields.worldedit_gui_transpose_axis1 then
gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1]
worldedit.show_page(name, "worldedit_gui_transpose")
return true
end
if fields.worldedit_gui_transpose_axis2 then
gui_axis2[name] = axis_indices[fields.worldedit_gui_transpose_axis2]
worldedit.show_page(name, "worldedit_gui_transpose")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_flip", {
name = "Flip", privs = minetest.chatcommands["/flip"].privs,
name = "Flip",
privs = we_privs("flip"),
get_formspec = function(name)
local axis = gui_axis2[name]
local axis = gui_axis1[name]
return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_flip") ..
string.format("dropdown[0,1;2.5;worldedit_gui_flip_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_flip_submit;Flip]"
@ -453,16 +550,22 @@ worldedit.register_gui_function("worldedit_gui_flip", {
worldedit.register_gui_handler("worldedit_gui_flip", function(name, fields)
if fields.worldedit_gui_flip_submit then
gui_axis2[name] = axis_indices[fields.worldedit_gui_flip_axis]
gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis]
worldedit.show_page(name, "worldedit_gui_flip")
minetest.chatcommands["/flip"].func(name, axis_values[gui_axis1[name]])
return true
end
if fields.worldedit_gui_flip_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis]
worldedit.show_page(name, "worldedit_gui_flip")
minetest.chatcommands["/flip"].func(name, axis_values[gui_axis2[name]])
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_rotate", {
name = "Rotate", privs = minetest.chatcommands["/rotate"].privs,
name = "Rotate",
privs = we_privs("rotate"),
get_formspec = function(name)
local axis, angle = gui_axis1[name], gui_angle[name]
return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_rotate") ..
@ -480,11 +583,22 @@ worldedit.register_gui_handler("worldedit_gui_rotate", function(name, fields)
minetest.chatcommands["/rotate"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], angle_values[gui_angle[name]]))
return true
end
if fields.worldedit_gui_rotate_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_rotate_axis]
worldedit.show_page(name, "worldedit_gui_rotate")
return true
end
if fields.worldedit_gui_rotate_angle then
gui_angle[name] = angle_indices[fields.worldedit_gui_rotate_angle]
worldedit.show_page(name, "worldedit_gui_rotate")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_orient", {
name = "Orient", privs = minetest.chatcommands["/orient"].privs,
name = "Orient",
privs = we_privs("orient"),
get_formspec = function(name)
local angle = gui_angle[name]
return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_orient") ..
@ -497,36 +611,43 @@ worldedit.register_gui_handler("worldedit_gui_orient", function(name, fields)
if fields.worldedit_gui_orient_submit then
gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle]
worldedit.show_page(name, "worldedit_gui_orient")
minetest.chatcommands["/orient"].func(name, angle_values[gui_angle[name]])
minetest.chatcommands["/orient"].func(name, tostring(angle_values[gui_angle[name]]))
return true
end
if fields.worldedit_gui_orient_angle then
gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle]
worldedit.show_page(name, "worldedit_gui_orient")
return true
end
return false
end)
worldedit.register_gui_function("worldedit_gui_fixlight", {
name = "Fix Lighting", privs = minetest.chatcommands["/fixlight"].privs,
name = "Fix Lighting",
privs = we_privs("fixlight"),
on_select = function(name)
minetest.chatcommands["/fixlight"].func(name, "")
end,
})
worldedit.register_gui_function("worldedit_gui_hide", {
name = "Hide Region", privs = minetest.chatcommands["/hide"].privs,
name = "Hide Region",
privs = we_privs("hide"),
on_select = function(name)
minetest.chatcommands["/hide"].func(name, "")
end,
})
worldedit.register_gui_function("worldedit_gui_suppress", {
name = "Suppress Nodes", privs = minetest.chatcommands["/suppress"].privs,
name = "Suppress Nodes",
privs = we_privs("suppress"),
get_formspec = function(name)
local node = gui_nodename1[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_suppress") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_suppress_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_suppress_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_suppress_submit;Suppress Nodes]"
end,
})
@ -536,7 +657,10 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node)
worldedit.show_page(name, "worldedit_gui_suppress")
if fields.worldedit_gui_suppress_submit then
minetest.chatcommands["/suppress"].func(name, gui_nodename1[name])
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/suppress"].func(name, n)
end
end
return true
end
@ -544,15 +668,15 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_highlight", {
name = "Highlight Nodes", privs = minetest.chatcommands["/highlight"].privs,
name = "Highlight Nodes",
privs = we_privs("highlight"),
get_formspec = function(name)
local node = gui_nodename1[name]
local nodename = worldedit.normalize_nodename(node)
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_highlight") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_highlight_node;Name;%s]", minetest.formspec_escape(node)) ..
"button[4,1.18;1.5,0.8;worldedit_gui_highlight_search;Search]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename)
or "image[5.5,1.1;1,1;unknown_node.png]") ..
formspec_node("5.5,1.1", nodename) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_highlight_submit;Highlight Nodes]"
end,
})
@ -562,7 +686,10 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node)
worldedit.show_page(name, "worldedit_gui_highlight")
if fields.worldedit_gui_highlight_submit then
minetest.chatcommands["/highlight"].func(name, gui_nodename1[name])
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/highlight"].func(name, n)
end
end
return true
end
@ -570,14 +697,16 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_restore", {
name = "Restore Region", privs = minetest.chatcommands["/restore"].privs,
name = "Restore Region",
privs = we_privs("restore"),
on_select = function(name)
minetest.chatcommands["/restore"].func(name, "")
end,
})
worldedit.register_gui_function("worldedit_gui_save_load", {
name = "Save/Load", privs = combine_privs(minetest.chatcommands["/save"].privs, minetest.chatcommands["/allocate"].privs, minetest.chatcommands["/load"].privs),
name = "Save/Load",
privs = combine_we_privs({"save", "allocate", "load"}),
get_formspec = function(name)
local filename = gui_filename[name]
return "size[6,4]" .. worldedit.get_formspec_header("worldedit_gui_save_load") ..
@ -588,9 +717,9 @@ worldedit.register_gui_function("worldedit_gui_save_load", {
end,
})
worldedit.register_gui_handler("worldedit_gui_save", function(name, fields)
if fields.worldedit_gui_save_load_submit_save or worldedit_gui_save_load_submit_allocate or worldedit_gui_save_load_submit_load then
gui_filename[name] = tostring(fields.worldedit_gui_save_axis)
worldedit.register_gui_handler("worldedit_gui_save_load", function(name, fields)
if fields.worldedit_gui_save_load_submit_save or fields.worldedit_gui_save_load_submit_allocate or fields.worldedit_gui_save_load_submit_load then
gui_filename[name] = tostring(fields.worldedit_gui_save_filename)
worldedit.show_page(name, "worldedit_gui_save_load")
if fields.worldedit_gui_save_load_submit_save then
minetest.chatcommands["/save"].func(name, gui_filename[name])
@ -605,7 +734,8 @@ worldedit.register_gui_handler("worldedit_gui_save", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_lua", {
name = "Run Lua",
name = "Run Lua", privs = minetest.chatcommands["/clearobjects"].privs,
privs = we_privs("lua"),
get_formspec = function(name)
local code = gui_code[name]
return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_lua") ..
@ -617,7 +747,7 @@ worldedit.register_gui_function("worldedit_gui_lua", {
worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields)
if fields.worldedit_gui_lua_run or fields.worldedit_gui_lua_transform then
gui_code[name] = fields.worldedit_gui_lua_value
gui_code[name] = fields.worldedit_gui_lua_code
worldedit.show_page(name, "worldedit_gui_lua")
if fields.worldedit_gui_lua_run then
minetest.chatcommands["/lua"].func(name, gui_code[name])
@ -630,28 +760,9 @@ worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields)
end)
worldedit.register_gui_function("worldedit_gui_clearobjects", {
name = "Clear Objects", privs = minetest.chatcommands["/clearobjects"].privs,
name = "Clear Objects",
privs = we_privs("clearobjects"),
on_select = function(name)
minetest.chatcommands["/clearobjects"].func(name, "")
end,
})
worldedit.register_gui_function("worldedit_gui_formspec_tester", {
name = "Formspec Tester",
get_formspec = function(name)
local value = gui_formspec[name]
return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_formspec_tester") ..
string.format("textarea[0.5,1;7.5,5.5;worldedit_gui_formspec_tester_value;Formspec Code;%s]", minetest.formspec_escape(value)) ..
"button_exit[0,6;3,0.8;worldedit_gui_formspec_tester_show;Show Formspec]"
end,
})
worldedit.register_gui_handler("worldedit_gui_formspec_tester", function(name, fields)
if fields.worldedit_gui_formspec_tester_show then
gui_formspec[name] = fields.worldedit_gui_formspec_tester_value or ""
worldedit.show_page(name, "worldedit_gui_formspec_tester")
minetest.show_formspec(name, "worldedit:formspec_tester", gui_formspec[name])
return true
end
return false
end)

View File

@ -14,7 +14,7 @@ Use `nil` for the `options` parameter to unregister the function associated with
Use `nil` for the `get_formspec` field to denote that the function does not have its own screen.
Use `nil` for the `privs` field to denote that no special privileges are required to use the function.
The `privs` field may not be `nil`.
If the identifier is already registered to another function, it will be replaced by the new one.
@ -24,6 +24,9 @@ The `on_select` function must not call `worldedit.show_page`
worldedit.pages = {} --mapping of identifiers to options
local identifiers = {} --ordered list of identifiers
worldedit.register_gui_function = function(identifier, options)
if options.privs == nil or next(options.privs) == nil then
error("privs unset")
end
worldedit.pages[identifier] = options
table.insert(identifiers, identifier)
end
@ -46,7 +49,7 @@ worldedit.register_gui_handler = function(identifier, handler)
--ensure the player has permission to perform the action
local entry = worldedit.pages[identifier]
if entry and minetest.check_player_privs(name, entry.privs or {}) then
if entry and minetest.check_player_privs(name, entry.privs) then
return handler(name, fields)
end
return false
@ -67,7 +70,7 @@ local get_formspec = function(name, identifier)
end
--implement worldedit.show_page(name, page) in different ways depending on the available APIs
if unified_inventory then --unified inventory installed
if rawget(_G, "unified_inventory") then --unified inventory installed
local old_func = worldedit.register_gui_function
worldedit.register_gui_function = function(identifier, options)
old_func(identifier, options)
@ -102,7 +105,7 @@ if unified_inventory then --unified inventory installed
player:set_inventory_formspec(get_formspec(name, page))
end
end
elseif inventory_plus then --inventory++ installed
elseif rawget(_G, "inventory_plus") then --inventory++ installed
minetest.register_on_joinplayer(function(player)
local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true})
if can_worldedit then
@ -120,7 +123,7 @@ elseif inventory_plus then --inventory++ installed
return true
elseif fields.worldedit_gui_exit then --return to original page
if gui_player_formspecs[name] then
inventory_plus.set_inventory_formspec(player, gui_player_formspecs[name])
inventory_plus.set_inventory_formspec(player, inventory_plus.get_formspec(player, "main"))
end
return true
end
@ -133,7 +136,37 @@ elseif inventory_plus then --inventory++ installed
inventory_plus.set_inventory_formspec(player, get_formspec(name, page))
end
end
elseif rawget(_G, "sfinv") then --sfinv installed (part of minetest_game since 0.4.15)
assert(sfinv.enabled)
local orig_get = sfinv.pages["sfinv:crafting"].get
sfinv.override_page("sfinv:crafting", {
get = function(self, player, context)
local can_worldedit = minetest.check_player_privs(player, {worldedit=true})
local fs = orig_get(self, player, context)
return fs .. (can_worldedit and "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" or "")
end
})
--show the form when the button is pressed and hide it when done
minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.worldedit_gui then --main page
worldedit.show_page(player:get_player_name(), "worldedit_gui")
return true
elseif fields.worldedit_gui_exit then --return to original page
sfinv.set_page(player, "sfinv:crafting")
return true
end
return false
end)
worldedit.show_page = function(name, page)
local player = minetest.get_player_by_name(name)
if player then
player:set_inventory_formspec(get_formspec(name, page))
end
end
else --fallback button
-- FIXME: this is a huge clusterfuck and the back button is broken
local player_formspecs = {}
local update_main_formspec = function(name)
@ -145,8 +178,21 @@ else --fallback button
if not player then --this is in case the player signs off while the media is loading
return
end
if (minetest.check_player_privs(name, {creative=true}) or minetest.setting_getbool("creative_mode")) and creative_inventory then --creative_inventory is active, add button to modified formspec
formspec = player:get_inventory_formspec() .. "image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
if (minetest.check_player_privs(name, {creative=true}) or
minetest.setting_getbool("creative_mode")) and
creative then --creative is active, add button to modified formspec
local creative_formspec = player:get_inventory_formspec()
local tab_id = tonumber(creative_formspec:match("tabheader%[.-;(%d+)%;"))
if tab_id == 1 then
formspec = creative_formspec ..
"image_button[0,1;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
elseif not tab_id then
formspec = creative_formspec ..
"image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
else
return
end
else
formspec = formspec .. "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
end
@ -199,6 +245,7 @@ end
worldedit.register_gui_function("worldedit_gui", {
name = "WorldEdit GUI",
privs = {interact=true},
get_formspec = function(name)
--create a form with all the buttons arranged in a grid
local buttons, x, y, index = {}, 0, 1, 0
@ -231,7 +278,7 @@ worldedit.register_gui_handler("worldedit_gui", function(name, fields)
for identifier, entry in pairs(worldedit.pages) do --check for WorldEdit GUI main formspec button selection
if fields[identifier] and identifier ~= "worldedit_gui" then
--ensure player has permission to perform action
local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs or {})
local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs)
if not has_privs then
worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")")
return false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

View File

@ -31,6 +31,7 @@ worldedit.alias_chatcommand("/hdo", "/hollowdome")
worldedit.alias_chatcommand("/do", "/dome")
worldedit.alias_chatcommand("/hcyl", "/hollowcylinder")
worldedit.alias_chatcommand("/cyl", "/cylinder")
worldedit.alias_chatcommand("/hpyr", "/hollowpyramid")
worldedit.alias_chatcommand("/pyr", "/pyramid")
worldedit.alias_chatcommand("/spl", "/spiral")
worldedit.alias_chatcommand("/m", "/move")
@ -47,4 +48,4 @@ worldedit.alias_chatcommand("/hlt", "/highlight")
worldedit.alias_chatcommand("/rsr", "/restore")
worldedit.alias_chatcommand("/l", "/lua")
worldedit.alias_chatcommand("/lt", "/luatransform")
worldedit.alias_chatcommand("/clro", "/clearobjects")
worldedit.alias_chatcommand("/clro", "/clearobjects")

@ -1 +1 @@
Subproject commit 94737b2494482df655664f14ebc391ac8ba527b3
Subproject commit 6feae4d3e083b5a635d0e13edc7a91e63cdbbef3

View File

@ -382,6 +382,8 @@ minetest.register_abm({
-- we need our own copy of that node, which moretrees will match against.
local jungle_tree = moretrees.clone_node("default:jungletree")
jungle_tree.drop = jungle_tree.drop or { items = {} }
table.insert(jungle_tree.drop.items, { items = {"default:jungletree"}})
minetest.register_node("moretrees:jungletree_trunk", jungle_tree)
-- For compatibility with old nodes, recently-changed nodes, and default nodes

View File

@ -0,0 +1,3 @@
Matthieu8fr
Matthieu8fr
WTFPL

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
*~

View File

@ -1,3 +0,0 @@
[submodule "src/LuaIRC"]
path = irc
url = https://github.com/ShadowNinja/LuaIRC.git

View File

@ -1,90 +0,0 @@
IRC Mod API
===========
This file documents the Minetest IRC mod API.
Basics
------
In order to allow your mod to interface with this mod, you must add `irc`
to your mod's `depends.txt` file.
Reference
---------
irc:say([name,] message)
Sends <message> to either the channel (if <name> is nil or not specified),
or to the given user (if <name> is specified).
Example:
irc:say("Hello, Channel!")
irc:say("john1234", "How are you?")
irc:register_bot_command(name, cmdDef)
Registers a new bot command named <name>.
When an user sends a private message to the bot with the command name, the
command's function is called.
Here's the format of a command definition (<cmdDef>):
cmdDef = {
params = "<param1> ...", -- A description of the command's parameters
description = "My command", -- A description of what the command does. (one-liner)
func = function(user, args)
-- This function gets called when the command is invoked.
-- <user> is a user table for the user that ran the command.
-- (See the LuaIRC documentation for details.)
-- It contains fields such as 'nick' and 'ident'
-- <args> is a string of arguments to the command (may be "")
-- This function should return boolean success and a message.
end,
};
Example:
irc:register_bot_command("hello", {
params = "",
description = "Greet user",
func = function(user, param)
return true, "Hello!"
end,
});
irc.joined_players[name]
This table holds the players who are currently on the channel (may be less
than the players in the game). It is modified by the /part and /join chat
commands.
Example:
if irc.joined_players["joe"] then
-- Joe is talking on IRC
end
irc:register_hook(name, func)
Registers a function to be called when an event happens. <name> is the name
of the event, and <func> is the function to be called. See HOOKS below
for more information
Example:
irc:register_hook("OnSend", function(line)
print("SEND: "..line)
end)
This mod also supplies some utility functions:
string.expandvars(string, vars)
Expands all occurrences of the pattern "$(varname)" with the value of
'varname' in the <vars> table. Variable names not found on the table
are left verbatim in the string.
Example:
local tpl = "$(foo) $(bar) $(baz)"
local s = tpl:expandvars({foo=1, bar="Hello"})
assert(s == "1 Hello $(baz)")
In addition, all the configuration options decribed in `README.txt` are
available to other mods, though they should be considered read-only. Do
not modify these settings at runtime or you might crash the server!
Hooks
-----
The `irc:register_hook` function can register functions to be called
when some events happen. The events supported are the same as the LuaIRC
ones with a few added (mostly for internal use).
See src/LuaIRC/doc/irc.luadoc for more information.

View File

@ -1,22 +0,0 @@
Copyright (c) 2013, Diego Martinez (kaeza)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,160 +0,0 @@
IRC Mod for Minetest
====================
Introduction
------------
This mod is just a glue between IRC and Minetest. It provides two-way
communication between the in-game chat, and an arbitrary IRC channel.
The forum topic is at http://minetest.net/forum/viewtopic.php?id=3905
Installing
----------
Quick one line install for linux:
cd <Mod directory> && git clone https://github.com/kaeza/minetest-irc.git irc && cd irc && git submodule update --init
Please change `<Mod directory>` to fit your installation of minetest.
For more information, see [the wiki](http://wiki.minetest.net/Installing_mods).
The Minetest IRC mod uses submodules, therefore you will have to run
`git submodule init` when first installing the mod, and `git submodule update`
every time that a submodule is updated. These steps can be combined as
`git submodule update --init`.
The Minetest IRC mod also requires LuaSocket. This can be installed using your
package manager on many distributions, for example on Arch Linux:
# pacman -S lua51-socket
Settings
--------
All settings are changed in `minetest.conf`. If any of these settings
are not set, the default value is used.
* `irc.server` (string, default "irc.freenode.net")
This is the IRC server the mod connects to.
* `irc.channel` (string, default "##mt-irc-mod")
The IRC channel to join.
* `irc.interval` (number, default 2.0)
This prevents the server from flooding. It should be at
least 2.0 but can be higher. After four messages this much
time must pass between folowing messages.
* `irc.nick` (string, default "MT-FFFFFF")
Nickname used as "proxy" for the in-game chat.
'F' stands for a random base-16 number.
* `irc.password` (string, default "")
Password to use when connecting to the server.
* `irc.NSPass` (string, default nil)
NickServ password. Don't use this if you use SASL authentication.
* `irc.sasl.pass` (string, default nil)
SASL password, same as nickserv password.
You should use this instead of NickServ authentication
if the server supports it.
* `irc.sasl.user` (string, default `irc.nick`)
The SASL username. This should normaly be set to your main NickServ account name.
* `irc.debug` (boolean, default false)
Whether to output debug information.
* `irc.disable_auto_connect` (boolean, default false)
If false, the bot is connected by default. If true, a player with
the 'irc_admin' privilege has to use the /irc_connect command to
connect to the server.
* `irc.disable_auto_join` (boolean, default false)
If false, players join the channel automatically upon entering the
game. If true, each user must manually use the /join command to
join the channel. In any case, the players may use the /part
command to opt-out of being in the channel.
* `irc.send_join_part` (boolean, default true)
Determines whether to send player join and part messages to the channel.
Usage
-----
Once the game is connected to the IRC channel, chatting using the 'T' or
F10 hotkeys will send the messages to the channel, and will be visible
by anyone. Also, when someone sends a message to the channel, that text
will be visible in-game.
Messages that begin with `[off]` from in-game or IRC are not sent to the
other side.
This mod also adds a few chat commands:
* `/irc_msg <nick> <message>`
Sends a private message to a IRC user.
* `/join`
Join the IRC chat.
* `/part`
Part the IRC chat.
* `/irc_connect`
Connect the bot manually to the IRC network.
* `/irc_disconnect`
Disconnect the bot manually from the IRC network (this does not
shutdown the game).
* `/irc_reconnect`
Equivilant to `/irc_disconnect` followed by `/irc_connect`.
You can also send private messages from IRC to in-game players.
To do it, you must send a private message to the bot (set with
the `irc.nick` option above), in the following format:
@playername message
For example, if there's a player named `mtuser`, you can send him/her
a private message from IRC with:
/msg server_nick @mtuser Hello!
To avoid possible misunderstandings (since all in-game players use the
same IRC user to converse with you), the "proxy" user will reject any
private messages that are not in that format, and will send back a
nice reminder as a private message.
The bot also supports some basic commands, which are invoked by sending
a private message to it. Use `!list` to get a list of commands, and
`!help <command>` to get help about a specific command.
Thanks
------
I'd like to thank the users who supported this mod both on the Minetest
Forums and on the #minetest channel. In no particular order:
0gb.us, ShadowNinja, Shaun/kizeren, RAPHAEL, DARGON, Calinou, Exio,
vortexlabs/mrtux, marveidemanis, marktraceur, jmf/john\_minetest,
sdzen/Muadtralk, VanessaE, PilzAdam, sfan5, celeron55, KikaRz,
OldCoder, RealBadAngel, and all the people who commented in the
forum topic. Thanks to you all!
License
-------
(C) 2012-2013 Diego Martínez <kaeza@users.sf.net>
See LICENSE.txt for licensing information.
The files in the irc directory are part of the LuaIRC project.
See irc/LICENSE.txt for licensing information.

View File

@ -1,165 +0,0 @@
irc.whereis_timer = {}
irc.whereis_timer_max_limit = 120
irc.bot_commands = {}
function irc:check_botcmd(msg)
local prefix = irc.config.command_prefix
local nick = irc.conn.nick:lower()
local text = msg.args[2]
local nickpart = text:sub(1, #nick + 2):lower()
-- First check for a nick prefix
if nickpart == nick..": " or
nickpart == nick..", " then
self:bot_command(msg, text:sub(#nick + 3))
return true
-- Then check for the configured prefix
elseif prefix and text:sub(1, #prefix):lower() == prefix:lower() then
self:bot_command(msg, text:sub(#prefix + 1))
return true
end
return false
end
function irc:bot_command(msg, text)
if text:sub(1, 1) == "@" then
local found, _, player_to, message = text:find("^.([^%s]+)%s(.+)$")
if not minetest.get_player_by_name(player_to) then
irc:reply("User '"..player_to.."' is not in the game.")
return
elseif not irc.joined_players[player_to] then
irc:reply("User '"..player_to.."' is not using IRC.")
return
end
minetest.chat_send_player(player_to,
"PM from "..msg.user.nick.."@IRC: "..message, false)
irc:reply("Message sent!")
return
end
local pos = text:find(" ", 1, true)
local cmd, args
if pos then
cmd = text:sub(1, pos - 1)
args = text:sub(pos + 1)
else
cmd = text
args = ""
end
if not self.bot_commands[cmd] then
self:reply("Unknown command '"..cmd.."'. Try 'list'."
.." Or use @playername <message> to send a private message")
return
end
local success, message = self.bot_commands[cmd].func(msg.user, args)
if message then
self:reply(message)
end
end
function irc:register_bot_command(name, def)
if (not def.func) or (type(def.func) ~= "function") then
error("Erroneous bot command definition. def.func missing.", 2)
elseif name:sub(1, 1) == "@" then
error("Erroneous bot command name. Command name begins with '@'.", 2)
end
self.bot_commands[name] = def
end
irc:register_bot_command("help", {
params = "<command>",
description = "Get help about a command",
func = function(user, args)
if args == "" then
return false, "No command name specified. Use 'list' for a list of commands."
end
local cmd = irc.bot_commands[args]
if not cmd then
return false, "Unknown command '"..cmdname.."'."
end
return true, ("Usage: %c%s %s -- %s"):format(
irc.config.command_prefix,
args,
cmd.params or "<no parameters>",
cmd.description or "<no description>")
end
})
irc:register_bot_command("list", {
params = "",
description = "List available commands.",
func = function(user, args)
local cmdlist = "Available commands: "
for name, cmd in pairs(irc.bot_commands) do
cmdlist = cmdlist..name..", "
end
return true, cmdlist.." -- Use 'help <command name>' to get"
.." help about a specific command."
end
})
irc:register_bot_command("whereis", {
params = "<player>",
description = "Tell the location of <player>",
func = function(user, args)
if args == "" then
return false, "Player name required."
end
local player = minetest.get_player_by_name(args)
if not player then
return false, "There is no player named '"..args.."'"
end
if irc.whereis_timer[user.nick] ~= nil then
local timer_player = os.difftime(os.time(),irc.whereis_timer[user.nick])
if timer_player < irc.whereis_timer_max_limit then
local answer = "Command used too often, retry in %d seconds."
return false,answer:format(irc.whereis_timer_max_limit - timer_player)
end
end
local fmt = "Player %s is at (%.2f,%.2f,%.2f)"
local pos = player:getpos()
irc.whereis_timer[user.nick] = os.time()
minetest.log("action","IRC user ".. user.nick.."!"..user.username.."@"..user.host.." asked for position of player "..player:get_player_name())
minetest.chat_send_player(player:get_player_name(),"IRC user ".. user.nick.."!"..user.username.."@"..user.host.." asked for your position")
return true, fmt:format(args, pos.x, pos.y, pos.z)
end
})
local starttime = os.time()
irc:register_bot_command("uptime", {
description = "Tell how much time the server has been up",
func = function(user, args)
local cur_time = os.time()
local diff = os.difftime(cur_time, starttime)
local fmt = "Server has been running for %d:%02d:%02d"
return true, fmt:format(
math.floor(diff / 60 / 60),
math.floor(diff / 60) % 60,
math.floor(diff) % 60
)
end
})
irc:register_bot_command("players", {
description = "List the players on the server",
func = function(user, args)
local players = minetest.get_connected_players()
local names = {}
for _, player in pairs(players) do
table.insert(names, player:get_player_name())
end
return true, "Connected players: "
..table.concat(names, ", ")
end
})

View File

@ -1,40 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
if irc.connected and irc.config.send_join_part then
irc:say("*** "..name.." joined the game")
end
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
if irc.connected and irc.config.send_join_part then
irc:say("*** "..name.." left the game")
end
end)
minetest.register_on_chat_message(function(name, message)
if not irc.connected
or message:sub(1, 1) == "/"
or message:sub(1, 5) == "[off]"
or not irc.joined_players[name]
or (not minetest.check_player_privs(name, {shout=true})) then
return
end
local nl = message:find("\n", 1, true)
if nl then
message = message:sub(1, nl - 1)
end
irc:say(irc:playerMessage(name, message))
end)
minetest.register_on_shutdown(function()
irc:disconnect("Game shutting down.")
end)

View File

@ -1,126 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
-- Note: This file does NOT conatin every chat command, only general ones.
-- Feature-specific commands (like /join) are in their own files.
minetest.register_chatcommand("irc_msg", {
params = "<name> <message>",
description = "Send a private message to an IRC user",
privs = {shout=true},
func = function(name, param)
if not irc.connected then
minetest.chat_send_player(name, "Not connected to IRC. Use /irc_connect to connect.")
return
end
local found, _, toname, message = param:find("^([^%s]+)%s(.+)")
if not found then
minetest.chat_send_player(name, "Invalid usage, see /help irc_msg.")
return
end
local toname_l = toname:lower()
local validNick = false
for nick, user in pairs(irc.conn.channels[irc.config.channel].users) do
if nick:lower() == toname_l then
validNick = true
break
end
end
if toname_l:find("serv$") or toname_l:find("bot$") then
validNick = false
end
if not validNick then
minetest.chat_send_player(name,
"You can not message that user. (Hint: They have to be in the channel)")
return
end
irc:say(toname, irc:playerMessage(name, message))
minetest.chat_send_player(name, "Message sent!")
end
})
minetest.register_chatcommand("irc_names", {
params = "",
description = "List the users in IRC.",
func = function(name, params)
if not irc.connected then
minetest.chat_send_player(name, "Not connected to IRC. Use /irc_connect to connect.")
return
end
local users = { }
for k, v in pairs(irc.conn.channels[irc.config.channel].users) do
table.insert(users, k)
end
minetest.chat_send_player(name, "Users in IRC: "..table.concat(users, ", "))
end
})
minetest.register_chatcommand("irc_connect", {
description = "Connect to the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if irc.connected then
minetest.chat_send_player(name, "You are already connected to IRC.")
return
end
minetest.chat_send_player(name, "IRC: Connecting...")
irc:connect()
end
})
minetest.register_chatcommand("irc_disconnect", {
params = "[message]",
description = "Disconnect from the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if not irc.connected then
minetest.chat_send_player(name, "You are not connected to IRC.")
return
end
if params == "" then
params = "Manual disconnect by "..name
end
irc:disconnect(param)
end
})
minetest.register_chatcommand("irc_reconnect", {
description = "Reconnect to the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if not irc.connected then
minetest.chat_send_player(name, "You are not connected to IRC.")
return
end
irc:disconnect("Reconnecting...")
irc:connect()
end
})
minetest.register_chatcommand("irc_quote", {
params = "<command>",
description = "Send a raw command to the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if not irc.connected then
minetest.chat_send_player(name, "You are not connected to IRC.")
return
end
irc:queue(param)
minetest.chat_send_player(name, "Command sent!")
end
})
local oldme = minetest.chatcommands["me"].func
minetest.chatcommands["me"].func = function(name, param, ...)
oldme(name, param, ...)
irc:say(("* %s %s"):format(name, param))
end

View File

@ -1,59 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
irc.config = {}
local function setting(stype, name, default)
local value
if stype == "bool" then
value = minetest.setting_getbool("irc."..name)
elseif stype == "string" then
value = minetest.setting_get("irc."..name)
elseif stype == "number" then
value = tonumber(minetest.setting_get("irc."..name))
end
if value == nil then
value = default
end
irc.config[name] = value
end
-------------------------
-- BASIC USER SETTINGS --
-------------------------
setting("string", "nick") -- Nickname (default "MT-<hash>", <hash> 6 random hexidecimal characters)
setting("string", "server", "irc.freenode.net") -- Server to connect on joinplayer
setting("number", "port", 6667) -- Port to connect on joinplayer
setting("string", "NSPass") -- NickServ password
setting("string", "sasl.user", irc.config.nick) -- SASL username
setting("string", "sasl.pass") -- SASL password
setting("string", "channel", "##mt-irc-mod") -- Channel to join
setting("string", "key") -- Key for the channel
setting("bool", "send_join_part", true) -- Whether to send player join and part messages to the channel
-----------------------
-- ADVANCED SETTINGS --
-----------------------
setting("string", "password") -- Server password
setting("bool", "secure", false) -- Enable a TLS connection, requires LuaSEC
setting("number", "timeout", 60) -- Underlying socket timeout in seconds.
setting("string", "command_prefix") -- Prefix to use for bot commands
setting("bool", "debug", false) -- Enable debug output
setting("bool", "enable_player_part", true) -- Whether to enable players joining and parting the channel
setting("bool", "auto_join", true) -- Whether to automatically show players in the channel when they join
setting("bool", "auto_connect", true) -- Whether to automatically connect to the server on mod load
-- Generate a random nickname if one isn't specified.
if not irc.config.nick then
local pr = PseudoRandom(os.time())
-- Workaround for bad distribution in minetest PRNG implementation.
irc.config.nick = ("MT-%02X%02X%02X"):format(
pr:next(0, 255),
pr:next(0, 255),
pr:next(0, 255)
)
end

View File

@ -1,260 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
-- MIME is part of LuaSocket
local b64e = require("mime").b64
irc.hooks = {}
irc.registered_hooks = {}
-- TODO: Add proper conversion from CP1252 to UTF-8.
local stripped_chars = {"\2", "\31"}
for c = 127, 255 do
table.insert(stripped_chars, string.char(c))
end
stripped_chars = "["..table.concat(stripped_chars, "").."]"
local function normalize(text)
-- Strip colors
text = text:gsub("\3[0-9][0-9,]*", "")
return text:gsub(stripped_chars, "")
end
function irc:doHook(conn)
for name, hook in pairs(self.registered_hooks) do
for _, func in pairs(hook) do
conn:hook(name, func)
end
end
end
function irc:register_hook(name, func)
self.registered_hooks[name] = self.registered_hooks[name] or {}
table.insert(self.registered_hooks[name], func)
end
function irc.hooks.raw(line)
if irc.config.debug then
print("RECV: "..line)
end
end
function irc.hooks.send(line)
if irc.config.debug then
print("SEND: "..line)
end
end
function irc.hooks.chat(msg)
local channel, text = msg.args[1], msg.args[2]
if text:sub(1, 1) == string.char(1) then
irc.conn:invoke("OnCTCP", msg)
return
end
if channel == irc.conn.nick then
irc.last_from = msg.user.nick
irc.conn:invoke("PrivateMessage", msg)
else
irc.last_from = channel
irc.conn:invoke("OnChannelChat", msg)
end
end
local function get_core_version()
local status = minetest.get_server_status()
local start_pos = select(2, status:find("version=", 1, true))
local end_pos = status:find(",", start_pos, true)
return status:sub(start_pos + 1, end_pos - 1)
end
function irc.hooks.ctcp(msg)
local text = msg.args[2]:sub(2, -2) -- Remove ^C
local args = text:split(' ')
local command = args[1]:upper()
local function reply(s)
irc:queue(irc.msgs.notice(msg.user.nick,
("\1%s %s\1"):format(command, s)))
end
if command == "ACTION" and msg.args[1] == irc.config.channel then
local action = text:sub(8, -1)
irc:sendLocal(("* %s@IRC %s"):format(msg.user.nick, action))
elseif command == "VERSION" then
reply(("Minetest version %s, IRC mod version %s.")
:format(get_core_version(), irc.version))
elseif command == "PING" then
reply(args[2])
elseif command == "TIME" then
reply(os.date())
end
end
function irc.hooks.channelChat(msg)
local text = normalize(msg.args[2])
irc:check_botcmd(msg)
-- Don't let a user impersonate someone else by using the nick "IRC"
if msg.user.nick == "IRC" then
irc:sendLocal("<IRC@IRC> "..text)
return
end
-- Support multiple servers in a channel better by converting:
-- "<server@IRC> <player> message" into "<player@server> message"
-- "<server@IRC> *** player joined/left the game" into "*** player joined/left server"
-- and "<server@IRC> * player orders a pizza" into "* player@server orders a pizza"
local foundchat, _, chatnick, chatmessage =
text:find("^<([^>]+)> (.*)$")
local foundjoin, _, joinnick =
text:find("^%*%*%* ([^%s]+) joined the game$")
local foundleave, _, leavenick =
text:find("^%*%*%* ([^%s]+) left the game$")
local foundaction, _, actionnick, actionmessage =
text:find("^%* ([^%s]+) (.*)$")
if text:sub(1, 5) == "[off]" then
return
elseif foundchat then
irc:sendLocal(("<%s@%s> %s")
:format(chatnick, msg.user.nick, chatmessage))
elseif foundjoin then
irc:sendLocal(("*** %s joined %s")
:format(joinnick, msg.user.nick))
elseif foundleave then
irc:sendLocal(("*** %s left %s")
:format(leavenick, msg.user.nick))
elseif foundaction then
irc:sendLocal(("* %s@%s %s")
:format(actionnick, msg.user.nick, actionmessage))
else
irc:sendLocal(("<%s@IRC> %s"):format(msg.user.nick, text))
end
end
function irc.hooks.pm(msg)
-- Trim prefix if it is found
local text = msg.args[2]
local prefix = irc.config.command_prefix
if prefix and text:sub(1, #prefix) == prefix then
text = text:sub(#prefix + 1)
end
irc:bot_command(msg, text)
end
function irc.hooks.kick(channel, target, prefix, reason)
if target == irc.conn.nick then
minetest.chat_send_all("IRC: kicked from "..channel.." by "..prefix.nick..".")
irc:disconnect("Kicked")
else
irc:sendLocal(("-!- %s was kicked from %s by %s [%s]")
:format(target, channel, prefix.nick, reason))
end
end
function irc.hooks.notice(user, target, message)
if user.nick and target == irc.config.channel then
irc:sendLocal("-"..user.nick.."@IRC- "..message)
end
end
function irc.hooks.mode(user, target, modes, ...)
local by = ""
if user.nick then
by = " by "..user.nick
end
local options = ""
if select("#", ...) > 0 then
options = " "
end
options = options .. table.concat({...}, " ")
minetest.chat_send_all(("-!- mode/%s [%s%s]%s")
:format(target, modes, options, by))
end
function irc.hooks.nick(user, newNick)
irc:sendLocal(("-!- %s is now known as %s")
:format(user.nick, newNick))
end
function irc.hooks.join(user, channel)
irc:sendLocal(("-!- %s joined %s")
:format(user.nick, channel))
end
function irc.hooks.part(user, channel, reason)
reason = reason or ""
irc:sendLocal(("-!- %s has left %s [%s]")
:format(user.nick, channel, reason))
end
function irc.hooks.quit(user, reason)
irc:sendLocal(("-!- %s has quit [%s]")
:format(user.nick, reason))
end
function irc.hooks.disconnect(message, isError)
irc.connected = false
if isError then
minetest.log("error", "IRC: Error: Disconnected, reconnecting in one minute.")
minetest.chat_send_all("IRC: Error: Disconnected, reconnecting in one minute.")
minetest.after(60, irc.connect, irc)
else
minetest.log("action", "IRC: Disconnected.")
minetest.chat_send_all("IRC: Disconnected.")
end
end
function irc.hooks.preregister(conn)
if not (irc.config["sasl.user"] and irc.config["sasl.pass"]) then return end
local authString = b64e(
("%s\x00%s\x00%s"):format(
irc.config["sasl.user"],
irc.config["sasl.user"],
irc.config["sasl.pass"])
)
conn:send("CAP REQ sasl")
conn:send("AUTHENTICATE PLAIN")
conn:send("AUTHENTICATE "..authString)
--LuaIRC will send CAP END
end
irc:register_hook("PreRegister", irc.hooks.preregister)
irc:register_hook("OnRaw", irc.hooks.raw)
irc:register_hook("OnSend", irc.hooks.send)
irc:register_hook("DoPrivmsg", irc.hooks.chat)
irc:register_hook("OnPart", irc.hooks.part)
irc:register_hook("OnKick", irc.hooks.kick)
irc:register_hook("OnJoin", irc.hooks.join)
irc:register_hook("OnQuit", irc.hooks.quit)
irc:register_hook("NickChange", irc.hooks.nick)
irc:register_hook("OnCTCP", irc.hooks.ctcp)
irc:register_hook("PrivateMessage", irc.hooks.pm)
irc:register_hook("OnNotice", irc.hooks.notice)
irc:register_hook("OnChannelChat", irc.hooks.channelChat)
irc:register_hook("OnModeChange", irc.hooks.mode)
irc:register_hook("OnDisconnect", irc.hooks.disconnect)

View File

@ -1,157 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
local modpath = minetest.get_modpath(minetest.get_current_modname())
package.path =
-- To find LuaIRC's init.lua
modpath.."/?/init.lua;"
-- For LuaIRC to find its files
..modpath.."/?.lua;"
..package.path
-- The build of Lua that Minetest comes with only looks for libraries under
-- /usr/local/share and /usr/local/lib but LuaSocket is often installed under
-- /usr/share and /usr/lib.
if not rawget(_G,"jit") and package.config:sub(1, 1) == "/" then
package.path = package.path..
";/usr/share/lua/5.1/?.lua"..
";/usr/share/lua/5.1/?/init.lua"
package.cpath = package.cpath..
-- ";/usr/lib/lua/5.1/?.so"
";/usr/lib/x86_64-linux-gnu/lua/5.1/?.so"
end
irc = {
version = "0.2.0",
connected = false,
cur_time = 0,
message_buffer = {},
recent_message_count = 0,
joined_players = {},
modpath = modpath,
lib = require("irc"),
}
-- Compatibility
mt_irc = irc
dofile(modpath.."/config.lua")
dofile(modpath.."/messages.lua")
dofile(modpath.."/hooks.lua")
dofile(modpath.."/callback.lua")
dofile(modpath.."/chatcmds.lua")
dofile(modpath.."/botcmds.lua")
if irc.config.enable_player_part then
dofile(modpath.."/player_part.lua")
else
setmetatable(irc.joined_players, {__index = function(index) return true end})
end
minetest.register_privilege("irc_admin", {
description = "Allow IRC administrative tasks to be performed.",
give_to_singleplayer = true
})
local stepnum = 0
minetest.register_globalstep(function(dtime) return irc:step(dtime) end)
function irc:step(dtime)
if stepnum == 3 then
if self.config.auto_connect then
self:connect()
end
end
stepnum = stepnum + 1
if not self.connected then return end
-- Hooks will manage incoming messages and errors
local good, err = xpcall(function() self.conn:think() end, debug.traceback)
if not good then
print(err)
return
end
end
function irc:connect()
if self.connected then
minetest.log("error", "IRC: Ignoring attempt to connect when already connected.")
return
end
self.conn = irc.lib.new({
nick = self.config.nick,
username = "Minetest",
realname = "Minetest",
})
self:doHook(self.conn)
local good, message = pcall(function()
self.conn:connect({
host = self.config.server,
port = self.config.port,
password = self.config.password,
timeout = self.config.timeout,
secure = self.config.secure
})
end)
if not good then
minetest.log("error", ("IRC: Connection error: %s: %s -- Reconnecting in ten minutes...")
:format(self.config.server, message))
minetest.after(600, function() self:connect() end)
return
end
print("== This is a debug line, please check for it ==")
print(self.config.NSPass)
print("=== DEBUG FINISHED ===")
if self.config.NSPass then
self:say("NickServ", "IDENTIFY "..self.config.NSPass)
end
self.conn:join(self.config.channel, self.config.key)
self.connected = true
minetest.log("action", "IRC: Connected!")
minetest.chat_send_all("IRC: Connected!")
end
function irc:disconnect(message)
if self.connected then
--The OnDisconnect hook will clear self.connected and print a disconnect message
self.conn:disconnect(message)
end
end
function irc:say(to, message)
if not message then
message = to
to = self.config.channel
end
to = to or self.config.channel
self:queue(irc.msgs.privmsg(to, message))
end
function irc:reply(message)
if not self.last_from then
return
end
message = message:gsub("[\r\n%z]", " \\n ")
self:say(self.last_from, message)
end
function irc:send(msg)
if not self.connected then return end
self.conn:send(msg)
end
function irc:queue(msg)
if not self.connected then return end
self.conn:queue(msg)
end

View File

@ -1,3 +0,0 @@
/gh-pages/
*~

View File

@ -1,26 +0,0 @@
--[[
Lua IRC library
Copyright (c) 2010 Jakob Ovrum
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.]]

View File

@ -1,18 +0,0 @@
LuaIRC
============
IRC library for Lua.
Dependencies
-------------
* [LuaSocket](http://w3.impa.br/~diego/software/luasocket/)
**Only required if you want to make use of the TLS support**
* [LuaSec](http://www.inf.puc-rio.br/~brunoos/luasec/)
Documentation
-------------
Documentation can be automatically generated by passing irc.luadoc (in doc/) to [LuaDoc](http://luadoc.luaforge.net/), or pre-generated documentation can be found in the 'gh-pages' branch, which can also be browsed [online](http://jakobovrum.github.com/LuaIRC/doc/modules/irc.html).

View File

@ -1,90 +0,0 @@
local irc = require("irc.main")
local meta = irc.meta
function meta:send(msg, ...)
if type(msg) == "table" then
msg = msg:toRFC1459()
else
if select("#", ...) > 0 then
msg = msg:format(...)
end
end
self:invoke("OnSend", msg)
local bytes, err = self.socket:send(msg .. "\r\n")
if not bytes and err ~= "timeout" and err ~= "wantwrite" then
self:invoke("OnDisconnect", err, true)
self:shutdown()
error(err, errlevel)
end
end
function meta:queue(msg)
table.insert(self.messageQueue, msg)
end
local function verify(str, errLevel)
if str:find("^:") or str:find("%s%z") then
error(("malformed parameter '%s' to irc command"):format(str), errLevel)
end
return str
end
function meta:sendChat(target, msg)
-- Split the message into segments if it includes newlines.
for line in msg:gmatch("([^\r\n]+)") do
self:queue(irc.msgs.privmsg(verify(target, 3), line))
end
end
function meta:sendNotice(target, msg)
-- Split the message into segments if it includes newlines.
for line in msg:gmatch("([^\r\n]+)") do
self:queue(irc.msgs.notice(verify(target, 3), line))
end
end
function meta:join(channel, key)
self:queue(irc.msgs.join(
verify(channel, 3),
key and verify(key, 3) or nil))
end
function meta:part(channel, reason)
channel = verify(channel, 3)
self:queue(irc.msgs.part(channel, reason))
if self.track_users then
self.channels[channel] = nil
end
end
function meta:trackUsers(b)
self.track_users = b
if not b then
for k,v in pairs(self.channels) do
self.channels[k] = nil
end
end
end
function meta:setMode(t)
local target = t.target or self.nick
local mode = ""
local add, rem = t.add, t.remove
assert(add or rem, "table contains neither 'add' nor 'remove'")
if add then
mode = table.concat{"+", verify(add, 3)}
end
if rem then
mode = table.concat{mode, "-", verify(rem, 3)}
end
self:queue(irc.msgs.mode(verify(target, 3), mode))
end

View File

@ -1,184 +0,0 @@
--- LuaIRC is a low-level IRC library for Lua.
-- All functions raise Lua exceptions on error.
--
-- Use <code>new</code> to create a new IRC object.<br/>
-- Example:<br/><br/>
--<code>
--require "irc"<br/>
--local sleep = require "socket".sleep<br/>
--<br/>
--local s = irc.new{nick = "example"}<br/>
--<br/>
--s:hook("OnChat", function(user, channel, message)<br/>
-- print(("[%s] %s: %s"):format(channel, user.nick, message))<br/>
--end)<br/>
--<br/>
--s:connect("irc.example.net")<br/>
--s:join("#example")<br/>
--<br/>
--while true do<br/>
-- s:think()<br/>
-- sleep(0.5)<br/>
--end<br/>
--</code>
module "irc"
--- Create a new IRC object. Use <code>irc:connect</code> to connect to a server.
-- @param user Table with fields <code>nick</code>, <code>username</code> and <code>realname</code>.
-- The <code>nick</code> field is required.
--
-- @return Returns a new <code>irc</code> object.
function new(user)
--- Hook a function to an event.
-- @param name Name of event.
-- @param id Unique tag.
-- @param f Callback function. [defaults to <code>id</code>]
-- @see Hooks
function irc:hook(name, id, f)
--- Remove previous hooked callback.
-- @param name Name of event.
-- @param id Unique tag.
function irc:unhook(name, id)
--- Connect <code>irc</code> to an IRC server.
-- @param host Host address.
-- @param port Server port. [default 6667]
function irc:connect(server, port)
-- @param table Table of connection details
-- @see Connection
function irc:connect(table)
--- Disconnect <code>irc</code> from the server.
-- @param message Quit message.
function irc:disconnect(message)
--- Handle incoming data for <code>irc</code>, and invoke previously hooked callbacks based on new server input.
-- You should call this in some kind of main loop, or at least often enough to not time out.
function irc:think()
--- Look up user info.
-- @param nick Nick of user to query.
-- @return Table with fields <code>userinfo</code>, <code>node</code>, <code>channels</code> and <code>account</code>.
function irc:whois(nick)
--- Look up topic.
-- Use this to invoke the hooks OnTopic and OnTopicInfo at any time.
-- @param channel Channel to query.
function irc:topic(channel)
--- Send a IRC message to the server.
-- @param msg Message or raw line to send, excluding newline characters.
-- @param ... Format parameters for <code>msg</code>, with <code>string.format</code> semantics. [optional]
function irc:send(msg, ...)
--- Queue Message to be sent to the server.
-- @param msg Message to be sent.
function irc:queue(msg)
--- Send a message to a channel or user.
-- @param target Nick or channel to send to.
-- @param message Message text.
function irc:sendChat(target, message)
--- Send a notice to a channel or user.
-- @param target Nick or channel to send to.
-- @param message Notice text.
function irc:sendNotice(target, message)
--- Join a channel.
-- @param channel Channel to join.
-- @param key Channel key. [optional]
function irc:join(channel, key)
--- Leave a channel.
-- @param channel Channel to leave.
function irc:part(channel)
--- Turn user information tracking on or off. User tracking is enabled by default.
-- @param b Boolean whether or not to track user information.
function irc:trackUsers(b)
--- Add/remove modes for a channel or nick.
-- @param t Table with fields <code>target, nick, add</code> and/or <code>rem</code>. <code>target</code> or <code>nick</code>
-- specifies the user or channel to add/remove modes. <code>add</code> is a list of modes to add to the user or channel.
-- <code>rem</code> is a list of modes to remove from the user or channel.
-- @usage Example which sets +m (moderated) for #channel: <br/>
-- <code>irc:setMode{target = "#channel", add = "m"}</code>
function irc:setMode(t)
--internal
function irc:invoke(name, ...)
function irc:handle(msg)
function irc:shutdown()
--- Table with connection information.
-- <ul>
-- <li><code>host</code> - Server host name.</li>
-- <li><code>port</code> - Server port. [defaults to <code>6667</code>]</li>
-- <li><code>timeout</code> - Connect timeout. [defaults to <code>30</code>]</li>
-- <li><code>password</code> - Server password.</li>
-- <li><code>secure</code> - Boolean to enable TLS connection, pass a params table (described, [luasec]) to control</li>
-- </ul>
-- [luasec]: http://www.inf.puc-rio.br/~brunoos/luasec/reference.html
-- @name Connection
-- @class table
--- Class representing an IRC message.
-- @name Message
-- @class table
-- @field args A list of the command arguments
-- @field command The IRC command
-- @field prefix The prefix of the message
-- @field raw A raw IRC line for this message
-- @field tags A table of IRCv3 tags
-- @field user A User object describing the sender of the message
-- Fields may be missing.
-- Messages have the following methods:
-- <ul>
-- <li><code>toRFC1459()</code> - Returns the message serialized in RFC 1459 format.</li>
-- </ul>
--- List of hooks you can use with irc:hook.
-- The parameter list describes the parameters passed to the callback function.
-- <ul>
-- <li><code>PreRegister(connection)</code>Useful for CAP commands and SASL.</li>
-- <li><code>OnRaw(line) - (any non false/nil return value assumes line handled and will not be further processed)</code></li>
-- <li><code>OnSend(line)</code></li>
-- <li><code>OnDisconnect(message, errorOccurred)</code></li>
-- <li><code>OnChat(user, channel, message)</code></li>
-- <li><code>OnNotice(user, channel, message)</code></li>
-- <li><code>OnJoin(user, channel)</code>*</li>
-- <li><code>OnPart(user, channel)</code>*</li>
-- <li><code>OnQuit(user, message)</code></li>
-- <li><code>NickChange(user, newnick, channel)</code>*†</li>
-- <li><code>NameList(channel, names)</code></li>
-- <li><code>OnTopic(channel, topic)</code></li>
-- <li><code>OnTopicInfo(channel, creator, timeCreated)</code></li>
-- <li><code>OnKick(channel, nick, kicker, reason)</code>* (kicker is a <code>user</code> table)</li>
-- <li><code>OnUserMode(modes)</code></li>
-- <li><code>OnChannelMode(user, channel, modes)</code></li>
-- <li><code>OnModeChange(user, target, modes, ...)</code>* ('...' contains mode options such as banmasks)</li>
-- <li><code>DoX(msg)</code>'X' is any IRC command or numeric with the first letter capitalized (eg, DoPing and Do001)</li>
-- </ul>
-- * Event also invoked for yourself.
-- † Channel passed only when user tracking is enabled
-- @name Hooks
-- @class table
--- Table with information about a user.
-- <ul>
-- <li><code>server</code> - Server name.</li>
-- <li><code>nick</code> - User nickname.</li>
-- <li><code>username</code> - User username.</li>
-- <li><code>host</code> - User hostname.</li>
-- <li><code>realname</code> - User real name.</li>
-- <li><code>access</code> - User access, available in channel-oriented callbacks. A table containing boolean fields for each access mode that the server supports. Eg: 'o', and 'v'.</li>
-- </ul>
-- Fields may be missing. To fill them in, enable user tracking and use irc:whois.
-- @name User
-- @class table

View File

@ -1,210 +0,0 @@
local irc = require("irc.main")
irc.handlers = {}
local handlers = irc.handlers
handlers["PING"] = function(conn, msg)
conn:send(irc.Message({command="PONG", args=msg.args}))
end
handlers["001"] = function(conn, msg)
conn.authed = true
conn.nick = msg.args[1]
end
handlers["PRIVMSG"] = function(conn, msg)
conn:invoke("OnChat", msg.user, msg.args[1], msg.args[2])
end
handlers["NOTICE"] = function(conn, msg)
conn:invoke("OnNotice", msg.user, msg.args[1], msg.args[2])
end
handlers["JOIN"] = function(conn, msg)
local channel = msg.args[1]
if conn.track_users then
if msg.user.nick == conn.nick then
conn.channels[channel] = {users = {}}
else
conn.channels[channel].users[msg.user.nick] = msg.user
end
end
conn:invoke("OnJoin", msg.user, msg.args[1])
end
handlers["PART"] = function(conn, msg)
local channel = msg.args[1]
if conn.track_users then
if msg.user.nick == conn.nick then
conn.channels[channel] = nil
else
conn.channels[channel].users[msg.user.nick] = nil
end
end
conn:invoke("OnPart", msg.user, msg.args[1], msg.args[2])
end
handlers["QUIT"] = function(conn, msg)
if conn.track_users then
for chanName, chan in pairs(conn.channels) do
chan.users[msg.user.nick] = nil
end
end
conn:invoke("OnQuit", msg.user, msg.args[1], msg.args[2])
end
handlers["NICK"] = function(conn, msg)
local newNick = msg.args[1]
if conn.track_users then
for chanName, chan in pairs(conn.channels) do
local users = chan.users
local oldinfo = users[msg.user.nick]
if oldinfo then
users[newNick] = oldinfo
users[msg.user.nick] = nil
conn:invoke("NickChange", msg.user, newNick, chanName)
end
end
else
conn:invoke("NickChange", msg.user, newNick)
end
if msg.user.nick == conn.nick then
conn.nick = newnick
end
end
local function needNewNick(conn, msg)
local newnick = conn.nickGenerator(msg.args[2])
if msg.nick then
conn:queue(msg.nick(newnick))
end
-- Ugly hack to fix crash
end
-- ERR_ERRONEUSNICKNAME (Misspelt but remains for historical reasons)
handlers["432"] = needNewNick
-- ERR_NICKNAMEINUSE
handlers["433"] = needNewNick
-- RPL_ISUPPORT
handlers["005"] = function(conn, msg)
local arglen = #msg.args
-- Skip first and last parameters (nick and info)
for i = 2, arglen - 1 do
local item = msg.args[i]
local pos = item:find("=")
if pos then
conn.supports[item:sub(1, pos - 1)] = item:sub(pos + 1)
else
conn.supports[item] = true
end
end
end
-- RPL_MOTDSTART
handlers["375"] = function(conn, msg)
conn.motd = ""
end
-- RPL_MOTD
handlers["372"] = function(conn, msg)
-- MOTD lines have a "- " prefix, strip it.
conn.motd = conn.motd .. msg.args[2]:sub(3) .. '\n'
end
-- NAMES list
handlers["353"] = function(conn, msg)
local chanType = msg.args[2]
local channel = msg.args[3]
local names = msg.args[4]
if conn.track_users then
conn.channels[channel] = conn.channels[channel] or {users = {}, type = chanType}
local users = conn.channels[channel].users
for nick in names:gmatch("(%S+)") do
local access, name = irc.parseNick(conn, nick)
users[name] = {access = access}
end
end
end
-- End of NAMES list
handlers["366"] = function(conn, msg)
if conn.track_users then
conn:invoke("NameList", msg.args[2], msg.args[3])
end
end
-- No topic
handlers["331"] = function(conn, msg)
conn:invoke("OnTopic", msg.args[2], nil)
end
handlers["TOPIC"] = function(conn, msg)
conn:invoke("OnTopic", msg.args[1], msg.args[2])
end
handlers["332"] = function(conn, msg)
conn:invoke("OnTopic", msg.args[2], msg.args[3])
end
-- Topic creation info
handlers["333"] = function(conn, msg)
conn:invoke("OnTopicInfo", msg.args[2], msg.args[3], tonumber(msg.args[4]))
end
handlers["KICK"] = function(conn, msg)
conn:invoke("OnKick", msg.args[1], msg.args[2], msg.user, msg.args[3])
end
-- RPL_UMODEIS
-- To answer a query about a client's own mode, RPL_UMODEIS is sent back
handlers["221"] = function(conn, msg)
conn:invoke("OnUserMode", msg.args[2])
end
-- RPL_CHANNELMODEIS
-- The result from common irc servers differs from that defined by the rfc
handlers["324"] = function(conn, msg)
conn:invoke("OnChannelMode", msg.args[2], msg.args[3])
end
handlers["MODE"] = function(conn, msg)
local target = msg.args[1]
local modes = msg.args[2]
local optList = {}
for i = 3, #msg.args do
table.insert(optList, msg.args[i])
end
if conn.track_users and target ~= conn.nick then
local add = true
local argNum = 1
irc.updatePrefixModes(conn)
for c in modes:gmatch(".") do
if c == "+" then add = true
elseif c == "-" then add = false
elseif conn.modeprefix[c] then
local nick = optList[argNum]
argNum = argNum + 1
local user = conn.channels[target].users[nick]
user.access = user.access or {}
local access = user.access
access[c] = add
if c == "o" then access.op = add
elseif c == "v" then access.voice = add
end
end
end
end
conn:invoke("OnModeChange", msg.user, target, modes, unpack(optList))
end
handlers["ERROR"] = function(conn, msg)
conn:invoke("OnDisconnect", msg.args[1], true)
conn:shutdown()
error(msg.args[1], 3)
end

View File

@ -1,9 +0,0 @@
local irc = require("irc.main")
require("irc.util")
require("irc.asyncoperations")
require("irc.handlers")
require("irc.messages")
return irc

View File

@ -1,241 +0,0 @@
local socket = require "socket"
-- Module table
local irc = {}
local meta = {}
meta.__index = meta
irc.meta = meta
local meta_preconnect = {}
function meta_preconnect.__index(o, k)
local v = rawget(meta_preconnect, k)
if not v and meta[k] then
error(("field '%s' is not accessible before connecting"):format(k), 2)
end
return v
end
function irc.new(data)
local o = {
nick = assert(data.nick, "Field 'nick' is required");
username = data.username or "lua";
realname = data.realname or "Lua owns";
nickGenerator = data.nickGenerator or irc.defaultNickGenerator;
hooks = {};
track_users = true;
supports = {};
messageQueue = {};
lastThought = 0;
recentMessages = 0;
}
assert(irc.checkNick(o.nick), "Erroneous nickname passed to irc.new")
return setmetatable(o, meta_preconnect)
end
function meta:hook(name, id, f)
f = f or id
self.hooks[name] = self.hooks[name] or {}
self.hooks[name][id] = f
return id or f
end
meta_preconnect.hook = meta.hook
function meta:unhook(name, id)
local hooks = self.hooks[name]
assert(hooks, "no hooks exist for this event")
assert(hooks[id], "hook ID not found")
hooks[id] = nil
end
meta_preconnect.unhook = meta.unhook
function meta:invoke(name, ...)
local hooks = self.hooks[name]
if hooks then
for id, f in pairs(hooks) do
if f(...) then
return true
end
end
end
end
function meta_preconnect:connect(_host, _port)
local host, port, password, secure, timeout
if type(_host) == "table" then
host = _host.host
port = _host.port
timeout = _host.timeout
password = _host.password
secure = _host.secure
else
host = _host
port = _port
end
host = host or error("host name required to connect", 2)
port = port or 6667
local s = socket.tcp()
s:settimeout(timeout or 30)
assert(s:connect(host, port))
if secure then
local work, ssl = pcall(require, "ssl")
if not work then
error("LuaSec required for secure connections", 2)
end
local params
if type(secure) == "table" then
params = secure
else
params = {mode = "client", protocol = "tlsv1"}
end
s = ssl.wrap(s, params)
local success, errmsg = s:dohandshake()
if not success then
error(("could not make secure connection: %s"):format(errmsg), 2)
end
end
self.socket = s
setmetatable(self, meta)
self:queue(irc.Message({command="CAP", args={"REQ", "multi-prefix"}}))
self:invoke("PreRegister", self)
self:queue(irc.Message({command="CAP", args={"END"}}))
if password then
self:queue(irc.Message({command="PASS", args={password}}))
end
self:queue(irc.msgs.nick(self.nick))
self:queue(irc.Message({command="USER", args={self.username, "0", "*", self.realname}}))
self.channels = {}
s:settimeout(0)
repeat
self:think()
socket.sleep(0.1)
until self.authed
end
function meta:disconnect(message)
message = message or "Bye!"
self:invoke("OnDisconnect", message, false)
self:send(irc.msgs.quit(message))
self:shutdown()
end
function meta:shutdown()
self.socket:close()
setmetatable(self, nil)
end
local function getline(self, errlevel)
local line, err = self.socket:receive("*l")
if not line and err ~= "timeout" and err ~= "wantread" then
self:invoke("OnDisconnect", err, true)
self:shutdown()
error(err, errlevel)
end
return line
end
function meta:think()
while true do
local line = getline(self, 3)
if line and #line > 0 then
if not self:invoke("OnRaw", line) then
self:handle(irc.Message({raw=line}))
end
else
break
end
end
-- Handle outgoing message queue
local diff = socket.gettime() - self.lastThought
self.recentMessages = self.recentMessages - (diff * 2)
if self.recentMessages < 0 then
self.recentMessages = 0
end
for i = 1, #self.messageQueue do
if self.recentMessages > 4 then
break
end
self:send(table.remove(self.messageQueue, 1))
self.recentMessages = self.recentMessages + 1
end
self.lastThought = socket.gettime()
end
local handlers = handlers
function meta:handle(msg)
local handler = irc.handlers[msg.command]
if handler then
handler(self, msg)
end
self:invoke("Do" .. irc.capitalize(msg.command), msg)
end
local whoisHandlers = {
["311"] = "userinfo";
["312"] = "node";
["319"] = "channels";
["330"] = "account"; -- Freenode
["307"] = "registered"; -- Unreal
}
function meta:whois(nick)
self:send(irc.msgs.whois(nick))
local result = {}
while true do
local line = getline(self, 3)
if line then
local msg = irc.Message({raw=line})
local handler = whoisHandlers[msg.command]
if handler then
result[handler] = msg.args
elseif msg.command == "318" then
break
else
self:handle(msg)
end
end
end
if result.account then
result.account = result.account[3]
elseif result.registered then
result.account = result.registered[2]
end
return result
end
function meta:topic(channel)
self:queue(irc.msgs.topic(channel))
end
return irc

View File

@ -1,200 +0,0 @@
local irc = require("irc.main")
irc.msgs = {}
local msgs = irc.msgs
local msg_meta = {}
msg_meta.__index = msg_meta
function irc.Message(opts)
opts = opts or {}
setmetatable(opts, msg_meta)
if opts.raw then
opts:fromRFC1459(opts.raw)
end
return opts
end
local tag_escapes = {
[";"] = "\\:",
[" "] = "\\s",
["\0"] = "\\0",
["\\"] = "\\\\",
["\r"] = "\\r",
["\n"] = "\\n",
}
local tag_unescapes = {}
for x, y in pairs(tag_escapes) do tag_unescapes[y] = x end
function msg_meta:toRFC1459()
local s = ""
if self.tags then
s = s.."@"
for key, value in pairs(self.tags) do
s = s..key
if value ~= true then
value = value:gsub("[; %z\\\r\n]", tag_escapes)
s = s.."="..value
end
s = s..";"
end
-- Strip trailing semicolon
s = s:sub(1, -2)
s = s.." "
end
s = s..self.command
local argnum = #self.args
for i = 1, argnum do
local arg = self.args[i]
local startsWithColon = (arg:sub(1, 1) == ":")
local hasSpace = arg:find(" ")
if i == argnum and (hasSpace or startsWithColon) then
s = s.." :"
else
assert(not hasSpace and not startsWithColon,
"Message arguments can not be "
.."serialized to RFC1459 format")
s = s.." "
end
s = s..arg
end
return s
end
local function parsePrefix(prefix)
local user = {}
user.nick, user.username, user.host = prefix:match("^(.+)!(.+)@(.+)$")
if not user.nick and prefix:find(".", 1, true) then
user.server = prefix
end
return user
end
function msg_meta:fromRFC1459(line)
-- IRCv3 tags
if line:sub(1, 1) == "@" then
self.tags = {}
local space = line:find(" ", 1, true)
-- For each semicolon-delimited section from after
-- the @ character to before the space character.
for tag in line:sub(2, space - 1):gmatch("([^;]+)") do
local eq = tag:find("=", 1, true)
if eq then
self.tags[tag:sub(1, eq - 1)] =
tag:sub(eq + 1):gsub("\\([:s0\\rn])", tag_unescapes)
else
self.tags[tag] = true
end
end
line = line:sub(space + 1)
end
if line:sub(1, 1) == ":" then
local space = line:find(" ", 1, true)
self.prefix = line:sub(2, space - 1)
self.user = parsePrefix(self.prefix)
line = line:sub(space + 1)
end
local pos
self.command, pos = line:match("(%S+)()")
line = line:sub(pos)
self.args = self.args or {}
for pos, param in line:gmatch("()(%S+)") do
if param:sub(1, 1) == ":" then
param = line:sub(pos + 1)
table.insert(self.args, param)
break
end
table.insert(self.args, param)
end
end
function msgs.privmsg(to, text)
return irc.Message({command="PRIVMSG", args={to, text}})
end
function msgs.notice(to, text)
return irc.Message({command="NOTICE", args={to, text}})
end
function msgs.action(to, text)
return irc.Message({command="PRIVMSG", args={to, ("\x01ACTION %s\x01"):format(text)}})
end
function msgs.ctcp(command, to, args)
s = "\x01"..command
if args then
s = ' '..args
end
s = s..'\x01'
return irc.Message({command="PRIVMSG", args={to, s}})
end
function msgs.kick(channel, target, reason)
return irc.Message({command="KICK", args={channel, target, reason}})
end
function msgs.join(channel, key)
return irc.Message({command="JOIN", args={channel, key}})
end
function msgs.part(channel, reason)
return irc.Message({command="PART", args={channel, reason}})
end
function msgs.quit(reason)
return irc.Message({command="QUIT", args={reason}})
end
function msgs.kill(target, reason)
return irc.Message({command="KILL", args={target, reason}})
end
function msgs.kline(time, mask, reason, operreason)
local args = nil
if time then
args = {time, mask, reason..'|'..operreason}
else
args = {mask, reason..'|'..operreason}
end
return irc.Message({command="KLINE", args=args})
end
function msgs.whois(nick, server)
local args = nil
if server then
args = {server, nick}
else
args = {nick}
end
return irc.Message({command="WHOIS", args=args})
end
function msgs.topic(channel, text)
return irc.Message({command="TOPIC", args={channel, text}})
end
function msgs.invite(channel, target)
return irc.Message({command="INVITE", args={channel, target}})
end
function msgs.nick(nick)
return irc.Message({command="NICK", args={nick}})
end
function msgs.mode(target, modes)
-- We have to split the modes parameter because the mode string and
-- each parameter are seperate arguments (The first command is incorrect)
-- MODE foo :+ov Nick1 Nick2
-- MODE foo +ov Nick1 Nick2
local mt = irc.split(modes)
return irc.Message({command="MODE", args={target, unpack(mt)}})
end

View File

@ -1,19 +0,0 @@
#!/bin/bash
if [ "$TRAVIS_REPO_SLUG" == "JakobOvrum/LuaIRC" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then
echo -e "Generating luadoc...\n"
git config --global user.email "travis@travis-ci.org"
git config --global user.name "travis-ci"
git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG} gh-pages > /dev/null
cd gh-pages
git rm -rf ./doc
sh ./generate.sh
git add -f ./doc
git commit -m "Lastest documentation on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
git push -fq origin gh-pages > /dev/null
echo -e "Published luadoc to gh-pages.\n"
fi

View File

@ -1,52 +0,0 @@
local select = require "socket".select
local m = {}
local set = {}
set.__index = set
function m.new(t)
t.connections = {}
t.sockets = {}
return setmetatable(t, set)
end
function set:add(connection)
local socket = connection.socket
insert(self.sockets, socket)
self.connections[socket] = connection
insert(self.connections, connection)
end
function set:remove(connection)
local socket = connection.socket
self.connections[socket] = nil
for k, s in ipairs(self.sockets) do
if socket == s then
remove(self.sockets, k)
remove(self.connections, k)
break
end
end
end
function set:select()
local read, write, err = select(self.sockets, nil, self.timeout)
if read then
for k, socket in ipairs(read) do
read[k] = self.connections[socket]
end
end
return read, err
end
-- Select - but if it times out, it returns all connections.
function set:poll()
local read, err = self:select()
return err == "timeout" and self.connections or read
end
return m

View File

@ -1,116 +0,0 @@
local irc = require("irc.main")
function irc.parseNick(conn, nick)
local access = {}
irc.updatePrefixModes(conn)
local namestart = 1
for i = 1, #nick - 1 do
local c = nick:sub(i, i)
if conn.prefixmode[c] then
access[conn.prefixmode[c]] = true
else
namestart = i
break
end
end
access.op = access.o
access.voice = access.v
local name = nick:sub(namestart)
return access, name
end
function irc.updatePrefixModes(conn)
if conn.prefixmode and conn.modeprefix then
return
end
conn.prefixmode = {}
conn.modeprefix = {}
if conn.supports.PREFIX then
local modes, prefixes = conn.supports.PREFIX:match("%(([^%)]*)%)(.*)")
for i = 1, #modes do
conn.prefixmode[prefixes:sub(i, i)] = modes:sub(i, i)
conn.modeprefix[ modes:sub(i, i)] = prefixes:sub(i, i)
end
else
conn.prefixmode['@'] = 'o'
conn.prefixmode['+'] = 'v'
conn.modeprefix['o'] = '@'
conn.modeprefix['v'] = '+'
end
end
-- mIRC markup scheme (de-facto standard)
irc.color = {
black = 1,
blue = 2,
green = 3,
red = 4,
lightred = 5,
purple = 6,
brown = 7,
yellow = 8,
lightgreen = 9,
navy = 10,
cyan = 11,
lightblue = 12,
violet = 13,
gray = 14,
lightgray = 15,
white = 16
}
local colByte = string.char(3)
setmetatable(irc.color, {__call = function(_, text, colornum)
colornum = (type(colornum) == "string" and
assert(irc.color[colornum], "Invalid color '"..colornum.."'") or
colornum)
return table.concat{colByte, tostring(colornum), text, colByte}
end})
local boldByte = string.char(2)
function irc.bold(text)
return boldByte..text..boldByte
end
local underlineByte = string.char(31)
function irc.underline(text)
return underlineByte..text..underlineByte
end
function irc.checkNick(nick)
return nick:find("^[a-zA-Z_%-%[|%]%^{|}`][a-zA-Z0-9_%-%[|%]%^{|}`]*$") ~= nil
end
function irc.defaultNickGenerator(nick)
-- LuaBot -> LuaCot -> LuaCou -> ...
-- We change a random character rather than appending to the
-- nickname as otherwise the new nick could exceed the ircd's
-- maximum nickname length.
local randindex = math.random(1, #nick)
local randchar = string.sub(nick, randindex, randindex)
local b = string.byte(randchar)
b = b + 1
if b < 65 or b > 125 then
b = 65
end
-- Get the halves before and after the changed character
local first = string.sub(nick, 1, randindex - 1)
local last = string.sub(nick, randindex + 1, #nick)
nick = first .. string.char(b) .. last -- Insert the new charachter
return nick
end
function irc.capitalize(text)
-- Converts first character to upercase and the rest to lowercase.
-- "PING" -> "Ping" | "hello" -> "Hello" | "123" -> "123"
return text:sub(1, 1):upper()..text:sub(2):lower()
end
function irc.split(str, sep)
local t = {}
for s in str:gmatch("%S+") do
table.insert(t, s)
end
return t
end

View File

@ -1,13 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
irc.msgs = irc.lib.msgs
function irc:sendLocal(message)
minetest.chat_send_all(message)
end
function irc:playerMessage(name, message)
return ("<%s> %s"):format(name, message)
end

View File

@ -1,69 +0,0 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
function irc:player_part(name)
if not self.joined_players[name] then
minetest.chat_send_player(name, "IRC: You are not in the channel.")
return
end
self.joined_players[name] = nil
minetest.chat_send_player(name, "IRC: You are now out of the channel.")
end
function irc:player_join(name)
if self.joined_players[name] then
minetest.chat_send_player(name, "IRC: You are already in the channel.")
return
end
self.joined_players[name] = true
minetest.chat_send_player(name, "IRC: You are now in the channel.")
end
minetest.register_chatcommand("join", {
description = "Join the IRC channel",
privs = {shout=true},
func = function(name, param)
irc:player_join(name)
end
})
minetest.register_chatcommand("part", {
description = "Part the IRC channel",
privs = {shout=true},
func = function(name, param)
irc:player_part(name)
end
})
minetest.register_chatcommand("who", {
description = "Tell who is currently on the channel",
privs = {},
func = function(name, param)
local s = ""
for name, _ in pairs(irc.joined_players) do
s = s..", "..name
end
minetest.chat_send_player(name, "Players On Channel:"..s)
end
})
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
irc.joined_players[name] = irc.config.auto_join
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
irc.joined_players[name] = nil
end)
function irc:sendLocal(message)
for name, _ in pairs(self.joined_players) do
minetest.chat_send_player(name, message)
end
end

View File

@ -1,3 +0,0 @@
Remarque :
Ajouter d'autres logos avec des cubes ou dessins à l'intérieur serait bien pour la diversité du sondage/vote futur ! :)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

View File

@ -1,644 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1080"
height="1080"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="mffnewconc2?.svg"
inkscape:export-filename="/home/robin/Documents/mffnewconc9?.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="260 : 540 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="820 : 541.42857 : 1"
inkscape:persp3d-origin="540 : 360 : 1"
id="perspective3815" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="220.16209 : 539.34384 : 1"
inkscape:vp_y="0 : 1147.5441 : 0"
inkscape:vp_z="859.51372 : 540.98317 : 1"
inkscape:persp3d-origin="539.83791 : 332.78589 : 1"
id="perspective3815-8" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="260.14183 : 540.00001 : 1"
inkscape:vp_y="0 : 999.99996 : 0"
inkscape:vp_z="819.5745 : 541.42856 : 1"
inkscape:persp3d-origin="539.85817 : 360 : 1"
id="perspective3815-8-7" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="300.12157 : 540.00002 : 1"
inkscape:vp_y="0 : 857.14285 : 0"
inkscape:vp_z="779.63528 : 541.22449 : 1"
inkscape:persp3d-origin="539.87843 : 385.71429 : 1"
id="perspective3815-8-7-0" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="340.10131 : 540.00001 : 1"
inkscape:vp_y="0 : 714.28573 : 0"
inkscape:vp_z="739.69607 : 541.02041 : 1"
inkscape:persp3d-origin="539.89869 : 411.42857 : 1"
id="perspective3815-8-7-0-6" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="420.06078 : 540.00001 : 1"
inkscape:vp_y="0 : 428.57144 : 0"
inkscape:vp_z="659.81765 : 540.61225 : 1"
inkscape:persp3d-origin="539.93922 : 462.85714 : 1"
id="perspective3815-8-7-0-6-3" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="240.16209 : 679.34384 : 1"
inkscape:vp_y="0 : 1147.5441 : 0"
inkscape:vp_z="879.51372 : 680.98317 : 1"
inkscape:persp3d-origin="559.83791 : 472.78589 : 1"
id="perspective3815-8-0" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="20.263402 : 540 : 1"
inkscape:vp_y="0 : 1857.1429 : 0"
inkscape:vp_z="1059.2098 : 542.65302 : 1"
inkscape:persp3d-origin="539.7366 : 205.71429 : 1"
id="perspective3815-8-8" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="529.95296"
inkscape:cy="538.9772"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1280"
inkscape:window-height="734"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Calque#3">
<g
id="g3910-2-6"
transform="matrix(0.61739494,0,0,0.61739493,206.60674,223.67019)"
style="fill:#4d4d4d">
<g
id="g3882-5-4"
style="fill:#4d4d4d">
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 540,0 320,0 0,320 l 0,440 320,320 220,0 0,-80 -180,0 L 80,720 80,360 360,80 540,80 z"
id="path3093-1-4"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,-27.637817 220,0 320,319.999997 0,440 -320,320.00002 -220,0 0,-80.00002 180,0 280,-280 0,-360 -280,-279.999997 -180,0 z"
id="path3093-2-8-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
transform="matrix(0.92620513,-0.37701997,0.37701997,0.92620513,-153.32155,241.40048)"
id="g3882-3-7-5"
style="fill:#4d4d4d">
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 540,0 320,0 0,320 l 0,440 320,320 220,0 0,-80 -180,0 L 80,720 80,360 360,80 540,80 z"
id="path3093-7-0-4"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,-27.637817 220,0 320,319.999997 0,440 -320,320.00002 -220,0 0,-80.00002 180,0 280,-280 0,-360 -280,-279.999997 -180,0 z"
id="path3093-2-3-9-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
</g>
<g
id="g3910-2-6-7"
transform="matrix(0.61739494,0,0,0.61739493,206.4991,223.67019)"
style="fill:#4d4d4d">
<g
id="g3882-5-4-8"
style="fill:#4d4d4d">
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 540,0 320,0 0,320 l 0,440 320,320 220,0 0,-80 -180,0 L 80,720 80,360 360,80 540,80 z"
id="path3093-1-4-1"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,-27.637817 220,0 320,319.999997 0,440 -320,320.00002 -220,0 0,-80.00002 180,0 280,-280 0,-360 -280,-279.999997 -180,0 z"
id="path3093-2-8-1-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
transform="matrix(0.92620513,-0.37701997,0.37701997,0.92620513,-153.32155,241.40048)"
id="g3882-3-7-5-2"
style="fill:#4d4d4d">
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 540,0 320,0 0,320 l 0,440 320,320 220,0 0,-80 -180,0 L 80,720 80,360 360,80 540,80 z"
id="path3093-7-0-4-2"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,-27.637817 220,0 320,319.999997 0,440 -320,320.00002 -220,0 0,-80.00002 180,0 280,-280 0,-360 -280,-279.999997 -180,0 z"
id="path3093-2-3-9-5-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
</g>
<g
id="g3910-2-6-7-5"
transform="matrix(0.61739494,0,0,0.61739493,206.4991,223.67019)"
style="fill:#4d4d4d">
<g
id="g3882-5-4-8-5"
style="fill:#4d4d4d">
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 540,0 320,0 0,320 l 0,440 320,320 220,0 0,-80 -180,0 L 80,720 80,360 360,80 540,80 z"
id="path3093-1-4-1-7"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,-27.637817 220,0 320,319.999997 0,440 -320,320.00002 -220,0 0,-80.00002 180,0 280,-280 0,-360 -280,-279.999997 -180,0 z"
id="path3093-2-8-1-3-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
transform="matrix(0.92620513,-0.37701997,0.37701997,0.92620513,-153.32155,241.40048)"
id="g3882-3-7-5-2-1"
style="fill:#4d4d4d">
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 540,0 320,0 0,320 l 0,440 320,320 220,0 0,-80 -180,0 L 80,720 80,360 360,80 540,80 z"
id="path3093-7-0-4-2-0"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccccccccc" />
<path
style="fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,-27.637817 220,0 320,319.999997 0,440 -320,320.00002 -220,0 0,-80.00002 180,0 280,-280 0,-360 -280,-279.999997 -180,0 z"
id="path3093-2-3-9-5-2-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Calque"
style="display:none"
sodipodi:insensitive="true">
<g
sodipodi:type="inkscape:box3d"
style="fill:#c83737;fill-opacity:1;stroke:none"
id="g3817-6-7"
inkscape:perspectiveID="#perspective3815-8-0"
inkscape:corner0="1 : 0.32 : 0 : 1"
inkscape:corner7="-2.220446e-16 : 0.04 : 1.0040647 : 1">
<path
sodipodi:type="inkscape:box3dside"
id="path3827-4-7"
style="fill:#c83737;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="13"
d="M 400,480.98425 559.83791,561.31235 720,480 560.27045,453.58784 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3819-4-1"
style="fill:#c83737;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="6"
d="m 400,320.32808 0,160.65617 160.27045,-27.39641 0,-106.9592 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3829-4-6"
style="fill:#c83737;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="11"
d="M 560.27045,346.62864 720,319.66968 720,480 560.27045,453.58784 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3821-3-2"
style="fill:#c83737;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="5"
d="M 400,320.32808 559.83791,240 720,319.66968 560.27045,346.62864 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3825-2-3"
style="fill:#c83737;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="14"
d="m 559.83791,240 0,321.31235 L 720,480 720,319.66968 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3823-6-2"
style="fill:#c83737;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="3"
d="M 400,320.32808 559.83791,240 l 0,321.31235 L 400,480.98425 z" />
</g>
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:none"
id="path3049-3"
sodipodi:cx="540"
sodipodi:cy="540"
sodipodi:rx="540"
sodipodi:ry="540"
d="M 1080,540 A 540,540 0 1 1 0,540 540,540 0 1 1 1080,540 z"
transform="matrix(0.77777778,0,0,0.77777778,120,120)" />
<path
sodipodi:type="arc"
style="fill:#b7c4c8;fill-opacity:1;stroke:none"
id="path3051-1"
sodipodi:cx="540"
sodipodi:cy="540"
sodipodi:rx="380"
sodipodi:ry="380"
d="m 920,540 a 380,380 0 1 1 -760,0 380,380 0 1 1 760,0 z" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Calque#2"
style="display:inline"
sodipodi:insensitive="true">
<path
style="fill:#1c2422;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,190 -300,200 0,300 300,200 300,-200 0,-300 z"
id="path4270"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b7c4c8;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 260,400 260,680 540,870 820,680 820,400 540,210 z"
id="path4272"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#8f9ca0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 260,680 20,-10 0,-260 -20,-10 280,-190 280,190 -20,10 0,260 20,10 -280,190 z"
id="path5704"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Calque#1"
sodipodi:insensitive="true">
<path
style="fill:#af69c0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 380,460 0,160 160,80 0,100 -260,-130 0,-260 z"
id="path5688"
inkscape:connector-curvature="0" />
<path
style="fill:#be78ca;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,700 0,100 260,-130 0,-260 -100,50 0,160 z"
id="path5698"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#cd87de;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 280,410 540,280 800,410 540,540 z"
id="path3873"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,27.637817)">
<g
sodipodi:type="inkscape:box3d"
style="fill:#333333;fill-opacity:1;stroke:none"
id="g3817"
inkscape:perspectiveID="#perspective3815"
inkscape:corner0="1 : 0.32 : 0 : 1"
inkscape:corner7="-2.220446e-16 : 0.04 : 1.0040647 : 1">
<path
sodipodi:type="inkscape:box3dside"
id="path3827"
style="fill:#afafde;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="13"
d="m 400,582.36218 140,70 140.28395,-70.85771 -139.90509,-23.01624 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3819"
style="fill:#353564;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="6"
d="m 400,442.36218 0,140 140.37886,-23.87395 0,-93.20705 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3829"
style="fill:#e9e9ff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="11"
d="m 540.37886,465.28118 139.90509,-23.49275 0,139.71604 -139.90509,-23.01624 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3821"
style="fill:#4d4d9f;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="5"
d="m 400,442.36218 140,-70 140.28395,69.42625 -139.90509,23.49275 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3825"
style="fill:#d7d7ff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="14"
d="m 540,372.36218 0,280 140.28395,-70.85771 0,-139.71604 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3823"
style="fill:#8686bf;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="3"
d="m 400,442.36218 140,-70 0,280 -140,-70 z" />
</g>
<g
sodipodi:type="inkscape:box3d"
style="fill:#71c837;fill-opacity:1;stroke:none"
id="g3817-6-3"
inkscape:perspectiveID="#perspective3815-8-7"
inkscape:corner0="1 : 0.32 : 0 : 1"
inkscape:corner7="-2.220446e-16 : 0.04 : 1.0040647 : 1">
<path
sodipodi:type="inkscape:box3dside"
id="path3827-4-0"
style="fill:#71c837;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="13"
d="m 400,582.36218 139.85817,70 L 680,581.50448 540.23664,558.48823 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3819-4-0"
style="fill:#71c837;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="6"
d="m 400,442.36218 0,140 140.23664,-23.87395 0,-93.20704 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3829-4-0"
style="fill:#71c837;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="11"
d="M 540.23664,465.28119 680,441.78844 680,581.50448 540.23664,558.48823 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3821-3-3"
style="fill:#71c837;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="5"
d="M 400,442.36218 539.85817,372.3622 680,441.78844 540.23664,465.28119 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3825-2-9"
style="fill:#71c837;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="14"
d="m 539.85817,372.3622 0,279.99998 L 680,581.50448 680,441.78844 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3823-6-1"
style="fill:#71c837;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="3"
d="m 400,442.36218 139.85817,-69.99998 0,279.99998 -139.85817,-70 z" />
</g>
<g
sodipodi:type="inkscape:box3d"
style="fill:#3771c8;fill-opacity:1;stroke:none"
id="g3817-6-3-1"
inkscape:perspectiveID="#perspective3815-8-7-0"
inkscape:corner0="1 : 0.32 : 0 : 1"
inkscape:corner7="-2.220446e-16 : 0.04 : 1.0040647 : 1">
<path
sodipodi:type="inkscape:box3dside"
id="path3827-4-0-7"
style="fill:#3771c8;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="13"
d="M 420,572.36217 539.87843,632.36218 660,571.627 540.20283,551.89878 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3819-4-0-0"
style="fill:#3771c8;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="6"
d="m 420,452.36217 0,120 120.20283,-20.46339 0,-79.89175 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3829-4-0-1"
style="fill:#3771c8;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="11"
d="M 540.20283,472.00703 660,451.87039 660,571.627 540.20283,551.89878 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3821-3-3-9"
style="fill:#3771c8;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="5"
d="M 420,452.36217 539.87843,392.36218 660,451.87039 540.20283,472.00703 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3825-2-9-5"
style="fill:#3771c8;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="14"
d="m 539.87843,392.36218 0,240 L 660,571.627 660,451.87039 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3823-6-1-9"
style="fill:#3771c8;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="3"
d="m 420,452.36217 119.87843,-59.99999 0,240 L 420,572.36217 z" />
</g>
<g
sodipodi:type="inkscape:box3d"
style="fill:#000000;fill-opacity:1;stroke:none"
id="g3817-6-3-1-0"
inkscape:perspectiveID="#perspective3815-8-7-0-6"
inkscape:corner0="1 : 0.32 : 0 : 1"
inkscape:corner7="-2.220446e-16 : 0.04 : 1.0040647 : 1">
<path
sodipodi:type="inkscape:box3dside"
id="path3827-4-0-7-6"
style="fill:#000000;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="13"
d="m 440,562.36218 99.89869,50 L 640,561.74953 540.16903,545.30935 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3819-4-0-0-2"
style="fill:#000000;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="6"
d="m 440,462.36218 0,100 100.16903,-17.05283 0,-66.57646 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3829-4-0-1-5"
style="fill:#000000;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="11"
d="M 540.16903,478.73289 640,461.95235 l 0,99.79718 -99.83097,-16.44018 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3821-3-3-9-8"
style="fill:#000000;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="5"
d="m 440,462.36218 99.89869,-50 L 640,461.95235 540.16903,478.73289 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3825-2-9-5-8"
style="fill:#000000;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="14"
d="m 539.89869,412.36218 0,200 L 640,561.74953 l 0,-99.79718 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3823-6-1-9-2"
style="fill:#000000;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="3"
d="m 440,462.36218 99.89869,-50 0,200 -99.89869,-50 z" />
</g>
<g
sodipodi:type="inkscape:box3d"
style="fill:#ffffff;fill-opacity:1;stroke:none"
id="g3817-6-3-1-0-1"
inkscape:perspectiveID="#perspective3815-8-7-0-6-3"
inkscape:corner0="1 : 0.32 : 0 : 1"
inkscape:corner7="-2.220446e-16 : 0.04 : 1.0040647 : 1">
<path
sodipodi:type="inkscape:box3dside"
id="path3827-4-0-7-6-2"
style="fill:#ffffff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="13"
d="m 480,542.36218 59.93922,30.00001 60.06079,-30.3676 -59.89859,-9.86411 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3819-4-0-0-2-5"
style="fill:#ffffff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="6"
d="m 480,482.36218 0,60 60.10142,-10.2317 0,-39.94587 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3829-4-0-1-5-4"
style="fill:#ffffff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="11"
d="m 540.10142,492.18461 59.89859,-10.06833 0,59.87831 -59.89859,-9.86411 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3821-3-3-9-8-1"
style="fill:#ffffff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="5"
d="m 480,482.36218 59.93922,-30 60.06079,29.7541 -59.89859,10.06833 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3825-2-9-5-8-9"
style="fill:#ffffff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="14"
d="m 539.93922,452.36218 0,120.00001 60.06079,-30.3676 0,-59.87831 z" />
<path
sodipodi:type="inkscape:box3dside"
id="path3823-6-1-9-2-0"
style="fill:#ffffff;fill-rule:evenodd;stroke:none"
inkscape:box3dsidetype="3"
d="m 480,482.36218 59.93922,-30 0,120.00001 L 480,542.36218 z" />
</g>
<path
style="fill:#b3b3b3;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 480,510 60,30 0,60 -60,-30 z"
id="path5680"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
<path
style="fill:#1449a0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 420,480 20,10 0,100 100,50 0,20 -120,-60 z"
id="path5682"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
<path
style="fill:#49a014;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 400,470 20,10 0,120 120,60 0,20 -140,-70 z"
id="path5684"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
<path
style="fill:#a01414;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 380,460 20,10 0,140 140,70 0,21 -160,-81 z"
id="path5686"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#cccccc;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 540,540 60,-30 0,60 -60,30 z"
id="path5690"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
<path
style="fill:#1e64b4;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 640,490 20,-10 0,120 -120,60 0,-20 100,-50 z"
id="path5692"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
<path
style="fill:#64b41e;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 660,480 20,-10 0,140 -140,70 0,-20 120,-60 z"
id="path5694"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
<path
style="fill:#b41e1e;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 680,470 20,-10 0,160 -160,81 0,-21 140,-70 z"
id="path5696"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#c83737;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 400,470 140,-70 140,70 20,-10 -160,-80 -160,80 z"
id="path3886"
inkscape:connector-curvature="0"
transform="translate(0,-27.637817)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,182 +0,0 @@
#!/usr/bin/env python2
# -*- coding: UTF-8 -*-
import sys, os, sqlite3
import encodings, calendar, time
from cStringIO import StringIO
home = os.environ.get("HOME")
db = "%s/rollback/rollback.sqlite" % (home)
#[id=6,actor=Mg,type=1;list=None,index=None,add=None,stacknode=None,stackquantity=None;x=252,y=59,z=-401;newnode=air,newparam1=15,newparam2=None,newmeta=None]
class Convert_id(object):
def __init__(self, base):
self.__players = dict()
self.__nodes = dict()
try:
conn = sqlite3.connect(db)
cur = conn.cursor()
except Exception as err:
print(err)
print("problème avec la base de données")
sys.exit(1)
else:
try:
cur.execute("SELECT * from actor")
except sqlite3.OperationalError as err:
print(err)
else:
result = cur.fetchall()
for res in result:
self.__players[res[0]] = res[1][len("player:"):]
try:
cur.execute("SELECT * from node")
except sqlite3.OperationalError as err:
print(err)
else:
result = cur.fetchall()
for res in result:
self.__nodes[res[0]] = res[1]
finally:
cur.close()
conn.close()
def get_player_name(self, player_id):
if self.__players.has_key(player_id):
return self.__players[player_id]
else:
return None
def get_node_name(self, node_id):
if self.__nodes.has_key(node_id):
return self.__nodes[node_id]
else:
return None
########################################################################
## Utilities
#
def ston(a):
"""
Returns a string equal to a or None
"""
if a:
return str(a)
else:
return None
def select_all_nodes(startstamp, endstamp):
try:
conn = sqlite3.connect(db)
conn.text_factory = str
cur = conn.cursor()
except sqlite3.OperationalError as err:
print(err)
sys.exit(1)
try:
# x because we need position
cur.execute("SELECT * FROM action WHERE x AND timestamp >=:startstamp AND timestamp < :endstamp", {"startstamp":startstamp, "endstamp":endstamp})
except sqlite3.OperationalError as err:
print(err)
sys.exit(1)
else:
result = cur.fetchall()
finally:
cur.close()
conn.close()
return(result)
# Big-endian!!!
def readU16(strm):
return (ord(strm.read(1)) << 16) + (ord(strm.read(1)))
def readU32(strm):
return (ord(strm.read(1)) << 24) + (ord(strm.read(1)) << 16) + (ord(strm.read(1)) << 8) + (ord(strm.read(1)))
def decode(chaine):
if not chaine :
return None
else:
strm = StringIO(chaine)
table = "["
nEntries = readU32(strm)
for n in range(nEntries):
keyLen = readU16(strm)
key = strm.read(keyLen)
valLen = readU32(strm)
val = strm.read(valLen)
# Beware of potential quotes in meta, they must be escaped
# Fortunately their escape codes are the same in Python and Lua
# (and pretty much every language influenced by C)
# ------------
# Attention aux potentiels quotes dedans les meta, il faut les escape
# Heureusement leur mode d'escape est quasi le même en Python que Lua
table += '%s="%s"' % (key, val.encode('string-escape').replace('"', '\\"'))
if n != nEntries-1:
table += ","
table += "]"
return table
def to_table(node):
# Init bracket and basic datas
table = "["
table += 'id=%d' % node[0]
table += ',actor=%s' % Id.get_player_name(node[1])
table += ',type=%d' % node[3]
# Inventory Manipulation
table += ';list=%s' % node[4]
table += ',index=%s' % node[5]
table += ',add=%s' % node[6]
table += ',stacknode=%s' % Id.get_node_name(node[7])
table += ',stackquantity=%s' % node[8]
# Position
table += ';x=%s,y=%s,z=%s' % (node[10], node[11], node[12])
# New node
table += ';newnode=%s' % Id.get_node_name(node[17])
table += ',newparam1=%s' % ston(node[18])
table += ',newparam2=%s' % ston(node[19])
table += ',newmeta=%s' % decode(node[20])
# Ending
table += ']\n'
return (table)
def write_list(nodes, i):
try:
name = "rollback/database-output.%s.txt" % i
f = open(name, "w")
except Exception as err:
print(err)
sys.exit(1)
for node in nodes:
table = to_table(node)
f.write(table)
f.close()
########################################################################
## Main
#
if __name__ == '__main__':
Id = Convert_id(db)
#select all nodes player as set i where time >= time
#timestamp = 1426978800
timestamp = 0
i = 0
while timestamp <= time.time():
all_nodes = select_all_nodes(timestamp, timestamp+24*60*60)
if len(all_nodes) > 0:
write_list(all_nodes, i)
i += 1
print("%s (%s) to %s (%s) => %s entries" % (
time.strftime("%m/%d/%Y", time.gmtime(timestamp)), timestamp,
time.strftime("%m/%d/%Y", time.gmtime(timestamp+24*60*60)), timestamp+24*60*60,
len(all_nodes))
)
timestamp += 24*60*60

View File

@ -1,21 +0,0 @@
# Rebase actives branches
# Script by LeMagnesium (20/12/15)
# @param : Branches to rebase from master
# Must be ran from the root of the repo or a subdirectory
for branch in $( git branch ); do
if [ branch != "master" ];
then
git checkout $branch
git pull origin $branch
git rebase master
git push -f origin $branch
fi
done
git checkout master
echo "Branches rebased."
#EOF

Some files were not shown because too many files have changed in this diff Show More