Proof-of-concept Server-Sent CSMs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
5.4KB

  1. --
  2. -- SSCSM: Server-Sent Client-Side Mods proof-of-concept
  3. -- Initial code sent to the client
  4. --
  5. -- Copyright © 2019 by luk3yx
  6. -- License: https://git.minetest.land/luk3yx/sscsm/src/branch/master/LICENSE.md
  7. --
  8. -- Make sure both table.unpack and unpack exist.
  9. if table.unpack then
  10. unpack = table.unpack
  11. else
  12. table.unpack = unpack
  13. end
  14. -- Make sure a few basic functions exist, these may have been blocked because
  15. -- of security or laziness.
  16. if not rawget then function rawget(n, name) return n[name] end end
  17. if not rawset then function rawset(n, k, v) n[k] = v end end
  18. if not rawequal then function rawequal(a, b) return a == b end end
  19. -- Older versions of the CSM don't provide assert(), this function exists for
  20. -- compatibility.
  21. if not assert then
  22. function assert(value, ...)
  23. if value then
  24. return value, ...
  25. else
  26. error(... or 'assertion failed!', 2)
  27. end
  28. end
  29. end
  30. -- Create the API
  31. sscsm = {}
  32. function sscsm.global_exists(name)
  33. return rawget(_G, name) ~= nil
  34. end
  35. if not sscsm.global_exists('minetest') then
  36. minetest = assert(core, 'No "minetest" global found!')
  37. end
  38. minetest.global_exists = sscsm.global_exists
  39. -- Check if join_mod_channel and leave_mod_channel exist.
  40. if sscsm.global_exists('join_mod_channel')
  41. and sscsm.global_exists('leave_mod_channel') then
  42. sscsm.join_mod_channel = join_mod_channel
  43. sscsm.leave_mod_channel = leave_mod_channel
  44. join_mod_channel, leave_mod_channel = nil, nil
  45. else
  46. local dummy = function() end
  47. sscsm.join_mod_channel = dummy
  48. sscsm.leave_mod_channel = dummy
  49. end
  50. -- Add print()
  51. function print(...)
  52. local msg = '[SSCSM] '
  53. for i = 1, select('#', ...) do
  54. if i > 1 then msg = msg .. '\t' end
  55. msg = msg .. tostring(select(i, ...))
  56. end
  57. minetest.log('none', msg)
  58. end
  59. print('Hello from the server-sent CSMs!')
  60. -- Add register_on_mods_loaded
  61. do
  62. local funcs = {}
  63. function sscsm.register_on_mods_loaded(callback)
  64. if funcs then table.insert(funcs, callback) end
  65. end
  66. function sscsm._done_loading_()
  67. sscsm._done_loading_ = nil
  68. for _, func in ipairs(funcs) do func() end
  69. funcs = nil
  70. end
  71. end
  72. sscsm.register_on_mods_loaded(function()
  73. print('SSCSMs loaded, leaving mod channel.')
  74. sscsm.leave_mod_channel()
  75. end)
  76. -- Helper functions
  77. if not minetest.get_node then
  78. function minetest.get_node(pos)
  79. return minetest.get_node_or_nil(pos) or {name = 'ignore', param1 = 0,
  80. param2 = 0}
  81. end
  82. end
  83. -- Make minetest.run_server_chatcommand allow param to be unspecified.
  84. function minetest.run_server_chatcommand(cmd, param)
  85. minetest.send_chat_message('/' .. cmd .. ' ' .. (param or ''))
  86. end
  87. -- Register "server-side" chatcommands
  88. -- Can allow instantaneous responses in some cases.
  89. sscsm.registered_chatcommands = {}
  90. local function on_chat_message(msg)
  91. if msg:sub(1, 1) ~= '/' then return false end
  92. local cmd, param = msg:match('^/([^ ]+) *(.*)')
  93. if not cmd then
  94. minetest.display_chat_message('-!- Empty command')
  95. return true
  96. end
  97. if not sscsm.registered_chatcommands[cmd] then return false end
  98. local _, res = sscsm.registered_chatcommands[cmd].func(param or '')
  99. if res then minetest.display_chat_message(tostring(res)) end
  100. return true
  101. end
  102. function sscsm.register_chatcommand(cmd, def)
  103. if type(def) == 'function' then
  104. def = {func = def}
  105. elseif type(def.func) ~= 'function' then
  106. error('Invalid definition passed to sscsm.register_chatcommand.')
  107. end
  108. sscsm.registered_chatcommands[cmd] = def
  109. if on_chat_message then
  110. minetest.register_on_sending_chat_message(on_chat_message)
  111. on_chat_message = nil
  112. end
  113. end
  114. function sscsm.unregister_chatcommand(cmd)
  115. sscsm.registered_chatcommands[cmd] = nil
  116. end
  117. -- A proper get_player_control doesn't exist yet.
  118. function sscsm.get_player_control()
  119. local n = minetest.localplayer:get_key_pressed()
  120. return {
  121. up = n % 2 == 1,
  122. down = math.floor(n / 2) % 2 == 1,
  123. left = math.floor(n / 4) % 2 == 1,
  124. right = math.floor(n / 8) % 2 == 1,
  125. jump = math.floor(n / 16) % 2 == 1,
  126. aux1 = math.floor(n / 32) % 2 == 1,
  127. sneak = math.floor(n / 64) % 2 == 1,
  128. LMB = math.floor(n / 128) % 2 == 1,
  129. RMB = math.floor(n / 256) % 2 == 1,
  130. }
  131. end
  132. -- Call func(...) every <interval> seconds.
  133. local function sscsm_every(interval, func, ...)
  134. minetest.after(interval, sscsm_every, interval, func, ...)
  135. return func(...)
  136. end
  137. function sscsm.every(interval, func, ...)
  138. assert(type(interval) == 'number' and type(func) == 'function',
  139. 'Invalid sscsm.every() invocation.')
  140. return sscsm_every(interval, func, ...)
  141. end
  142. -- Allow SSCSMs to know about CSM restriction flags.
  143. -- "__FLAGS__" is replaced with the actual value in init.lua.
  144. local flags = __FLAGS__
  145. sscsm.restriction_flags = assert(flags)
  146. sscsm.restrictions = {
  147. chat_messages = math.floor(flags / 2) % 2 == 1,
  148. read_itemdefs = math.floor(flags / 4) % 2 == 1,
  149. read_nodedefs = math.floor(flags / 8) % 2 == 1,
  150. lookup_nodes_limit = math.floor(flags / 16) % 2 == 1,
  151. read_playerinfo = math.floor(flags / 32) % 2 == 1,
  152. }
  153. sscsm.restrictions.lookup_nodes = sscsm.restrictions.lookup_nodes_limit
  154. -- Add minetest.get_csm_restrictions() if it doesn't exist already.
  155. if not minetest.get_csm_restrictions then
  156. function minetest.get_csm_restrictions()
  157. return table.copy(sscsm.restrictions)
  158. end
  159. end