Start updating stuff

Xdecor worktable
Remove xban2 (server-specific mod)
master
James Stevenson 2016-01-29 14:07:37 -05:00
parent a7cd967ecb
commit 0e9c16f9ea
74 changed files with 1088 additions and 903 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
player_*.png
*.bak
.*.swp
clothing.conf

26
mods/stairs/README.txt Normal file
View File

@ -0,0 +1,26 @@
Minetest Game mod: stairs
=========================
License of source code:
-----------------------
Copyright (C) 2011-2012 Kahrl <kahrl@gmx.net>
Copyright (C) 2011-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
http://www.gnu.org/licenses/lgpl-2.1.html
License of media (textures and sounds)
--------------------------------------
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
Authors of media files
-----------------------
Everything not listed in here:
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>

2
mods/stairs/depends.txt Normal file
View File

@ -0,0 +1,2 @@
default
farming

409
mods/stairs/init.lua Normal file
View File

@ -0,0 +1,409 @@
-- Minetest 0.4 mod: stairs
-- See README.txt for licensing and other information.
-- Global namespace for functions
stairs = {}
-- Register aliases for new pine node names
minetest.register_alias("stairs:stair_pinewood", "stairs:stair_pine_wood")
minetest.register_alias("stairs:slab_pinewood", "stairs:slab_pine_wood")
-- Get setting for replace ABM
local replace = minetest.setting_getbool("enable_stairs_replace_abm")
-- Register stairs.
-- Node will be called stairs:stair_<subname>
function stairs.register_stair(subname, recipeitem, groups, images, description, sounds)
groups.stair = 1
minetest.register_node(":stairs:stair_" .. subname, {
description = description,
drawtype = "mesh",
mesh = "stairs_stair.obj",
tiles = images,
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
groups = groups,
sounds = sounds,
selection_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5},
},
},
collision_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5},
},
},
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
local p0 = pointed_thing.under
local p1 = pointed_thing.above
local param2 = 0
local placer_pos = placer:getpos()
if placer_pos then
local dir = {
x = p1.x - placer_pos.x,
y = p1.y - placer_pos.y,
z = p1.z - placer_pos.z
}
param2 = minetest.dir_to_facedir(dir)
end
if p0.y - 1 == p1.y then
param2 = param2 + 20
if param2 == 21 then
param2 = 23
elseif param2 == 23 then
param2 = 21
end
end
return minetest.item_place(itemstack, placer, pointed_thing, param2)
end,
})
-- for replace ABM
if replace then
minetest.register_node(":stairs:stair_" .. subname .. "upside_down", {
replace_name = "stairs:stair_" .. subname,
groups = {slabs_replace = 1},
})
end
minetest.register_craft({
output = 'stairs:stair_' .. subname .. ' 6',
recipe = {
{recipeitem, "", ""},
{recipeitem, recipeitem, ""},
{recipeitem, recipeitem, recipeitem},
},
})
-- Flipped recipe for the silly minecrafters
minetest.register_craft({
output = 'stairs:stair_' .. subname .. ' 6',
recipe = {
{"", "", recipeitem},
{"", recipeitem, recipeitem},
{recipeitem, recipeitem, recipeitem},
},
})
end
-- Register slabs.
-- Node will be called stairs:slab_<subname>
function stairs.register_slab(subname, recipeitem, groups, images, description, sounds)
groups.slab = 1
minetest.register_node(":stairs:slab_" .. subname, {
description = description,
drawtype = "nodebox",
tiles = images,
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
groups = groups,
sounds = sounds,
node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
-- If it's being placed on an another similar one, replace it with
-- a full block
local slabpos = nil
local slabnode = nil
local p0 = pointed_thing.under
local p1 = pointed_thing.above
local n0 = minetest.get_node(p0)
local n1 = minetest.get_node(p1)
local param2 = 0
local n0_is_upside_down = (n0.name == "stairs:slab_" .. subname and
n0.param2 >= 20)
if n0.name == "stairs:slab_" .. subname and not n0_is_upside_down and
p0.y + 1 == p1.y then
slabpos = p0
slabnode = n0
elseif n1.name == "stairs:slab_" .. subname then
slabpos = p1
slabnode = n1
end
if slabpos then
-- Remove the slab at slabpos
minetest.remove_node(slabpos)
-- Make a fake stack of a single item and try to place it
local fakestack = ItemStack(recipeitem)
fakestack:set_count(itemstack:get_count())
pointed_thing.above = slabpos
local success
fakestack, success = minetest.item_place(fakestack, placer,
pointed_thing)
-- If the item was taken from the fake stack, decrement original
if success then
itemstack:set_count(fakestack:get_count())
-- Else put old node back
else
minetest.set_node(slabpos, slabnode)
end
return itemstack
end
-- Upside down slabs
if p0.y - 1 == p1.y then
-- Turn into full block if pointing at a existing slab
if n0_is_upside_down then
-- Remove the slab at the position of the slab
minetest.remove_node(p0)
-- Make a fake stack of a single item and try to place it
local fakestack = ItemStack(recipeitem)
fakestack:set_count(itemstack:get_count())
pointed_thing.above = p0
local success
fakestack, success = minetest.item_place(fakestack, placer,
pointed_thing)
-- If the item was taken from the fake stack, decrement original
if success then
itemstack:set_count(fakestack:get_count())
-- Else put old node back
else
minetest.set_node(p0, n0)
end
return itemstack
end
-- Place upside down slab
param2 = 20
end
-- If pointing at the side of a upside down slab
if n0_is_upside_down and p0.y + 1 ~= p1.y then
param2 = 20
end
return minetest.item_place(itemstack, placer, pointed_thing, param2)
end,
})
-- for replace ABM
if replace then
minetest.register_node(":stairs:slab_" .. subname .. "upside_down", {
replace_name = "stairs:slab_".. subname,
groups = {slabs_replace = 1},
})
end
minetest.register_craft({
output = 'stairs:slab_' .. subname .. ' 6',
recipe = {
{recipeitem, recipeitem, recipeitem},
},
})
end
-- Optionally replace old "upside_down" nodes with new param2 versions.
-- Disabled by default.
if replace then
minetest.register_abm({
nodenames = {"group:slabs_replace"},
interval = 8,
chance = 1,
action = function(pos, node)
node.name = minetest.registered_nodes[node.name].replace_name
node.param2 = node.param2 + 20
if node.param2 == 21 then
node.param2 = 23
elseif node.param2 == 23 then
node.param2 = 21
end
minetest.set_node(pos, node)
end,
})
end
-- Stair/slab registration function.
-- Nodes will be called stairs:{stair,slab}_<subname>
function stairs.register_stair_and_slab(subname, recipeitem, groups, images,
desc_stair, desc_slab, sounds)
stairs.register_stair(subname, recipeitem, groups, images, desc_stair, sounds)
stairs.register_slab(subname, recipeitem, groups, images, desc_slab, sounds)
end
-- Register default stairs and slabs
stairs.register_stair_and_slab("wood", "default:wood",
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
{"default_wood.png"},
"Wooden Stair",
"Wooden Slab",
default.node_sound_wood_defaults())
stairs.register_stair_and_slab("junglewood", "default:junglewood",
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
{"default_junglewood.png"},
"Junglewood Stair",
"Junglewood Slab",
default.node_sound_wood_defaults())
stairs.register_stair_and_slab("pine_wood", "default:pine_wood",
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
{"default_pine_wood.png"},
"Pine Wood Stair",
"Pine Wood Slab",
default.node_sound_wood_defaults())
stairs.register_stair_and_slab("acacia_wood", "default:acacia_wood",
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
{"default_acacia_wood.png"},
"Acacia Wood Stair",
"Acacia Wood Slab",
default.node_sound_wood_defaults())
stairs.register_stair_and_slab("aspen_wood", "default:aspen_wood",
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
{"default_aspen_wood.png"},
"Aspen Wood Stair",
"Aspen Wood Slab",
default.node_sound_wood_defaults())
stairs.register_stair_and_slab("stone", "default:stone",
{cracky = 3},
{"default_stone.png"},
"Stone Stair",
"Stone Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("cobble", "default:cobble",
{cracky = 3},
{"default_cobble.png"},
"Cobblestone Stair",
"Cobblestone Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("stonebrick", "default:stonebrick",
{cracky = 3},
{"default_stone_brick.png"},
"Stone Brick Stair",
"Stone Brick Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("desert_stone", "default:desert_stone",
{cracky = 3},
{"default_desert_stone.png"},
"Desertstone Stair",
"Desertstone Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("desert_cobble", "default:desert_cobble",
{cracky = 3},
{"default_desert_cobble.png"},
"Desert Cobblestone Stair",
"Desert Cobblestone Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("desert_stonebrick", "default:desert_stonebrick",
{cracky = 3},
{"default_desert_stone_brick.png"},
"Desert Stone Brick Stair",
"Desert Stone Brick Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("sandstone", "default:sandstone",
{crumbly = 2, cracky = 2},
{"default_sandstone.png"},
"Sandstone Stair",
"Sandstone Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("sandstonebrick", "default:sandstonebrick",
{crumbly = 2, cracky = 2},
{"default_sandstone_brick.png"},
"Sandstone Brick Stair",
"Sandstone Brick Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("obsidian", "default:obsidian",
{cracky = 1, level = 2},
{"default_obsidian.png"},
"Obsidian Stair",
"Obsidian Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("obsidianbrick", "default:obsidianbrick",
{cracky = 1, level = 2},
{"default_obsidian_brick.png"},
"Obsidian Brick Stair",
"Obsidian Brick Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("brick", "default:brick",
{cracky = 3},
{"default_brick.png"},
"Brick Stair",
"Brick Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("straw", "farming:straw",
{snappy = 3, flammable = 4},
{"farming_straw.png"},
"Straw Stair",
"Straw Slab",
default.node_sound_leaves_defaults())
stairs.register_stair_and_slab("steelblock", "default:steelblock",
{cracky = 1, level = 2},
{"default_steel_block.png"},
"Steel Block Stair",
"Steel Block Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("copperblock", "default:copperblock",
{cracky = 1, level = 2},
{"default_copper_block.png"},
"Copper Block Stair",
"Copper Block Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("bronzeblock", "default:bronzeblock",
{cracky = 1, level = 2},
{"default_bronze_block.png"},
"Bronze Block Stair",
"Bronze Block Slab",
default.node_sound_stone_defaults())
stairs.register_stair_and_slab("goldblock", "default:goldblock",
{cracky = 1},
{"default_gold_block.png"},
"Gold Block Stair",
"Gold Block Slab",
default.node_sound_stone_defaults())

View File

@ -0,0 +1,113 @@
# Blender v2.72 (sub 0) OBJ File: ''
# www.blender.org
mtllib stairs.mtl
o stairs_top
v -0.500000 0.000000 -0.500000
v -0.500000 0.000000 0.000000
v 0.500000 0.000000 0.000000
v 0.500000 0.000000 -0.500000
v -0.500000 0.500000 0.000000
v 0.500000 0.500000 0.000000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 0.500000
vt 0.000000 0.500000
vt 1.000000 1.000000
vt 0.000000 1.000000
vn 0.000000 1.000000 0.000000
g stairs_top
usemtl None
s off
f 4/1/1 1/2/1 2/3/1 3/4/1
f 7/5/1 8/6/1 6/4/1 5/3/1
o stairs_bottom
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vn 0.000000 -1.000000 -0.000000
g stairs_bottom
usemtl None
s off
f 11/7/2 9/8/2 10/9/2 12/10/2
o stairs_right
v -0.500000 0.000000 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.000000 0.000000
v -0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.000000
v -0.500000 0.500000 0.500000
vt 0.000000 0.500000
vt 0.000000 0.000000
vt 0.500000 0.500000
vt 1.000000 1.000000
vt 0.500000 1.000000
vt 1.000000 0.000000
vn -1.000000 0.000000 0.000000
g stairs_right
usemtl None
s off
f 13/11/3 14/12/3 15/13/3
f 15/13/3 18/14/3 17/15/3
f 14/12/3 16/16/3 18/14/3
o stairs_left
v 0.500000 0.000000 0.000000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.000000 -0.500000
v 0.500000 -0.500000 0.500000
v 0.500000 0.500000 0.000000
v 0.500000 0.500000 0.500000
vt 0.500000 0.500000
vt 1.000000 0.000000
vt 1.000000 0.500000
vt 0.500000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vn 1.000000 0.000000 0.000000
g stairs_left
usemtl None
s off
f 19/17/4 20/18/4 21/19/4
f 19/17/4 23/20/4 24/21/4
f 20/18/4 24/21/4 22/22/4
o stairs_back
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vn 0.000000 -0.000000 1.000000
g stairs_back
usemtl None
s off
f 26/23/5 28/24/5 27/25/5 25/26/5
o stairs_front
v -0.500000 0.000000 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.000000 0.000000
v 0.500000 0.000000 0.000000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.000000 -0.500000
v -0.500000 0.500000 0.000000
v 0.500000 0.500000 0.000000
vt 1.000000 0.000000
vt 1.000000 0.500000
vt 0.000000 0.500000
vt 0.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vn 0.000000 0.000000 -1.000000
g stairs_front
usemtl None
s off
f 30/27/6 29/28/6 34/29/6 33/30/6
f 31/28/6 35/31/6 36/32/6 32/29/6

View File

@ -1,38 +0,0 @@
xban.importers = { }
dofile(xban.MP.."/importers/minetest.lua")
dofile(xban.MP.."/importers/v1.lua")
dofile(xban.MP.."/importers/v2.lua")
minetest.register_chatcommand("xban_dbi", {
description = "Import old databases",
params = "<importer>",
privs = { server=true },
func = function(name, params)
if params == "--list" then
local names = { }
for name in pairs(xban.importers) do
table.insert(names, name)
end
minetest.chat_send_player(name,
("[xban] Known importers: %s"):format(
table.concat(names, ", ")))
return
elseif not xban.importers[params] then
minetest.chat_send_player(name,
("[xban] Unknown importer `%s'"):format(params))
minetest.chat_send_player(name, "[xban] Try `--list'")
return
end
local f = xban.importers[params]
local ok, err = f()
if ok then
minetest.chat_send_player(name,
"[xban] Import successfull")
else
minetest.chat_send_player(name,
("[xban] Import failed: %s"):format(err))
end
end,
})

View File

@ -1,32 +0,0 @@
## Extended Ban Mod API
### ban_player
`xban.ban_player(player_or_ip, source, expires, reason)`
Ban a player and all of his/her alternative names and IPs.
#### Arguments:
* `player_or_ip` - Player to search for and ban. See note 1 below.
* `source` - Source of the ban. See note 2 below.
* `expires` - Time at which the ban expires. If nil, ban is permanent.
* `reason` - Reason for ban.
### unban_player
`xban.unban_player(player_or_ip, source)`
Unban a player and all of his/her alternative names and IPs.
#### Arguments:
* `player_or_ip` - Player to search for and unban.
* `source` - Source of the ban. See note 2 below.
### Notes
* 1: If player is currently online, all his accounts are kicked.
* 2: Mods using the xban API are advised to use the `"modname:source"`
format for `source` (for example: `"anticheat:main"`).

View File

@ -1,45 +0,0 @@
Database is a regular Lua script that returns a table.
Table has a single named field `timestamp' containing the time_t the
DB was last saved. It's not used in the mod and is only meant for
external use (I don't find filesystem timestamps too reliable).
Next is a simple array (number indices) of entries.
Each entry contains following fields:
[1] = {
-- Names/IPs associated with this entry
names = {
["foo"] = true,
["bar"] = true,
["123.45.67.89"] = true,
},
banned = true, -- Whether this user is banned
-- Other fields do not apply if false
time = 12341234, -- Time of last ban (*1)
expires = 43214321 -- Time at which ban expires (*2)
-- If nil, permanent ban
reason = "asdf", -- Reason for ban
source = "qwerty", -- Source of ban (*2)
record = {
[1] = {
source = "asdf",
reason = "qwerty",
time = 12341234,
expires = 43214321,
},
[1] = {
source = "asdf",
reason = "Unbanned", -- When unbanned
time = 12341234,
},
},
}
Notes:
(*1) All times are expressed in whatever unit `os.time()' uses
(`time_t' on most (all?) systems).
(*2) Mods using the xban API are advised to use the "modname:source"
format for `source' (for example: "anticheat:main").

View File

@ -1,90 +0,0 @@
local FORMNAME = "xban2:main"
local states = { }
local table_insert, table_concat =
table.insert, table.concat
local ESC = minetest.formspec_escape
local function make_fs(name)
local state = states[name]
if not state then return end
local list, index, filter = state.list, state.index, state.filter
if index > #list then
index = #list
end
local fs = {
"size[10,8]",
"label[0.5,0.6;Filter]",
"field[1.5,0.5;6,2;filter;;"..ESC(filter).."]",
"button[7.5,0.5;2,1;search;Search]",
}
table_insert(fs,
("textlist[0.5,2;3,5.5;player;%s;%d;0]"):
format(table_concat(list, ","), index))
local record_name = list[index]
if record_name then
local record, err = xban.get_record(record_name)
if record then
local reclist = { }
for _, r in ipairs(record) do
table_insert(reclist, ESC(r))
end
table_insert(fs,
("textlist[4,2;5,5.5;entry;%s;0;0]"):
format(table_concat(reclist, ",")))
else
table_insert(fs,
"textlist[4,2;5,5.5;entry;"..ESC(err)..";0]")
end
end
return table_concat(fs)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then return end
local name = player:get_player_name()
local state = states[name]
if fields.player then
local t = minetest.explode_textlist_event(fields.player)
if (t.type == "CHG") or (t.type == "DCL") then
state.index = t.index
minetest.show_formspec(name, FORMNAME, make_fs(name))
end
return
end
if fields.search then
local filter = fields.filter or ""
state.filter = filter
local list = { }
state.list = list
for k in pairs(minetest.auth_table) do
if k:find(filter, 1, true) then
table_insert(list, k)
end
end
table.sort(list)
minetest.show_formspec(name, FORMNAME, make_fs(name))
end
end)
minetest.register_chatcommand("xban_gui", {
description = "Show XBan GUI",
params = "",
func = function(name, params)
local state = states[name]
if not state then
state = { index=1, filter="" }
states[name] = state
local list = { }
state.list = list
for k in pairs(minetest.auth_table) do
table_insert(list, k)
end
table.sort(list)
end
minetest.show_formspec(name, FORMNAME, make_fs(name))
end,
})

View File

@ -1,29 +0,0 @@
function xban.importers.minetest()
local f, e = io.open(minetest.get_worldpath().."/ipban.txt")
if not f then
return false, "Unable to open `ipban.txt': "..e
end
for line in f:lines() do
local ip, name = line:match("([^|]+)%|(.+)")
if ip and name then
local entry
entry = xban.find_entry(ip, true)
entry.banned = true
entry.reason = "Banned in `ipban.txt'"
entry.names[name] = true
entry.names[ip] = true
entry.time = os.time()
entry.expires = nil
entry.source = "xban:importer_minetest"
table.insert(entry.record, {
source = entry.source,
reason = entry.reason,
time = entry.time,
expires = nil,
})
end
end
f:close()
return true
end

View File

@ -1,33 +0,0 @@
function xban.importers.v1()
local f, e = io.open(minetest.get_worldpath().."/players.iplist")
if not f then
return false, "Unable to open `players.iplist': "..e
end
for line in f:lines() do
local list = line:split("|")
if #list >= 2 then
local banned = (list[1]:sub(1, 1) == "!")
local entry
entry = xban.find_entry(list[1], true)
entry.banned = banned
for _, name in ipairs(list) do
entry.names[name] = true
end
if banned then
entry.reason = "Banned in `players.iplist'"
entry.time = os.time()
entry.expires = nil
entry.source = "xban:importer_v1"
table.insert(entry.record, {
source = entry.source,
reason = entry.reason,
time = entry.time,
expires = nil,
})
end
end
end
f:close()
return true
end

View File

@ -1,35 +0,0 @@
function xban.importers.v2()
return pcall(function()
local f, e = io.open(minetest.get_worldpath().."/players.iplist.v2")
if not f then
error("Unable to open `players.iplist.v2': "..e)
end
local text = f:read("*a")
f:close()
local db = minetest.deserialize(text)
for _, e in ipairs(db) do
for name in pairs(e.names) do
local entry = xban.find_entry(name, true)
if entry.source ~= "xban:importer_v2" then
for nm in pairs(e.names) do
entry.names[nm] = true
end
if e.banned then
entry.banned = true
entry.reason = e.banned
entry.source = "xban:importer_v2"
entry.time = e.time
entry.expires = e.expires
table.insert(entry.record, {
source = entry.source,
reason = entry.reason,
time = entry.time,
expires = entry.expires,
})
end
end
end
end
end)
end

View File

@ -1,327 +0,0 @@
xban = { MP = minetest.get_modpath(minetest.get_current_modname()) }
dofile(xban.MP.."/serialize.lua")
local db = { }
local tempbans = { }
local DEF_SAVE_INTERVAL = 300 -- 5 minutes
local DEF_DB_FILENAME = minetest.get_worldpath().."/xban.db"
local DB_FILENAME = minetest.setting_get("xban.db_filename")
local SAVE_INTERVAL = tonumber(
minetest.setting_get("xban.db_save_interval")) or DEF_SAVE_INTERVAL
if (not DB_FILENAME) or (DB_FILENAME == "") then
DB_FILENAME = DEF_DB_FILENAME
end
local function make_logger(level)
return function(text, ...)
minetest.log(level, "[xban] "..text:format(...))
end
end
local ACTION = make_logger("action")
local INFO = make_logger("info")
local WARNING = make_logger("warning")
local ERROR = make_logger("error")
local unit_to_secs = {
s = 1, m = 60, h = 3600,
D = 86400, W = 604800, M = 2592000, Y = 31104000,
[""] = 1,
}
local function parse_time(t) --> secs
local secs = 0
for num, unit in t:gmatch("(%d+)([smhDWMY]?)") do
secs = secs + (tonumber(num) * (unit_to_secs[unit] or 1))
end
return secs
end
function xban.find_entry(player, create) --> entry, index
for index, e in ipairs(db) do
for name in pairs(e.names) do
if name == player then
return e, index
end
end
end
if create then
print(("Created new entry for `%s'"):format(player))
local e = {
names = { [player]=true },
banned = false,
record = { },
}
table.insert(db, e)
return e, #db
end
return nil
end
function xban.get_info(player) --> ip_name_list, banned, last_record
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
end
return e.names, e.banned, e.record[#e.record]
end
function xban.ban_player(player, source, expires, reason) --> bool, err
local e = xban.find_entry(player, true)
local rec = {
source = source,
time = os.time(),
expires = expires,
reason = reason,
}
table.insert(e.record, rec)
e.names[player] = true
local pl = minetest.get_player_by_name(player)
if pl then
local ip = minetest.get_player_ip(player)
if ip then
e.names[ip] = true
end
e.last_pos = pl:getpos()
end
e.reason = reason
e.time = rec.time
e.expires = expires
e.banned = true
local msg
local date = (expires and os.date("%c", expires)
or "the end of time")
if expires then
table.insert(tempbans, e)
msg = ("Banned: Expires: %s, Reason: %s"):format(date, reason)
else
msg = ("Banned: Reason: %s"):format(reason)
end
for nm in pairs(e.names) do
minetest.kick_player(nm, msg)
end
ACTION("%s bans %s until %s for reason: %s", source, player,
date, reason)
ACTION("Banned Names/IPs: %s", table.concat(e.names, ", "))
return true
end
function xban.unban_player(player, source) --> bool, err
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
end
local rec = {
source = source,
time = os.time(),
reason = "Unbanned",
}
table.insert(e.record, rec)
e.banned = false
e.reason = nil
e.expires = nil
e.time = nil
ACTION("%s unbans %s", source, player)
ACTION("Unbanned Names/IPs: %s", table.concat(e.names, ", "))
return true
end
function xban.get_record(player)
local e = xban.find_entry(player)
if not e then
return nil, ("No entry for `%s'"):format(player)
elseif (not e.record) or (#e.record == 0) then
return nil, ("`%s' has no ban records"):format(player)
end
local record = { }
for _, rec in ipairs(e.record) do
local msg = rec.reason or "No reason given."
if rec.expires then
msg = msg..(", Expires: %s"):format(os.date("%c", e.expires))
end
if rec.source then
msg = msg..", Source: "..rec.source
end
table.insert(record, ("[%s]: %s"):format(os.date("%c", e.time), msg))
end
local last_pos
if e.last_pos then
last_pos = ("User was last seen at %s"):format(
minetest.pos_to_string(e.last_pos))
end
return record, last_pos
end
minetest.register_on_prejoinplayer(function(name, ip)
local e = xban.find_entry(name) or xban.find_entry(ip)
if not e then return end
if e.banned then
local date = (e.expires and os.date("%c", e.expires)
or "the end of time")
return ("Banned: Expires: %s, Reason: %s"):format(
date, e.reason)
end
end)
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
local e = xban.find_entry(name)
local ip = minetest.get_player_ip(name)
if not e then
if ip then
e = xban.find_entry(ip, true)
else
return
end
end
e.names[name] = true
if ip then
e.names[ip] = true
end
e.last_seen = os.time()
end)
minetest.register_chatcommand("xban", {
description = "XBan a player",
params = "<player> <reason>",
privs = { ban=true },
func = function(name, params)
local plname, reason = params:match("(%S+)%s+(.+)")
if not (plname and reason) then
return false, "Usage: /xban <player> <reason>"
end
xban.ban_player(plname, name, nil, reason)
return true, ("Banned %s."):format(plname)
end,
})
minetest.register_chatcommand("xtempban", {
description = "XBan a player temporarily",
params = "<player> <time> <reason>",
privs = { ban=true },
func = function(name, params)
local plname, time, reason = params:match("(%S+)%s+(%S+)%s+(.+)")
if not (plname and time and reason) then
return false, "Usage: /xtempban <player> <time> <reason>"
end
time = parse_time(time)
if time < 60 then
return false, "You must ban for at least 60 seconds."
end
local expires = os.time() + time
xban.ban_player(plname, name, expires, reason)
return true, ("Banned %s until %s."):format(plname, os.date("%c", expires))
end,
})
minetest.register_chatcommand("xunban", {
description = "XUnBan a player",
params = "<player_or_ip>",
privs = { ban=true },
func = function(name, params)
local plname = params:match("%S+")
if not plname then
minetest.chat_send_player(name,
"Usage: /xunban <player_or_ip>")
return
end
local ok, e = xban.unban_player(plname, name)
return ok, ok and ("Unbanned %s."):format(plname) or e
end,
})
minetest.register_chatcommand("xban_record", {
description = "Show the ban records of a player",
params = "<player_or_ip>",
privs = { ban=true },
func = function(name, params)
local plname = params:match("%S+")
if not plname then
return false, "Usage: /xban_record <player_or_ip>"
end
local record, last_pos = xban.get_record(plname)
if not record then
local err = last_pos
minetest.chat_send_player(name, "[xban] "..err)
return
end
for _, e in ipairs(record) do
minetest.chat_send_player(name, "[xban] "..e)
end
if last_pos then
minetest.chat_send_player(name, "[xban] "..last_pos)
end
return true, "Record listed."
end,
})
local function check_temp_bans()
minetest.after(60, check_temp_bans)
local to_rm = { }
local now = os.time()
for i, e in ipairs(tempbans) do
if e.expires and (e.expires <= now) then
table.insert(to_rm, i)
e.banned = false
e.expires = nil
e.reason = nil
e.time = nil
end
end
for _, i in ipairs(to_rm) do
table.remove(tempbans, i)
end
end
local function save_db()
minetest.after(SAVE_INTERVAL, save_db)
local f, e = io.open(DB_FILENAME, "wt")
db.timestamp = os.time()
if f then
local ok = f:write(xban.serialize(db))
WARNING("Unable to save database: %s", "Write failed")
end
if f then f:close() end
return
end
local function load_db()
local f, e = io.open(DB_FILENAME, "rt")
if not f then
WARNING("Unable to load database: %s", e)
return
end
local cont = f:read("*a")
if not cont then
WARNING("Unable to load database: %s", "Read failed")
return
end
local t = minetest.deserialize(cont)
if not t then
WARNING("Unable to load database: %s",
"Deserialization failed")
return
end
db = t
tempbans = { }
for _, entry in ipairs(db) do
if entry.banned and entry.expires then
table.insert(tempbans, entry)
end
end
end
minetest.register_on_shutdown(save_db)
minetest.after(SAVE_INTERVAL, save_db)
load_db()
xban.db = db
minetest.after(1, check_temp_bans)
dofile(xban.MP.."/dbimport.lua")
dofile(xban.MP.."/gui.lua")

View File

@ -1,31 +0,0 @@
local function repr(x)
if type(x) == "string" then
return ("%q"):format(x)
else
return tostring(x)
end
end
local function my_serialize_2(t, level)
level = level or 0
local lines = { }
local indent = ("\t"):rep(level)
for k, v in pairs(t) do
local typ = type(v)
if typ == "table" then
table.insert(lines,
indent..("[%s] = {\n"):format(repr(k))
..my_serialize_2(v, level + 1).."\n"
..indent.."},")
else
table.insert(lines,
indent..("[%s] = %s,"):format(repr(k), repr(v)))
end
end
return table.concat(lines, "\n")
end
function xban.serialize(t)
return "return {\n"..my_serialize_2(t, 1).."\n}"
end

View File

@ -1,8 +1,16 @@
xdecor.box = {
slab_y = function(height, shift) return { -0.5, -0.5+(shift or 0), -0.5, 0.5, -0.5+height+(shift or 0), 0.5 } end,
slab_z = function(depth) return { -0.5, -0.5, -0.5+depth, 0.5, 0.5, 0.5 } end,
bar_y = function(radius) return {-radius, -0.5, -radius, radius, 0.5, radius} end,
cuboid = function(radius_x, radius_y, radius_z) return {-radius_x, -radius_y, -radius_z, radius_x, radius_y, radius_z} end
slab_y = function(height, shift)
return { -0.5, -0.5+(shift or 0), -0.5, 0.5, -0.5+height+(shift or 0), 0.5 }
end,
slab_z = function(depth)
return { -0.5, -0.5, -0.5+depth, 0.5, 0.5, 0.5 }
end,
bar_y = function(radius)
return { -radius, -0.5, -radius, radius, 0.5, radius }
end,
cuboid = function(radius_x, radius_y, radius_z)
return { -radius_x, -radius_y, -radius_z, radius_x, radius_y, radius_z }
end
}
xdecor.nodebox = {
@ -10,10 +18,27 @@ xdecor.nodebox = {
null = { type = "fixed", fixed = { 0, 0, 0, 0, 0, 0 } }
}
xdecor.pixelnodebox = function(size, boxes)
local fixed = {}
for _, box in pairs(boxes) do
local x, y, z, w, h, l = unpack(box)
fixed[#fixed+1] = {
(x / size) - 0.5,
(y / size) - 0.5,
(z / size) - 0.5,
((x + w) / size) - 0.5,
((y + h) / size) - 0.5,
((z + l) / size) - 0.5
}
end
return { type = "fixed", fixed = fixed }
end
local mt = {}
mt.__index = function(table, key)
local ref = xdecor.box[key]
local ref_type = type(ref)
if ref_type == "function" then
return function(...)
return { type = "fixed", fixed = ref(...) }
@ -21,8 +46,11 @@ mt.__index = function(table, key)
elseif ref_type == "table" then
return { type = "fixed", fixed = ref }
elseif ref_type == "nil" then
error(key .. "could not be found among nodebox presets and functions")
error(key.."could not be found among nodebox presets and functions")
end
error("unexpected datatype " .. tostring(type(ref)) .. " while looking for " .. key)
error("unexpected datatype "..tostring(type(ref)).." while looking for "..key)
end
setmetatable(xdecor.nodebox, mt)

View File

@ -1,40 +1,42 @@
local default_can_dig = function(pos, _)
--[[ local default_can_dig = function(pos, _)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main")
end
local xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
end --]]
xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
local default_inventory_size = 32
local default_inventory_formspecs = {
["8"] = "size[8,6]"..xbg..
"list[context;main;0,0;8,1;]"..
"list[current_player;main;0,2;8,4;]"..
"listring[]"..
default.get_hotbar_bg(0,2),
["8"] = [[ size[8,6]
list[context;main;0,0;8,1;]
list[current_player;main;0,2;8,4;]
listring[current_player;main]
listring[context;main] ]]
..default.get_hotbar_bg(0,2),
["16"] = "size[8,7]"..xbg..
"list[context;main;0,0;8,2;]"..
"list[current_player;main;0,3;8,4;]"..
"listring[]"..
default.get_hotbar_bg(0,3),
["16"] = [[ size[8,7]
list[context;main;0,0;8,2;]
list[current_player;main;0,3;8,4;]
listring[current_player;main]
listring[context;main] ]]
..default.get_hotbar_bg(0,3),
["24"] = "size[8,7.5]"..xbg..
"list[context;main;0,0;8,3;]"..
"list[current_player;main;0,3.5;8,1;]"..
"list[current_player;main;0,4.75;8,3;8]"..
"listring[current_name;main]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0,3.5),
["24"] = [[ size[8,8]
list[context;main;0,0;8,3;]
list[current_player;main;0,4;8,4;]
listring[current_player;main]
listring[context;main]" ]]
..default.get_hotbar_bg(0,4),
["32"] = "size[8,9]"..xbg..
"list[context;main;0,0.3;8,4;]"..
"list[current_player;main;0,4.85;8,1;]"..
"listring[]"..
"list[current_player;main;0,6.08;8,3;8]"..
default.get_hotbar_bg(0, 4.85)
["32"] = [[ size[8,9]
list[context;main;0,0.3;8,4;]
list[current_player;main;0,4.85;8,1;]
list[current_player;main;0,6.08;8,3;8]
listring[current_player;main]
listring[context;main] ]]
..default.get_hotbar_bg(0,4.85)
}
local function get_formspec_by_size(size)
@ -42,14 +44,34 @@ local function get_formspec_by_size(size)
return formspec or default_inventory_formspecs
end
local function drop_stuff()
return function(pos, oldnode, oldmetadata, digger)
local meta = minetest.get_meta(pos)
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for i=1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
local p = {
x = pos.x + math.random(0,5) / 5 - 0.5,
y = pos.y,
z = pos.z + math.random(0,5) / 5 - 0.5
}
minetest.add_item(p, stack)
end
end
end
end
function xdecor.register(name, def)
def.drawtype = def.drawtype or (def.node_box and "nodebox")
def.paramtype = def.paramtype or "light"
def.sounds = def.sounds or default.node_sound_defaults()
if not (def.drawtype == "glasslike_framed" or
def.drawtype == "glasslike_framed_optional" or def.drawtype == "plantlike" or
def.drawtype == "signlike" or def.drawtype == "normal") then
if not (def.drawtype == "normal" or def.drawtype == "signlike" or
def.drawtype == "plantlike" or def.drawtype == "glasslike_framed" or
def.drawtype == "glasslike_framed_optional") then
def.paramtype2 = def.paramtype2 or "facedir"
end
@ -68,13 +90,14 @@ function xdecor.register(name, def)
if infotext then
meta:set_string("infotext", infotext)
end
local size = inventory.size or default_inventory_size
local inv = meta:get_inventory()
inv:set_size("main", size)
meta:set_string("formspec", inventory.formspec or get_formspec_by_size(size))
meta:set_string("formspec", (inventory.formspec or get_formspec_by_size(size))..xbg)
end
def.can_dig = def.can_dig or default_can_dig
def.after_dig_node = def.after_dig_node or drop_stuff()
--def.can_dig = def.can_dig or default_can_dig
elseif infotext and not def.on_construct then
def.on_construct = function(pos)
local meta = minetest.get_meta(pos)
@ -82,5 +105,5 @@ function xdecor.register(name, def)
end
end
minetest.register_node("xdecor:".. name, def)
minetest.register_node("xdecor:"..name, def)
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 B

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 B

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 B

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 B

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 300 B

View File

@ -1,195 +1,418 @@
local worktable = {}
local xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
screwdriver = screwdriver or {}
local nodes = { -- Nodes allowed to be cut.
"default:wood", "default:junglewood", "default:pine_wood", "default:acacia_wood",
"default:tree", "default:jungletree", "default:pine_tree", "default:acacia_tree",
"default:pinewood", "default:pinetree",
"default:cobble", "default:mossycobble", "default:desert_cobble",
"default:stone", "default:sandstone", "default:desert_stone", "default:obsidian",
"default:stonebrick", "default:sandstonebrick", "default:desert_stonebrick", "default:obsidianbrick",
"default:coalblock", "default:copperblock", "default:steelblock", "default:goldblock",
"default:bronzeblock", "default:mese", "default:diamondblock",
"default:brick", "default:cactus", "default:ice", "default:meselamp",
"default:glass", "default:obsidian_glass", "default:gravel",
"default:leaves", "default:dirt", "default:snowblock", "default:sand",
"default:desert_sand", "default:clay",
-- Nodes allowed to be cut.
-- Registration format: [mod name] = [[ node names ]].
worktable.nodes = {
["default"] = [[
wood junglewood pine_wood acacia_wood
tree jungletree pine_tree acacia_tree
pinewood pinetree
cobble mossycobble desert_cobble
stone sandstone desert_stone obsidian
stonebrick sandstonebrick desert_stonebrick obsidianbrick
coalblock copperblock steelblock goldblock
bronzeblock mese diamondblock
brick cactus ice meselamp glass obsidian_glass
clay desert_sand sand snowblock dirt
leaves gravel
]],
"farming:straw",
["xdecor"] = [[
coalstone_tile desertstone_tile stone_rune stone_tile
cactusbrick hard_clay packed_ice moonbrick
woodframed_glass wood_tile
]],
"bones:bones",
["oresplus"] = [[
emerald_block glowstone
]],
"oresplus:emerald_block", "oresplus:glowstone",
["farming"] = "straw",
"xdecor:coalstone_tile", "xdecor:desertstone_tile", "xdecor:stone_rune", "xdecor:stone_tile",
"xdecor:hard_clay", "xdecor:packed_ice", "xdecor:moonbrick",
"xdecor:woodframed_glass", "xdecor:wood_tile",
["bones"] = "bones",
"wool:black", "wool:brown", "wool:dark_green", "wool:green",
"wool:magenta", "wool:pink", "wool:violet", "wool:yellow",
"wool:blue", "wool:cyan", "wool:dark_grey", "wool:grey",
"wool:orange", "wool:red", "wool:white",
["wool"] = [[
black brown dark_green green
magenta pink violet yellow
blue cyan dark_grey grey
orange red white
]],
"caverealms:glow_crystal", "caverealms:glow_emerald", "caverealms:glow_mese",
"caverealms:glow_ruby", "caverealms:glow_amethyst", "caverealms:glow_ore",
"caverealms:glow_emerald_ore", "caverealms:glow_ruby_ore", "caverealms:glow_amethyst_ore",
"caverealms:thin_ice", "caverealms:salt_crystal", "caverealms:mushroom_cap",
"caverealms:mushroom_stem", "caverealms:stone_with_salt", "caverealms:hot_cobble",
"caverealms:glow_obsidian", "caverealms:glow_obsidian_2", "caverealms:coal_dust"
["caverealms"] = [[
glow_crystal glow_emerald glow_mese
glow_ruby glow_amethyst glow_ore
glow_emerald_ore glow_ruby_ore glow_amethyst_ore
thin_ice salt_crystal mushroom_cap
mushroom_stem stone_with_salt hot_cobble
glow_obsidian glow_obsidian_2 coal_dust
]]
}
local def = { -- Nodebox name, anzhal, definition.
{"nanoslab", 16, {-.5,-.5,-.5,0,-.4375,0}},
{"micropanel", 16, {-.5,-.5,-.5,.5,-.4375,0}},
{"microslab", 8, {-.5,-.5,-.5,.5,-.4375,.5}},
{"thinstair", 8, {{-.5,-.0625,-.5,.5,0,0},{-.5,.4375,0,.5,.5,.5}}},
{"cube", 4, {-.5,-.5,0,0,0,.5}},
{"panel", 4, {-.5,-.5,-.5,.5,0,0}},
{"slab", 2, {-.5,-.5,-.5,.5,0,.5}},
{"doublepanel", 2, {{-.5,-.5,-.5,.5,0,0},{-.5,0,0,.5,.5,.5}}},
{"halfstair", 2, {{-.5,-.5,-.5,0,0,.5},{-.5,0,0,0,.5,.5}}},
{"outerstair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,0,.5,.5}}},
{"stair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,.5,.5,.5}}},
{"innerstair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,.5,.5,.5},{-.5,0,-.5,0,.5,0}}}
-- Nodeboxes definitions.
worktable.defs = {
-- Name Yield X Y Z W H L
{"nanoslab", 16, { 0, 0, 0, 8, 1, 8 }},
{"micropanel", 16, { 0, 0, 0, 16, 1, 8 }},
{"microslab", 8, { 0, 0, 0, 16, 1, 16 }},
{"thinstair", 8, { 0, 7, 0, 16, 1, 8 },
{ 0, 15, 8, 16, 1, 8 }},
{"cube", 4, { 0, 0, 0, 8, 8, 8 }},
{"panel", 4, { 0, 0, 0, 16, 8, 8 }},
{"slab", 2, nil },
{"doublepanel", 2, { 0, 0, 0, 16, 8, 8 },
{ 0, 8, 8, 16, 8, 8 }},
{"halfstair", 2, { 0, 0, 0, 8, 8, 16 },
{ 0, 8, 8, 8, 8, 8 }},
{"outerstair", 1, { 0, 0, 0, 16, 8, 16 },
{ 0, 8, 8, 8, 8, 8 }},
{"stair", 1, nil },
{"innerstair", 1, { 0, 0, 0, 16, 8, 16 },
{ 0, 8, 8, 16, 8, 8 },
{ 0, 8, 0, 8, 8, 8 }}
}
function worktable.crafting(pos)
local meta = minetest.get_meta(pos)
return "size[8,7;]"..xbg..
"list[current_player;main;0,3.3;8,4;]"..
"image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
"list[current_player;craft;2,0;3,3;]"..
"list[current_player;craftpreview;6,1;1,1;]"
function worktable.get_recipe(item)
if item:find("^group:") then
if item:find("wool$") or item:find("dye$") then
item = item:sub(7)..":white"
elseif minetest.registered_items["default:"..item:sub(7)] then
item = item:gsub("group:", "default:")
else
for node, def in pairs(minetest.registered_items) do
if def.groups[item:match("[^,:]+$")] then
item = node
end
end
end
end
return item
end
function worktable.storage(pos)
local inv = minetest.get_meta(pos):get_inventory()
local f = "size[8,7]"..xbg..
"list[context;storage;0,0;8,2;]list[current_player;main;0,3.25;8,4;]"
inv:set_size("storage", 8*2)
return f
function worktable.craftguide_formspec(meta, pagenum, item, recipe_num, filter, tab_id)
local inv_size = meta:get_int("inv_size")
local npp, i, s = 8*3, 0, 0
local pagemax = math.floor((inv_size - 1) / npp + 1)
if pagenum > pagemax then
pagenum = 1
elseif pagenum == 0 then
pagenum = pagemax
end
local formspec = [[ size[8,6.6;]
tablecolumns[color;text;color;text]
tableoptions[background=#00000000;highlight=#00000000;border=false]
button[5.5,0;0.7,1;prev;<]
button[7.3,0;0.7,1;next;>]
button[4,0.2;0.7,0.5;search;?]
button[4.6,0.2;0.7,0.5;clearfilter;X]
button[0,0;1.5,1;backcraft;< Back]
tooltip[search;Search]
tooltip[clearfilter;Reset] ]]
.."tabheader[0,0;tabs;All,Nodes,Tools,Items;"..tostring(tab_id)..";true;false]"..
"table[6.1,0.2;1.1,0.5;pagenum;#FFFF00,"..tostring(pagenum)..
",#FFFFFF,/ "..tostring(pagemax).."]"..
"field[1.8,0.32;2.6,1;filter;;"..filter.."]"..xbg
for _, name in pairs(worktable.craftguide_main_list(meta, filter, tab_id)) do
if s < (pagenum - 1) * npp then
s = s + 1
else
if i >= npp then break end
formspec = formspec.."item_image_button["..(i%8)..","..
(math.floor(i/8)+1)..";1,1;"..name..";"..name..";]"
i = i + 1
end
end
if item and minetest.registered_items[item] then
--print(dump(minetest.get_all_craft_recipes(item)))
local items_num = #minetest.get_all_craft_recipes(item)
if recipe_num > items_num then recipe_num = 1 end
if items_num > 1 then
formspec = formspec.."button[0,6;1.6,1;alternate;Alternate]"..
"label[0,5.5;Recipe "..recipe_num.." of "..items_num.."]"
end
local type = minetest.get_all_craft_recipes(item)[recipe_num].type
if type == "cooking" then
formspec = formspec.."image[3.75,4.6;0.5,0.5;default_furnace_fire_fg.png]"
end
local items = minetest.get_all_craft_recipes(item)[recipe_num].items
local width = minetest.get_all_craft_recipes(item)[recipe_num].width
local yield = minetest.get_all_craft_recipes(item)[recipe_num].output:match("%s(%d+)") or ""
if width == 0 then width = math.min(3, #items) end
local rows = math.ceil(table.maxn(items) / width)
local function is_group(item)
if item:find("^group:") then return "G" end
return ""
end
for i, v in pairs(items) do
formspec = formspec.."item_image_button["..((i-1) % width + 4.5)..","..
(math.floor((i-1) / width + (6 - math.min(2, rows))))..";1,1;"..
worktable.get_recipe(v)..";"..worktable.get_recipe(v)..";"..is_group(v).."]"
end
formspec = formspec.."item_image_button[2.5,5;1,1;"..item..";"..item..";"..yield.."]"..
"image[3.5,5;1,1;gui_furnace_arrow_bg.png^[transformR90]"
end
meta:set_string("formspec", formspec)
end
local function tab_category(tab_id)
local id_category = {
minetest.registered_items,
minetest.registered_nodes,
minetest.registered_tools,
minetest.registered_craftitems
}
return id_category[tab_id] or id_category[1]
end
function worktable.craftguide_main_list(meta, filter, tab_id)
local items_list = {}
for name, def in pairs(tab_category(tab_id)) do
if not (def.groups.not_in_creative_inventory == 1) and
minetest.get_craft_recipe(name).items and
def.description and def.description ~= "" and
(not filter or def.name:find(filter, 1, true)) then
items_list[#items_list+1] = name
end
end
meta:set_int("inv_size", #items_list)
table.sort(items_list)
return items_list
end
worktable.formspecs = {
crafting = function(meta)
meta:set_string("formspec", [[ size[8,7;]
image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
image[0.06,2.12;0.8,0.8;trash_icon.png]
button[0,0;1.5,1;back;< Back]
button[0,0.85;1.5,1;craftguide;Guide]
list[context;trash;0,2;1,1;]
list[current_player;main;0,3.3;8,4;]
list[current_player;craft;2,0;3,3;]
list[current_player;craftpreview;6,1;1,1;]
listring[current_player;main]
listring[current_player;craft] ]]
..xbg..default.get_hotbar_bg(0,3.3))
end,
storage = function(meta)
meta:set_string("formspec", [[ size[8,7]
image[7.06,0.12;0.8,0.8;trash_icon.png]
list[context;trash;7,0;1,1;]
list[context;storage;0,1;8,2;]
list[current_player;main;0,3.25;8,4;]
listring[context;storage]
listring[current_player;main]
button[0,0;1.5,1;back;< Back] ]]
..xbg..default.get_hotbar_bg(0,3.25))
end,
main = function(meta)
meta:set_string("formspec", [[ size[8,7;]
label[0.9,1.23;Cut]
label[0.9,2.23;Repair]
box[-0.05,1;2.05,0.9;#555555]
box[-0.05,2;2.05,0.9;#555555]
image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
image[0,1;1,1;worktable_saw.png]
image[0,2;1,1;worktable_anvil.png]
image[3,2;1,1;hammer_layout.png]
list[context;input;2,1;1,1;]
list[context;tool;2,2;1,1;]
list[context;hammer;3,2;1,1;]
list[context;forms;4,0;4,3;]
list[current_player;main;0,3.25;8,4;]
button[0,0;2,1;craft;Crafting]
button[2,0;2,1;storage;Storage] ]]
..xbg..default.get_hotbar_bg(0,3.25))
end
}
function worktable.construct(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("forms", 4*3)
inv:set_size("input", 1)
inv:set_size("tool", 1)
inv:set_size("trash", 1)
inv:set_size("input", 1)
inv:set_size("hammer", 1)
local formspec = "size[8,7;]"..xbg..
"list[context;forms;4,0;4,3;]" ..
"label[0.95,1.23;Cut]box[-0.05,1;2.05,0.9;#555555]"..
"image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
"label[0.95,2.23;Repair]box[-0.05,2;2.05,0.9;#555555]"..
"image[0,1;1,1;xdecor_saw.png]image[0,2;1,1;xdecor_anvil.png]"..
"image[3,2;1,1;hammer_layout.png]"..
"list[current_name;input;2,1;1,1;]"..
"list[current_name;tool;2,2;1,1;]list[current_name;hammer;3,2;1,1;]"..
"button[0,0;2,1;craft;Crafting]"..
"button[2,0;2,1;storage;Storage]"..
"list[current_player;main;0,3.25;8,4;]"
meta:set_string("formspec", formspec)
inv:set_size("forms", 4*3)
inv:set_size("storage", 8*2)
meta:set_string("infotext", "Work Table")
worktable.formspecs.main(meta)
end
function worktable.fields(pos, _, fields, sender)
local player = sender:get_player_name()
function worktable.fields(pos, _, fields)
if fields.quit then return end
local meta = minetest.get_meta(pos)
local formspec = meta:to_table().fields.formspec
local filter = formspec:match("filter;;([%w_:]+)") or ""
local pagenum = tonumber(formspec:match("#FFFF00,(%d+)")) or 1
local tab_id = tonumber(formspec:match("tabheader%[.*;(%d+)%;.*%]")) or 1
if fields.back then
worktable.formspecs.main(meta)
elseif fields.craft or fields.backcraft then
worktable.formspecs.crafting(meta)
elseif fields.storage then
worktable.formspecs.storage(meta)
elseif fields.craftguide or fields.clearfilter then
worktable.craftguide_main_list(meta, nil, tab_id)
worktable.craftguide_formspec(meta, 1, nil, 1, "", tab_id)
elseif fields.alternate then
local item = formspec:match("item_image_button%[.*;([%w_:]+);.*%]") or ""
local recipe_num = tonumber(formspec:match("Recipe%s(%d+)")) or 1
recipe_num = recipe_num + 1
worktable.craftguide_formspec(meta, pagenum, item, recipe_num, filter, tab_id)
elseif fields.search then
worktable.craftguide_main_list(meta, fields.filter:lower(), tab_id)
worktable.craftguide_formspec(meta, 1, nil, 1, fields.filter:lower(), tab_id)
elseif fields.tabs then
worktable.craftguide_main_list(meta, filter, tonumber(fields.tabs))
worktable.craftguide_formspec(meta, 1, nil, 1, filter, tonumber(fields.tabs))
elseif fields.prev or fields.next then
if fields.prev then
pagenum = pagenum - 1
else
pagenum = pagenum + 1
end
worktable.craftguide_formspec(meta, pagenum, nil, 1, filter, tab_id)
else
for item in pairs(fields) do
if item:match(".-:") and minetest.get_craft_recipe(item).items then
worktable.craftguide_formspec(meta, pagenum, item, 1, filter, tab_id)
end
end
end
end
function worktable.dig(pos)
local inv = minetest.get_meta(pos):get_inventory()
if fields.storage then
minetest.show_formspec(player, "", worktable.storage(pos))
end
if fields.craft then
minetest.show_formspec(player, "", worktable.crafting(pos))
end
return inv:is_empty("input") and inv:is_empty("hammer") and
inv:is_empty("tool") and inv:is_empty("storage")
end
function worktable.dig(pos, _)
function worktable.allowed(mod, node)
if not mod then return end
for it in mod:gmatch("[%w_]+") do
if it == node then return true end
end
return false
end
local function trash_delete(pos)
local inv = minetest.get_meta(pos):get_inventory()
if not inv:is_empty("input") or not inv:is_empty("hammer") or not
inv:is_empty("tool") or not inv:is_empty("storage") then
return false
end
return true
minetest.after(0, function()
inv:set_stack("trash", 1, "")
end)
end
function worktable.put(_, listname, _, stack, _)
local stn = stack:get_name()
local count = stack:get_count()
local mat = table.concat(nodes)
function worktable.put(pos, listname, _, stack)
local stackname = stack:get_name()
local mod, node = stackname:match("(.*):(.*)")
local allowed_tools = "pick, axe, shovel, sword, hoe, armor"
if listname == "forms" then return 0 end
if listname == "input" then
if mat:match(stn) then return count end
for v in allowed_tools:gmatch("[%w_]+") do
if listname == "tool" and stack:get_wear() > 0 and stackname:find(v) then
return stack:get_count()
end
end
if (listname == "input" and worktable.allowed(worktable.nodes[mod], node)) or
(listname == "hammer" and stackname == "xdecor:hammer") or
listname == "storage" or listname == "trash" then
if listname == "trash" then trash_delete(pos) end
return stack:get_count()
end
return 0
end
function worktable.take(_, listname, _, stack, player)
if listname == "forms" then
local inv = player:get_inventory()
if inv:room_for_item("main", stack:get_name()) then
return -1
end
return 0
end
if listname == "hammer" then
if stn ~= "xdecor:hammer" then return 0 end
end
if listname == "tool" then
local tdef = minetest.registered_tools[stn]
local twear = stack:get_wear()
if not (tdef and twear > 0) then return 0 end
end
return count
end
function worktable.take(_, listname, _, stack, _)
if listname == "forms" then return -1 end
return stack:get_count()
end
function worktable.move(_, from_list, _, to_list, _, count, _)
if from_list == "storage" and to_list == "storage" then
return count else return 0 end
function worktable.move(pos, _, _, to_list, _, count)
if to_list == "storage" then
return count
elseif to_list == "trash" then
trash_delete(pos)
return count
end
return 0
end
local function update_inventory(inv, inputstack)
if inv:is_empty("input") then inv:set_list("forms", {}) return end
local output = {}
for _, n in pairs(def) do
local mat = inputstack:get_name()
local input = inv:get_stack("input", 1)
local count = math.min(n[2] * input:get_count(), inputstack:get_stack_max())
output[#output+1] = string.format("%s_%s %d", mat, n[1], count)
function worktable.get_output(inv, input, name)
if inv:is_empty("input") then
inv:set_list("forms", {})
return
end
local output = {}
for _, n in pairs(worktable.defs) do
local count = math.min(n[2] * input:get_count(), input:get_stack_max())
local item = name.."_"..n[1]
if not n[3] then item = "stairs:"..n[1].."_"..name:match(":(.*)") end
output[#output+1] = item.." "..count
end
inv:set_list("forms", output)
end
function worktable.on_put(pos, listname, _, stack, _)
function worktable.on_put(pos, listname, _, stack)
if listname == "input" then
local inv = minetest.get_meta(pos):get_inventory()
update_inventory(inv, stack)
local input = inv:get_stack("input", 1)
worktable.get_output(inv, input, stack:get_name())
end
end
function worktable.on_take(pos, listname, index, stack, _)
function worktable.on_take(pos, listname, index, stack)
local inv = minetest.get_meta(pos):get_inventory()
local input = inv:get_stack("input", 1)
if listname == "input" then
update_inventory(inv, stack)
if stack:get_name() == input:get_name() then
worktable.get_output(inv, input, stack:get_name())
else
inv:set_list("forms", {})
end
elseif listname == "forms" then
local inputstack = inv:get_stack("input", 1)
inputstack:take_item(math.ceil(stack:get_count() / def[index][2]))
inv:set_stack("input", 1, inputstack)
update_inventory(inv, inputstack)
input:take_item(math.ceil(stack:get_count() / worktable.defs[index][2]))
inv:set_stack("input", 1, input)
worktable.get_output(inv, input, input:get_name())
end
end
xdecor.register("worktable", {
description = "Work Table",
groups = {cracky=2, choppy=2},
groups = {cracky=2, choppy=2, oddly_breakable_by_hand=1},
sounds = default.node_sound_wood_defaults(),
tiles = {
"xdecor_worktable_top.png", "xdecor_worktable_top.png",
"xdecor_worktable_sides.png", "xdecor_worktable_sides.png",
"xdecor_worktable_front.png", "xdecor_worktable_front.png"
},
on_rotate = screwdriver.rotate_simple,
can_dig = worktable.dig,
on_construct = worktable.construct,
on_receive_fields = worktable.fields,
@ -200,105 +423,125 @@ xdecor.register("worktable", {
allow_metadata_inventory_move = worktable.move
})
local function description(node, shape)
local desc = node:gsub("%w+:", " "):gsub("_", " "):gsub(" %l", string.upper):sub(2)..
" "..shape:gsub("^%l", string.upper)
return desc
end
for _, d in pairs(worktable.defs) do
for mod, n in pairs(worktable.nodes) do
for name in n:gmatch("[%w_]+") do
local ndef = minetest.registered_nodes[mod..":"..name]
if ndef and d[3] then
local groups, tiles, light = {}, {}, 0
groups.not_in_creative_inventory = 1
local function groups(node)
if node:find("tree") or node:find("wood") or node:find("cactus") then
return {choppy=3, not_in_creative_inventory=1}
end
return {cracky=3, not_in_creative_inventory=1}
end
for k, v in pairs(ndef.groups) do
if k ~= "wood" and k ~= "stone" and k ~= "level" then
groups[k] = v
end
end
local function shady(shape)
if shape == "stair" or shape == "slab" or shape == "innerstair" or
shape == "outerstair" then return false end
return true
end
if #ndef.tiles > 1 and not ndef.drawtype:find("glass") then
tiles = ndef.tiles
else
tiles = {ndef.tiles[1]}
end
local function tiles(node, ndef)
if node:find("glass") then return {node:gsub(":", "_")..".png"} end
return ndef.tiles
end
stairs.register_stair_and_slab(name, mod..":"..name, groups, tiles,
ndef.description.." Stair", ndef.description.." Slab", ndef.sounds)
for _, d in pairs(def) do
for _, n in pairs(nodes) do
local ndef = minetest.registered_nodes[n]
if ndef then
minetest.register_node(":"..n.."_"..d[1], {
description = description(n, d[1]),
if ndef.light_source > 3 then
light = ndef.light_source - 1
minetest.override_item("stairs:slab_"..name, {light_source=light})
minetest.override_item("stairs:stair_"..name, {light_source=light})
end
minetest.register_node(":"..mod..":"..name.."_"..d[1], {
description = ndef.description.." "..d[1]:gsub("^%l", string.upper),
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
light_source = ndef.light_source,
light_source = light,
sounds = ndef.sounds,
tiles = tiles(n, ndef),
groups = groups(n),
node_box = {type = "fixed", fixed = d[3]},
sunlight_propagates = shady(d[1]),
on_place = minetest.rotate_node
tiles = tiles,
groups = groups,
node_box = xdecor.pixelnodebox(16, {d[3], d[4], d[5]}),
sunlight_propagates = true,
on_place = minetest.rotate_node,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local pointed_nodebox = minetest.get_node(pos).name:match("(%w+)$")
local wield_item = clicker:get_wielded_item():get_name()
local player_name = clicker:get_player_name()
local newnode = ""
if minetest.is_protected(pos, player_name) then
minetest.record_protection_violation(pos, player_name)
return
end
local T = {
{"nanoslab", nil, 2},
{"micropanel", nil, 3},
{"cube", nil, 6},
{"cube", "panel", 9},
{"cube", "outerstair", 11},
{"cube", "halfstair", 7},
{"cube", "innerstair", nil},
{"panel", nil, 7},
{"panel", "outerstair", 12},
{"halfstair", nil, 11},
{"halfstair", "outerstair", nil}
}
for _, x in pairs(T) do
if wield_item == mod..":"..name.."_"..x[1] then
if not x[2] then x[2] = x[1] end
if x[2] == pointed_nodebox then
if not x[3] then
newnode = mod..":"..name
else
newnode = mod..":"..name.."_"..worktable.defs[x[3]][1]
end
end
end
end
if clicker:get_player_control().sneak then
if not minetest.registered_nodes[newnode] then return end
minetest.set_node(pos, {name=newnode, param2=node.param2})
else
minetest.item_place_node(ItemStack(wield_item), clicker, pointed_thing)
end
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end
})
end
if n:match("default:") then
minetest.register_alias("xdecor:"..d[1].."_"..n:match(":(.+)"), n.."_"..d[1])
--print("xdecor:"..d[1].."_"..n:match(":(.+)"), n.."_"..d[1])
else
minetest.register_alias("xdecor:"..d[1].."_"..n:gsub(":", "_", 1), n.."_"..d[1])
--print("xdecor:"..d[1].."_"..n:gsub(":", "_", 1), n.."_"..d[1])
if not d[3] then
minetest.register_alias(mod..":"..name.."_"..d[1], "stairs:"..d[1].."_"..name)
end
end
end
-- Register craft recipes and aliases for stairs and slabs.
for _, n in pairs(nodes) do
local bolly = string.gsub(n, "(default_)", "")
minetest.register_alias("stairs:stair_"..bolly, "xdecor:stair_"..bolly)
minetest.register_craft({
output = bolly.."_stair 6",
recipe = {
{n, "", ""},
{n, n, ""},
{n, n, n}
}
})
minetest.register_craft({
output = bolly.."_stair 6",
recipe = {
{"", "", n},
{"", n, n},
{n, n, n}
}
})
minetest.register_alias("stairs:slab_"..bolly, "xdecor:slab_"..bolly)
minetest.register_craft({
output = bolly.."_slab 3",
recipe = {
{"", "", ""},
{"", "", ""},
{n, n, n}
}
})
end
minetest.register_abm({
nodenames = {"xdecor:worktable"},
interval = 3, chance = 1,
action = function(pos, _, _, _)
action = function(pos)
local inv = minetest.get_meta(pos):get_inventory()
local tool = inv:get_stack("tool", 1)
local hammer = inv:get_stack("hammer", 1)
local wear = tool:get_wear()
if tool:is_empty() or hammer:is_empty() or wear == 0 then return end
if tool:is_empty() or hammer:is_empty() or tool:get_wear() == 0 then
return
end
-- Wear : 0-65535 | 0 = new condition.
tool:add_wear(-500)
hammer:add_wear(300)
hammer:add_wear(700)
inv:set_stack("tool", 1, tool)
inv:set_stack("hammer", 1, hammer)
end
})