init.lua 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. --[===================================================================[--
  2. ffi_accel - Mod for Minetest to accelerate handling of LuaVoxelManip
  3. and PerlinNoiseMap through the foreign function interface
  4. Copyright © 2019 Pedro Gimeno Fortea. All rights reserved.
  5. Permission is hereby granted to everyone to copy, modify, distribute
  6. and use this file, for any purpose, in whole or in part, free of
  7. charge, under the sole condition that the above copyright notice,
  8. together with this permission grant and the disclaimer below, are
  9. included in all copies of this software or of a substantial portion
  10. of it.
  11. THIS SOFTWARE COMES WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED.
  12. --]===================================================================]--
  13. -- Based on an idea of EvidenceBKidsCode.
  14. -- This mod needs to apply a patch to the engine in order to work; see
  15. --
  16. local modname = "Mod ffi_accel"
  17. local err_prefix = modname .. " won't have any effect because "
  18. local ie = minetest.request_insecure_environment()
  19. if not ie then
  20. minetest.log("warning", err_prefix .. "it's not declared in secure.trusted_mods")
  21. return
  22. end
  23. local ok, ffi = pcall(ie.require, 'ffi')
  24. if not ok or not ffi then
  25. minetest.log("warning", err_prefix .. "FFI is not available in this system")
  26. return
  27. end
  28. ffi.cdef[[
  29. typedef uint8_t u8;
  30. typedef uint16_t u16;
  31. typedef int32_t s32;
  32. typedef struct {
  33. u16 nodeid;
  34. u8 light;
  35. u8 param2;
  36. } MapNode;
  37. int get_mapnode_version(void);
  38. void VoxelManip_get_pointer(void **lvmp, void **ptr);
  39. s32 VoxelManip_get_volume(void **lvmp);
  40. void PerlinNoiseMap_get_pointer(void **pnmp, void **ptr);
  41. size_t PerlinNoiseMap_get_area(void **pnmp);
  42. size_t PerlinNoiseMap_get_volume(void **pnmp);
  43. ]]
  44. local ffiC = ffi.C
  45. local ffinew = ffi.new
  46. local VoidPtrPtr = ffi.typeof('void*[1]')
  47. local MapNodePtr = ffi.typeof('MapNode *')
  48. local mt_set_data, mt_set_light_data, mt_set_param2_data
  49. if not pcall(function ()
  50. assert(ffiC.get_mapnode_version and ffiC.VoxelManip_get_pointer
  51. and ffiC.VoxelManip_get_volume and ffiC.PerlinNoiseMap_get_pointer)
  52. end)
  53. then
  54. minetest.log("warning", err_prefix .. "your Minetest executable has not been patched to support FFI")
  55. return
  56. end
  57. if ffiC.get_mapnode_version() ~= 1 then
  58. minetest.log("warning", err_prefix .. "of incompatible map node structure")
  59. return
  60. end
  61. -- Table of pointers (keys are weak refs)
  62. local ptrtable = setmetatable({}, {__mode = 'k'})
  63. -- Table of sizes (keys are weak refs)
  64. local sizetable = setmetatable({}, {__mode = 'k'})
  65. -- Metamethod: get a value
  66. local function mm_get(t, idx)
  67. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1] or rawget(t, idx)
  68. end
  69. -- Metamethod: set a value
  70. local function mm_set(t, idx, value)
  71. if idx >= 1 and idx <= sizetable[t] then
  72. ptrtable[t][idx - 1] = value
  73. else
  74. rawset(t, idx, value)
  75. end
  76. end
  77. -- Metamethod: get the nodeid of an index
  78. local function mm_get_nodeid(t, idx)
  79. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].nodeid or rawget(t, idx)
  80. end
  81. -- Metamethod: set the nodeid of an index to a value
  82. local function mm_set_nodeid(t, idx, value)
  83. if idx >= 1 and idx <= sizetable[t] then
  84. ptrtable[t][idx - 1].nodeid = value
  85. else
  86. rawset(t, idx, value)
  87. end
  88. end
  89. -- Metamethod: get the light of an index
  90. local function mm_get_light(t, idx)
  91. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].light or rawget(t, idx)
  92. end
  93. -- Metamethod: set the light of an index to a value
  94. local function mm_set_light(t, idx, value)
  95. if idx >= 1 and idx <= sizetable[t] then
  96. ptrtable[t][idx - 1].light = value
  97. else
  98. rawset(t, idx, value)
  99. end
  100. end
  101. -- Metamethod: get the param2 of an index
  102. local function mm_get_param2(t, idx)
  103. return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].param2 or rawget(t, idx)
  104. end
  105. -- Metamethod: set the param2 of an index to a value
  106. local function mm_set_param2(t, idx, value)
  107. if idx >= 1 and idx <= sizetable[t] then
  108. ptrtable[t][idx - 1].param2 = value
  109. else
  110. rawset(t, idx, value)
  111. end
  112. end
  113. -- Metatables for VoxelManip nodeid, light and param2
  114. local VoxelManip_nodeid_mt = { __index = mm_get_nodeid, __newindex = mm_set_nodeid }
  115. local VoxelManip_light_mt = { __index = mm_get_light, __newindex = mm_set_light }
  116. local VoxelManip_param2_mt = { __index = mm_get_param2, __newindex = mm_set_param2 }
  117. -- Replacement VoxelManip.get_data and VoxelManip.set_data methods
  118. local function VoxelManip_get_data(self, buffer)
  119. -- Set up the metatable and pointer
  120. buffer = setmetatable(buffer or {}, VoxelManip_nodeid_mt)
  121. do
  122. local ptr = VoidPtrPtr()
  123. ffiC.VoxelManip_get_pointer(self, ptr)
  124. ptrtable[buffer] = MapNodePtr(ptr[0])
  125. end
  126. sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
  127. return buffer
  128. end
  129. local function VoxelManip_set_data(self, buffer)
  130. -- Call the original set_data method if the metatable is not
  131. -- the one we've set
  132. if getmetatable(buffer) ~= VoxelManip_nodeid_mt then
  133. mt_set_data(self, buffer)
  134. end
  135. end
  136. -- Replacement VoxelManip.get/set_light_data methods
  137. local function VoxelManip_get_light_data(self, buffer)
  138. -- Set up the metatable and pointer
  139. buffer = setmetatable(buffer or {}, VoxelManip_light_mt)
  140. do
  141. local ptr = VoidPtrPtr()
  142. ffiC.VoxelManip_get_pointer(self, ptr)
  143. ptrtable[buffer] = MapNodePtr(ptr[0])
  144. end
  145. sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
  146. return buffer
  147. end
  148. local function VoxelManip_set_light_data(self, buffer)
  149. -- Call the original set_light_data method if the metatable is not
  150. -- the one we've set
  151. if getmetatable(buffer) ~= VoxelManip_light_mt then
  152. mt_set_light_data(self, buffer)
  153. end
  154. end
  155. -- Replacement VoxelManip.get/set_param2_data methods
  156. local function VoxelManip_get_param2_data(self, buffer)
  157. -- Set up the metatable and pointer
  158. buffer = setmetatable(buffer or {}, VoxelManip_param2_mt)
  159. do
  160. local ptr = VoidPtrPtr()
  161. ffiC.VoxelManip_get_pointer(self, ptr)
  162. ptrtable[buffer] = MapNodePtr(ptr[0])
  163. end
  164. sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
  165. return buffer
  166. end
  167. local function VoxelManip_set_param2_data(self, buffer)
  168. -- Call the original set_param2_data method if the metatable is not
  169. -- the one we've set
  170. if getmetatable(buffer) ~= VoxelManip_param2_mt then
  171. mt_set_param2_data(self, buffer)
  172. end
  173. end
  174. -- Metatable for PerlinNoiseMap
  175. local PerlinNoiseMap_mt = { __index = mm_get }
  176. -- Replacement methods for PerlinNoiseMap.*
  177. local function PerlinNoiseMap_get_2d_map_flat(self, pos, buffer)
  178. -- Calculate the noise
  179. self:calc_2d_map(pos)
  180. -- Prepare the metatable for direct access to the data
  181. buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
  182. do
  183. local ptr = VoidPtrPtr()
  184. ffiC.PerlinNoiseMap_get_pointer(self, ptr)
  185. ptrtable[buffer] = MapNodePtr(ptr[0])
  186. end
  187. sizetable[buffer] = ffiC.PerlinNoiseMap_get_area(self)
  188. return buffer
  189. end
  190. local function PerlinNoiseMap_get_3d_map_flat(self, pos, buffer)
  191. -- Calculate the noise
  192. self:calc_3d_map(pos)
  193. -- Prepare the metatable for direct access to the data
  194. buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
  195. do
  196. local ptr = VoidPtrPtr()
  197. ffiC.PerlinNoiseMap_get_pointer(self, ptr)
  198. ptrtable[buffer] = MapNodePtr(ptr[0])
  199. end
  200. sizetable[buffer] = ffiC.PerlinNoiseMap_get_volume(self)
  201. return buffer
  202. end
  203. --[[
  204. local function PerlinNoiseMap_get_map_slice(slice_offset, slice_size, buffer)
  205. -- TODO
  206. end
  207. --]]
  208. -- Monkey-patch VoxelManip and PerlinNoiseMap
  209. minetest.after(0, function()
  210. -- VoxelManip buffer-intensive methods
  211. local mt = getmetatable(minetest.get_voxel_manip())
  212. mt.get_data = VoxelManip_get_data
  213. mt_set_data = mt.set_data
  214. mt.set_data = VoxelManip_set_data
  215. mt.get_light_data = VoxelManip_get_light_data
  216. mt_set_light_data = mt.set_light_data
  217. mt.set_light_data = VoxelManip_set_light_data
  218. mt.get_param2_data = VoxelManip_get_param2_data
  219. mt_set_param2_data = mt.set_param2_data
  220. mt.set_param2_data = VoxelManip_set_param2_data
  221. -- PerlinNoiseMap buffer-intensive methods
  222. mt = getmetatable(minetest.get_perlin_map({spread = {x=100, y=100, z=100}}, {x=1, y=1, z=1}))
  223. mt.get_2d_map_flat = PerlinNoiseMap_get_2d_map_flat
  224. mt.get_3d_map_flat = PerlinNoiseMap_get_3d_map_flat
  225. -- mt.get_map_slice = PerlinNoiseMap_get_map_slice
  226. -- Aliases
  227. mt.get2DMap_flat = mt.get_2d_map_flat
  228. mt.get3DMap_flat = mt.get_3d_map_flat
  229. -- mt.getMapSlice = mt.get_map_slice
  230. end)
  231. minetest.log("action", modname .. " activated!")