123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- --[===================================================================[--
- ffi_accel - Mod for Minetest to accelerate handling of LuaVoxelManip
- and PerlinNoiseMap through the foreign function interface
- Copyright © 2019 Pedro Gimeno Fortea. All rights reserved.
- Permission is hereby granted to everyone to copy, modify, distribute
- and use this file, for any purpose, in whole or in part, free of
- charge, under the sole condition that the above copyright notice,
- together with this permission grant and the disclaimer below, are
- included in all copies of this software or of a substantial portion
- of it.
- THIS SOFTWARE COMES WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED.
- --]===================================================================]--
- -- Based on an idea of EvidenceBKidsCode.
- -- This mod needs to apply a patch to the engine in order to work; see
- --
- local modname = "Mod ffi_accel"
- local err_prefix = modname .. " won't have any effect because "
- local ie = minetest.request_insecure_environment()
- if not ie then
- minetest.log("warning", err_prefix .. "it's not declared in secure.trusted_mods")
- return
- end
- -- We need raw read access to the metatables for monkey-patching
- local getmetatable = ie.debug.getmetatable
- local setmetatable = setmetatable
- local ok, ffi = pcall(ie.require, 'ffi')
- if not ok or not ffi then
- minetest.log("warning", err_prefix .. "FFI is not available in this system")
- return
- end
- ffi.cdef[[
- typedef uint8_t u8;
- typedef uint16_t u16;
- typedef int32_t s32;
- typedef struct {
- u16 nodeid;
- u8 light;
- u8 param2;
- } MapNode;
- int get_mapnode_version(void);
- void VoxelManip_get_pointer(void **lvmp, void **ptr);
- s32 VoxelManip_get_volume(void **lvmp);
- void PerlinNoiseMap_get_pointer(void **pnmp, void **ptr);
- size_t PerlinNoiseMap_get_area(void **pnmp);
- size_t PerlinNoiseMap_get_volume(void **pnmp);
- ]]
- local ffiC = ffi.C
- local ffinew = ffi.new
- local VoidPtrPtr = ffi.typeof('void*[1]')
- local MapNodePtr = ffi.typeof('MapNode *')
- local mt_set_data, mt_set_light_data, mt_set_param2_data
- if not pcall(function ()
- assert(ffiC.get_mapnode_version and ffiC.VoxelManip_get_pointer
- and ffiC.VoxelManip_get_volume and ffiC.PerlinNoiseMap_get_pointer)
- end)
- then
- minetest.log("warning", err_prefix .. "your Minetest executable has not been patched to support FFI")
- return
- end
- if ffiC.get_mapnode_version() ~= 1 then
- minetest.log("warning", err_prefix .. "of incompatible map node structure")
- return
- end
- -- Table of pointers (keys are weak refs)
- local ptrtable = setmetatable({}, {__mode = 'k'})
- -- Table of sizes (keys are weak refs)
- local sizetable = setmetatable({}, {__mode = 'k'})
- -- Metamethod: get a value
- local function mm_get(t, idx)
- return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1] or rawget(t, idx)
- end
- -- Metamethod: set a value
- local function mm_set(t, idx, value)
- if idx >= 1 and idx <= sizetable[t] then
- ptrtable[t][idx - 1] = value
- else
- rawset(t, idx, value)
- end
- end
- -- Metamethod: get the nodeid of an index
- local function mm_get_nodeid(t, idx)
- return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].nodeid or rawget(t, idx)
- end
- -- Metamethod: set the nodeid of an index to a value
- local function mm_set_nodeid(t, idx, value)
- if idx >= 1 and idx <= sizetable[t] then
- ptrtable[t][idx - 1].nodeid = value
- else
- rawset(t, idx, value)
- end
- end
- -- Metamethod: get the light of an index
- local function mm_get_light(t, idx)
- return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].light or rawget(t, idx)
- end
- -- Metamethod: set the light of an index to a value
- local function mm_set_light(t, idx, value)
- if idx >= 1 and idx <= sizetable[t] then
- ptrtable[t][idx - 1].light = value
- else
- rawset(t, idx, value)
- end
- end
- -- Metamethod: get the param2 of an index
- local function mm_get_param2(t, idx)
- return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].param2 or rawget(t, idx)
- end
- -- Metamethod: set the param2 of an index to a value
- local function mm_set_param2(t, idx, value)
- if idx >= 1 and idx <= sizetable[t] then
- ptrtable[t][idx - 1].param2 = value
- else
- rawset(t, idx, value)
- end
- end
- -- Metatables for VoxelManip nodeid, light and param2
- local VoxelManip_nodeid_mt = { __index = mm_get_nodeid, __newindex = mm_set_nodeid }
- local VoxelManip_light_mt = { __index = mm_get_light, __newindex = mm_set_light }
- local VoxelManip_param2_mt = { __index = mm_get_param2, __newindex = mm_set_param2 }
- -- Replacement VoxelManip.get_data and VoxelManip.set_data methods
- local function VoxelManip_get_data(self, buffer)
- -- Set up the metatable and pointer
- buffer = setmetatable(buffer or {}, VoxelManip_nodeid_mt)
- do
- local ptr = VoidPtrPtr()
- ffiC.VoxelManip_get_pointer(self, ptr)
- ptrtable[buffer] = MapNodePtr(ptr[0])
- end
- sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
- return buffer
- end
- local function VoxelManip_set_data(self, buffer)
- -- Call the original set_data method if the metatable is not
- -- the one we've set
- if getmetatable(buffer) ~= VoxelManip_nodeid_mt then
- mt_set_data(self, buffer)
- end
- end
- -- Replacement VoxelManip.get/set_light_data methods
- local function VoxelManip_get_light_data(self, buffer)
- -- Set up the metatable and pointer
- buffer = setmetatable(buffer or {}, VoxelManip_light_mt)
- do
- local ptr = VoidPtrPtr()
- ffiC.VoxelManip_get_pointer(self, ptr)
- ptrtable[buffer] = MapNodePtr(ptr[0])
- end
- sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
- return buffer
- end
- local function VoxelManip_set_light_data(self, buffer)
- -- Call the original set_light_data method if the metatable is not
- -- the one we've set
- if getmetatable(buffer) ~= VoxelManip_light_mt then
- mt_set_light_data(self, buffer)
- end
- end
- -- Replacement VoxelManip.get/set_param2_data methods
- local function VoxelManip_get_param2_data(self, buffer)
- -- Set up the metatable and pointer
- buffer = setmetatable(buffer or {}, VoxelManip_param2_mt)
- do
- local ptr = VoidPtrPtr()
- ffiC.VoxelManip_get_pointer(self, ptr)
- ptrtable[buffer] = MapNodePtr(ptr[0])
- end
- sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
- return buffer
- end
- local function VoxelManip_set_param2_data(self, buffer)
- -- Call the original set_param2_data method if the metatable is not
- -- the one we've set
- if getmetatable(buffer) ~= VoxelManip_param2_mt then
- mt_set_param2_data(self, buffer)
- end
- end
- -- Metatable for PerlinNoiseMap
- local PerlinNoiseMap_mt = { __index = mm_get }
- -- Replacement methods for PerlinNoiseMap.*
- local function PerlinNoiseMap_get_2d_map_flat(self, pos, buffer)
- -- Calculate the noise
- self:calc_2d_map(pos)
- -- Prepare the metatable for direct access to the data
- buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
- do
- local ptr = VoidPtrPtr()
- ffiC.PerlinNoiseMap_get_pointer(self, ptr)
- ptrtable[buffer] = MapNodePtr(ptr[0])
- end
- sizetable[buffer] = ffiC.PerlinNoiseMap_get_area(self)
- return buffer
- end
- local function PerlinNoiseMap_get_3d_map_flat(self, pos, buffer)
- -- Calculate the noise
- self:calc_3d_map(pos)
- -- Prepare the metatable for direct access to the data
- buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
- do
- local ptr = VoidPtrPtr()
- ffiC.PerlinNoiseMap_get_pointer(self, ptr)
- ptrtable[buffer] = MapNodePtr(ptr[0])
- end
- sizetable[buffer] = ffiC.PerlinNoiseMap_get_volume(self)
- return buffer
- end
- --[[
- local function PerlinNoiseMap_get_map_slice(slice_offset, slice_size, buffer)
- -- TODO
- end
- --]]
- -- Monkey-patch VoxelManip and PerlinNoiseMap
- minetest.after(0, function()
- -- VoxelManip buffer-intensive methods
- local mt = getmetatable(minetest.get_voxel_manip())
- mt_set_data = mt.set_data
- mt_set_light_data = mt.set_light_data
- mt_set_param2_data = mt.set_param2_data
- mt.get_data = VoxelManip_get_data
- mt.set_data = VoxelManip_set_data
- mt.get_light_data = VoxelManip_get_light_data
- mt.set_light_data = VoxelManip_set_light_data
- mt.get_param2_data = VoxelManip_get_param2_data
- mt.set_param2_data = VoxelManip_set_param2_data
- -- PerlinNoiseMap buffer-intensive methods
- mt = getmetatable(minetest.get_perlin_map({spread = {x=100, y=100, z=100}}, {x=1, y=1, z=1}))
- mt.get_2d_map_flat = PerlinNoiseMap_get_2d_map_flat
- mt.get_3d_map_flat = PerlinNoiseMap_get_3d_map_flat
- -- mt.get_map_slice = PerlinNoiseMap_get_map_slice
- -- Aliases
- mt.get2DMap_flat = mt.get_2d_map_flat
- mt.get3DMap_flat = mt.get_3d_map_flat
- -- mt.getMapSlice = mt.get_map_slice
- end)
- minetest.log("action", modname .. " activated!")
|