Merge branch 'NALC2' into NALC
@ -13,14 +13,23 @@ minetest.register_craftitem("default:paper", {
|
|||||||
groups = {flammable = 3},
|
groups = {flammable = 3},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
local lpp = 14 -- Lines per book's page
|
local lpp = 14 -- Lines per book's page
|
||||||
local function book_on_use(itemstack, user)
|
local function book_on_use(itemstack, user)
|
||||||
local player_name = user:get_player_name()
|
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 title, text, owner = "", "", player_name
|
||||||
local page, page_max, lines, string = 1, 1, {}, ""
|
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
|
title = data.title
|
||||||
text = data.text
|
text = data.text
|
||||||
owner = data.owner
|
owner = data.owner
|
||||||
@ -66,12 +75,16 @@ local function book_on_use(itemstack, user)
|
|||||||
minetest.show_formspec(player_name, "default:book", formspec)
|
minetest.show_formspec(player_name, "default:book", formspec)
|
||||||
end
|
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)
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
if formname ~= "default:book" then return end
|
if formname ~= "default:book" then return end
|
||||||
local inv = player:get_inventory()
|
local inv = player:get_inventory()
|
||||||
local stack = player:get_wielded_item()
|
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
|
local new_stack, data
|
||||||
if stack:get_name() ~= "default:book_written" then
|
if stack:get_name() ~= "default:book_written" then
|
||||||
local count = stack:get_count()
|
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")
|
new_stack = ItemStack("default:book_written")
|
||||||
end
|
end
|
||||||
else
|
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
|
end
|
||||||
|
|
||||||
if not data then data = {} end
|
if not data then data = {} end
|
||||||
data.title = fields.title
|
data.title = fields.title:sub(1, max_title_size)
|
||||||
data.text = fields.text
|
data.owner = player:get_player_name()
|
||||||
data.text_len = #data.text
|
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 = 1
|
||||||
data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp)
|
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
|
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
|
if inv:room_for_item("main", new_stack) then
|
||||||
inv:add_item("main", new_stack)
|
inv:add_item("main", new_stack)
|
||||||
else
|
else
|
||||||
minetest.add_item(player:getpos(), new_stack)
|
minetest.add_item(player:getpos(), new_stack)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stack:set_metadata(data_str)
|
stack:get_meta():from_table({ fields = data })
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif fields.book_next or fields.book_prev then
|
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
|
if not data or not data.page then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
data.page = tonumber(data.page)
|
||||||
|
data.page_max = tonumber(data.page_max)
|
||||||
|
|
||||||
if fields.book_next then
|
if fields.book_next then
|
||||||
data.page = data.page + 1
|
data.page = data.page + 1
|
||||||
if data.page > data.page_max then
|
if data.page > data.page_max then
|
||||||
@ -123,11 +147,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local data_str = minetest.serialize(data)
|
stack:get_meta():from_table({fields = data})
|
||||||
stack:set_metadata(data_str)
|
stack = book_on_use(stack, player)
|
||||||
book_on_use(stack, player)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Update stack
|
||||||
player:set_wielded_item(stack)
|
player:set_wielded_item(stack)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -168,13 +192,69 @@ minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv
|
|||||||
if not original then
|
if not original then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local copymeta = original:get_metadata()
|
local copymeta = original:get_meta():to_table()
|
||||||
-- copy of the book held by player's mouse cursor
|
-- 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
|
-- put the book with metadata back in the craft grid
|
||||||
craft_inv:set_stack("craft", index, original)
|
craft_inv:set_stack("craft", index, original)
|
||||||
end)
|
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", {
|
minetest.register_craftitem("default:coal_lump", {
|
||||||
description = "Coal Lump",
|
description = "Coal Lump",
|
||||||
inventory_image = "default_coal_lump.png",
|
inventory_image = "default_coal_lump.png",
|
||||||
@ -293,4 +373,3 @@ minetest.register_craftitem("default:flint", {
|
|||||||
description = "Flint",
|
description = "Flint",
|
||||||
inventory_image = "default_flint.png"
|
inventory_image = "default_flint.png"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
55
mods/WorldEdit/ChatCommands.md
Executable file → Normal file
@ -22,6 +22,9 @@ Many commands also have shorter names that can be typed faster. For example, if
|
|||||||
| `//hdo` | `//hollowdome` |
|
| `//hdo` | `//hollowdome` |
|
||||||
| `//do` | `//dome` |
|
| `//do` | `//dome` |
|
||||||
| `//hcyl` | `//hollowcylinder` |
|
| `//hcyl` | `//hollowcylinder` |
|
||||||
|
| `//cyl` | `//cylinder` |
|
||||||
|
| `//hpyr` | `//hollowpyramid` |
|
||||||
|
| `//pyr` | `//pyramid` |
|
||||||
|
|
||||||
### `//about`
|
### `//about`
|
||||||
|
|
||||||
@ -100,7 +103,7 @@ Display the volume of the current WorldEdit region.
|
|||||||
|
|
||||||
### `//deleteblocks`
|
### `//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
|
//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 z -12 3 mesecons:wire_00000000_off
|
||||||
//cylinder ? 2 4 default:stone
|
//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>`
|
### `//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>`.
|
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
|
//fixlight
|
||||||
|
|
||||||
|
### `//drain`
|
||||||
|
|
||||||
|
Removes any fluid node within the current WorldEdit region.
|
||||||
|
|
||||||
|
//drain
|
||||||
|
|
||||||
### `//hide`
|
### `//hide`
|
||||||
|
|
||||||
Hide all nodes in the current WorldEdit region non-destructively.
|
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.
|
Clears all objects within the WorldEdit region.
|
||||||
|
|
||||||
//clearobjects
|
//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
@ -45,6 +45,12 @@ Copies the region defined by positions `pos1` and `pos2` along the `axis` axis (
|
|||||||
|
|
||||||
Returns the number of nodes copied.
|
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)
|
### 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.
|
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.
|
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.
|
Returns the number of nodes added.
|
||||||
|
|
||||||
|
258
mods/WorldEdit/worldedit/cuboid.lua
Normal 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
|
@ -36,6 +36,7 @@ load_module(path .. "/visualization.lua")
|
|||||||
load_module(path .. "/serialization.lua")
|
load_module(path .. "/serialization.lua")
|
||||||
load_module(path .. "/code.lua")
|
load_module(path .. "/code.lua")
|
||||||
load_module(path .. "/compatibility.lua")
|
load_module(path .. "/compatibility.lua")
|
||||||
|
load_module(path .. "/cuboid.lua")
|
||||||
|
|
||||||
|
|
||||||
if minetest.setting_getbool("log_mods") then
|
if minetest.setting_getbool("log_mods") then
|
||||||
|
45
mods/WorldEdit/worldedit/manipulations.lua
Executable file → Normal file
@ -90,7 +90,7 @@ function worldedit.stack2(pos1, pos2, direction, amount, finished)
|
|||||||
translated.x = translated.x + direction.x
|
translated.x = translated.x + direction.x
|
||||||
translated.y = translated.y + direction.y
|
translated.y = translated.y + direction.y
|
||||||
translated.z = translated.z + direction.z
|
translated.z = translated.z + direction.z
|
||||||
worldedit.copy2(pos1, pos2, translated, volume)
|
worldedit.copy2(pos1, pos2, translated)
|
||||||
minetest.after(0, next_one)
|
minetest.after(0, next_one)
|
||||||
else
|
else
|
||||||
if finished then
|
if finished then
|
||||||
@ -164,6 +164,38 @@ function worldedit.copy(pos1, pos2, axis, amount)
|
|||||||
return worldedit.volume(pos1, pos2)
|
return worldedit.volume(pos1, pos2)
|
||||||
end
|
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.
|
--- Moves a region along `axis` by `amount` nodes.
|
||||||
-- @return The number of nodes moved.
|
-- @return The number of nodes moved.
|
||||||
@ -543,14 +575,11 @@ end
|
|||||||
function worldedit.fixlight(pos1, pos2)
|
function worldedit.fixlight(pos1, pos2)
|
||||||
local pos1, pos2 = worldedit.sort_pos(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")
|
return worldedit.volume(pos1, pos2)
|
||||||
local dig_node = minetest.dig_node
|
|
||||||
for _, pos in ipairs(nodes) do
|
|
||||||
dig_node(pos)
|
|
||||||
end
|
|
||||||
return #nodes
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
13
mods/WorldEdit/worldedit/primitives.lua
Executable file → Normal file
@ -150,14 +150,15 @@ end
|
|||||||
-- @param axis Axis ("x", "y", or "z")
|
-- @param axis Axis ("x", "y", or "z")
|
||||||
-- @param height Pyramid height.
|
-- @param height Pyramid height.
|
||||||
-- @param node_name Name of node to make pyramid of.
|
-- @param node_name Name of node to make pyramid of.
|
||||||
|
-- @param hollow Whether the pyramid should be hollow.
|
||||||
-- @return The number of nodes added.
|
-- @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)
|
local other1, other2 = worldedit.get_axis_others(axis)
|
||||||
|
|
||||||
-- Set up voxel manipulator
|
-- Set up voxel manipulator
|
||||||
local manip, area = mh.init_axis_radius(pos, axis,
|
local manip, area = mh.init_axis_radius(pos, axis,
|
||||||
height >= 0 and height or -height)
|
height >= 0 and height or -height)
|
||||||
local data = mh.get_empty_data()
|
local data = mh.get_empty_data(area)
|
||||||
|
|
||||||
-- Handle inverted pyramids
|
-- Handle inverted pyramids
|
||||||
local start_axis, end_axis, step
|
local start_axis, end_axis, step
|
||||||
@ -177,7 +178,7 @@ function worldedit.pyramid(pos, axis, height, node_name)
|
|||||||
y = pos.y - area.MinEdge.y,
|
y = pos.y - area.MinEdge.y,
|
||||||
z = pos.z - area.MinEdge.z,
|
z = pos.z - area.MinEdge.z,
|
||||||
}
|
}
|
||||||
local size = height * step
|
local size = math.abs(height * step)
|
||||||
local count = 0
|
local count = 0
|
||||||
-- For each level of the pyramid
|
-- For each level of the pyramid
|
||||||
for index1 = 0, height, step do
|
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]
|
local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1]
|
||||||
for index3 = -size, size do
|
for index3 = -size, size do
|
||||||
local i = new_index2 + (index3 + offset[other2]) * stride[other2]
|
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
|
||||||
end
|
end
|
||||||
count = count + (size * 2 + 1) ^ 2
|
|
||||||
size = size - 1
|
size = size - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
6
mods/WorldEdit/worldedit/serialization.lua
Executable file → Normal file
@ -144,9 +144,9 @@ local function load_schematic(value)
|
|||||||
"([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
|
"([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
|
||||||
param1, param2 = tonumber(param1), tonumber(param2)
|
param1, param2 = tonumber(param1), tonumber(param2)
|
||||||
table.insert(nodes, {
|
table.insert(nodes, {
|
||||||
x = originx + tonumber(x),
|
x = tonumber(x),
|
||||||
y = originy + tonumber(y),
|
y = tonumber(y),
|
||||||
z = originz + tonumber(z),
|
z = tonumber(z),
|
||||||
name = name,
|
name = name,
|
||||||
param1 = param1 ~= 0 and param1 or nil,
|
param1 = param1 ~= 0 and param1 or nil,
|
||||||
param2 = param2 ~= 0 and param2 or nil,
|
param2 = param2 ~= 0 and param2 or nil,
|
||||||
|
BIN
mods/WorldEdit/worldedit/textures/worldedit_wand.png
Normal file
After Width: | Height: | Size: 442 B |
1
mods/WorldEdit/worldedit_commands/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*~
|
240
mods/WorldEdit/worldedit_commands/cuboid.lua
Normal 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
@ -10,8 +10,10 @@ if minetest.place_schematic then
|
|||||||
worldedit.prob_list = {}
|
worldedit.prob_list = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")
|
||||||
dofile(minetest.get_modpath("worldedit_commands") .. "/mark.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 function get_position(name) --position 1 retrieval function for when not using `safe_region`
|
||||||
local pos1 = worldedit.pos1[name]
|
local pos1 = worldedit.pos1[name]
|
||||||
@ -91,6 +93,56 @@ minetest.register_chatcommand("/about", {
|
|||||||
end,
|
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", {
|
minetest.register_chatcommand("/inspect", {
|
||||||
params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
|
params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
|
||||||
description = "Enable or disable node inspection",
|
description = "Enable or disable node inspection",
|
||||||
@ -110,16 +162,28 @@ minetest.register_chatcommand("/inspect", {
|
|||||||
end,
|
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)
|
minetest.register_on_punchnode(function(pos, node, puncher)
|
||||||
local name = puncher:get_player_name()
|
local name = puncher:get_player_name()
|
||||||
if worldedit.inspect[name] then
|
if worldedit.inspect[name] then
|
||||||
if minetest.check_player_privs(name, {worldedit=true}) then
|
local axis, sign = worldedit.player_axis(name)
|
||||||
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",
|
||||||
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, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
|
||||||
node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-"))
|
|
||||||
else
|
|
||||||
message = "inspector: worldedit privileges required"
|
|
||||||
end
|
|
||||||
worldedit.player_notify(name, message)
|
worldedit.player_notify(name, message)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@ -134,6 +198,8 @@ minetest.register_chatcommand("/reset", {
|
|||||||
worldedit.mark_pos1(name)
|
worldedit.mark_pos1(name)
|
||||||
worldedit.mark_pos2(name)
|
worldedit.mark_pos2(name)
|
||||||
worldedit.set_pos[name] = nil
|
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")
|
worldedit.player_notify(name, "region reset")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@ -516,6 +582,39 @@ minetest.register_chatcommand("/cylinder", {
|
|||||||
end, check_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", {
|
minetest.register_chatcommand("/pyramid", {
|
||||||
params = "x/y/z/? <height> <node>",
|
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>",
|
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 node = get_node(name, nodename)
|
||||||
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
|
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
|
||||||
worldedit.player_notify(name, count .. " nodes added")
|
worldedit.player_notify(name, count .. " nodes added")
|
||||||
end,
|
end, 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("/spiral", {
|
minetest.register_chatcommand("/spiral", {
|
||||||
@ -570,7 +654,7 @@ minetest.register_chatcommand("/spiral", {
|
|||||||
end
|
end
|
||||||
local node = get_node(name, nodename)
|
local node = get_node(name, nodename)
|
||||||
if not node then return nil end
|
if not node then return nil end
|
||||||
return check_region(name, param)
|
return 1 -- TODO: return an useful value
|
||||||
end),
|
end),
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -838,6 +922,30 @@ minetest.register_chatcommand("/fixlight", {
|
|||||||
end),
|
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", {
|
minetest.register_chatcommand("/hide", {
|
||||||
params = "",
|
params = "",
|
||||||
description = "Hide all nodes in the current WorldEdit region non-destructively",
|
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",
|
description = "Executes <code> as a Lua chunk in the global namespace",
|
||||||
privs = {worldedit=true, server=true},
|
privs = {worldedit=true, server=true},
|
||||||
func = function(name, param)
|
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)
|
local err = worldedit.lua(param)
|
||||||
if err then
|
if err then
|
||||||
worldedit.player_notify(name, "code error: " .. err)
|
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",
|
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},
|
privs = {worldedit=true, server=true},
|
||||||
func = safe_region(function(name, param)
|
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)
|
local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
|
||||||
if err then
|
if err then
|
||||||
worldedit.player_notify(name, "code error: " .. err, false)
|
worldedit.player_notify(name, "code error: " .. err, false)
|
||||||
@ -1127,8 +1224,8 @@ minetest.register_chatcommand("/mtschemprob", {
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
for k,v in pairs(problist) do
|
for k,v in pairs(problist) do
|
||||||
local prob = math.floor(((v["prob"] / 256) * 100) * 100 + 0.5) / 100
|
local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
|
||||||
text = text .. minetest.pos_to_string(v["pos"]) .. ": " .. prob .. "% | "
|
text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
|
||||||
end
|
end
|
||||||
worldedit.player_notify(name, "currently set node probabilities:")
|
worldedit.player_notify(name, "currently set node probabilities:")
|
||||||
worldedit.player_notify(name, text)
|
worldedit.player_notify(name, text)
|
||||||
@ -1138,16 +1235,14 @@ minetest.register_chatcommand("/mtschemprob", {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_on_player_receive_fields(
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
function(player, formname, fields)
|
if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
|
||||||
if (formname == "prob_val_enter") and (fields.text ~= "") then
|
local name = player:get_player_name()
|
||||||
local name = player:get_player_name()
|
local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
|
||||||
local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
|
local index = table.getn(worldedit.prob_list[name]) + 1
|
||||||
local index = table.getn(worldedit.prob_list[name]) + 1
|
worldedit.prob_list[name][index] = prob_entry
|
||||||
worldedit.prob_list[name][index] = prob_entry
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
)
|
end)
|
||||||
|
|
||||||
minetest.register_chatcommand("/clearobjects", {
|
minetest.register_chatcommand("/clearobjects", {
|
||||||
params = "",
|
params = "",
|
||||||
|
47
mods/WorldEdit/worldedit_commands/mark.lua
Executable file → Normal file
@ -58,8 +58,19 @@ worldedit.mark_region = function(name)
|
|||||||
end
|
end
|
||||||
worldedit.marker_region[name] = nil
|
worldedit.marker_region[name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if pos1 ~= nil and pos2 ~= nil then
|
if pos1 ~= nil and pos2 ~= nil then
|
||||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
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 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
|
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
|
--XY plane markers
|
||||||
for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
|
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")
|
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({
|
if marker ~= nil then
|
||||||
visual_size={x=sizex * 2, y=sizey * 2},
|
marker:set_properties({
|
||||||
collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
|
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)
|
marker:get_luaentity().player_name = name
|
||||||
|
table.insert(markers, marker)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--YZ plane markers
|
--YZ plane markers
|
||||||
for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
|
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")
|
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({
|
if marker ~= nil then
|
||||||
visual_size={x=sizez * 2, y=sizey * 2},
|
marker:set_properties({
|
||||||
collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
|
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
|
marker:setyaw(math.pi / 2)
|
||||||
table.insert(markers, marker)
|
marker:get_luaentity().player_name = name
|
||||||
|
table.insert(markers, marker)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
worldedit.marker_region[name] = markers
|
worldedit.marker_region[name] = markers
|
||||||
@ -153,7 +168,11 @@ minetest.register_entity(":worldedit:region_cube", {
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_punch = function(self, hitter)
|
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()
|
entity:remove()
|
||||||
end
|
end
|
||||||
worldedit.marker_region[self.player_name] = nil
|
worldedit.marker_region[self.player_name] = nil
|
||||||
|
15
mods/WorldEdit/worldedit_commands/safe.lua
Executable file → Normal file
@ -30,6 +30,10 @@ local function safe_region(callback, nodes_needed)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function reset_pending(name)
|
||||||
|
safe_region_callback[name], safe_region_param[name] = nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
minetest.register_chatcommand("/y", {
|
minetest.register_chatcommand("/y", {
|
||||||
params = "",
|
params = "",
|
||||||
description = "Confirm a pending operation",
|
description = "Confirm a pending operation",
|
||||||
@ -40,15 +44,8 @@ minetest.register_chatcommand("/y", {
|
|||||||
return
|
return
|
||||||
end
|
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
|
safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation
|
||||||
callback(name, param, pos1, pos2)
|
callback(name, param)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -64,5 +61,5 @@ minetest.register_chatcommand("/n", {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return safe_region, check_region
|
|
||||||
|
|
||||||
|
return safe_region, check_region, reset_pending
|
||||||
|
BIN
mods/WorldEdit/worldedit_commands/textures/worldedit_cube.png
Executable file → Normal file
Before Width: | Height: | Size: 105 B After Width: | Height: | Size: 147 B |
BIN
mods/WorldEdit/worldedit_commands/textures/worldedit_pos1.png
Executable file → Normal file
Before Width: | Height: | Size: 123 B After Width: | Height: | Size: 142 B |
BIN
mods/WorldEdit/worldedit_commands/textures/worldedit_pos2.png
Executable file → Normal file
Before Width: | Height: | Size: 134 B After Width: | Height: | Size: 157 B |
24
mods/WorldEdit/worldedit_commands/wand.lua
Normal 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
@ -2,4 +2,5 @@ worldedit
|
|||||||
worldedit_commands
|
worldedit_commands
|
||||||
unified_inventory?
|
unified_inventory?
|
||||||
inventory_plus?
|
inventory_plus?
|
||||||
creative?
|
sfinv?
|
||||||
|
creative?
|
||||||
|
@ -40,13 +40,13 @@ local angle_values = {90, 180, 270}
|
|||||||
setmetatable(angle_indices, {__index = function () return 1 end})
|
setmetatable(angle_indices, {__index = function () return 1 end})
|
||||||
setmetatable(angle_values, {__index = function () return 90 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 combine_privs = function(...)
|
||||||
local result = {}
|
local result = {}
|
||||||
for i, privs in ipairs({...}) do
|
for i, privs in ipairs({...}) do
|
||||||
for name, value in pairs(privs) 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
|
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
|
end
|
||||||
result[name] = value
|
result[name] = value
|
||||||
end
|
end
|
||||||
@ -54,22 +54,44 @@ local combine_privs = function(...)
|
|||||||
return result
|
return result
|
||||||
end
|
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", {
|
worldedit.register_gui_function("worldedit_gui_about", {
|
||||||
name = "About", privs = minetest.chatcommands["/about"].privs,
|
name = "About",
|
||||||
|
privs = {interact=true},
|
||||||
on_select = function(name)
|
on_select = function(name)
|
||||||
minetest.chatcommands["/about"].func(name, "")
|
minetest.chatcommands["/about"].func(name, "")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_inspect", {
|
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)
|
on_select = function(name)
|
||||||
minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable")
|
minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_region", {
|
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)
|
get_formspec = function(name)
|
||||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||||
return "size[9,7]" .. worldedit.get_formspec_header("worldedit_gui_region") ..
|
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)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_set", {
|
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)
|
get_formspec = function(name)
|
||||||
local node = gui_nodename1[name]
|
local node = gui_nodename1[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_set") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
"button_exit[0,2.5;3,0.8;worldedit_gui_set_submit;Set Nodes]"
|
"button_exit[0,2.5;3,0.8;worldedit_gui_set_submit;Set Nodes]"
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@ -166,7 +188,10 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
|
|||||||
gui_nodename1[name] = tostring(fields.worldedit_gui_set_node)
|
gui_nodename1[name] = tostring(fields.worldedit_gui_set_node)
|
||||||
worldedit.show_page(name, "worldedit_gui_set")
|
worldedit.show_page(name, "worldedit_gui_set")
|
||||||
if fields.worldedit_gui_set_submit then
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -174,19 +199,18 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_replace", {
|
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)
|
get_formspec = function(name)
|
||||||
local search, replace = gui_nodename1[name], gui_nodename2[name]
|
local search, replace = gui_nodename1[name], gui_nodename2[name]
|
||||||
local search_nodename, replace_nodename = worldedit.normalize_nodename(search), worldedit.normalize_nodename(replace)
|
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") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", search_nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
string.format("field[0.5,2.5;4,0.8;worldedit_gui_replace_replace;Name;%s]", minetest.formspec_escape(replace)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,2.1", replace_nodename) ..
|
||||||
or "image[5.5,2.1;1,1;unknown_node.png]") ..
|
|
||||||
"button_exit[0,3.5;3,0.8;worldedit_gui_replace_submit;Replace Nodes]" ..
|
"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]"
|
"button_exit[3.5,3.5;3,0.8;worldedit_gui_replace_submit_inverse;Replace Inverse]"
|
||||||
end,
|
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_nodename1[name] = tostring(fields.worldedit_gui_replace_search)
|
||||||
gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace)
|
gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace)
|
||||||
worldedit.show_page(name, "worldedit_gui_replace")
|
worldedit.show_page(name, "worldedit_gui_replace")
|
||||||
|
|
||||||
|
local submit = nil
|
||||||
if fields.worldedit_gui_replace_submit then
|
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
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -209,15 +242,15 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_sphere_dome", {
|
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)
|
get_formspec = function(name)
|
||||||
local node, radius = gui_nodename1[name], gui_distance2[name]
|
local node, radius = gui_nodename1[name], gui_distance2[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_sphere_dome") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
string.format("field[0.5,2.5;4,0.8;worldedit_gui_sphere_dome_radius;Radius;%s]", minetest.formspec_escape(radius)) ..
|
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[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]" ..
|
"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_nodename1[name] = tostring(fields.worldedit_gui_sphere_dome_node)
|
||||||
gui_distance2[name] = tostring(fields.worldedit_gui_sphere_dome_radius)
|
gui_distance2[name] = tostring(fields.worldedit_gui_sphere_dome_radius)
|
||||||
worldedit.show_page(name, "worldedit_gui_sphere_dome")
|
worldedit.show_page(name, "worldedit_gui_sphere_dome")
|
||||||
|
|
||||||
|
local submit = nil
|
||||||
if fields.worldedit_gui_sphere_dome_submit_hollow then
|
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
|
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
|
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
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -248,15 +289,15 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_cylinder", {
|
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)
|
get_formspec = function(name)
|
||||||
local node, axis, length, radius = gui_nodename1[name], gui_axis1[name], gui_distance1[name], gui_distance2[name]
|
local node, axis, length, radius = gui_nodename1[name], gui_axis1[name], gui_distance1[name], gui_distance2[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_cylinder") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
string.format("field[0.5,2.5;4,0.8;worldedit_gui_cylinder_length;Length;%s]", minetest.formspec_escape(length)) ..
|
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("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)) ..
|
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_distance1[name] = tostring(fields.worldedit_gui_cylinder_length)
|
||||||
gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius)
|
gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius)
|
||||||
worldedit.show_page(name, "worldedit_gui_cylinder")
|
worldedit.show_page(name, "worldedit_gui_cylinder")
|
||||||
|
|
||||||
|
local submit = nil
|
||||||
if fields.worldedit_gui_cylinder_submit_hollow then
|
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
|
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
|
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
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_pyramid", {
|
worldedit.register_gui_function("worldedit_gui_pyramid", {
|
||||||
name = "Pyramid", privs = minetest.chatcommands["/pyramid"].privs,
|
name = "Pyramid",
|
||||||
|
privs = we_privs("pyramid"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local node, axis, length = gui_nodename1[name], gui_axis1[name], gui_distance1[name]
|
local node, axis, length = gui_nodename1[name], gui_axis1[name], gui_distance1[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_pyramid") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
string.format("field[0.5,2.5;4,0.8;worldedit_gui_pyramid_length;Length;%s]", minetest.formspec_escape(length)) ..
|
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) ..
|
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,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields)
|
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_nodename1[name] = tostring(fields.worldedit_gui_pyramid_node)
|
||||||
gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
|
gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
|
||||||
gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length)
|
gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length)
|
||||||
worldedit.show_page(name, "worldedit_gui_pyramid")
|
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
|
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
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_spiral", {
|
worldedit.register_gui_function("worldedit_gui_spiral", {
|
||||||
name = "Spiral", privs = minetest.chatcommands["/spiral"].privs,
|
name = "Spiral",
|
||||||
|
privs = we_privs("spiral"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local node, length, height, space = gui_nodename1[name], gui_distance1[name], gui_distance2[name], gui_distance3[name]
|
local node, length, height, space = gui_nodename1[name], gui_distance1[name], gui_distance2[name], gui_distance3[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,6]" .. worldedit.get_formspec_header("worldedit_gui_spiral") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
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,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,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)) ..
|
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)
|
gui_distance3[name] = tostring(fields.worldedit_gui_spiral_space)
|
||||||
worldedit.show_page(name, "worldedit_gui_spiral")
|
worldedit.show_page(name, "worldedit_gui_spiral")
|
||||||
if fields.worldedit_gui_spiral_submit then
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -346,7 +419,8 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_copy_move", {
|
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)
|
get_formspec = function(name)
|
||||||
local axis = gui_axis1[name] or 4
|
local axis = gui_axis1[name] or 4
|
||||||
local amount = gui_distance1[name] or "10"
|
local amount = gui_distance1[name] or "10"
|
||||||
@ -370,11 +444,17 @@ worldedit.register_gui_handler("worldedit_gui_copy_move", function(name, fields)
|
|||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
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
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_stack", {
|
worldedit.register_gui_function("worldedit_gui_stack", {
|
||||||
name = "Stack", privs = minetest.chatcommands["/stack"].privs,
|
name = "Stack",
|
||||||
|
privs = we_privs("stack"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local axis, count = gui_axis1[name], gui_count1[name]
|
local axis, count = gui_axis1[name], gui_count1[name]
|
||||||
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") ..
|
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]))
|
minetest.chatcommands["/stack"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_count1[name]))
|
||||||
return true
|
return true
|
||||||
end
|
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
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_stretch", {
|
worldedit.register_gui_function("worldedit_gui_stretch", {
|
||||||
name = "Stretch", privs = minetest.chatcommands["/stretch"].privs,
|
name = "Stretch",
|
||||||
|
privs = we_privs("stretch"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local stretchx, stretchy, stretchz = gui_count1[name], gui_count2[name], gui_count3[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") ..
|
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)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_transpose", {
|
worldedit.register_gui_function("worldedit_gui_transpose", {
|
||||||
name = "Transpose", privs = minetest.chatcommands["/transpose"].privs,
|
name = "Transpose",
|
||||||
|
privs = we_privs("transpose"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local axis1, axis2 = gui_axis1[name], gui_axis2[name]
|
local axis1, axis2 = gui_axis1[name], gui_axis2[name]
|
||||||
return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_transpose") ..
|
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)
|
worldedit.register_gui_handler("worldedit_gui_transpose", function(name, fields)
|
||||||
if fields.worldedit_gui_transpose_submit then
|
if fields.worldedit_gui_transpose_submit then
|
||||||
gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1]
|
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")
|
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]]))
|
minetest.chatcommands["/transpose"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], axis_values[gui_axis2[name]]))
|
||||||
return true
|
return true
|
||||||
end
|
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
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_flip", {
|
worldedit.register_gui_function("worldedit_gui_flip", {
|
||||||
name = "Flip", privs = minetest.chatcommands["/flip"].privs,
|
name = "Flip",
|
||||||
|
privs = we_privs("flip"),
|
||||||
get_formspec = function(name)
|
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") ..
|
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) ..
|
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]"
|
"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)
|
worldedit.register_gui_handler("worldedit_gui_flip", function(name, fields)
|
||||||
if fields.worldedit_gui_flip_submit then
|
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")
|
worldedit.show_page(name, "worldedit_gui_flip")
|
||||||
minetest.chatcommands["/flip"].func(name, axis_values[gui_axis2[name]])
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_rotate", {
|
worldedit.register_gui_function("worldedit_gui_rotate", {
|
||||||
name = "Rotate", privs = minetest.chatcommands["/rotate"].privs,
|
name = "Rotate",
|
||||||
|
privs = we_privs("rotate"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local axis, angle = gui_axis1[name], gui_angle[name]
|
local axis, angle = gui_axis1[name], gui_angle[name]
|
||||||
return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_rotate") ..
|
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]]))
|
minetest.chatcommands["/rotate"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], angle_values[gui_angle[name]]))
|
||||||
return true
|
return true
|
||||||
end
|
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
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_orient", {
|
worldedit.register_gui_function("worldedit_gui_orient", {
|
||||||
name = "Orient", privs = minetest.chatcommands["/orient"].privs,
|
name = "Orient",
|
||||||
|
privs = we_privs("orient"),
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
local angle = gui_angle[name]
|
local angle = gui_angle[name]
|
||||||
return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_orient") ..
|
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
|
if fields.worldedit_gui_orient_submit then
|
||||||
gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle]
|
gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle]
|
||||||
worldedit.show_page(name, "worldedit_gui_orient")
|
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
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_fixlight", {
|
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)
|
on_select = function(name)
|
||||||
minetest.chatcommands["/fixlight"].func(name, "")
|
minetest.chatcommands["/fixlight"].func(name, "")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_hide", {
|
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)
|
on_select = function(name)
|
||||||
minetest.chatcommands["/hide"].func(name, "")
|
minetest.chatcommands["/hide"].func(name, "")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_suppress", {
|
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)
|
get_formspec = function(name)
|
||||||
local node = gui_nodename1[name]
|
local node = gui_nodename1[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_suppress") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
"button_exit[0,2.5;3,0.8;worldedit_gui_suppress_submit;Suppress Nodes]"
|
"button_exit[0,2.5;3,0.8;worldedit_gui_suppress_submit;Suppress Nodes]"
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@ -536,7 +657,10 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
|
|||||||
gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node)
|
gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node)
|
||||||
worldedit.show_page(name, "worldedit_gui_suppress")
|
worldedit.show_page(name, "worldedit_gui_suppress")
|
||||||
if fields.worldedit_gui_suppress_submit then
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -544,15 +668,15 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_highlight", {
|
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)
|
get_formspec = function(name)
|
||||||
local node = gui_nodename1[name]
|
local node = gui_nodename1[name]
|
||||||
local nodename = worldedit.normalize_nodename(node)
|
local nodename = worldedit.normalize_nodename(node)
|
||||||
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_highlight") ..
|
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)) ..
|
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]" ..
|
"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)
|
formspec_node("5.5,1.1", nodename) ..
|
||||||
or "image[5.5,1.1;1,1;unknown_node.png]") ..
|
|
||||||
"button_exit[0,2.5;3,0.8;worldedit_gui_highlight_submit;Highlight Nodes]"
|
"button_exit[0,2.5;3,0.8;worldedit_gui_highlight_submit;Highlight Nodes]"
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@ -562,7 +686,10 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
|
|||||||
gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node)
|
gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node)
|
||||||
worldedit.show_page(name, "worldedit_gui_highlight")
|
worldedit.show_page(name, "worldedit_gui_highlight")
|
||||||
if fields.worldedit_gui_highlight_submit then
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -570,14 +697,16 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_restore", {
|
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)
|
on_select = function(name)
|
||||||
minetest.chatcommands["/restore"].func(name, "")
|
minetest.chatcommands["/restore"].func(name, "")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_save_load", {
|
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)
|
get_formspec = function(name)
|
||||||
local filename = gui_filename[name]
|
local filename = gui_filename[name]
|
||||||
return "size[6,4]" .. worldedit.get_formspec_header("worldedit_gui_save_load") ..
|
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,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
worldedit.register_gui_handler("worldedit_gui_save", function(name, fields)
|
worldedit.register_gui_handler("worldedit_gui_save_load", 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
|
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_axis)
|
gui_filename[name] = tostring(fields.worldedit_gui_save_filename)
|
||||||
worldedit.show_page(name, "worldedit_gui_save_load")
|
worldedit.show_page(name, "worldedit_gui_save_load")
|
||||||
if fields.worldedit_gui_save_load_submit_save then
|
if fields.worldedit_gui_save_load_submit_save then
|
||||||
minetest.chatcommands["/save"].func(name, gui_filename[name])
|
minetest.chatcommands["/save"].func(name, gui_filename[name])
|
||||||
@ -605,7 +734,8 @@ worldedit.register_gui_handler("worldedit_gui_save", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_lua", {
|
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)
|
get_formspec = function(name)
|
||||||
local code = gui_code[name]
|
local code = gui_code[name]
|
||||||
return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_lua") ..
|
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)
|
worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields)
|
||||||
if fields.worldedit_gui_lua_run or fields.worldedit_gui_lua_transform then
|
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")
|
worldedit.show_page(name, "worldedit_gui_lua")
|
||||||
if fields.worldedit_gui_lua_run then
|
if fields.worldedit_gui_lua_run then
|
||||||
minetest.chatcommands["/lua"].func(name, gui_code[name])
|
minetest.chatcommands["/lua"].func(name, gui_code[name])
|
||||||
@ -630,28 +760,9 @@ worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui_clearobjects", {
|
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)
|
on_select = function(name)
|
||||||
minetest.chatcommands["/clearobjects"].func(name, "")
|
minetest.chatcommands["/clearobjects"].func(name, "")
|
||||||
end,
|
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)
|
|
||||||
|
@ -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 `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.
|
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
|
worldedit.pages = {} --mapping of identifiers to options
|
||||||
local identifiers = {} --ordered list of identifiers
|
local identifiers = {} --ordered list of identifiers
|
||||||
worldedit.register_gui_function = function(identifier, options)
|
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
|
worldedit.pages[identifier] = options
|
||||||
table.insert(identifiers, identifier)
|
table.insert(identifiers, identifier)
|
||||||
end
|
end
|
||||||
@ -46,7 +49,7 @@ worldedit.register_gui_handler = function(identifier, handler)
|
|||||||
|
|
||||||
--ensure the player has permission to perform the action
|
--ensure the player has permission to perform the action
|
||||||
local entry = worldedit.pages[identifier]
|
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)
|
return handler(name, fields)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@ -67,7 +70,7 @@ local get_formspec = function(name, identifier)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--implement worldedit.show_page(name, page) in different ways depending on the available APIs
|
--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
|
local old_func = worldedit.register_gui_function
|
||||||
worldedit.register_gui_function = function(identifier, options)
|
worldedit.register_gui_function = function(identifier, options)
|
||||||
old_func(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))
|
player:set_inventory_formspec(get_formspec(name, page))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif inventory_plus then --inventory++ installed
|
elseif rawget(_G, "inventory_plus") then --inventory++ installed
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true})
|
local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true})
|
||||||
if can_worldedit then
|
if can_worldedit then
|
||||||
@ -120,7 +123,7 @@ elseif inventory_plus then --inventory++ installed
|
|||||||
return true
|
return true
|
||||||
elseif fields.worldedit_gui_exit then --return to original page
|
elseif fields.worldedit_gui_exit then --return to original page
|
||||||
if gui_player_formspecs[name] then
|
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
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -133,7 +136,37 @@ elseif inventory_plus then --inventory++ installed
|
|||||||
inventory_plus.set_inventory_formspec(player, get_formspec(name, page))
|
inventory_plus.set_inventory_formspec(player, get_formspec(name, page))
|
||||||
end
|
end
|
||||||
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
|
else --fallback button
|
||||||
|
-- FIXME: this is a huge clusterfuck and the back button is broken
|
||||||
local player_formspecs = {}
|
local player_formspecs = {}
|
||||||
|
|
||||||
local update_main_formspec = function(name)
|
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
|
if not player then --this is in case the player signs off while the media is loading
|
||||||
return
|
return
|
||||||
end
|
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
|
if (minetest.check_player_privs(name, {creative=true}) or
|
||||||
formspec = player:get_inventory_formspec() .. "image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
|
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
|
else
|
||||||
formspec = formspec .. "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
|
formspec = formspec .. "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
|
||||||
end
|
end
|
||||||
@ -199,6 +245,7 @@ end
|
|||||||
|
|
||||||
worldedit.register_gui_function("worldedit_gui", {
|
worldedit.register_gui_function("worldedit_gui", {
|
||||||
name = "WorldEdit GUI",
|
name = "WorldEdit GUI",
|
||||||
|
privs = {interact=true},
|
||||||
get_formspec = function(name)
|
get_formspec = function(name)
|
||||||
--create a form with all the buttons arranged in a grid
|
--create a form with all the buttons arranged in a grid
|
||||||
local buttons, x, y, index = {}, 0, 1, 0
|
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
|
for identifier, entry in pairs(worldedit.pages) do --check for WorldEdit GUI main formspec button selection
|
||||||
if fields[identifier] and identifier ~= "worldedit_gui" then
|
if fields[identifier] and identifier ~= "worldedit_gui" then
|
||||||
--ensure player has permission to perform action
|
--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
|
if not has_privs then
|
||||||
worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")")
|
worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")")
|
||||||
return false
|
return false
|
||||||
|
BIN
mods/WorldEdit/worldedit_gui/textures/inventory_plus_worldedit_gui.png
Executable file → Normal file
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 25 KiB |
BIN
mods/WorldEdit/worldedit_gui/textures/worldedit_gui_unknown.png
Normal file
After Width: | Height: | Size: 597 B |
@ -31,6 +31,7 @@ worldedit.alias_chatcommand("/hdo", "/hollowdome")
|
|||||||
worldedit.alias_chatcommand("/do", "/dome")
|
worldedit.alias_chatcommand("/do", "/dome")
|
||||||
worldedit.alias_chatcommand("/hcyl", "/hollowcylinder")
|
worldedit.alias_chatcommand("/hcyl", "/hollowcylinder")
|
||||||
worldedit.alias_chatcommand("/cyl", "/cylinder")
|
worldedit.alias_chatcommand("/cyl", "/cylinder")
|
||||||
|
worldedit.alias_chatcommand("/hpyr", "/hollowpyramid")
|
||||||
worldedit.alias_chatcommand("/pyr", "/pyramid")
|
worldedit.alias_chatcommand("/pyr", "/pyramid")
|
||||||
worldedit.alias_chatcommand("/spl", "/spiral")
|
worldedit.alias_chatcommand("/spl", "/spiral")
|
||||||
worldedit.alias_chatcommand("/m", "/move")
|
worldedit.alias_chatcommand("/m", "/move")
|
||||||
@ -47,4 +48,4 @@ worldedit.alias_chatcommand("/hlt", "/highlight")
|
|||||||
worldedit.alias_chatcommand("/rsr", "/restore")
|
worldedit.alias_chatcommand("/rsr", "/restore")
|
||||||
worldedit.alias_chatcommand("/l", "/lua")
|
worldedit.alias_chatcommand("/l", "/lua")
|
||||||
worldedit.alias_chatcommand("/lt", "/luatransform")
|
worldedit.alias_chatcommand("/lt", "/luatransform")
|
||||||
worldedit.alias_chatcommand("/clro", "/clearobjects")
|
worldedit.alias_chatcommand("/clro", "/clearobjects")
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 94737b2494482df655664f14ebc391ac8ba527b3
|
Subproject commit 6feae4d3e083b5a635d0e13edc7a91e63cdbbef3
|
@ -382,6 +382,8 @@ minetest.register_abm({
|
|||||||
-- we need our own copy of that node, which moretrees will match against.
|
-- we need our own copy of that node, which moretrees will match against.
|
||||||
|
|
||||||
local jungle_tree = moretrees.clone_node("default:jungletree")
|
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)
|
minetest.register_node("moretrees:jungletree_trunk", jungle_tree)
|
||||||
|
|
||||||
-- For compatibility with old nodes, recently-changed nodes, and default nodes
|
-- For compatibility with old nodes, recently-changed nodes, and default nodes
|
||||||
|
3
mods/u_skins/u_skins/meta/mff_character_10.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Matthieu8fr
|
||||||
|
Matthieu8fr
|
||||||
|
WTFPL
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1005 B After Width: | Height: | Size: 930 B |
BIN
mods/u_skins/u_skins/textures/mff_character_10.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
mods/u_skins/u_skins/textures/mff_character_10_preview.png
Normal file
After Width: | Height: | Size: 380 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 625 B |
2
other_things/irc-old-save/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
*~
|
|
||||||
|
|
3
other_things/irc-old-save/.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "src/LuaIRC"]
|
|
||||||
path = irc
|
|
||||||
url = https://github.com/ShadowNinja/LuaIRC.git
|
|
@ -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.
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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
|
|
||||||
})
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
3
other_things/irc-old-save/irc/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/gh-pages/
|
|
||||||
*~
|
|
||||||
|
|
@ -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.]]
|
|
||||||
|
|
@ -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).
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
local irc = require("irc.main")
|
|
||||||
require("irc.util")
|
|
||||||
require("irc.asyncoperations")
|
|
||||||
require("irc.handlers")
|
|
||||||
require("irc.messages")
|
|
||||||
|
|
||||||
return irc
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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 ! :)
|
|
Before Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 415 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 696 B |
Before Width: | Height: | Size: 731 B |
@ -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 |
@ -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
|
|
||||||
|
|
@ -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
|
|