1 function mesecon
.move_node(pos
, newpos
)
2 local node
= minetest
.get_node(pos
)
3 local meta
= minetest
.get_meta(pos
):to_table()
4 minetest
.remove_node(pos
)
5 minetest
.set_node(newpos
, node
)
6 minetest
.get_meta(pos
):from_table(meta
)
9 --Rules rotation Functions:
10 function mesecon
.rotate_rules_right(rules
)
12 for i
, rule
in ipairs(rules
) do
18 spread
= rule
.spread
,})
23 function mesecon
.rotate_rules_left(rules
)
25 for i
, rule
in ipairs(rules
) do
31 spread
= rule
.spread
,})
36 function mesecon
.rotate_rules_down(rules
)
38 for i
, rule
in ipairs(rules
) do
44 spread
= rule
.spread
,})
49 function mesecon
.rotate_rules_up(rules
)
51 for i
, rule
in ipairs(rules
) do
57 spread
= rule
.spread
,})
62 function mesecon
.flattenrules(allrules
)
80 local shallowrules
= {}
81 for _
, metarule
in ipairs( allrules
) do
82 for _
, rule
in ipairs(metarule
) do
83 table.insert(shallowrules
, rule
)
97 function mesecon
.rule2bit(findrule
, allrules
)
98 --get the bit of the metarule the rule is in, or bit 1
104 for m
,metarule
in ipairs( allrules
) do
105 for _
, rule
in ipairs(metarule
) do
106 if vector
.equals(findrule
, rule
) then
113 function mesecon
.rule2metaindex(findrule
, allrules
)
114 --get the metarule the rule is in, or allrules
115 if allrules
[1].x
then
119 if not(findrule
) then
120 return mesecon
.flattenrules(allrules
)
123 for m
, metarule
in ipairs( allrules
) do
124 for _
, rule
in ipairs(metarule
) do
125 if vector
.equals(findrule
, rule
) then
132 function mesecon
.rule2meta(findrule
, allrules
)
133 if #allrules
== 0 then return {} end
135 local index
= mesecon
.rule2metaindex(findrule
, allrules
)
137 if allrules
[1].x
then
143 return allrules
[index
]
146 -- Returns the 6 immediate neighbors of pos
147 -- (nodes which touch the sides of pos).
148 -- NOT PART OF ORIGINAL MESECONS!
149 function mesecon
.mcl_get_neighbors(pos
)
151 { x
= 0, y
= 0, z
=-1 },
152 { x
= 0, y
= 0, z
= 1 },
153 { x
= 0, y
=-1, z
= 0 },
154 { x
= 0, y
= 1, z
= 0 },
155 { x
=-1, y
= 0, z
= 0 },
156 { x
= 1, y
= 0, z
= 0 },
160 table.insert(e
, { pos
= vector
.add(pos
, r
[i
]), link
= r
[i
] })
165 function mesecon
.dec2bin(n
)
166 local x
, y
= math
.floor(n
/ 2), n
% 2
168 return mesecon
.dec2bin(x
)..y
174 function mesecon
.getstate(nodename
, states
)
175 for state
, name
in ipairs(states
) do
176 if name
== nodename
then
180 error(nodename
.." doesn't mention itself in "..dump(states
))
183 function mesecon
.getbinstate(nodename
, states
)
184 return mesecon
.dec2bin(mesecon
.getstate(nodename
, states
)-1)
187 function mesecon
.get_bit(binary
,bit
)
189 local c
= binary
:len()-(bit
-1)
190 return binary
:sub(c
,c
) == "1"
193 function mesecon
.set_bit(binary
,bit
,value
)
195 if not mesecon
.get_bit(binary
,bit
) then
196 return mesecon
.dec2bin(tonumber(binary
,2)+math
.pow(2,bit
-1))
198 elseif value
== "0" then
199 if mesecon
.get_bit(binary
,bit
) then
200 return mesecon
.dec2bin(tonumber(binary
,2)-math
.pow(2,bit
-1))
207 function mesecon
.invertRule(r
)
208 return vector
.multiply(r
, -1)
211 function mesecon
.tablecopy(table) -- deep table copy
212 if type(table) ~= "table" then return table end -- no need to copy
215 for idx
, item
in pairs(table) do
216 if type(item
) == "table" then
217 newtable
[idx
] = mesecon
.tablecopy(item
)
226 function mesecon
.cmpAny(t1
, t2
)
227 if type(t1
) ~= type(t2
) then return false end
228 if type(t1
) ~= "table" and type(t2
) ~= "table" then return t1
== t2
end
230 for i
, e
in pairs(t1
) do
231 if not mesecon
.cmpAny(e
, t2
[i
]) then return false end
237 -- does not overwrite values; number keys (ipairs) are appended, not overwritten
238 function mesecon
.mergetable(source
, dest
)
239 local rval
= mesecon
.tablecopy(dest
)
241 for k
, v
in pairs(source
) do
242 rval
[k
] = dest
[k
] or mesecon
.tablecopy(v
)
244 for i
, v
in ipairs(source
) do
245 table.insert(rval
, mesecon
.tablecopy(v
))
251 function mesecon
.register_node(name
, spec_common
, spec_off
, spec_on
)
252 spec_common
.drop
= spec_common
.drop
or name
.. "_off"
253 spec_common
.__mesecon_basename
= name
254 spec_on
.__mesecon_state
= "on"
255 spec_off
.__mesecon_state
= "off"
257 spec_on
= mesecon
.mergetable(spec_common
, spec_on
);
258 spec_off
= mesecon
.mergetable(spec_common
, spec_off
);
260 minetest
.register_node(name
.. "_on", spec_on
)
261 minetest
.register_node(name
.. "_off", spec_off
)
264 -- swap onstate and offstate nodes, returns new state
265 function mesecon
.flipstate(pos
, node
)
266 local nodedef
= minetest
.registered_nodes
[node
.name
]
268 if (nodedef
.__mesecon_state
== "on") then newstate
= "off" end
269 if (nodedef
.__mesecon_state
== "off") then newstate
= "on" end
271 minetest
.swap_node(pos
, {name
= nodedef
.__mesecon_basename
.. "_" .. newstate
,
272 param2
= node
.param2
})
277 -- File writing / reading utilities
278 local wpath
= minetest
.get_worldpath()
279 function mesecon
.file2table(filename
)
280 local f
= io
.open(wpath
..DIR_DELIM
..filename
, "r")
281 if f
== nil then return {} end
282 local t
= f
:read("*all")
284 if t
== "" or t
== nil then return {} end
285 return minetest
.deserialize(t
)
288 function mesecon
.table2file(filename
, table)
289 local f
= io
.open(wpath
..DIR_DELIM
..filename
, "w")
290 f
:write(minetest
.serialize(table))
294 -- Block position "hashing" (convert to integer) functions for voxelmanip cache
297 -- convert node position --> block hash
298 local function hash_blockpos(pos
)
299 return minetest
.hash_node_position({
300 x
= math
.floor(pos
.x
/BLOCKSIZE
),
301 y
= math
.floor(pos
.y
/BLOCKSIZE
),
302 z
= math
.floor(pos
.z
/BLOCKSIZE
)
306 -- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
309 -- Contents of the table are:
310 -- “vm” → the VoxelManipulator
311 -- “va” → the VoxelArea
312 -- “data” → the data array
313 -- “param1” → the param1 array
314 -- “param2” → the param2 array
315 -- “dirty” → true if data has been modified
317 -- Nil if no VM-based transaction is in progress.
320 -- Starts a VoxelManipulator-based transaction.
322 -- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
323 -- cached copy of the world loaded via VoxelManipulators. That cache can later
324 -- be committed to the real map by means of vm_commit or discarded by means of
326 function mesecon
.vm_begin()
330 -- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
331 -- and writing back any modified areas.
332 function mesecon
.vm_commit()
333 for hash
, tbl
in pairs(vm_cache
) do
336 vm
:set_data(tbl
.data
)
344 -- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
345 -- away any modified areas.
346 function mesecon
.vm_abort()
350 -- Gets the cache entry covering a position, populating it if necessary.
351 local function vm_get_or_create_entry(pos
)
352 local hash
= hash_blockpos(pos
)
353 local tbl
= vm_cache
[hash
]
355 local vm
= minetest
.get_voxel_manip(pos
, pos
)
356 local min_pos
, max_pos
= vm
:get_emerged_area()
357 local va
= VoxelArea
:new
{MinEdge
= min_pos
, MaxEdge
= max_pos
}
358 tbl
= {vm
= vm
, va
= va
, data
= vm
:get_data(), param1
= vm
:get_light_data(), param2
= vm
:get_param2_data(), dirty
= false}
364 -- Gets the node at a given position during a VoxelManipulator-based
366 function mesecon
.vm_get_node(pos
)
367 local tbl
= vm_get_or_create_entry(pos
)
368 local index
= tbl
.va
:indexp(pos
)
369 local node_value
= tbl
.data
[index
]
370 if node_value
== core
.CONTENT_IGNORE
then
373 local node_param1
= tbl
.param1
[index
]
374 local node_param2
= tbl
.param2
[index
]
375 return {name
= minetest
.get_name_from_content_id(node_value
), param1
= node_param1
, param2
= node_param2
}
379 -- Sets a node’s name during a VoxelManipulator-based transaction.
381 -- Existing param1, param2, and metadata are left alone.
382 function mesecon
.vm_swap_node(pos
, name
)
383 local tbl
= vm_get_or_create_entry(pos
)
384 local index
= tbl
.va
:indexp(pos
)
385 tbl
.data
[index
] = minetest
.get_content_id(name
)
389 -- Gets the node at a given position, regardless of whether it is loaded or
390 -- not, respecting a transaction if one is in progress.
392 -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
393 -- the server’s main map data cache and then accessed from there.
395 -- Inside a VM transaction, the transaction’s VM cache is used.
396 function mesecon
.get_node_force(pos
)
398 return mesecon
.vm_get_node(pos
)
400 local node
= minetest
.get_node_or_nil(pos
)
402 -- Node is not currently loaded; use a VoxelManipulator to prime
403 -- the mapblock cache and try again.
404 minetest
.get_voxel_manip(pos
, pos
)
405 node
= minetest
.get_node_or_nil(pos
)
411 -- Swaps the node at a given position, regardless of whether it is loaded or
412 -- not, respecting a transaction if one is in progress.
414 -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
415 -- the server’s main map data cache and then accessed from there.
417 -- Inside a VM transaction, the transaction’s VM cache is used.
419 -- This function can only be used to change the node’s name, not its parameters
421 function mesecon
.swap_node_force(pos
, name
)
423 return mesecon
.vm_swap_node(pos
, name
)
425 -- This serves to both ensure the mapblock is loaded and also hand us
426 -- the old node table so we can preserve param2.
427 local node
= mesecon
.get_node_force(pos
)
429 minetest
.swap_node(pos
, node
)
434 -- Nodes like conductors may change their appearance and their connection rules
435 -- right after being placed or after being dug, e.g. the default wires use this
436 -- to automatically connect to linking nodes after placement.
437 -- After placement, the update function will be executed immediately so that the
438 -- possibly changed rules can be taken into account when recalculating the circuit.
439 -- After digging, the update function will be queued and executed after
440 -- recalculating the circuit. The update function must take care of updating the
441 -- node at the given position itself, but also all of the other nodes the given
442 -- position may have (had) a linking connection to.
443 mesecon
.autoconnect_hooks
= {}
445 -- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function.
446 -- fct: The update function with parameters function(pos, node)
447 function mesecon
.register_autoconnect_hook(name
, fct
)
448 mesecon
.autoconnect_hooks
[name
] = fct
449 mesecon
.queue
:add_function("autoconnect_hook_"..name
, fct
)
452 function mesecon
.execute_autoconnect_hooks_now(pos
, node
)
453 for _
, fct
in pairs(mesecon
.autoconnect_hooks
) do
458 function mesecon
.execute_autoconnect_hooks_queue(pos
, node
)
459 for name
in pairs(mesecon
.autoconnect_hooks
) do
460 mesecon
.queue
:add_action(pos
, "autoconnect_hook_"..name
, {node
})