Add files via upload

main-winbuilds-fork
VoidCosmos 2021-09-22 13:50:26 +05:30 committed by GitHub
parent 157dcc6ec7
commit 0f008823cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 4966 additions and 0 deletions

51
clientmods/mods.conf Normal file
View File

@ -0,0 +1,51 @@
load_mod_world = true
load_mod_respawn = true
load_mod_inventory = true
load_mod_commands = true
load_mod_chat = true
load_mod_schematicas = true
load_mod_warp = true
load_mod_cchat = true
load_mod_autofly = true
load_mod_wisp = false
load_mod_tchat = true
load_mod_esp = true
load_mod_dte = true
load_mod_hpchange = true
load_mod_autominer = true
load_mod_itemcount = false
load_mod_pathfinding = true
load_mod_autoeat = true
load_mod_perlin = true
load_mod_hignore = true
load_mod_quotebot = true
load_mod_autosneak = true
load_mod_list = true
load_mod_supernotes = true
load_mod_autoaim = true
load_mod_peek = true
load_mod_goddessmode = true
load_mod_turtle = true
load_mod_undying = true
load_mod_randomscreenshot = true
load_mod_scaffold = true
load_mod_speedlimit = true
load_mod_frenemies = true
load_mod_autocraft = true
load_mod_quint = true
load_mod_automt = true
load_mod_nlist = true
load_mod_kamikaze = true
load_mod_muse = true
load_mod_optimize = true
load_mod_render = true
load_mod_combat = true
load_mod_waterbot = true
load_mod_bookbot = true
load_mod_invrefill = true
load_mod_haxnotify = true
load_mod_incrementaltp = true
load_mod_test = true
load_mod_dodgebot = false
load_mod_furrybot = false
load_mod_antigone = false

4
clientmods/mods_here.txt Normal file
View File

@ -0,0 +1,4 @@
You can install Minetest or Dragonfire clientmods by copying (and extracting) them into this folder.
To enable them write
load_mod_<modname> = true
in mods.conf in this directory.

View File

@ -0,0 +1,8 @@
ws.rg('AutoTower','Scaffold','atower',function()
local it=minetest.localplayer:get_wielded_item():get_name()
local lp=ws.dircoord(0,0,0)
local nds=minetest.find_nodes_near_under_air(lp,4,{it},false)
for k,v in ipairs(nds) do
ws.place(vector.add(v,vector.new(0,1,0)),it)
end
end,function() end,function() end, {'autorefill'})

View File

@ -0,0 +1,77 @@
-- CC0/Unlicense Emilia 2020
local seeds = {
"mcl_farming:wheat_seeds",
"mcl_farming:beetroot_seeds",
"mcl_farming:carrot_item",
"mcl_farming:potato_item"
}
local nodeseeds = {
"mcl_farming:melon_seeds",
"mcl_farming:pumpkin_seeds"
}
local tillable = {
"mcl_core:dirt",
"mcl_core:dirt_with_grass",
"mcl_farming:soil"
}
local hoes = {
"mcl_farming:hoe_wood",
"mcl_farming:hoe_stone",
"mcl_farming:hoe_iron",
"mcl_farming:hoe_gold",
"mcl_farming:hoe_diamond"
}
local water = {
"mcl_core:water_source",
"mcl_buckets:bucket_water",
"mcl_buckets:bucket_river_water"
}
scaffold.register_template_scaffold("AutoFarm", "scaffold_farm", function(below)
local lp = vector.round(minetest.localplayer:get_pos())
-- farmland
if below.x % 5 ~= 0 or below.z % 5 ~= 0 then
if scaffold.place_if_needed(tillable, below) then
if scaffold.can_place_at(lp) then
if scaffold.find_any_swap(hoes) then
minetest.interact("place", below)
scaffold.place_if_needed(seeds, lp)
end
end
end
-- water
else
scaffold.place_if_needed(water, below)
end
end)
scaffold.register_template_scaffold("AutoMelon", "scaffold_melon", function(below)
local lp = vector.round(minetest.localplayer:get_pos())
local x = below.x % 5
local z = below.z % 5
-- water
if x == 0 and z == 0 then
scaffold.place_if_needed(water, below)
-- dirt
elseif z == 2 or z == 4 or ((x == 2 or x == 4) and z == 0) then
scaffold.place_if_needed(tillable, below)
-- farmland
elseif (x == 1 or z == 1) or (x == 3 or z == 3) then
if scaffold.place_if_needed(tillable, below) then
if scaffold.can_place_at(lp) then
if scaffold.find_any_swap(hoes) then
minetest.interact("place", below)
scaffold.place_if_needed(nodeseeds, lp)
end
end
end
end
end)

View File

@ -0,0 +1,2 @@
nlist
turtle

View File

@ -0,0 +1,392 @@
-- CC0/Unlicense Emilia & cora 2020
local category = "Scaffold"
scaffold = {}
scaffold.lockdir = false
scaffold.locky = false
scaffold.constrain1 = false
scaffold.constrain2 = false
local hwps={}
local multiscaff_width=5
local multiscaff_depth=1
local multiscaff_above=0
local multiscaff_mod=1
local storage=minetest.get_mod_storage()
local nodes_per_tick = 8
local function setnpt()
nodes_per_tick = tonumber(minetest.settings:get("nodes_per_tick")) or 8
end
function scaffold.template(setting, func, offset, funcstop )
offset = offset or {x = 0, y = -1, z = 0}
funcstop = funcstop or function() end
return function()
if minetest.localplayer and minetest.settings:get_bool(setting) then
if scaffold.constrain1 and not inside_constraints(tgt) then return end
local tgt=vector.add(minetest.localplayer:get_pos(),offset)
func(tgt)
end
end
end
function scaffold.register_template_scaffold(name, setting, func, offset, funcstop)
ws.rg(name,'Scaffold',setting,scaffold.template(setting, func, offset),funcstop )
end
local function between(x, y, z) return y <= x and x <= z end -- x is between y and z (inclusive)
function scaffold.in_cube(tpos,wpos1,wpos2)
local xmax=wpos2.x
local xmin=wpos1.x
local ymax=wpos2.y
local ymin=wpos1.y
local zmax=wpos2.z
local zmin=wpos1.z
if wpos1.x > wpos2.x then
xmax=wpos1.x
xmin=wpos2.x
end
if wpos1.y > wpos2.y then
ymax=wpos1.y
ymin=wpos2.y
end
if wpos1.z > wpos2.z then
zmax=wpos1.z
zmin=wpos2.z
end
if between(tpos.x,xmin,xmax) and between(tpos.y,ymin,ymax) and between(tpos.z,zmin,zmax) then
return true
end
return false
end
local function set_hwp(name,pos)
ws.display_wp(pos,name)
end
function scaffold.set_pos1(pos)
if not pos then local pos=minetest.localplayer:get_pos() end
scaffold.constrain1=vector.round(pos)
local pstr=minetest.pos_to_string(scaffold.constrain1)
set_hwp('scaffold_pos1 '..pstr,scaffold.constrain1)
minetest.display_chat_message("scaffold pos1 set to "..pstr)
end
function scaffold.set_pos2(pos)
if not pos then pos=minetest.localplayer:get_pos() end
scaffold.constrain2=vector.round(pos)
local pstr=minetest.pos_to_string(scaffold.constrain2)
set_hwp('scaffold_pos2 '..pstr,scaffold.constrain2)
minetest.display_chat_message("scaffold pos2 set to "..pstr)
end
function scaffold.reset()
scaffold.constrain1=false
scaffold.constrain2=false
for k,v in pairs(hwps) do
minetest.localplayer:hud_remove(v)
table.remove(hwps,k)
end
end
local function inside_constraints(pos)
if (scaffold.constrain1 and scaffold.constrain2 and scaffold.in_cube(pos,scaffold.constrain1,scaffold.constrain2)) then return true
elseif not scaffold.constrain1 then return true
end
return false
end
minetest.register_chatcommand("sc_pos1", { func = scaffold.set_pos1 })
minetest.register_chatcommand("sc_pos2", { func = scaffold.set_pos2 })
minetest.register_chatcommand("sc_reset", { func = scaffold.reset })
function scaffold.can_place_at(pos)
local node = minetest.get_node_or_nil(pos)
return (node and (node.name == "air" or node.name=="mcl_core:water_source" or node.name=="mcl_core:water_flowing" or node.name=="mcl_core:lava_source" or node.name=="mcl_core:lava_flowing" or minetest.get_node_def(node.name).buildable_to))
end
-- should check if wield is placeable
-- minetest.get_node(wielded:get_name()) ~= nil should probably work
-- otherwise it equips armor and eats food
function scaffold.can_place_wielded_at(pos)
local wield_empty = minetest.localplayer:get_wielded_item():is_empty()
return not wield_empty and scaffold.can_place_at(pos)
end
function scaffold.find_any_swap(items)
local ts=8
for i, v in ipairs(items) do
local n = minetest.find_item(v)
if n then
ws.switch_to_item(v)
return true
end
end
return false
end
function scaffold.in_list(val, list)
if type(list) ~= "table" then return false end
for i, v in ipairs(list) do
if v == val then
return true
end
end
return false
end
-- swaps to any of the items and places if need be
-- returns true if placed and in inventory or already there, false otherwise
local lastact=0
local lastplc=0
local lastdig=0
local actint=10
function scaffold.place_if_needed(items, pos, place)
if not inside_constraints(pos) then return end
if not pos then return end
place = place or minetest.place_node
local node = minetest.get_node_or_nil(pos)
if not node then return end
-- already there
if node and scaffold.in_list(node.name, items) then
return true
else
local swapped = scaffold.find_any_swap(items)
-- need to place
if swapped and scaffold.can_place_at(pos) then
--minetest.after("0.05",place,pos)
place(pos)
return true
-- can't place
else
return false
end
end
end
function scaffold.place_if_able(pos)
if not pos then return end
if not inside_constraints(pos) then return end
if scaffold.can_place_wielded_at(pos) then
minetest.place_node(pos)
end
end
local function is_diggable(pos)
if not pos then return false end
local nd=minetest.get_node_or_nil(pos)
if not nd then return false end
local n = minetest.get_node_def(nd.name)
if n and n.diggable then return true end
return false
end
function scaffold.dig(pos)
if not inside_constraints(pos) then return end
if is_diggable(pos) then
local nd=minetest.get_node_or_nil(pos)
minetest.select_best_tool(nd.name)
if emicor then emicor.supertool()
end
--minetest.select_best_tool(nd.name)
minetest.dig_node(pos)
end
return false
end
local mpath = minetest.get_modpath(minetest.get_current_modname())
dofile(mpath .. "/sapscaffold.lua")
dofile(mpath .. "/slowscaffold.lua")
dofile(mpath .. "/autofarm.lua")
dofile(mpath .. "/railscaffold.lua")
dofile(mpath .. "/wallbot.lua")
dofile(mpath .. "/ow2bot.lua")
dofile(mpath .. "/atower.lua")
--dofile(mpath .. "/squarry.lua")
ws.rg('DigHead','Player','dighead',function() ws.dig(ws.dircoord(0,1,0)) end)
local function checknode(pos)
local node = minetest.get_node_or_nil(pos)
if node then return true end
return false
end
minetest.register_chatcommand('scaffw', {
func = function(param) multiscaff_width=tonumber(param) end
})
minetest.register_chatcommand('scaffd', {
func = function(param) multiscaff_depth=tonumber(param) end
})
minetest.register_chatcommand('scaffa', {
func = function(param) multiscaff_above=tonumber(param) end
})
minetest.register_chatcommand('scaffm', {
func = function(param) multiscaff_mod=tonumber(param) end
})
local multiscaff_node=nil
ws.rg('MultiScaff','Scaffold','scaffold',function()
if not multiscaff_node then return end
local n=math.floor(multiscaff_width/2)
for i=-n,n do
for j=(multiscaff_depth * -1), -1 do
local p=ws.dircoord(0,j,i)
local nd=minetest.get_node_or_nil(p)
ws.place(p,{multiscaff_node})
end
end
end,function()
multiscaff_node=minetest.localplayer:get_wielded_item():get_name()
ws.dcm("Multiscaff started. Width: "..multiscaff_width..', depth:'..multiscaff_depth..' Selected node: '..multiscaff_node)
end,function()
ws.dcm("Multiscaff stopped")
end)
ws.rg('MScaffModulo','Scaffold','multiscaffm',function()
if not multiscaff_node then return end
ws.switch_to_item(multiscaff_node)
local n=math.floor(multiscaff_width/2)
for i=-n,n do
for j=(multiscaff_depth * -1), -1 do
local p=vector.round(ws.dircoord(0,j,i))
if p.z % multiscaff_mod == 0 then
if p.x % multiscaff_mod ~=0 then
core.place_node(p)
end
else
if p.x % multiscaff_mod == 0 then
core.place_node(p)
end
end
end
end
end,function()
multiscaff_node=minetest.localplayer:get_wielded_item():get_name()
ws.dcm("ModuloScaff started. Width: "..multiscaff_width..', depth:'..multiscaff_depth..' Selected node: '..multiscaff_node)
end,function()
ws.dcm("Moduloscaff stopped")
end)
scaffold.register_template_scaffold("WallScaffold", "scaffold_five_down", function(pos)
scaffold.place_if_able(ws.dircoord(0, -1, 0))
scaffold.place_if_able(ws.dircoord(0, -2, 0))
scaffold.place_if_able(ws.dircoord(0, -3, 0))
scaffold.place_if_able(ws.dircoord(0, -4, 0))
scaffold.place_if_able(ws.dircoord(0, -5, 0))
end)
scaffold.register_template_scaffold("headTriScaff", "scaffold_three_wide_head", function(pos)
scaffold.place_if_able(ws.dircoord(0, 3, 0))
scaffold.place_if_able(ws.dircoord(0, 3, 1))
scaffold.place_if_able(ws.dircoord(0, 3, -1))
end)
scaffold.register_template_scaffold("RandomScaff", "scaffold_rnd", function(below)
local n = minetest.get_node_or_nil(below)
local nl=nlist.get('randomscaffold')
table.shuffle(nl)
if n and not scaffold.in_list(n.name, nl) then
scaffold.dig(below)
scaffold.place_if_needed(nl, below)
end
end)
ws.rg("HighwayZ","World","highwayz",function()
local positions = {
{x = 0, y = 0, z = z},
{x = 1, y = 0, z = z},
{x = 2, y = 1, z = z},
{x = -2, y = 1, z = z},
{x = -2, y = 0, z = z},
{x = -1, y = 0, z = z},
{x = 2, y = 0, z = z}
}
for i, p in pairs(positions) do
if i > nodes_per_tick then break end
minetest.place_node(p)
end
end, setnpt)
ws.rg("BlockWater","World","block_water",function()
local lp=ws.dircoord(0,0,0)
local positions = minetest.find_nodes_near(lp, 5, {"mcl_core:water_source", "mcl_core:water_flowing"}, true)
for i, p in pairs(positions) do
if i > nodes_per_tick then return end
minetest.place_node(p)
end
end,setnpt)
ws.rg("BlockLava","World","block_lava",function()
local lp=ws.dircoord(0,0,0)
local positions = minetest.find_nodes_near(lp, 5, {"mcl_core:lava_source", "mcl_core:lava_flowing"}, true)
for i, p in pairs(positions) do
if i > nodes_per_tick then return end
minetest.place_node(p)
end
end,setnpt)
ws.rg("BlockSources","World","block_sources",function()
local lp=ws.dircoord(0,0,0)
local positions = minetest.find_nodes_near(lp, 5, {"mcl_core:lava_source","mcl_nether:nether_lava_source","mcl_core:water_source"}, true)
for i, p in pairs(positions) do
if p.y<2 then
if p.x>500 and p.z>500 then return end
end
if i > nodes_per_tick then return end
minetest.place_node(p)
end
end,setnpt)
ws.rg("PlaceOnTop","World","place_on_top",function()
local lp=ws.dircoord(0,0,0)
local positions = minetest.find_nodes_near_under_air_except(lp, 5, item:get_name(), true)
for i, p in pairs(positions) do
if i > nodes_per_tick then break end
minetest.place_node(vector.add(p, {x = 0, y = 1, z = 0}))
end
end,setnpt)
ws.rg("Nuke","World","nuke",function()
local pos=ws.dircoord(0,0,0)
local i = 0
for x = pos.x - 4, pos.x + 4 do
for y = pos.y - 4, pos.y + 4 do
for z = pos.z - 4, pos.z + 4 do
local p = vector.new(x, y, z)
local node = minetest.get_node_or_nil(p)
local def = node and minetest.get_node_def(node.name)
if def and def.diggable then
if i > nodes_per_tick then return end
minetest.dig_node(p)
i = i + 1
end
end
end
end
end,setnpt)

View File

@ -0,0 +1,18 @@
local Y=1337
local plnodes={'mcl_core:cobble','mcl_core:dirt','mcl_core:dirt_with_grass','mcl_core:obsidian'}
ws.rg("OW2Bot","Bots","ow2bot", function(pos)
local lp=minetest.localplayer:get_pos()
local r=3
local pos1=vector.add(lp,{x=r,y=0,z=r})
local pos2=vector.add(lp,{x=-r,y=0,z=-r})
pos1.y=Y
pos2.y=Y
ws.do_area(3,function(pos)
ws.place(pos,plnodes)
end,true)
end,function()
end)

View File

@ -0,0 +1,318 @@
-- CC0/Unlicense Emilia/cora 2020
-- south:5,1.5
--west:-x,1.5,-5
--east:-x,1.5,5
-- north 5,1.5(3096:2.5,25025:1.5),z
local direction = ""
local ground = {
"mesecons_torch:redstoneblock"
}
local rails = {
"mcl_minecarts:golden_rail",
"mcl_minecarts:rail"
}
local tunnelmaterial = {
'mcl_core:glass_light_blue',
'mcl_core:glass',
'mcl_core:cobble',
'mcl_core:stone',
'mcl_nether:netherrack',
'mcl_core:dirt',
'mcl_core:andesite',
'mcl_core:diorite',
'mcl_core:granite',
"mesecons_torch:redstoneblock"
}
local lightblock = "mcl_ocean:sea_lantern"
--local lightblock = "mcl_nether:glowstone"
local function is_rail(pos)
pos=vector.round(pos)
if pos.y ~= 1 then return false end
if pos.z > 5 then
if pos.x == -5 then return "north" end
elseif pos.z < -5 then
if pos.x == 5 then return "south" end
end
if pos.x > 5 then
if pos.z == 5 then return "east" end
elseif pos.x < -5 then
if pos.z == -5 then return "west" end
end
return false
end
local function get_railnode(pos)
if is_rail(pos) then
return "mcl_minecarts:golden_rail"
end
if is_rail(vector.add(pos,{x=0,y=-1,x=0})) then
return "mesecons_torch:redstoneblock"
end
return false
end
local function is_lantern(pos)
local dir=ws.getdir()
pos=vector.round(pos)
if dir == "north" or dir == "south" then
if pos.z % 8 == 0 then
return true
end
else
if pos.x % 8 == 0 then
return true
end
end
return false
end
local function checknode(pos)
local lp = ws.dircoord(0,0,0)
local node = minetest.get_node_or_nil(pos)
if pos.y == lp.y then
if node and not node.name:find("_rail") then return true end
elseif node and node.name ~="mesecons_torch:redstoneblock" then return true
end
return false
end
local function dignodes(poss)
for k,v in pairs(poss) do
if checknode(v) then ws.dig(v) end
end
end
local function findliquids(pos,range)
range = range or 1
if not pos then return end
local liquids={'mcl_core:lava_source','mcl_core:water_source','mcl_core:lava_flowing','mcl_core:water_flowing','mcl_nether:nether_lava_source','mcl_nether:nether_lava_flowing'}
local bn=minetest.find_nodes_near(pos, range, liquids, true)
if #bn < 0 then return bn end
return false
end
local function blockliquids(pos)
if not pos then return end
local lp=ws.dircoord(0,0,0)
local liquids={'mcl_core:lava_source','mcl_core:water_source','mcl_core:lava_flowing','mcl_core:water_flowing','mcl_nether:nether_lava_source','mcl_nether:nether_lava_flowing'}
local bn=minetest.find_nodes_near(pos, 1, liquids, true)
local rt=false
if not bn then return rt end
for kk,vv in pairs(bn) do
if vv.y > lp.y - 1 or vv.y < -40 then
rt=true
scaffold.place_if_needed(tunnelmaterial,vv)
for i=-4,5,1 do
local tpos=vector.new(pos.x,lp.y,pos.z)
scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,2,0,tpos))
scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,0,1,tpos))
scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,1,1,tpos))
scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,0,-1,tpos))
scaffold.place_if_needed(tunnelmaterial,ws.dircoord(i,1,-1,tpos))
end
end
end
return rt
end
local function digob(sc)
local obpos={
ws.dircoord(0,1,2,sc),
ws.dircoord(0,1,-2,sc),
ws.dircoord(0,1,1,sc),
ws.dircoord(0,1,-1,sc),
ws.dircoord(0,0,1,sc),
ws.dircoord(0,0,-1,sc)
}
ws.dignodes(obpos,function(pos)
local nd=minetest.get_node_or_nil(pos)
if nd and (nd.name == "mcl_core:obsidian" or nd.name == "mcl_minecarts:golden_rail_on" or nd.name == "mcl_minecarts:golden_rail" )then return true end
return false
end)
end
local function invcheck(item)
if mintetest.switch_to_item(item) then return true end
refill.refill_at(ws.dircoord(1,1,0),'railkit')
end
local function invcheck(item)
if mintetest.switch_to_item(item) then return true end
refill.refill_at(ws.dircoord(1,1,0),'railkit')
end
local function rnd(n)
return math.ceil(n)
end
local function fmt(c)
return tostring(rnd(c.x))..","..tostring(rnd(c.y))..","..tostring(rnd(c.z))
end
local function map_pos(value)
if value.x then
return value
else
return {x = value[1], y = value[2], z = value[3]}
end
end
local function invparse(location)
if type(location) == "string" then
if string.match(location, "^[-]?[0-9]+,[-]?[0-9]+,[-]?[0-9]+$") then
return "nodemeta:" .. location
else
return location
end
elseif type(location) == "table" then
return "nodemeta:" .. fmt(map_pos(location))
end
end
local function take_railkit(pos)
local plinv = minetest.get_inventory(invparse(pos))
local epos=ws.find_named(plinv,'railkit')
local mv = InventoryAction("move")
mv:from(invparse(pos), "main", epos)
mv:to("current_player", "main", 8)
mv:apply()
minetest.localplayer:set_wield_index(8)
return true
end
local restashing=false
function scaffold.restash()
if restashing then return end
restashing=true
ws.dig(ws.dircoord(1,0,1))
ws.dig(ws.dircoord(1,1,1))
ws.dig(ws.dircoord(2,0,1))
ws.dig(ws.dircoord(2,1,1))
ws.place(ws.dircoord(1,0,1),{'mcl_chests:chest_small','mcl_chests:chest'})
ws.place(ws.dircoord(1,1,1),{'railroad'})
take_railkit(ws.dircoord(1,1,1))
minetest.after("0.5",function()
ws.place(ws.dircoord(2,0,1),{'railkit'})
ws.dig(ws.dircoord(1,1,1))
end)
minetest.after("1.0",function()
autodupe.invtake(ws.dircoord(2,0,1))
restashing=false
end)
end
local function slowdown(s)
minetest.localplayer:set_velocity(vector.new(0,0,0))
minetest.settings:set('movement_speed_fast',math.abs(s))
end
local fullspeed=100
local function speedup()
minetest.settings:set('movement_speed_fast',fullspeed)
end
ws.rg("RailBot","Bots", "railbot", function()
local oldi=500
for i=-50,50,1 do
local lpos=ws.dircoord(i,2,0)
local lpn=minetest.get_node_or_nil(ws.dircoord(i,0,0))
local bln=minetest.get_node_or_nil(ws.dircoord(i,-1,0))
local ltpn=minetest.get_node_or_nil(lpos)
if not bln or not lpn or not ltpn then
speedup()
elseif ( is_lantern(lpos) and ltpn.name ~= lightblock ) then
if (oldi > i) then
slowdown(8)
oldi=i
end
elseif bln.name=="mesecons_torch:redstoneblock" and lpn.name == "mcl_minecarts:golden_rail_on" then
speedup()
else
if (oldi > i) then
slowdown(8)
oldi=i
end
end
end
local goon=false
for i=-4,4,1 do
local lpos=ws.dircoord(i,2,0)
local lpn=minetest.get_node_or_nil(ws.dircoord(i,0,0))
local bln=minetest.get_node_or_nil(ws.dircoord(i,-1,0))
local lpos=ws.dircoord(i,2,0)
if not ( bln and bln.name=="mesecons_torch:redstoneblock" and lpn and lpn.name == "mcl_minecarts:golden_rail_on" ) then
goon=false
else
goon=true
end
digob(ws.dircoord(i,0,0))
blockliquids(ws.dircoord(i,1,0))
blockliquids(ws.dircoord(i,0,0))
ws.dig(ws.dircoord(i,1,0))
if checknode(ws.dircoord(i,0,0)) then ws.dig(ws.dircoord(i,0,0)) end
if checknode(ws.dircoord(i,-1,0)) then ws.dig(ws.dircoord(i,-1,0)) end
ws.place(ws.dircoord(i,-1,0),ground,7)
ws.place(ws.dircoord(i,0,0),rails,6)
local lpos=ws.dircoord(i,2,0)
if is_lantern(lpos) then
local ln=minetest.get_node_or_nil(lpos)
if not ln or ln.name ~= lightblock then
goon=false
ws.dig(lpos)
ws.place(lpos,{lightblock},5)
end
end
end
if (goon) then
local dir=ws.getdir()
local lp=minetest.localplayer:get_pos()
local rlp=vector.round(lp)
minetest.localplayer:set_pos(vector.new(rlp.x,lp.y,rlp.z))
minetest.settings:set_bool('continuous_forward',true)
else
slowdown(5)
minetest.localplayer:set_velocity(vector.new(0,0,0))
minetest.settings:set_bool('continuous_forward',false)
end
end,
function()--startfunc
minetest.settings:set('movement_speed_fast',500)
minetest.settings:set_bool('continuous_forward',false)
end,function() --stopfunc
minetest.localplayer:set_velocity(vector.new(0,0,0))
minetest.settings:set('movement_speed_fast',20)
minetest.settings:set_bool('continuous_forward',false)
end,{'afly_snap','autorefill'}) --'scaffold_ltbm'
scaffold.register_template_scaffold("LanternTBM", "scaffold_ltbm", function()
local dir=ws.getdir()
local lp=vector.round(ws.dircoord(0,0,0))
local pl=is_lantern(lp)
if pl then
local lpos=ws.dircoord(0,2,0)
local nd=minetest.get_node_or_nil(lpos)
if nd and nd.name ~= lightblock then
ws.dig(lpos)
ws.place(lpos,lightblock,5)
end
end
end)

View File

@ -0,0 +1,25 @@
-- CC0/Unlicense Emilia 2020
local dirt = {
"mcl_core:dirt",
"mcl_core:dirt_with_grass",
"mcl_core:dirt_with_grass_snow",
"mcl_core:podzol"
}
local saplings = {
"mcl_core:sapling",
"mcl_core:darksapling",
"mcl_core:junglesapling",
"mcl_core:sprucesapling",
"mcl_core:birchsapling",
"mcl_core:acaciasapling"
}
scaffold.register_template_scaffold("SapScaffold", "scaffold_saplings", function(below)
local lp = vector.round(minetest.localplayer:get_pos())
if scaffold.place_if_needed(dirt, below) then
scaffold.place_if_needed(saplings, lp)
end
end)

View File

@ -0,0 +1 @@
slow_blocks_per_second (Blocks placed per second) int 8

View File

@ -0,0 +1,52 @@
-- CC0/Unlicense Emilia 2020
if minetest.settings:get("slow_blocks_per_second") == nil then
minetest.settings:set("slow_blocks_per_second", 8)
end
-- Could remove the queue and have nowplace() check if it can place at the position
local lastt = 0
local posqueue = {}
local function posq_pos(pos)
local plen = #posqueue
for i = 0, #posqueue - 1 do
if vector.equals(pos, posqueue[plen - i]) then
return plen - i
end
end
end
local function nowplace(pos)
local p = posq_pos(pos)
if p then
table.remove(posqueue, p)
end
minetest.place_node(pos)
end
local function place(pos)
if not posq_pos(pos) then
local now = os.clock()
if lastt < now then
lastt = now
end
local interval = 1 / minetest.settings:get("slow_blocks_per_second")
lastt = lastt + interval
minetest.after(lastt - now, nowplace, pos)
posqueue[#posqueue + 1] = pos
end
end
scaffold.register_template_scaffold("SlowScaffold", "scaffold_slow", function(pos)
if scaffold.can_place_wielded_at(pos) then
place(pos)
end
end)

View File

@ -0,0 +1,155 @@
local sq_pos1={x=-30800,y=1,z=-30800}
local sq_pos2={x=-30880,y=80,z=-30880}
local digging=false
local flying=false
local target=vector.new(0,0,0)
local function between(x, y, z) return y <= x and x <= z end -- x is between y and z (inclusive)
local function in_cube(tpos,wpos1,wpos2)
local xmax=wpos2.x
local xmin=wpos1.x
local ymax=wpos2.y
local ymin=wpos1.y
local zmax=wpos2.z
local zmin=wpos1.z
if wpos1.x > wpos2.x then
xmax=wpos1.x
xmin=wpos2.x
end
if wpos1.y > wpos2.y then
ymax=wpos1.y
ymin=wpos2.y
end
if wpos1.z > wpos2.z then
zmax=wpos1.z
zmin=wpos2.z
end
if between(tpos.x,xmin,xmax) and between(tpos.y,ymin,ymax) and between(tpos.z,zmin,zmax) then
return true
end
return false
end
local function get_nodes_in_range(range,air)
local lp=minetest.localplayer:get_pos()
local p1=vector.add(lp,{x=range,y=range,z=range})
local p2=vector.add(lp,{x=-range,y=-range,z=-range})
local nn=nlist.get_mclnodes()
if air then table.insert(nn,'air') end
local nds,cnt=minetest.find_nodes_in_area(p1,p2,nn,true)
local rt={}
for k,v in pairs(nds) do for kk,vv in pairs(v) do
local nd=minetest.get_node_or_nil(vv)
if nd then table.insert(rt,vv) end
end end
return rt
end
local function get_randompos(wpos1,wpos2)
local xmax=wpos2.x
local xmin=wpos1.x
local ymax=wpos2.y
local ymin=wpos1.y
local zmax=wpos2.z
local zmin=wpos1.z
if wpos1.x > wpos2.x then
xmax=wpos1.x
xmin=wpos2.x
end
if wpos1.y > wpos2.y then
ymax=wpos1.y
ymin=wpos2.y
end
if wpos1.z > wpos2.z then
zmax=wpos1.z
zmin=wpos2.z
end
return vector.new(math.random(xmin,xmax),math.random(ymin,ymax),math.random(zmin,zmax))
end
local nextdig=0
local function flythere(pos)
flying=true
minetest.settings:set_bool('noclip',false)
minetest.settings:set_bool('scaffold_quarrytool',true)
minetest.settings:set_bool("pitch_move",true)
minetest.settings:set_bool("free_move",true)
minetest.settings:set_bool("continuous_forward",true)
autofly.aim(pos)
core.set_keypress("special1", true)
end
local function stopflight()
flying=false
digging=true
minetest.settings:set_bool("continuous_forward",false)
minetest.settings:set_bool('scaffold_walltool',false)
minetest.settings:set_bool("noclip",false)
minetest.settings:set_bool("pitch_move",false)
core.set_keypress("special1", false)
end
local function do_nodes_in_range(action)
local nds={}
if action == 'dig' then nds=get_nodes_in_range(6)
else nds=get_nodes_in_range(6,true) end
if #nds > 0 then diggin=true else diggin=false end
for k,v in pairs(nds) do
if v then
--minetest.switch_to_item("mcl_books:book_written")
if action == 'dig' then
minetest.select_best_tool(minetest.get_node_or_nil(v).name)
minetest.dig_node(v)
else
local headpos=vector.add(minetest.localplayer:get_pos(),{x=0,y=0,z=0})
if vector.distance(headpos,v) == 0 then return end
scaffold.place_if_able(v)
end
end
end
end
--randomseed(os.clock())
scaffold.register_template_scaffold("QuarryTool", "scaffold_quarrytool", function(pos)
do_nodes_in_range('dig')
end)
scaffold.register_template_scaffold("PlaceRange", "scaffold_placer", function(pos)
do_nodes_in_range()
local headpos=vector.add(minetest.localplayer:get_pos(),vector.new(0,1,0))
local headnod=minetest.get_node_or_nil(headpos)
if headnod.name ~= 'air' then scaffold.dig(headpos) end
end)
local qbot_wason=false
ws.register_globalhacktemplate("QuarryBot", "Bots", "scaffold_quarrybot", function(pos)
local lp=minetest.localplayer:get_pos()
if not digging and not flying then
local nds=get_nodes_in_range(50)
if #nds == 0 then
target=get_randompos(sq_pos1,sq_pos2)
else
target=nds[math.random(#nds)]
end
flythere(target)
elseif vector.distance(lp,target) < 5 then
stopflight()
end
end,function()
scaffold.set_pos1(sq_pos1)
scaffold.set_pos2(sq_pos2)
minetest.settings:set_bool('scaffold_constrain',true)
minetest.settings:set_bool('scaffold_quarrytool',true)
end,function()
qbot_wason=false
flying=false
digging=false
minetest.settings:set_bool('scaffold_quarrytool',false)
stopflight()
end)

View File

@ -0,0 +1,263 @@
local bpos = {
{x=-1265,y=40,z=802},
{x=-1265,y=40,z=972},
{x=-1443,y=40,z=972},
{x=-1443,y=40,z=802}
}
local wbtarget = bpos[1]
local function between(x, y, z) -- x is between y and z (inclusive)
return y <= x and x <= z
end
local function mkposvec(vec)
vec.x=vec.x + 30927
vec.y=vec.y + 30927
vec.z=vec.z + 30927
return vec
end
local function normvec(vec)
vec.x=vec.x - 30927
vec.y=vec.y - 30927
vec.z=vec.z - 30927
return vec
end
local wall_pos1={x=-1254,y=-4,z=791}
local wall_pos2={x=-1454,y=80,z=983}
local iwall_pos1={x=-1264,y=-4,z=801}
local iwall_pos2={x=-1444,y=80,z=973}
local function in_cube(tpos,wpos1,wpos2)
local xmax=wpos2.x
local xmin=wpos1.x
local ymax=wpos2.y
local ymin=wpos1.y
local zmax=wpos2.z
local zmin=wpos1.z
if wpos1.x > wpos2.x then
xmax=wpos1.x
xmin=wpos2.x
end
if wpos1.y > wpos2.y then
ymax=wpos1.y
ymin=wpos2.y
end
if wpos1.z > wpos2.z then
zmax=wpos1.z
zmin=wpos2.z
end
if between(tpos.x,xmin,xmax) and between(tpos.y,ymin,ymax) and between(tpos.z,zmin,zmax) then
return true
end
return false
end
local function in_wall(pos)
if in_cube(pos,wall_pos1,wall_pos2) and not in_cube(pos,iwall_pos1,iwall_pos2) then
return true end
return false
end
local function iwall_node(pos)
if pos.y>80 or pos.y < -2 then return false end
local dir=ws.getdir()
if dir == "north" then
if pos.z == 973 and pos.x < -1264 and pos.x > -1444 then
if pos.y % 2 == 0 then
if pos .x % 2 == 0 then
return "mcl_core:obsidian"
else
return "mcl_core:stonebrick"
end
else
if pos .x % 2 == 0 then
return "mcl_core:stonebrick"
else
return "mcl_core:obsidian"
end
end
end
elseif dir == "east" then
if pos.x == -1264 and pos.z > 801 and pos.z < 973 then
if pos.y % 2 == 0 then
if pos .z % 2 == 0 then
return "mcl_core:stonebrick"
else
return "mcl_core:obsidian"
end
else
if pos .z % 2 == 0 then
return "mcl_core:obsidian"
else
return "mcl_core:stonebrick"
end
end
end
elseif dir == "south" then
if pos.z == 801 and pos.x < -1264 and pos.x > -1444 then
if pos.y % 2 == 0 then
if pos .x % 2 == 0 then
return "mcl_core:obsidian"
else
return "mcl_core:stonebrick"
end
else
if pos .x % 2 == 0 then
return "mcl_core:stonebrick"
else
return "mcl_core:obsidian"
end
end
end
elseif dir == "west" then
if pos.x == -1444 and pos.z > 801 and pos.z < 973 then
if pos.y % 2 == 0 then
if pos .z % 2 == 0 then
return "mcl_core:stonebrick"
else
return "mcl_core:obsidian"
end
else
if pos.z % 2 == 0 then
return "mcl_core:obsidian"
else
return "mcl_core:stonebrick"
end
end
end
end
return false
end
local lwltime=0
scaffold.register_template_scaffold("WallTool", "scaffold_walltool", function(pos)
if os.clock() < lwltime then return end
lwltime=os.clock()+.5
local lp=minetest.localplayer:get_pos()
local p1=vector.add(lp,{x=5,y=5,z=5})
local p2=vector.add(lp,{x=-5,y=-5,z=-5})
local nn=nlist.get_mclnodes()
local cobble='mcl_core:cobble'
table.insert(nn,'air')
--local nds,cnt=minetest.find_nodes_in_area(p1,p2,nn,true)
--local nds=minetest.find_nodes_near_except(lp,5,{cobble})
local i=1
local nds=minetest.find_nodes_near(lp,10,{'air'})
for k,vv in pairs(nds) do
if i > 8 then return end
local iwn=iwall_node(vv)
local nd=minetest.get_node_or_nil(vv)
if vv and in_wall(vv) then
i = i + 1
if nd and nd.name ~= 'air' then
scaffold.dig(vv)
else
ws.place(vv,{cobble})
end
elseif vv and iwn then
i = i + 1
if nd and nd.name ~= iwn and nd.name ~= 'air' then
ws.dig(vv)
else
ws.place(vv,iwn)
end
end
end
end)
ws.rg('AWalltool','Bots','scaffold_awalltool',function()
--local nds=minetest.find_nodes_near_except(ws.dircoord(0,0,0),6,{'mcl_core:cobble'})
local nds=minetest.find_nodes_near(ws.dircoord(0,0,0),7,{'air'})
local rt=true
for k,v in ipairs(nds) do
if in_wall(v) then
rt=false
end
end
minetest.settings:set_bool('continuous_forward',rt)
end,function() end, function()end, {'scaffold_walltool','afly_snap'})
local function find_closest_safe(pos)
local odst=500
local res=pos
local poss=minetest.find_nodes_near(pos,10,{'air'})
for k,v in ipairs(poss) do
local dst=vector.distance(pos,v)
if not in_wall(v) and dst < odst then
odst=dst
res=vector.add(v,vector.new(0,-1,0))
end
end
return res
end
local function wallbot_find(range)
local lp=ws.dircoord(0,0,0)
local nds=minetest.find_nodes_near_except(lp,range,{'mcl_core:cobble'})
local odst=500
local tg=nil
res=nil
for k,v in ipairs(nds) do
if in_wall(v) then
local dst=vector.distance(lp,v)
if odst > dst then odst=dst res=v end
end
end
if res then find_closest_safe(res)
else return false end
end
local function random_iwall()
math.randomseed(os.clock())
local x=math.random(0,90)
local y=math.random(10,70)
local z=math.random(0,90)
local rpos={x=-1254 - x,y=y,z=791 + z}
end
local wallbot_state=0
local wallbot_target=nil
ws.rg('WallBot','Bots','scaffold_wallbot',function()
local nds=nil
if not wallbot_target then wallbot_state=0 end
if wallbot_state == 0 then --searching
wallbot_target=wallbot_find(79)
if wallbot_target then
wallbot_state=2
else
wallbot_target=random_iwall()
wallbot_state=1
end
elseif wallbot_state == 1 then --flying - searching
if incremental_tp.tpactive then return end
if vector.distance(ws.dircoord(0,0,0),wallbot_target) < 10 then
minetest.after(5,function() wallbot_state=0 end)
return
end
incremental_tp.tp(wallbot_target,1,1)
elseif wallbot_state == 2 then --flying - target
if incremental_tp.tpactive then return end
if vector.distance(ws.dircoord(0,0,0),wallbot_target) < 10 then
wallbot_state=3
return
end
incremental_tp.tp(wallbot_target,1,1)
elseif wallbot_state == 3 then --filling
if not wallbot_find(10) then
wallbot_state=0
return
end
else
wallbot_state=0
end
end,function() wallbot_state=0 end,function() end,{'scaffold_walltool'})

View File

@ -0,0 +1,12 @@
local wason=false;
minetest.register_globalstep(function()
if minetest.localplayer and minetest.settings:get_bool("movement_ignore_server_speed") then
minetest.localplayer:set_speeds_from_local_settings()
wason=true
elseif wason then
wason=false
minetest.localplayer:set_speeds_from_server_settings()
end
end)
minetest.register_cheat("IgnSrvSpd", "Movement", "movement_ignore_server_speed")

View File

@ -0,0 +1,177 @@
-- CC0/Unlicense system32 2020
-- WIKI FORMATTING
-- [[link]] -> link to 'link'
-- [[link|Wow]] -> link to 'link' with the display value of 'Wow'
-- {{coords|X Y Z}} -> coordinates for X Y Z that adds an autofly waypoint at that coordinate when clicked
--[[
TODO/wishlist:
<nowiki></nowiki> maybe extract text between these and put them somewhere, replacing them with a temp marker which is replaced with the original text once formatting is done
headers
maybe make the page list a wiki page?
page history
--]]
local storage = minetest.get_mod_storage()
local start_page = "Welcome to supernotes! Click Edit/Save to edit or save a page, Pages to list all pages, and type in that Page bar to go to a page."
local sidebar_page = "Sidebar"
local example_json = '{"sidebar": "' .. sidebar_page .. '", "start": "' .. start_page .. '"}'
local pages = minetest.parse_json(storage:get("supernotes_pages") or example_json)
local function save()
storage:set_string("supernotes_pages", minetest.write_json(pages))
end
local formspec_base = [[
size[10,9]
field[0.3,0.5;6.5,1;page;Page/search;NOTE_TITLE]
button[6.6,0.2;1.5,1;go;Go]
button[8.2,0.2;1.5,1;pages;Pages]
hypertext[0.3,1.7;2,7;sidebar;SIDEBAR_TEXT]
ARTICLE_AREA
button_exit[0,8.2;2,1;quit;Quit]
button[7.9,8.2;2,1;edit;Edit/Save]
field_close_on_enter[page;false]
]]
local formspec_article = formspec_base:gsub("ARTICLE_AREA", "hypertext[2.5,1.7;7,7;article;ARTICLE_TEXT]")
local formspec_edit = formspec_base:gsub("ARTICLE_AREA", "textarea[2.5,1.7;7,7;article;;ARTICLE_TEXT]")
local formspec_pagelist = formspec_base:gsub("ARTICLE_AREA", "textlist[2.5,1.7;7,6.3;article;ARTICLES]")
local function format_coords(text)
local num = "([-]?%d+.?%d*)"
local space = "%s+"
text = text:gsub("{{coords|" .. num .. space .. num .. space .. num .. "}}", "<action name=coords_%1_%2_%3>%1 %2 %3</action>")
return text
end
local function format_wikilinks(text)
local tmatch = "[^%[%|%]]"
text = text:gsub("%[%[(" .. tmatch .. "-)|(" .. tmatch .. "-)%]%]", "<action name=link_%1>%2</action>")
text = text:gsub("%[%[(" .. tmatch .. "-)%]%]", "<action name=link_%1>%1</action>")
return text
end
local function wikiformat(text)
text = format_coords(text)
text = format_wikilinks(text)
return text
end
local function getkeys(t)
local out = {}
for k, v in pairs(t) do
out[#out + 1] = k
end
return out
end
local function startswith(s1, s2)
return string.sub(s1, 1, string.len(s2)) == s2
end
local editing = false
local current_page = nil
local function show_page(page, mode)
page = page or ""
current_page = page
local fs = ""
if mode == nil then
fs = formspec_article
fs = fs:gsub("ARTICLE_TEXT", wikiformat(pages[page] or "Page empty. Click Edit to create."))
elseif mode == "list" then
fs = formspec_pagelist
fs = fs:gsub("ARTICLES", table.concat(getkeys(pages), ","))
elseif mode == "edit" then
editing = true
fs = formspec_edit
fs = fs:gsub("ARTICLE_TEXT", minetest.formspec_escape(pages[page] or ""))
end
fs = fs:gsub("NOTE_TITLE", page)
fs = fs:gsub("SIDEBAR_TEXT", wikiformat(pages.sidebar or ""))
minetest.show_formspec("supernotes", fs)
end
local function linkfield(field)
if startswith(field or "", "action:") then
local action = field:match("action:(.*)")
local wikilink = action:match("link_(.*)")
local x, y, z = action:match("coords_([^_]+)_([^_]+)_([^_]+)")
if wikilink then
show_page(wikilink)
return true
elseif x and y and z then
if autofly then
-- maybe use autofly.set_hud_wp instead?
autofly.set_waypoint(x .. "," .. y .. "," .. z, "Supernotes: " .. x .. "," .. y .. "," .. z)
autofly.display_formspec()
end
return true
end
end
end
minetest.register_on_formspec_input(function(formspec, fields)
if formspec == "supernotes" then
-- go to page
if fields.page ~= "" and (fields.go or fields.page ~= current_page) then
show_page(fields.page)
-- enter edit mode
elseif fields.edit and not editing then
show_page(fields.page, "edit")
-- exit edit mode
elseif fields.edit and editing then
editing = false
pages[fields.page] = fields.article
save()
show_page(fields.page)
-- list pages
elseif fields.pages then
show_page(fields.page, "list")
-- make sure to exit editing mode if exiting
elseif fields.quit then
editing = false
-- process links for article and sidebar
elseif linkfield(fields.article) then
elseif linkfield(fields.sidebar) then
-- list link clicking
elseif startswith(fields.article, "CHG") then
local target = fields.article:match("CHG:(.*)")
show_page(getkeys(pages)[tonumber(target)])
end
end
end)
minetest.register_on_shutdown(save)
minetest.register_chatcommand("notes", {
params = "<page/empty>",
description = "Open Supernotes at the specified page or last opened page.",
func = function(params)
local page = string.split(params, " ")[1]
show_page(page or current_page or "start")
end
})

View File

@ -0,0 +1,2 @@
name = supernotes
description = Adds a wiki-like local notetaking system. Integrates with cora's autofly.

643
clientmods/tchat/init.lua Normal file
View File

@ -0,0 +1,643 @@
---
-- coras teamchat .. indev v0.5
--
-- adds a team chat for you and a couple friends, also prevents accidental sending of coordinates
-- to say something in teamchat either activate teammode in the dragonfire menu or use .t message
--
-- supports the Wisp encrypted whisper mod
--
-- .t to say something in team chat (or regular chat if team mode is on)
-- .tadd to add a team member
-- .tdel to remove
-- .tlist to list team
--
-- .coords to send a message containing coordinates
-- .mcoord to send a player your current coordinates
--[[
Public methods
tchat.contains_coords(message) - returns true if the message contains coordinates (2d or 3d)
tchat.send(message) - send a message to teamchat, returns true if sent, nil if not
tchat.send_conditional(message, inverse?) - send a message to teamchat or regular chat, returns true if sent to teamchat, false if main chat, nil if not sent
tchat.send_coords(message) - send a message containing coordinates, true if sent, nil if not
tchat.whisper_coords(player) - DM current coords to a player
tchat.chat_clear() - clear chat widget
tchat.chat_set([]) - set chat widget
tchat.chat_append([] or message) - append to chat widget
tchat.team_add_player(player) - add player to team list
tchat.team_remove_player(player) - remove player from team list
tchat.team_clear() - clear team list
tchat.team_set([]) - set team list
Public properties
tchat.chat: last few chat messages
tchat.team: team list
tchat.team_online: online team list
tchat.players: currently online players
Settings
bool tchat_view_chat - if the team chat is shown
bool tchat_view_team_list - if the team list is shown
bool tchat_view_player_list - if the player list is shown
bool tchat_team_mode - if team mode is on
bool tchat_colorize_team - if true, team list will show all team members colored for who is online
bool tchat_use_wisp - if true, encrypt all messages using Wisp
str tchat_prefix_message - prefix for teamchat messages
str tchat_prefix_receive - prefix for received messages
str tchat_prefix_self - prefix for self sent messages
str tchat_prefix_send - prefix for sent messages
str tchat_blacklist - comma separated list of accounts that cannot send team chat messages (useful for secret alts)
num tchat_chat_length - chat length (messages, not lines)
num tchat_chat_width - chat width (columns)
--]]
---
-- settings
local function init_settings(setting_table)
for k, v in pairs(setting_table) do
if minetest.settings:get(k) == nil then
if type(v) == "boolean" then
minetest.settings:set_bool(k, v)
else
minetest.settings:set(k, v)
end
end
end
end
init_settings({
tchat_view_chat = false,
tchat_view_team_list = true,
tchat_view_player_list = true,
tchat_team_mode = false,
tchat_colorize_team = false,
tchat_prefix_message = "TCHAT",
tchat_prefix_receive = "From",
tchat_prefix_self = "To Yourself",
tchat_prefix_send = "To",
tchat_use_wisp = false,
tchat_hide_sent = true,
tchat_blacklist = "",
tchat_chat_length = 6,
tchat_chat_width = 80
})
---
-- globals
tchat = {}
tchat.team = {}
tchat.team_online = {}
tchat.chat = {}
tchat.players = {}
-- used for logs
local server_info = minetest.get_server_info()
local server_id = server_info.address .. ':' .. server_info.port
local max_total_chat_length = 1024
local player_list_epoch = 0
local message_prefix = minetest.settings:get("tchat_prefix_message")
local message_receive = minetest.settings:get("tchat_prefix_receive")
local message_receive_self = minetest.settings:get("tchat_prefix_self")
local message_to = minetest.settings:get("tchat_prefix_send")
local team_mode = minetest.settings:get_bool("tchat_team_mode")
local use_wisp = minetest.settings:get_bool("tchat_use_wisp")
local hide_sent = minetest.settings:get_bool("tchat_hide_sent")
local blacklist = string.split(minetest.settings:get("tchat_blacklist"))
local chat_length = tonumber(minetest.settings:get("tchat_chat_length"))
local chat_width = tonumber(minetest.settings:get("tchat_chat_width"))
local storage = minetest.get_mod_storage()
if storage:get("tchat_team") == nil or storage:get("tchat_team") == "null" then
storage:set_string("tchat_team", "[]")
end
tchat.team = minetest.parse_json(storage:get_string("tchat_team"))
-- overrides contains_coords() the next time it runs
local message_confirmed_safe = false
-- coordinate matching
local pattern = "[-]?%d[.%d]*"
local space = "[,%s]+"
local pattern_three = pattern .. space .. pattern .. space .. pattern
local pattern_two = pattern .. space .. pattern
local chat_idx
local player_list_idx
local team_list_idx
local chat_str = ""
---
-- private stuff
local function apply(list, func, filter)
local out = {}
for k, v in ipairs(list) do
if filter(v) then
out[#out + 1] = func(v)
else
out[#out + 1] = v
end
end
return out
end
local function uniq(list)
local last
local out = {}
for k, v in ipairs(list) do
if last ~= v then
out[#out + 1] = v
end
last = v
end
return out
end
-- limit a list to the last size elements
local function limit_list(list, size)
local out = {}
for i = math.max(1, #list - size), #list do
out[#out + 1] = list[i]
end
return out
end
local function in_list(list, value)
for k, v in ipairs(list) do
if v == value then
return true
end
end
return false
end
local function get_team_str()
if minetest.settings:get_bool("tchat_colorize_team") then
return table.concat(apply(tchat.team,
function(value)
return minetest.colorize("#00FFFF", value)
end,
function(value)
return in_list(tchat.team_online, value)
end), "\n")
else
return table.concat(tchat.team_online, "\n")
end
end
local function display_chat()
return minetest.localplayer:hud_add({
hud_elem_type = 'text',
name = "Teamchat",
text = "Team Chat\n\n" .. chat_str,
number = 0xEEFFEE,
direction = 0,
position = {x=0.01, y=0.45},
scale = {x=0.9, y=0.9},
alignment = {x=1, y=1},
offset = {x=0, y=0}
})
end
local function display_player_list()
return minetest.localplayer:hud_add({
hud_elem_type = 'text',
name = "Online Players",
text = "Players\n\n" .. table.concat(tchat.players, "\n"),
number = 0xDDFFDD,
direction = 0,
position = {x=0.9, y=0.01},
alignment = {x=1, y=1},
offset = {x=0, y=0}
})
end
-- should prob have all team members with online ones colored
local function display_team_list()
return minetest.localplayer:hud_add({
hud_elem_type = 'text',
name = "Team",
text = "Team\n\n" .. get_team_str(),
number = 0x00FF00,
direction = 0,
position = {x=0.8, y=0.01},
alignment = {x=1, y=1},
offset = {x=0, y=0}
})
end
local function auto_display(idx, setting, func)
if minetest.settings:get_bool(setting) then
if not idx then
return func()
end
else
if idx then
minetest.localplayer:hud_remove(idx)
return nil
end
end
return idx
end
local function auto_update(idx, text)
if idx ~= nil then
minetest.localplayer:hud_change(idx, "text", text)
end
end
local function update_team_online()
tchat.team_online = {}
for k, v in ipairs(tchat.players) do
if in_list(tchat.team, v) then
tchat.team_online[#tchat.team_online + 1] = v
end
end
end
local function update_chat_str()
chat_str = ""
for k, v in ipairs(limit_list(tchat.chat, chat_length - 1)) do
chat_str = chat_str .. "\n" .. minetest.wrap_text(v, chat_width)
end
chat_str = table.concat(limit_list(string.split(chat_str, "\n"), chat_length - 1), "\n")
-- update chat (do it here so external mods can add to the chat)
auto_update(chat_idx, "Team Chat\n\n" .. chat_str)
end
local function team_add_self()
tchat.team_add_player(minetest.localplayer:get_name())
end
---
-- public interface
function tchat.contains_coords(message)
if (not message_confirmed_safe and (message:find(pattern_three) or message:find(pattern_two))) then
return true
end
return false
end
local function dm(player, message)
if wisp == nil or not use_wisp then
minetest.send_chat_message("/msg " .. player .." " .. message)
else
wisp.send(player, message, true)
end
end
-- send
function tchat.send(message, force_coords, force_commands)
if (tchat.contains_coords(message) and not force_coords) or in_list(blacklist, minetest.localplayer:get_name()) then
return
end
if message:sub(1,1) == "/" and not force_commands then
minetest.display_chat_message("A /command was scheduled to be sent to team chat but wasn't sent.")
return
end
local me = minetest.localplayer:get_name()
if not in_list(tchat.team, minetest.localplayer:get_name()) then
team_add_self()
end
update_team_online()
local prepend = ""
if use_wisp then
prepend = "E "
end
tchat.chat_append(prepend .. me .. ": " .. message)
for k, p in ipairs(tchat.team_online) do
if p ~= me then
dm(p, message_prefix .. " " .. message)
end
end
return true
end
function tchat.send_conditional(message, inverse, force_coords)
if tchat.contains_coords(message) and not force_coords then
return
end
team_mode = minetest.settings:get_bool("tchat_team_mode")
local tm = team_mode
if inverse then
tm = not team_mode
end
if tm then
tchat.send(message)
return true
else
minetest.send_chat_message(message)
return false
end
end
function tchat.send_coords(message)
message_confirmed_safe = true
local ret = tchat.send_conditional(message)
message_confirmed_safe = false
return ret
end
function tchat.whisper_coords(player)
if player == "" then
return
end
local coords = minetest.pos_to_string(vector.round(minetest.localplayer:get_pos()))
minetest.run_server_chatcommand("w", param .. " " .. coords)
end
-- chat
local function autoclear_chat()
if #tchat.chat > max_total_chat_length then
tchat = limit_list(tchat.chat, max_chat_total_length)
end
end
function tchat.chat_clear()
tchat.chat = {}
update_chat_str()
end
function tchat.chat_set(message_list)
chat = message_list
autoclear_chat()
update_chat_str()
end
function tchat.chat_append(message)
tchat.chat[#tchat.chat + 1] = message
autoclear_chat()
minetest.log("action", "[tchat] " .. minetest.localplayer:get_name() .. "@" .. server_id .. " " .. message)
update_chat_str()
-- popup chat if its closed
minetest.settings:set_bool("tchat_view_chat", true)
chat_idx = auto_display(chat_idx, "tchat_view_chat", display_chat)
end
local function team_save()
storage:set_string("tchat_team" , minetest.write_json(tchat.team))
end
-- team
function tchat.team_add_player(player)
if not in_list(tchat.team, player) then
tchat.team[#tchat.team + 1] = player
update_team_online()
team_save()
end
end
function tchat.team_remove_player(player)
local out = {}
for k, v in ipairs(tchat.team) do
if v ~= player then
out[#out + 1] = v
end
end
tchat.team = out
team_save()
end
function tchat.team_clear()
tchat.team = {}
team_save()
end
function tchat.team_set(player_list)
tchat.team = player_list
team_save()
end
---
-- callbacks
minetest.register_on_sending_chat_message(function(message)
if tchat.contains_coords(message) then
minetest.display_chat_message("Message contained coordinates, be careful.")
return true
end
team_mode = minetest.settings:get_bool("tchat_team_mode")
if not team_mode then
return
end
tchat.send(message)
return true
end)
local function message_sent(message)
return message == "Message sent."
end
local function clean_message(message)
-- dirty, strips out legitimate uses of the prefix
message = message:gsub(message_prefix, "")
message = message:gsub("^" .. message_receive, "")
message = message:gsub("^" .. message_receive_self, minetest.localplayer:get_name())
message = message:gsub(": ", ": ")
message = message:match("^%s*(.-)%s*$")
return message
end
-- greedily be the first in the receiving list (prob doesnt always work)
table.insert(minetest.registered_on_receiving_chat_message, 1, function(message)
if hide_sent and message_sent(message) then
return true
end
-- bit dirty, doesnt check the prefix position
if not message:find(message_prefix) then
return
end
local player = message:match(message_receive .. " (.+): " .. message_prefix)
local from_self = message:sub(1, message_receive_self:len()) == message_receive_self
local received = message:sub(1, message_receive:len()) == message_receive
local sent = message:sub(1, message_to:len()) == message_to
if sent and not from_self then
return true
end
if not from_self and not in_list(tchat.team_online, player) then
return
end
-- add to chat list
if from_self or received then
tchat.chat_append(clean_message(message))
return true
end
end)
if wisp ~= nil then
wisp.register_on_receive_split(function(player, message)
if message:find(message_prefix) then
tchat.chat_append("E " .. player .. ": " .. clean_message(message))
return true
end
end)
end
minetest.register_globalstep(function()
-- update data
if player_list_epoch < os.time() + 2 then
-- update players, remove duplicates
tchat.players = minetest.get_player_names()
table.sort(tchat.players)
tchat.players = uniq(tchat.players)
update_team_online()
-- update HUD
auto_update(player_list_idx, "Players\n\n" .. table.concat(tchat.players, "\n"))
auto_update(team_list_idx, "Team\n\n" .. get_team_str())
player_list_epoch = os.time()
end
-- display (if we need to)
if minetest.localplayer then
chat_idx = auto_display(chat_idx, "tchat_view_chat", display_chat)
player_list_idx = auto_display(player_list_idx, "tchat_view_player_list", display_player_list)
team_list_idx = auto_display(team_list_idx, "tchat_view_team_list", display_team_list)
end
end)
---
-- command/cheat interface
minetest.register_chatcommand("t", {
params = "<message>",
description = "Send a message to your team chat, or regular chat if team mode is on.",
func = function(message)
if tchat.contains_coords(message) then
minetest.display_chat_message("Message contained coordinates, be careful.")
return
end
tchat.send_conditional(message, true)
end
})
minetest.register_chatcommand("tcoords", {
params = "<message>",
description = "Send a message containing coordinates to teamchat.",
func = function(message)
tchat.send(message, true)
end
})
minetest.register_chatcommand("tlist", {
description = "List your team.",
func = function(param)
minetest.display_chat_message(table.concat(tchat.team, ", "))
end
})
minetest.register_chatcommand("tadd", {
params = "<player>",
description = "Add player to your team.",
func = tchat.team_add_player
})
minetest.register_chatcommand("tdel", {
params = "<player>",
description = "Remove player from your team.",
func = tchat.team_remove_player
})
minetest.register_chatcommand("tclear", {
description = "Clear team list.",
func = tchat.team_clear
})
minetest.register_chatcommand("tchat_clear", {
description = "Clear team chat widget.",
func = tchat.chat_clear
})
minetest.register_chatcommand("coords", {
params = "<message>",
description = "Send message containing coordinates.",
func = tchat.send_coords
})
minetest.register_chatcommand("mcoord", {
params = "<player>",
description = "Whisper current coordinates to player.",
func = tchat.whisper_coords
})
-- this fallbacks to showing everything if the cheat menu is unavailable
-- use advanced settings instead :]
if (_G["minetest"]["register_cheat"] == nil) then
minetest.settings:set_bool('tchat_team_mode', true)
minetest.settings:set_bool('tchat_view_team_list', true)
minetest.settings:set_bool('tchat_view_player_list', true)
minetest.settings:set_bool('tchat_view_chat', true)
else
minetest.register_cheat("Teamchat Mode", "Chat", "tchat_team_mode")
minetest.register_cheat("Show Team List", "Chat", "tchat_view_team_list")
minetest.register_cheat("Show Player List", "Chat", "tchat_view_player_list")
minetest.register_cheat("Show Teamchat", "Chat", "tchat_view_chat")
end

View File

@ -0,0 +1,3 @@
name = tchat
author = cora, system32
description = Adds a team chat and prevents accidental sending of coordinates. Supports Wisp for encrypting messages.

View File

@ -0,0 +1,18 @@
tchat_view_chat (Show team chat) bool false
tchat_view_team_list (Show team list) bool true
tchat_view_player_list (Show player list) bool true
tchat_team_mode (Team mode) bool false
tchat_colorize_team (Show colorized team list) bool false
tchat_use_wisp (Use Wisp to encrypt outgoing messages) bool false
tchat_prefix_message (Message Prefix) string TCHAT
tchat_prefix_receive (Received PM starting string) string From
tchat_prefix_self (Received PM starting string to yourself) string To Yourself
tchat_prefix_send (Outgoing PM starting string) string To
tchat_blacklist (Names that can't use team chat) string
tchat_hide_sent (Hide "Message sent." server messages) bool true
tchat_chat_length (Maximum team chat messages) int 6
tchat_chat_width (Team chat width in columns) int 80

4
clientmods/test/init.lua Normal file
View File

@ -0,0 +1,4 @@
if minetest.settings:get_bool("test_chain") then
local prefix = minetest.get_modpath(minetest.get_current_modname())
dofile(prefix .. "/chain.lua")
end

View File

@ -0,0 +1 @@
test_chain (Load the chain file) bool false

518
clientmods/turtle/init.lua Normal file
View File

@ -0,0 +1,518 @@
-- CC0/Unlicense Emilia 2020
turtle = {}
local mod_prefix = minetest.get_modpath(minetest.get_current_modname())
tlang = dofile(mod_prefix .. "/tlang.lua")
function turtle.coord(x, y, z)
return {x = x, y = y, z = z}
end
turtle.pos1 = turtle.coord(0, 0, 0)
turtle.pos2 = turtle.coord(0, 0, 0)
local function format_coord(c)
return tostring(c.x) .. " " .. tostring(c.y) .. " " .. tostring(c.z)
end
local function parse_coord(c)
end
-- can include ~ + - along with num and ,
local function parse_relative_coord(c)
end
function turtle.ordercoord(c)
if c.x == nil then
return {x = c[1], y = c[2], z = c[3]}
else
return c
end
end
-- x or {x,y,z} or {x=x,y=y,z=z}
function turtle.optcoord(x, y, z)
if y and z then
return turtle.coord(x, y, z)
else
return turtle.ordercoord(x)
end
end
-- swap x and y if x > y
local function swapg(x, y)
if x > y then
return y, x
else
return x, y
end
end
-- swaps coordinates around such that (matching ords of) c1 < c2 and the overall cuboid is the same shape
function turtle.rectify(c1, c2)
c1.x, c2.x = swapg(c1.x, c2.x)
c1.y, c2.y = swapg(c1.y, c2.y)
c1.z, c2.z = swapg(c1.z, c2.z)
return c1, c2
end
-- converts a coordinate to a system where 0,0 is the southwestern corner of the world
function turtle.zeroidx(c)
local side = 30912
return turtle.coord(c.x + side, c.y + side, c.z + side)
end
-- swaps coords and subtracts such that c1 == {0, 0, 0} and c2 is the distance from c1
-- returns rectified c1/c2 and the relativized version
function turtle.relativize(c1, c2)
c1, c2 = turtle.rectify(c1, c2)
local c1z = turtle.zeroidx(c1)
local c2z = turtle.zeroidx(c2)
local rel = turtle.coord(c2z.x - c1z.x, c2z.y - c1z.y, c2z.z - c1z.z)
return c1, rel
end
-- get the inventory index of the best tool to mine x, y, z
-- returns a wield index, which starts at 0
function turtle.get_best_tool_index(x, y, z)
local node = minetest.get_node_or_nil(turtle.optcoord(x, y, z))
if not node then
return
end
local nodecaps = minetest.get_node_def(node.name).groups
local idx = minetest.localplayer:get_wield_index()
local best = math.huge
for i, v in ipairs(minetest.get_inventory("current_player").main) do
for gk, gv in pairs(v:get_tool_capabilities().groupcaps) do
local level = nodecaps[gk]
if level and gv.times[level] < best then
idx = i
best = gv.times[level]
end
end
end
return idx
end
-- switch to the fastest tool to mine x, y, z
function turtle.switch_best(x, y, z)
local prev = minetest.localplayer:get_wield_index()
local index = turtle.get_best_tool_index(x, y, z)
if prev ~= index then
minetest.localplayer:set_wield_index(index)
end
end
function turtle.mine(x, y, z)
turtle.switch_best(x, y, z)
minetest.dig_node(turtle.optcoord(x, y, z))
end
function turtle.place(x, y, z)
minetest.place_node(turtle.optcoord(x, y, z))
end
function turtle.cadd(c1, c2)
return turtle.coord(c1.x + c2.x, c1.y + c2.y, c1.z + c2.z)
end
function turtle.relcoord(x, y, z)
local pos = minetest.localplayer:get_pos()
if pos.y > -5000 then pos.y=pos.y-1 end
return turtle.cadd(pos, turtle.optcoord(x, y, z))
end
local function between(x, y, z) -- x is between y and z (inclusive)
return y <= x and x <= z
end
function turtle.getdir() --
local rot = minetest.localplayer:get_yaw() % 360
if between(rot, 315, 360) or between(rot, 0, 45) then
return "north"
elseif between(rot, 135, 225) then
return "south"
elseif between(rot, 225, 315) then
return "east"
elseif between(rot, 45, 135) then
return "west"
end
end
function turtle.setdir(dir) --
if dir == "north" then
minetest.localplayer:set_yaw(0)
elseif dir == "south" then
minetest.localplayer:set_yaw(180)
elseif dir == "east" then
minetest.localplayer:set_yaw(270)
elseif dir == "west" then
minetest.localplayer:set_yaw(90)
end
end
function turtle.dircoord(f, y, r)
local dir=turtle.getdir()
local coord = turtle.optcoord(f, y, r)
local f = coord.x
local y = coord.y
local r = coord.z
local lp=minetest.localplayer:get_pos()
if dir == "north" then
return turtle.relcoord(r, y, f)
elseif dir == "south" then
return turtle.relcoord(-r, y, -f)
elseif dir == "east" then
return turtle.relcoord(f, y, -r)
elseif dir== "west" then
return turtle.relcoord(-f, y, r)
end
return turtle.relcoord(0, 0, 0)
end
function turtle.move(x, y, z)
minetest.localplayer:set_pos(turtle.optcoord(x, y, z))
end
function turtle.advance(amount)
amount = amount or 1
turtle.move(turtle.dircoord(amount, 0, 0))
end
function turtle.descend(amount)
amount = amount or 1
turtle.move(turtle.relcoord(0, -amount, 0))
end
function turtle.rotate_abs(deg)
minetest.localplayer:set_yaw(deg)
end
function turtle.rotate(deg)
local prev = minetest.localplayer:get_yaw()
minetest.localplayer:set_yaw((prev + deg) % 360)
end
function turtle.rotate_right(deg)
deg = deg or 90
turtle.rotate(-deg)
end
function turtle.rotate_left(deg)
deg = deg or 90
turtle.rotate(deg)
end
function turtle.isblock(block, x, y, z)
local node = minetest.get_node_or_nil(turtle.optcoord(x, y, z))
return node ~= nil and block == node.name
end
function turtle.checkmine(x, y, z)
while true do
turtle.mine(x, y, z)
busysleep(0.125)
-- i hate lua
minetest.log(tostring(turtle.isblock("air", x, y, z)))
if turtle.isblock("air", x, y, z) then
break
end
end
end
function turtle.tp(coords)
minetest.localplayer:set_pos(coords)
end
function turtle.moveto(x, y, z)
turtle.tp(turtle.optcoord(x, y, z))
end
function turtle.linemine(distance, func)
for i = 1, distance do
turtle.checkmine(turtle.dircoord(1, 1, 0))
turtle.checkmine(turtle.dircoord(1, 0, 0))
turtle.advance()
if func then
func()
end
end
end
local function left_or_right(left)
if left then
turtle.rotate_left()
else
turtle.rotate_right()
end
end
local function quarry_clear_liquids()
-- puts blocks in front, both sides, and two below where they are liquid
-- it does all this one step ahead so no spillage may occur
end
-- needs to check for liquids (would need to be done in linemine)
function turtle.quarry(cstart, cend)
-- get a nice cuboid
cstart, cend = turtle.rectify(cstart, cend)
local start, relend = turtle.relativize(cstart, cend)
-- makes it start at the top rather than the bottom
cend.y, cstart.y = swapg(cend.y, cstart.y)
-- go to the start
turtle.moveto(turtle.cadd(cstart, turtle.coord(0, 1, 0)))
turtle.rotate_abs(0)
-- main loop (zig zag pattern)
for height = 0, math.floor(relend.y / 2) do
-- go down two blocks
turtle.mine(turtle.relcoord(0, -1, 0))
turtle.mine(turtle.relcoord(0, -2, 0))
turtle.descend(2)
for width = 0, relend.x do
-- swaps left/right rotations each layer and zig zag
local leftiness = ((height + width + 1) % 2) == 0
-- actually mine
turtle.linemine(relend.z) -- maybe relend.z to make the end inclusive?
left_or_right(leftiness)
-- dont rotate at the end of the layer
if width ~= relend.x then
turtle.linemine(1)
left_or_right(leftiness)
end
end
-- flip around to start again on the next layer
turtle.rotate(180)
end
end
minetest.register_chatcommand("quarry", {
func = function()
turtle.quarry({x = -60, y = 1, z = -60}, {x = -40, y = -5, z = -40})
end
})
turtle.builtins = {}
function turtle.builtins.mine(state)
end
function turtle.builtins.advance(state)
end
function turtle.builtins.descend(state)
end
function turtle.builtins.v3add(state)
end
function turtle.builtins.rotate(state)
end
function turtle.builtins.relativize(state)
end
function turtle.builtins.swapg(state)
end
function turtle.builtins.rectify(state)
end
local quarry_tlang = [[
# turtle.builtins: mine advance v3add descend rotate relativize swapg rectify
# tlang operators: //
################################
# Mine ahead length nodes (including head and feet)
{ 0 `length args
####
{
i length == {break} if
[1 1 0] dircoord mine
[1 0 0] dircoord mine
1 wait
advance
} `i forever
} `linemine =
################################
# Mine the cuboid defined by start and end
{ 0 `start `end args
####
rectify `start = `end =
start end relativize
start end swapg_y `relstart = `relend =
start [0 1 0] v3add moveto
0 rotate_abs
relend.y 2 // `yend =
{
height yend > {break} if
[0 -1 0] dircoord mine
[0 -2 0] dircoord mine
2 descend
{
width relend.x > {break} if
height width + 1 + 2 % 0 == `leftiness =
relend.z linemine
leftiness left_or_right
width relend.x != {
1 linemine
leftiness left_or_right
} if
} `width forever
180 rotate
} `height forever
} `quarry =
]]
turtle.states = {}
turtle.states_available = false
function turtle.schedule(name, state)
if type(name) == "table" then
error("turtle.schedule: first parameter should be the task's name")
return
end
turtle.states[#turtle.states + 1] = {name = name, state = state}
turtle.states_available = true
end
function turtle.get_symbolic(name)
local dead = {}
for i, v in ipairs(turtle.states) do
if i == name or v.name == name then
table.insert(dead, 1, i)
end
end
return dead
end
function turtle.kill_symbolic(name)
local dead = turtle.get_symbolic(name)
for i, v in ipairs(dead) do
table.remove(turtle.states, v)
end
end
function turtle.pause_symbolic(name)
local dead = turtle.get_symbolic(name)
for i, v in ipairs(dead) do
turtle.states[v].state.paused = true
end
end
function turtle.resume_symbolic(name)
local dead = turtle.get_symbolic(name)
for i, v in ipairs(dead) do
turtle.states[v].state.paused = nil
end
end
function turtle.run_states(dtime)
if turtle.states_available then
local dead = {}
for i, v in ipairs(turtle.states) do
local ret = tlang.step(v.state)
if ret ~= true and ret ~= nil then
if type(ret) == "string" then
minetest.display_chat_message("Turtle/tlang ERROR in " .. v.name .. ": " .. ret)
end
table.insert(dead, 1, i)
end
end
for i, v in ipairs(dead) do
table.remove(turtle.states, v)
end
turtle.states_available = #turtle.states ~= 0
end
end
minetest.register_globalstep(turtle.run_states)
minetest.register_chatcommand("tlang", {
description = "Run a tlang program.",
params = "<code>",
func = function(params)
local state = tlang.get_state(params)
turtle.schedule("chat_script", state)
end
})
minetest.register_chatcommand("tl_list", {
description = "List running tlang states.",
func = function()
for i, v in ipairs(turtle.states) do
minetest.display_chat_message(tostring(i) .. " " .. v.name)
end
end
})
minetest.register_chatcommand("tl_kill", {
description = "Kill a tlang state.",
params = "<task>",
func = turtle.kill_symbolic
})
minetest.register_chatcommand("tl_pause", {
description = "Pause a tlang state.",
params = "<task>",
func = turtle.pause_symbolic
})
minetest.register_chatcommand("tl_resume", {
description = "Resume a tlang state.",
params = "<task>",
func = turtle.resume_symbolic
})

View File

@ -0,0 +1,3 @@
name = turtle
author = system32
description = Adds a system to control the player like a graphics turtle.

263
clientmods/turtle/tlang.lua Normal file
View File

@ -0,0 +1,263 @@
-- CC0/Unlicense Emilia 2020
local tlang = {}
local prefix = ""
if minetest ~= nil then
prefix = minetest.get_modpath(minetest.get_current_modname()) .. "/"
end
local function merge_tables(l1, l2)
local out = {}
for k, v in pairs(l1) do
out[k] = v
end
for k, v in pairs(l2) do
out[k] = v
end
return out
end
local function load_api_file(file)
loadfile(prefix .. file)(tlang)
end
load_api_file("tlang_lex.lua")
load_api_file("tlang_parse.lua")
load_api_file("tlang_vm.lua")
function tlang.combine_builtins(b1, b2)
return merge_tables(b1, b2)
end
function tlang.construct_builtins(builtins)
return merge_tables(tlang.builtins, builtins)
end
-- TODO
--[[
lexer should include line/character number in symbols
error messages
maps should be able to have out of order number indexes (like [1 2 3 10:"Out of order"])
map.key accessing syntax
parse as identifier, include . as identifier character, split on . and thats the indexing tree
--]]
function tlang.run(state)
while true do
local more = tlang.step(state)
if more == true or more == nil then
-- continue along
elseif type(more) == "string" then
print(more) -- error
elseif more == false then
return -- done
else
print("Unknown error, tlang.step returned: " .. tostring(more))
end
end
end
function tlang.get_state(code)
local lexed = tlang.lex(code)
local parsed = tlang.parse(lexed)
return {
locals = {{
pc = {sg = 1, pos = {"__ast__"}, elem = 1},
vars = {
__src__ = tlang.value_to_tlang(code),
__lex__ = tlang.value_to_tlang(lexed),
__ast__ = {type = "code", value = parsed}
}
}},
stack = {},
code_stack = {},
builtins = tlang.builtins
}
end
function tlang.exec(code)
local state = tlang.get_state(code)
tlang.run(state)
end
function tlang.pretty_pc(pc)
return tostring(pc.sg) .. ";" .. table.concat(pc.pos, ".") .. ";" .. tostring(pc.elem)
end
function tlang.format_table(t, depth, maxdepth)
depth = depth or 0
maxdepth = maxdepth or -1
if depth == maxdepth then
return "{...}"
end
local out = {}
out[1] = "{\n"
for k, v in pairs(t) do
local idx = k
if type(k) == "string" then
idx = '"' .. k .. '"'
elseif type(k) == "table" then
idx = "{...}"
end
out[#out + 1] = string.rep("\t", depth + 1) .. "[" .. idx .. "] = "
if type(v) == "table" then
out[#out + 1] = tlang.format_table(v, depth + 1, maxdepth)
elseif type(v) == "string" then
out[#out + 1] = '"' .. v .. '"'
else
out[#out + 1] = tostring(v)
end
out[#out + 1] = ",\n"
end
out[#out + 1] = string.rep("\t", depth) .. "}"
return table.concat(out)
end
function tlang.print_table(t, maxdepth)
print(tlang.format_table(t, nil, maxdepth))
end
local function test()
local complex = [[{dup *} `square =
-5.42 square
"Hello, world!" print
[1 2 3 str:"String"]
]]
local number = "-4.2123"
local simple = "{dup *}"
local map = "[this:2 that:3]"
local square = [[{dup *} `square =
5 square print]]
local square_run = "5 {dup *} run print"
local comment_test = "'asd' print # 'aft' print"
local forever_test = [[
5 # iteration count
{
dup # duplicate iter count
print # print countdown
-- # decrement
dup 0 == # check if TOS is 0
{break} if # break if TOS == 0
}
forever # run loop
]]
local local_test = [[
'outside' `var =
{
var print # should be 'outside'
'inside' `var =
var print # should be 'inside'
} run
var print # should be 'inside'
]]
local while_test = [[
5 `cur =
{
`cur --
cur
} {
"four times" print
} while
]]
local repeat_test = [[
{
"four times" print
} 4 repeat
{
i print
} 5 `i repeat
]]
local stack_test = "5 5 == print"
local args_test = [[
{ 0 `first `second args
first print
second print
} `test =
1 2 test
]]
local ifelse_test = [[
{
{
'if' print
} {
'else' print
} if
} `ifprint =
1 ifprint
0 ifprint
]]
local nest_run = [[
{
{
'innermost' print
} run
} run
'work' print
]]
local mapid_test = "this.that.2.here .81..wao.88912"
local paren_test = "('works' print) 'out' print"
local mapdot_test = [[
[1 a:5 b:[a:2 b:3] 3] `a =
4 `a.a =
a.1 print
a.2 print
a.a print
a.b.b print
]]
local stackdot_test = [[
[a:1 b:2]
.b print
6 `.a =
.a print
]]
local funcfunc_test = [[
{dup *} `square =
{dup square *} `cube =
5 cube print
]]
local test = funcfunc_test
--tlang.print_table(tlang.lex(test))
--tlang.print_table(tlang.parse(tlang.lex(test)))
tlang.exec(test)
end
if minetest == nil then
test()
end
return tlang

View File

@ -0,0 +1,345 @@
-- CC0/Unlicense Emilia 2020
local tlang = ...
local function in_list(value, list)
for k, v in ipairs(list) do
if v == value then
return true
end
end
return false
end
-- lex state
--[[
{
code = "",
position = int
}
--]]
-- lex types
--[[
literal
number
quote
identifier
mapid -- TEMP
string
symbol
code_open
code_close
code_e_open
code_e_close
map_open
map_close
map_relation
--]]
-- yeah yeah regex im lazy in this time consuming way shush
local whitespace = {" ", "\t", "\n", "\r", "\v"}
local identifier_start = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"_", "."
}
local identifier_internal = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"_",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
}
local symbol_start = {"!", "-", "+", "=", "&", "*", "/", "^", "%", ">", "<", "?", "~"}
local symbol_values = {
"!", "-", "+", "=", "&", "*", "/", "^", "%", ">", "<", "?", "~"
}
local string_start = {"\"", "'"}
local number_start = {"-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
local number_values = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
local escape_values = {n = "\n", r = "\r", v = "\v", t = "\t", ['"'] = '"', ["'"] = "'"}
local symbols = {
"!", "-", "+", "=", "&", "*", "/", "^", "%", ">", "<", "?", "~",
"&&", "||", "==", "!=", ">=", "<=", "--", "++"
}
local function lex_peek(state)
local out = state.code:sub(state.position, state.position)
if out == "" then
return nil
end
return out
end
local function lex_next(state)
local value = lex_peek(state)
state.position = state.position + 1
return value
end
local function lex_expect(state, chars)
if type(chars) == "string" then
chars = {chars}
end
local n = lex_next(state)
if in_list(n, chars) then
return n
else
return nil -- ERROR!
end
end
local function lex_whitespace(state)
while true do
local n = lex_peek(state)
if not in_list(n, whitespace) then
return
end
lex_next(state)
end
end
local function lex_identifier_raw(state, top)
local identifier = {}
local n = 1
while true do
local cur = lex_peek(state)
if in_list(cur, identifier_internal) then
identifier[n] = lex_next(state)
n = n + 1
elseif cur == "." then
lex_next(state)
local subs = lex_identifier_raw(state)
if type(subs) == "string" then
subs = {subs}
end
if n > 1 then
table.insert(subs, 1, table.concat(identifier))
elseif top then -- TOS .key.key syntax
table.insert(subs, 1, '')
end
return subs
else
break
end
end
return {table.concat(identifier)}
end
local function lex_identifier(state)
local id = lex_identifier_raw(state, true)
return {type = "literal", subtype = "identifier", value = id}
end
-- `identifier
local function lex_quote(state)
lex_next(state)
local val = lex_identifier(state)
val.subtype = "quote"
return val
end
local function lex_single_char(state, t, char)
lex_next(state)
return {type = t, value = char}
end
local function lex_code_open(state)
return lex_single_char(state, "code_open", "{")
end
local function lex_code_close(state)
return lex_single_char(state, "code_close", "}")
end
local function lex_code_e_open(state)
return lex_single_char(state, "code_e_open", "(")
end
local function lex_code_e_close(state)
return lex_single_char(state, "code_e_close", ")")
end
local function lex_map_open(state)
return lex_single_char(state, "map_open", "[")
end
local function lex_map_relation(state)
return lex_single_char(state, "map_relation", ":")
end
local function lex_map_close(state)
return lex_single_char(state, "map_close", "]")
end
local function lex_string_escape(state)
local n = lex_next(state)
return escape_values[n]
end
local function lex_string(state)
local bchar = lex_next(state)
local escaped = false
local string = {}
local stringi = 1
while true do
local n = lex_next(state)
if n == bchar then
return {type = "literal", subtype = "string", value = table.concat(string)}
elseif n == "\\" then
n = lex_string_escape(state)
end
if n == nil then
return nil -- ERROR
end
string[stringi] = n
stringi = stringi + 1
end
end
local function lex_number(state)
local used_sep = false
local num = {}
local numi = 1
local n = lex_peek(state)
if in_list(n, number_start) then
num[numi] = lex_next(state)
numi = numi + 1
while true do
n = lex_peek(state)
if n == "." and not used_sep then
used_sep = true
elseif not in_list(n, number_values) then
return {type = "literal", subtype = "number", value = table.concat(num)}
end
num[numi] = lex_next(state)
numi = numi + 1
end
end
end
local function lex_symbol(state)
local sym = {}
local symi = 1
while true do
local n = lex_peek(state)
if not in_list(n, symbol_values) then
local symbol = table.concat(sym)
if in_list(symbol, symbols) then
return {type = "symbol", value = symbol}
else
return nil -- ERROR
end
elseif n == nil then
return nil -- ERROR
else
sym[symi] = lex_next(state)
symi = symi + 1
end
end
end
local function lex_number_or_symbol(state)
local nextpeek = state.code:sub(state.position + 1, state.position + 1)
if in_list(nextpeek, number_values) then
return lex_number(state)
else
return lex_symbol(state)
end
end
local function lex_comment(state)
while true do
local n = lex_next(state)
if n == nil or n == "\n" then
return false
end
end
end
local function lex_step(state)
local cur = lex_peek(state)
if cur == nil then
return nil
end
if in_list(cur, whitespace) then
lex_whitespace(state)
end
cur = lex_peek(state)
if cur == "`" then
return lex_quote(state)
elseif cur == "-" then -- special case for negative numbers and the minus
return lex_number_or_symbol(state)
elseif in_list(cur, symbol_start) then
return lex_symbol(state)
elseif cur == "{" then
return lex_code_open(state)
elseif cur == "}" then
return lex_code_close(state)
elseif cur == "(" then
return lex_code_e_open(state)
elseif cur == ")" then
return lex_code_e_close(state)
elseif cur == "[" then
return lex_map_open(state)
elseif cur == "]" then
return lex_map_close(state)
elseif cur == ":" then
return lex_map_relation(state)
elseif in_list(cur, identifier_start) then
return lex_identifier(state)
elseif in_list(cur, string_start) then
return lex_string(state)
elseif in_list(cur, number_start) then
return lex_number(state)
elseif cur == "#" then
return lex_comment(state)
end
end
-- lex
function tlang.lex(code)
local state = {code = code, position = 1}
local lexed = {}
local lexi = 1
while true do
local n = lex_step(state)
if n == nil then
if state.position <= #state.code then
return nil
else
return lexed
end
end
-- comment lexer returns false
if n ~= false then
lexed[lexi] = n
lexi = lexi + 1
end
end
end

View File

@ -0,0 +1,208 @@
-- CC0/Unlicense Emilia 2020
-- parse types
--[[
quote
identifier
code
map
string
number
symbol
--]]
local tlang = ...
local internal = {}
local function sublist(list, istart, iend, inclusive)
local o = {}
local oi = 1
inclusive = inclusive or false
for i, v in ipairs(list) do
iend = iend or 0 -- idk how but iend can become nil
local uninc = i > istart and i < iend
local incl = i >= istart and i <= iend
if (inclusive and incl) or (not inclusive and uninc) then
o[oi] = v
oi = oi + 1
end
end
return o
end
local function parse_peek(state)
return state.lexed[state.position]
end
local function parse_next(state)
local n = parse_peek(state)
state.position = state.position + 1
return n
end
local function parse_identifier(state)
local lexid = parse_next(state).value
for i, v in ipairs(lexid) do
if v:match("^[0-9]+$") then
lexid[i] = tonumber(v)
end
end
return {type = "identifier", value = lexid}
end
local function parse_map(state)
local map = {}
local mapi = 1
if parse_next(state).type ~= "map_open" then
return nil -- ERROR
end
while true do
local n = parse_next(state)
local skip = false -- lua has no continue, 5.1 has no goto
if n == nil then
return nil -- ERROR
end
if n.type == "map_close" then
break
elseif n.type == "literal" and (n.subtype == "identifier" or n.subtype == "string") then
local key = n.value
local mr = parse_peek(state)
if type(key) == "table" then
key = key[1]
end
if mr.type == "map_relation" then
parse_next(state)
local nval = internal.parse_step(state)
if nval == nil then
return nil -- ERROR
end
map[key] = nval
skip = true
end
end
if not skip then
local nval = tlang.parse({n})
if nval == nil then
return nil -- ERROR
end
map[mapi] = nval[1]
mapi = mapi + 1
end
end
return {type = "map", value = map}
end
local function parse_find_matching(state, open, close)
local level = 1
parse_next(state) -- skip beginning
while level ~= 0 do
local n = parse_next(state)
if n == nil then
return nil -- ERROR
elseif n.type == open then
level = level + 1
elseif n.type == close then
level = level - 1
end
end
return state.position - 1
end
local function parse_code(state, open, close)
local istart = state.position
local iend = parse_find_matching(state, open, close)
return {
type = "code",
value = tlang.parse(sublist(state.lexed, istart, iend))
}
end
function internal.parse_step(state)
local n = parse_peek(state)
if n == nil then
return nil
elseif n.type == "code_open" then
return parse_code(state, "code_open", "code_close")
elseif n.type == "code_e_open" then
return {
parse_code(state, "code_e_open", "code_e_close"),
{type = "identifier", value = "run"}
}
-- also return run
elseif n.type == "map_open" then
local istart = state.position
local iend = parse_find_matching(state, "map_open", "map_close")
return parse_map({lexed = sublist(state.lexed, istart, iend, true), position = 1})
elseif n.type == "literal" then
if n.subtype == "number" then
parse_next(state)
return {type = "number", value = tonumber(n.value)}
elseif n.subtype == "string" then
parse_next(state)
return {type = "string", value = n.value}
elseif n.subtype == "identifier" then
return parse_identifier(state)
elseif n.subtype == "quote" then
parse_next(state)
return {type = "quote", value = n.value}
end
elseif n.type == "symbol" then
parse_next(state)
return {type = "symbol", value = n.value}
end
end
-- parse
function tlang.parse(lexed)
local state = {lexed = lexed, position = 1}
local tree = {}
local treei = 1
while true do
local n = internal.parse_step(state)
if n == nil then
if state.position <= #state.lexed then
return nil
else
return tree
end
end
if n.type == nil then -- () = {} run
tree[treei] = n[1]
tree[treei + 1] = n[2]
treei = treei + 2
else
tree[treei] = n
treei = treei + 1
end
end
end

View File

@ -0,0 +1,741 @@
-- CC0/Unlicense Emilia 2020
local tlang = ...
local function in_list(value, list)
for k, v in ipairs(list) do
if v == value then
return true
end
end
return false
end
local function in_keys(value, list)
return list[value] ~= nil
end
-- state
--[[
{
locals = {},
stack = {},
builtins = {},
code_stack = {},
wait_target = float,
paused = f/t,
nextpop = f/t
}
--]]
-- program counter
--[[
sg = 0/1,
pos = int/string,
elem = int
--]]
function tlang.boolean_to_number(b)
if b then
return 1
else
return 0
end
end
function tlang.number_to_boolean(n)
if n ~= 0 then
return true
else
return false
end
end
-- convert a lua value into a tlang literal
function tlang.value_to_tlang(value)
local t = type(value)
if t == "string" then
return {type = "string", value = value}
elseif t == "number" then
return {type = "number", value = value}
elseif t == "boolean" then
return {type = "number", value = tlang.boolean_to_number(value)}
elseif t == "table" then
local map = {}
for k, v in pairs(value) do
map[k] = tlang.value_to_tlang(v)
end
return {type = "map", value = map}
end
end
-- convert a tlang literal to a lua value
function tlang.tlang_to_value(tl)
if type(tl) ~= "table" then
return
end
if tl.type == "map" then
local o = {}
for k, v in pairs(tl.value) do
o[k] = tlang.tlang_to_value(v)
end
return o
else
return tl.value
end
end
local literals = {
"quote",
"code",
"map",
"string",
"number"
}
function tlang.call(state, target)
if target.sg == 0 then
state.code_stack[#state.code_stack + 1] = state.stack[target.pos]
table.remove(state.stack, target.pos)
target.pos = #state.code_stack
end
state.locals[#state.locals + 1] = {vars = {}, pc = target}
end
function tlang.call_tos(state)
tlang.call(state, {sg = 0, pos = #state.stack, elem = 1})
end
function tlang.call_var(state, name)
if type(name) ~= "table" then
name = {name}
end
tlang.call(state, {sg = 1, pos = name, elem = 1})
end
function tlang.call_builtin(state, name)
local f = state.builtins[name]
f(state)
end
function tlang.call_var_or_builtin(state, name)
if in_keys(name, state.builtins) then
tlang.call_builtin(state, name)
else
tlang.call_var(state, name)
end
end
function tlang.push_values(state, vals)
for i, v in ipairs(vals) do
tlang.push(state, v)
end
end
function tlang.lua_call_tos(state, ...)
tlang.push_values(state, {...})
tlang.call_tos(state)
end
function tlang.lua_call_var(state, name, ...)
tlang.push_values(state, {...})
tlang.call_var(state, name)
end
local function find_var_pos(state, name)
local slen = #state.locals
for i = 1, slen do
local v = state.locals[slen + 1 - i]
if in_keys(name, v.vars) then
return slen + 1 - i
end
end
end
function tlang.map_access_assign(state, index, start, assign)
local container
local curtab
if start then
container = start
elseif index[1] == "" and #index > 1 then
curtab = state.stack[#state.stack].value
else
local pos = find_var_pos(state, index[1])
-- assignments can go at the current scope
if assign then
pos = pos or #state.locals
elseif not pos then
return nil -- ERROR, variable undefined
end
container = state.locals[pos].vars
end
if not container and not curtab then
return
end
if #index == 1 then
if assign then
container[index[1]] = assign
return
else
return container[index[1]]
end
end
curtab = curtab or container[index[1]].value
for idx = 2, #index - 1 do
curtab = curtab[index[idx]]
if not curtab then
return nil
end
curtab = curtab.value
end
if assign then
curtab[index[#index]] = assign
else
return curtab[index[#index]]
end
end
function tlang.near_access(state, index)
return tlang.map_access_assign(state, index)
end
function tlang.near_assign(state, index, value)
tlang.map_access_assign(state, index, nil, value)
end
function tlang.global_access(state, index)
tlang.map_access_assign(state, index, state.locals[1].vars)
end
function tlang.global_assign(state, index, value)
tlang.map_access_assign(state, index, state.locals[1].vars, value)
end
function tlang.local_access(state, index)
tlang.map_access_assign(state, index, state.locals[#state.locals].vars)
end
function tlang.local_assign(state, index, value)
tlang.map_access_assign(state, index, state.locals[#state.locals].vars, value)
end
function tlang.get_pc(state)
return state.locals[#state.locals].pc
end
local function accesspc(state, pc)
local code
if pc.sg == 0 then -- stack
code = state.code_stack[pc.pos]
elseif pc.sg == 1 then -- global
code = tlang.near_access(state, pc.pos)
end
if code then
return code.value[pc.elem]
end
end
function tlang.increment_pc(state, pc)
local next_pc = {sg = pc.sg, pos = pc.pos, elem = pc.elem + 1}
if accesspc(state, next_pc) then
return next_pc
end
end
local function getnext(state)
if state.locals[#state.locals].nextpop then
local pc = tlang.get_pc(state)
-- allows for finished states to be used in calls
if #state.locals == 1 then
return nil
end
state.locals[#state.locals] = nil
-- pop code stack
if pc.sg == 0 then
state.code_stack[pc.pos] = nil
end
return getnext(state)
end
local current
if not state.locals[#state.locals].nextpop then
state.current_pc = tlang.get_pc(state)
current = accesspc(state, state.current_pc)
end
local incd = tlang.increment_pc(state, tlang.get_pc(state))
if not incd then
state.locals[#state.locals].nextpop = true
else
state.locals[#state.locals].pc = incd
end
return current
end
-- doesn't support jumping out of scope yet
function tlang.set_next_pc(state, pc)
-- this probably causes issues when jumping outside scope
state.locals[#state.locals].nextpop = nil
state.locals[#state.locals].pc = pc
end
function tlang.peek_raw(state)
return state.stack[#state.stack]
end
function tlang.pop_raw(state)
local tos = tlang.peek_raw(state)
state.stack[#state.stack] = nil
return tos
end
function tlang.push_raw(state, value)
state.stack[#state.stack + 1] = value
end
function tlang.peek(state)
return tlang.tlang_to_value(tlang.peek_raw(state))
end
function tlang.pop(state)
return tlang.tlang_to_value(tlang.pop_raw(state))
end
function tlang.push(state, value)
tlang.push_raw(state, tlang.value_to_tlang(value))
end
local function statepeek_type(state, t)
local tos = tlang.peek_raw(state)
if tos.type == t then
return tos
else
return nil -- ERROR
end
end
local function statepop_type(state, t)
local tos = tlang.peek_raw(state)
if tos.type == t then
return tlang.pop_raw(state)
else
return nil -- ERROR
end
end
local function statepop_num(state)
return statepop_type(state, "number")
end
local function statepush_num(state, number)
tlang.push_raw(state, {type = "number", value = number})
end
tlang.builtins = {}
function tlang.builtins.run(state)
tlang.call_tos(state)
end
tlang.builtins["="] = function(state)
local name = statepop_type(state, "quote")
local value = tlang.pop_raw(state)
tlang.near_assign(state, name.value, value)
end
function tlang.unary(func)
return function(state)
local tos = tlang.pop_raw(state)
if tos.type == "number" then
statepush_num(state, func(tos.value))
elseif tos.type == "quote" then
local n = tlang.near_access(state, tos.value)
tlang.near_assign(state, tos.value, {type = "number", value = func(n.value)})
end
end
end
function tlang.binary(func)
return function(state)
local tos = statepop_num(state)
local tos1 = statepop_num(state)
statepush_num(state, func(tos1.value, tos.value))
end
end
tlang.builtins["--"] = tlang.unary(function(v)
return v - 1
end)
tlang.builtins["++"] = tlang.unary(function(v)
return v + 1
end)
tlang.builtins["!"] = tlang.unary(function(v)
return tlang.boolean_to_number(not tlang.number_to_boolean(v))
end)
tlang.builtins["+"] = tlang.binary(function(v1, v2)
return v1 + v2
end)
tlang.builtins["-"] = tlang.binary(function(v1, v2)
return v1 - v2
end)
tlang.builtins["*"] = tlang.binary(function(v1, v2)
return v1 * v2
end)
tlang.builtins["/"] = tlang.binary(function(v1, v2)
return v1 / v2
end)
tlang.builtins["%"] = tlang.binary(function(v1, v2)
return v1 % v2
end)
tlang.builtins["=="] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(v1 == v2)
end)
tlang.builtins["!="] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(v1 ~= v2)
end)
tlang.builtins[">="] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(v1 >= v2)
end)
tlang.builtins["<="] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(v1 <= v2)
end)
tlang.builtins[">"] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(v1 > v2)
end)
tlang.builtins["<"] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(v1 < v2)
end)
tlang.builtins["&&"] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(
tlang.number_to_boolean(v1) and tlang.number_to_boolean(v2))
end)
tlang.builtins["||"] = tlang.binary(function(v1, v2)
return tlang.boolean_to_number(
tlang.number_to_boolean(v1) or tlang.number_to_boolean(v2))
end)
tlang.builtins["if"] = function(state)
local tos = statepop_type(state, "code")
local tos1 = tlang.pop_raw(state)
if tos1.type == "number" then
if tos1.value ~= 0 then
tlang.push_raw(state, tos)
tlang.call_tos(state)
end
elseif tos1.type == "code" then
local tos2 = statepop_num(state)
if tos2.value ~= 0 then
tlang.push_raw(state, tos1)
tlang.call_tos(state)
else
tlang.push_raw(state, tos)
tlang.call_tos(state)
end
end
end
function tlang.builtins.print(state)
local value = tlang.pop_raw(state)
if minetest then
local message = "[tlang] " .. tostring(value.value)
minetest.display_chat_message(message)
minetest.log("info", message)
else
print(value.value)
end
end
function tlang.builtins.dup(state)
tlang.push_raw(state, tlang.peek_raw(state))
end
function tlang.builtins.popoff(state)
state.stack[#state.stack] = nil
end
function tlang.builtins.wait(state)
local tos = statepop_type(state, "number")
state.wait_target = os.clock() + tos.value
end
tlang.builtins["forever"] = function(state)
local slen = #state.locals
if state.locals[slen].broke == true then
state.locals[slen].broke = nil
state.locals[slen].loop_code = nil
return
end
if state.locals[slen].loop_code == nil then
local tos = tlang.pop_raw(state)
if tos.type == "code" then
state.locals[slen].loop_code = tos
elseif tos.type == "quote" then
state.locals[slen].loop_code = statepop_type(state, "code")
state.locals[slen].repeat_n = 0
state.locals[slen].loop_var = tos.value
end
end
if state.locals[slen].loop_var then
tlang.local_assign(state,
state.locals[slen].loop_var,
{type = "number", value = state.locals[slen].repeat_n})
state.locals[slen].repeat_n = state.locals[slen].repeat_n + 1
end
tlang.push_raw(state, state.locals[slen].loop_code)
tlang.set_next_pc(state, state.current_pc)
tlang.call_tos(state)
end
tlang.builtins["while"] = function(state)
local slen = #state.locals
if state.locals[slen].broke == true then
state.locals[slen].broke = nil
state.locals[slen].loop_code = nil
state.locals[slen].test_code = nil
state.locals[slen].loop_stage = nil
return
end
if state.locals[slen].loop_code == nil then
local while_block = statepop_type(state, "code")
local test_block = statepop_type(state, "code")
state.locals[slen].test_code = test_block
state.locals[slen].loop_code = while_block
state.locals[slen].loop_stage = 0
end
-- stage 0, run test
if state.locals[slen].loop_stage == 0 then
tlang.push_raw(state, state.locals[slen].test_code)
tlang.set_next_pc(state, state.current_pc)
tlang.call_tos(state)
state.locals[slen].loop_stage = 1
-- stage 1, run while
elseif state.locals[slen].loop_stage == 1 then
local tos = tlang.pop_raw(state)
if tos and tos.value ~= 0 then
tlang.push_raw(state, state.locals[slen].loop_code)
tlang.set_next_pc(state, state.current_pc)
tlang.call_tos(state)
else
tlang.set_next_pc(state, state.current_pc)
state.locals[slen].broke = true
end
state.locals[slen].loop_stage = 0
end
end
tlang.builtins["repeat"] = function(state)
local slen = #state.locals
if state.locals[slen].broke == true then
state.locals[slen].broke = nil
state.locals[slen].loop_code = nil
state.locals[slen].repeat_count = nil
state.locals[slen].repeat_n = nil
state.locals[slen].loop_var = nil
return
end
if state.locals[slen].loop_code == nil then
local num_var = tlang.pop_raw(state)
local count
local block
if num_var.type == "quote" then
count = statepop_num(state)
state.locals[slen].loop_var = num_var.value
else
count = num_var
end
block = statepop_type(state, "code")
state.locals[slen].loop_code = block
state.locals[slen].repeat_count = count.value
state.locals[slen].repeat_n = 0
end
if state.locals[slen].repeat_n ~= state.locals[slen].repeat_count then
if state.locals[slen].loop_var then
tlang.local_assign(state,
state.locals[slen].loop_var,
{type = "number", value = state.locals[slen].repeat_n})
end
tlang.push_raw(state, state.locals[slen].loop_code)
tlang.set_next_pc(state, state.current_pc)
tlang.call_tos(state)
state.locals[slen].repeat_n = state.locals[slen].repeat_n + 1
else
tlang.set_next_pc(state, state.current_pc)
state.locals[slen].broke = true
end
end
tlang.builtins["break"] = function(state)
local slen = #state.locals
local pos = 0
local found = false
-- find highest loop_code
-- slen - i to perform basically bitwise inverse
-- it allows it to count down the list effectively
for i = 1, slen do
if state.locals[slen + 1 - i].loop_code then
pos = slen + 1 - i
found = true
end
end
if found then
-- pop the top layers
for i = pos + 1, #state.locals do
state.locals[i] = nil
end
-- break in the lower layer
state.locals[#state.locals].broke = true
end
end
tlang.builtins["return"] = function(state)
state.locals[#state.locals] = nil
end
tlang.builtins["args"] = function(state)
local vars = {}
local vari = 1
while true do
local n = tlang.pop_raw(state)
if n.type == "quote" then
vars[vari] = n.value
vari = vari + 1
elseif n.type == "number" and n.value == 0 then
break
else
return false
end
end
for i, v in ipairs(vars) do
tlang.local_assign(state, v, tlang.pop_raw(state))
end
end
-- returns:
-- true - more to do
-- nil - more to do but waiting
-- false - finished
-- string - error
function tlang.step(state)
if state.paused or (state.wait_target and os.clock() < state.wait_target) then
return nil
end
local cur = getnext(state)
if cur == nil then
if state.locals[1].nextpop then
state.finished = true
return false
else
return "Error: code exited early"
end
else
state.finished = false
end
if in_list(cur.type, literals) then
state.stack[#state.stack + 1] = cur
elseif cur.type == "identifier" or cur.type == "symbol" then
local strname = cur.value
if type(cur.value) == "table" then
strname = cur.value[1]
end
if in_keys(strname, state.builtins) then
local f = state.builtins[strname]
f(state)
else
local var = tlang.near_access(state, cur.value)
if var == nil then
return "Undefined identifier: " .. table.concat(cur.value, ".")
elseif var.type == "code" then
tlang.call_var(state, cur.value)
else
state.stack[#state.stack + 1] = var
end
end
end
return true
end

View File

@ -0,0 +1,54 @@
--
-- undying
local sh=false
local function findbones()
return minetest.find_node_near(minetest.localplayer:get_pos(), 6, {"bones:bones"},true)
end
local function digbones()
local bn=findbones()
if not bn then return false end
minetest.dig_node(bn)
if findbones() then minetest.after("0.1",digbones) end
end
minetest.register_on_death(function()
if not minetest.settings:get_bool("undying") then return end
sh=false
minetest.after("0.1",function() minetest.send_chat_message("/home") end)
minetest.after("0.2",function()
digbones()
for k, v in ipairs(minetest.localplayer.get_nearby_objects(10)) do
if (v:is_player() and v:get_name() ~= minetest.localplayer:get_name()) then
local pos = v:get_pos()
pos.y = pos.y - 1
autofly.aim(pos)
end
end
end)
end)
minetest.register_on_damage_taken(function(hp)
if not sh and minetest.settings:get_bool("undying") then
local hhp=minetest.localplayer:get_hp()
--if (hhp==0 ) then return end
if (hhp < 2 ) then
sh=true
minetest.settings:set_bool("autorespawn",true)
minetest.send_chat_message("/sethome") end
end
end
)
minetest.register_on_receiving_chat_message(function(msg)
if (msg:find('Teleported to home!') or msg:find('Home set!')) then return true end
end)
-- REG cheats on DF
if (_G["minetest"]["register_cheat"] ~= nil) then
minetest.register_cheat("Undying", "Combat", "undying")
else
minetest.settings:set_bool('undying',true)
end

View File

@ -0,0 +1,3 @@
name = undying
author = cora
description = revert death on servers with /sethome / bones

View File

@ -0,0 +1 @@
undying (Undying) bool false

View File

@ -0,0 +1,131 @@
-- CC0/Unlicense Emilia 2020
waterbot = {}
-- TODO: FreeRefills tries to pick up too much at once
-- quint time :]
-- Lua doesnt have enums and tables look gross
-- should still be a table tho
local WATER_USABLE = 0 -- water source
local WATER_STABLE = 1 -- water source used for refreshing other sources
local WATER_USED = 2 -- water source that can be bucketed
local AIR = 3 -- something that water can flow into and renew
local SOLID = 4 -- something that water cannot flow into and renew
local function get_offset(pos, radius)
return vector.round({
x = pos.x - radius - 1,
y = pos.y - radius - 1,
z = pos.z - radius - 1
})
end
-- returns {{{n n n} {n n n} ...} {...} ...}
local function get_intarr(pos, radius)
local offset = get_offset(pos, radius)
local out = {}
local diameter = radius * 2 + 1
for z = 1, diameter do
table.insert(out, {})
for y = 1, diameter do
table.insert(out[#out], {})
for x = 1, diameter do
local npos = {x = x, y = y, z = z}
local node = minetest.get_node_or_nil(vector.add(offset, npos))
local v = SOLID
if node then
if node.name == "mcl_core:water_source" then
v = WATER_USABLE
elseif node.name == "air" then
v = AIR
end
end
table.insert(out[#out][#out[#out]], v)
end
end
end
return out
end
local function coord_valid(coord, width, height)
return ((coord[1] > 0) and (coord[2] > 0)) and ((coord[1] <= width) and (coord[2] <= height))
end
-- returns modified list and safe sources
-- table is [z][y][x] accessed
-- safe sources is a coordinate list
-- this is like cellular automata but the state is mogrified in place
local function mogrify_stable(t, offset)
local safe = {}
-- indented like this because this is necessary and full indent would be ugly
for zi, zv in ipairs(t) do
for yi, yv in ipairs(zv) do
for xi, xv in ipairs(yv) do
if xv == WATER_USABLE then
local nhood = {
{xi - 1, zi},
{xi, zi - 1},
{xi + 1, zi},
{xi, zi + 1}
}
local last
local applied = false
for i, v in ipairs(nhood) do
if not applied and coord_valid(v, #yv, #t) then
local check = t[v[2]][yi][v[1]]
if check == WATER_USABLE or check == WATER_STABLE then
if not last then
last = v
else
t[ v[2]][yi][ v[1]] = WATER_STABLE
t[last[2]][yi][last[1]] = WATER_STABLE
yv[xi] = WATER_USED
table.insert(safe,
vector.add(offset,
{x = xi, y = yi, z = zi}))
applied = true
end
end
end
end
end
end
end
end
return t, safe
end
function waterbot.find_renewable_water_near(pos, radius)
local int = get_intarr(pos, radius)
local offset = get_offset(pos, radius)
local mint, safe = mogrify_stable(int, offset)
return safe
end
local epoch = os.clock()
minetest.register_globalstep(function()
if minetest.settings:get_bool("waterbot_refill") and os.clock() >= epoch + 2 then
local pos = minetest.localplayer:get_pos()
local sources = waterbot.find_renewable_water_near(pos, 6)
for i, v in ipairs(sources) do
if minetest.switch_to_item("mcl_buckets:bucket_empty") then
minetest.interact("place", v)
else
break
end
end
epoch = os.clock()
end
end)
minetest.register_cheat("FreeRefills", "Inventory", "waterbot_refill")

463
clientmods/wisp/init.lua Normal file
View File

@ -0,0 +1,463 @@
-- Wisp by system32
-- CC0/Unlicense 2020
-- version 1.0
--
-- a clientmod for minetest that lets people send 1 on 1 encrypted messages
-- also has a public interface for other mods
--
-- check out cora's tchat mod, which supports using wisp as a backend
-- uses the lua-openssl library by George Zhao: https://github.com/zhaozg/lua-openssl
-- public interface
--
-- Methods
-- send(player, message) - send a message
-- register_on_receive(function(message)) - register a receiving callback (includes To: messages), if it returns true the message will not be shown to the player
-- register_on_receive_split(function(player, message)) - register_on_receive but player and message are pre split
-- register_on_send_split(function(player, message)) - register a sending callback, if it returns true the message will not be sent
--
-- Properties
-- players - list of online players (updated every 2 seconds , when someone may have left, and when a message is queued)
-- minetest mod security doesn't work so require() is still disabled while modsec is off
-- so this doesnt work without patches (it should tho :])
-- PATCHING MINETEST
--
-- in src/script/lua_api/l_util.cpp add the following to ModApiUtil:InitializeClient() below API_FCT(decompress);
--[[
API_FCT(request_insecure_environment);
--]]
--
-- in src/script/cpp_api/s_security.cpp add the following below int thread = getThread(L); in ScriptApiSecurity:initializeSecurityClient()
--[[
// Backup globals to the registry
lua_getglobal(L, "_G");
lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
--]]
--
-- Recompile Minetest (just using make -j$(nproc) is fine)
-- INSTALLING OPENSSL
--
-- Git clone, make, make install (git repo is https://github.com/zhaozg/lua-openssl)
-- # mkdir /usr/lib/lua/5.1
-- # mv /usr/lib/lua/openssl.so /usr/lib/lua/5.1
-- ADDING TO TRUSTED
--
-- add wisp to the trusted mods setting in Minetest
--[[ protocol:
on joining a game, generate a keypair for ECDH
medium is minetest private messages for all conversation
alice and bob dont know each other
alice introduces herself, giving her ECDH public component to bob (using PEM)
bob generates the secret and gives alice his public component
alice generates the same secret
then at any point alice or bob can talk to the other (for eg, alice talks)
alice generates a 256 bit nonce and encrypts her message using AES 256 CBC with the nonce as the initialization vector, sending the nonce and message to bob (both base64 encoded and separated by a space character)
bob decrypts her message using AES 256 CBC with the nonce as the initialization vector
you can swap alice with bob and vice versa to get what will happen if bob messages alice
the key exchanging step is performed whenever alice or bob don't have the other's key
the encryption step is performed every time a private encrypted message is sent
if a player leaves all players with their public key and other data will forget them, it is important to do this since the keys for a player are not persistent across joining/leaving servers
if this was not done alice may use a stale key for bob or vice versa, giving an incorrect shared secret
this is not damaging to security, it just wouldn't let them talk
--]]
if minetest.request_insecure_environment == nil then
error("Wisp: Minetest scripting patches were not applied, please apply them and recompile Minetest.")
end
local env = minetest.request_insecure_environment()
if env == nil then
error("Wisp: not in trusted mods (secure.trusted_mods), please go into the advanced settings and add wisp (all lowercase).")
end
local openssl = env.require("openssl")
-- private stuff
local function init_settings(setting_table)
for k, v in pairs(setting_table) do
if minetest.settings:get(k) == nil then
if type(v) == "boolean" then
minetest.settings:set_bool(k, v)
else
minetest.settings:set(k, v)
end
end
end
end
init_settings({
wisp_prefix = "&**&",
wisp_curve = "prime256v1",
wisp_cipher = "aes256",
wisp_digest = "sha256",
wisp_iv_size = 8,
wisp_whisper = "msg",
wisp_hide_sent = true,
wisp_timeout = 10
})
-- players must agree on these
local prefix = minetest.settings:get("wisp_prefix")
local curve = minetest.settings:get("wisp_curve")
local cipher = minetest.settings:get("wisp_cipher")
local digest = minetest.settings:get("wisp_digest")
local iv_size = minetest.settings:get("wisp_iv_size")
local whisper = minetest.settings:get("wisp_whisper")
local hide_sent = minetest.settings:get_bool("wisp_hide_sent")
local timeout = tonumber(minetest.settings:get("wisp_timeout"))
local my_key = openssl.pkey.new("ec", curve)
local my_ec = my_key:parse().ec
local my_export = my_key:get_public():export()
local pem_begin = "-----BEGIN PUBLIC KEY-----\n"
local pem_end = "\n-----END PUBLIC KEY-----\n"
my_export = my_export:sub(pem_begin:len() + 1, -pem_end:len() - 1):gsub("\n", "~")
local friends = {}
-- convenience aliases
local function qsplit(message)
return string.split(message, " ")
end
local function b64_decode(message)
return minetest.decode_base64(message)
end
local function b64_encode(message)
return minetest.encode_base64(message)
end
local function in_list(list, value)
for k, v in ipairs(list) do
if v == value then
return true
end
end
return false
end
local function append(list, item)
list[#list + 1] = item
end
local function popfirst(t)
local out = {}
for i = 2, #t do
out[#out + 1] = t[i]
end
return out
end
local function unpack(t, i)
if type(t) ~= "table" then
return t
end
i = i or 1
if t[i] ~= nil then
return t[i], unpack(t, i + 1)
end
end
-- key trading
local function dm(player, message)
minetest.send_chat_message("/" .. whisper .. " " .. player .. " " .. message)
end
-- initialize
local function establish(player)
dm(player, prefix .. "I " .. my_export)
end
-- receiving
local function establish_receive(player, message, sendout)
friends[player] = {}
local friend = friends[player]
local key = pem_begin .. message:gsub("~", "\n") .. pem_end
friend.pubkey = openssl.pkey.read(key)
friend.secret = my_ec:compute_key(friend.pubkey:parse().ec)
friend.key = openssl.digest.digest(digest, friend.secret, true)
if sendout == true then
dm(player, prefix .. "R " .. my_export)
end
end
-- encryption
local function run_callbacks(list, params)
for k, v in ipairs(list) do
if v(unpack(params)) then
return true
end
end
end
-- encrypt and send
local function message_send(player, message, hide_to, force_send)
local me = minetest.localplayer:get_name()
if run_callbacks(wisp.send_split_callbacks, {player, message}) then
return
end
-- for displaying the To: stuff
if not hide_to then
local target = player
if target == me then
target = "Yourself"
end
local display_message = "To " .. target .. ": " .. message
local callback_value = run_callbacks(wisp.receive_callbacks, display_message)
callback_value = callback_value or run_callbacks(wisp.receive_split_callbacks, {player, message})
if not callback_value then
minetest.display_chat_message(display_message)
end
end
-- actual encryption
local friend = friends[player]
if friend == nil then
return
end
local nonce = openssl.random(iv_size, true)
local enc_message = openssl.cipher.encrypt(cipher, message, friend.key, nonce)
local final_message = b64_encode(nonce) .. " " .. b64_encode(enc_message)
if player ~= me or force_send then
dm(player, prefix .. "E " .. final_message)
end
end
-- decrypt and show
local function message_receive(player, message)
local friend = friends[player]
if friend == nil then
return
end
local nonce = b64_decode(qsplit(message)[1])
local enc_message = b64_decode(qsplit(message)[2])
local dec_message = openssl.cipher.decrypt(cipher, enc_message, friend.key, nonce)
final_message = "From " .. player .. ": " .. dec_message
local callback_value = run_callbacks(wisp.receive_callbacks, final_message)
callback_value = callback_value or run_callbacks(wisp.receive_split_callbacks, {player, dec_message})
if not callback_value then
minetest.display_chat_message(final_message)
end
end
-- check if a player actually left
local function player_left(message)
for player in message:gmatch("[^ ]* (.+) left the game.") do
wisp.players = minetest.get_player_names()
for k, v in ipairs(wisp.players) do
if v == player then
return player
end
end
end
end
-- check if a message is a PM
local function pm(message)
for player, message in message:gmatch(".*rom (.+): (.*)") do
return player, message
end
return nil, nil
end
-- check if a message is encrypted
local function encrypted(message)
local split = string.split(message, " ")
if split[1] == prefix then
return string.sub(message, string.len(prefix) + 2)
end
end
-- check if a message is 'Message sent.' or similar
local function message_sent(message)
return message == "Message sent."
end
wisp = {}
wisp.receive_callbacks = {}
wisp.receive_split_callbacks = {}
wisp.send_split_callbacks = {}
wisp.players = {}
local player_check_epoch = 0
-- message queue, accounts for establishing taking non-zero time
-- messages are enqueued and dequeued once they can be sent
local queue = {}
local function enqueue(player, message, hide_to, force_send)
append(queue, {
player = player,
message = message,
hide_to = hide_to,
force_send = force_send,
time = os.time()
})
wisp.players = minetest.get_player_names()
end
local function dequeue()
local new_queue = {}
local out = queue[1]
for k, v in ipairs(queue) do
if k ~= 1 then
append(new_queue, v)
end
end
queue = new_queue
return out
end
local function peek()
return queue[1]
end
function wisp.send(player, message, hide_to, force_send)
if (player ~= minetest.localplayer:get_name() or force_send) and friends[player] == nil then
establish(player)
end
enqueue(player, message, hide_to, force_send)
end
function wisp.register_on_receive(func)
append(wisp.receive_callbacks, func)
end
function wisp.register_on_receive_split(func)
append(wisp.receive_split_callbacks, func)
end
function wisp.register_on_send_split(func)
append(wisp.send_split_callbacks, func)
end
-- glue
minetest.register_on_receiving_chat_message(
function(message)
-- hide Message sent.
if hide_sent and message_sent(message) then
return true
end
-- if its a PM
local player, msg = pm(message)
if player and msg then
local split = qsplit(msg)
local plain = table.concat(popfirst(split), " ")
-- initial key trade
if split[1] == prefix .. "I" then
establish_receive(player, plain, true)
return true
-- key trade response
elseif split[1] == prefix .. "R" then
establish_receive(player, plain)
return true
-- encrypted message receive
elseif split[1] == prefix .. "E" then -- encrypt
message_receive(player, plain)
return true
end
end
-- remove friends if they leave
local player = player_left(message)
if player then
friends[player] = nil
end
end
)
minetest.register_globalstep(
function()
if os.time() > player_check_epoch + 2 then
wisp.players = minetest.get_player_names()
end
local p = peek()
if p then
if not in_list(wisp.players, peek().player) then
minetest.display_chat_message("Player " .. p.player .. " is not online. If they are please resend the message.")
dequeue()
return
end
if os.time() > p.time + timeout then
minetest.display_chat_message("Player " .. p.player .. " is not responsive.")
dequeue()
return
end
if (p.player == minetest.localplayer:get_name() and not p.force_send) or friends[p.player] then
local v = dequeue()
message_send(v.player, v.message, v.hide_to, v.force_send)
end
end
end
)
minetest.register_chatcommand("e", {
params = "<player>",
description = "Send encrypted whisper to player",
func = function(param)
local player = qsplit(param)[1]
local message = table.concat(popfirst(qsplit(param)), " ")
if player == nil then
minetest.display_chat_message("Player not specified.")
return
end
wisp.send(player, message)
end
})

3
clientmods/wisp/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = wisp
author = system32
description = Encrypted whisper/msg (.e). Keys are handled automatically in the background. Also has public methods for other mods.

View File

@ -0,0 +1,7 @@
wisp_prefix (Prefix for encrypted messages) string &**&
wisp_curve (Curve for ECDH) string prime256v1
wisp_cipher (Cipher for messages) string aes256
wisp_digest (Digest for shared secrets) string sha256
wisp_iv_size (Size in bytes for initialization vector nonce) int 8
wisp_whisper (Whisper command) string msg
wisp_hide_sent (Hide "Message sent." server messages) bool true