api.lua 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. music_api = {}
  2. local players = {}
  3. local tracks = {}
  4. --Settingtypes
  5. local time_interval = tonumber(minetest.settings:get("music_time_interval")) or 60
  6. local cleanup_interval = tonumber(minetest.settings:get("music_cleanup_interval")) or 5
  7. local global_gain = tonumber(minetest.settings:get("music_global_gain")) or 0.3
  8. local add_random_delay = minetest.settings:get_bool("music_add_random_delay", true)
  9. local maximum_random_delay = tonumber(minetest.settings:get("music_maximum_random_delay")) or 30
  10. local display_playback_messages = minetest.settings:get_bool("music_display_playback_messages", true)
  11. local random_delay = 0
  12. --Initialize random delay on the first run
  13. if add_random_delay then
  14. random_delay = math.random(maximum_random_delay)
  15. end
  16. --Internal functions
  17. local function load_player_settings(name)
  18. local file = io.open(minetest.get_worldpath() .. "/music_settings.mt", "r")
  19. if file then
  20. local rawfile = file:read()
  21. io.close(file)
  22. if rawfile then
  23. local settings = minetest.deserialize(rawfile)
  24. if settings[name] then
  25. players[name].settings = settings[name]
  26. end
  27. else
  28. minetest.log("error", "[Music_api] Unable to read volume settings!")
  29. end
  30. end
  31. end
  32. local function save_player_settings(name)
  33. local path = minetest.get_worldpath() .. "/music_settings.mt"
  34. local file = io.open(path, "r")
  35. local settings = {}
  36. if file then
  37. local rawfile = file:read()
  38. io.close(file)
  39. if rawfile then
  40. settings = minetest.deserialize(rawfile) or {}
  41. end
  42. end
  43. settings[name] = players[name].settings
  44. file = io.open(path, "w")
  45. if file then
  46. local rawfile = minetest.serialize(settings)
  47. file:write(rawfile)
  48. io.close(file)
  49. minetest.log("action", "[Music_api] Saving volume settings for " .. name)
  50. else
  51. minetest.log("error", "[Music_api] Unable to save volume settings!")
  52. end
  53. end
  54. local function play_track(name)
  55. local player = minetest.get_player_by_name(name)
  56. local player_pos = player:get_pos()
  57. local possible_tracks = {}
  58. local time = minetest.get_timeofday()
  59. --Assemble list of fitting tracks
  60. for _,track in pairs(tracks) do
  61. if track.name ~= players[name].previous and ((track.day and time > 0.25 and time < 0.75) or
  62. (track.night and ((time < 0.25 and time >= 0) or (time > 0.75 and time <= 1)))) and
  63. player_pos.y >= track.ymin and player_pos.y < track.ymax then
  64. table.insert(possible_tracks, track)
  65. end
  66. end
  67. --Return if no music fits
  68. if #possible_tracks == 0 then
  69. players[name].previous = nil
  70. return
  71. end
  72. --Select random track from fitting
  73. local track = possible_tracks[math.random(#possible_tracks)]
  74. --Start playback
  75. if not players[name].playing then
  76. if display_playback_messages then
  77. minetest.log("action", "[Music_api]: Starting playblack for: " .. name .. " " .. track.name .. " Available tracks for user: " .. #possible_tracks .. " Random delay: " .. random_delay)
  78. end
  79. players[name].track_handle = minetest.sound_play(track.name, {to_player = name, gain = track.gain * global_gain * players[name].settings.gain})
  80. players[name].playing = true
  81. players[name].previous = track.name
  82. players[name].playback_started = os.time()
  83. players[name].track_def = track
  84. end
  85. end
  86. local function stop_track(name)
  87. if players[name] and players[name].playing and players[name].track_handle then
  88. minetest.sound_stop(players[name].track_handle)
  89. players[name].playing = false
  90. players[name].track_handle = nil
  91. players[name].playback_started = nil
  92. players[name].track_def = nil
  93. if display_playback_messages then
  94. minetest.log("action", "[Music_api]: Stopped playback for: " .. name)
  95. end
  96. end
  97. end
  98. local function display_music_settings(name)
  99. local user
  100. if type(name) ~= "string" and name:is_player() then
  101. user = name:get_player_name()
  102. else
  103. user = name
  104. end
  105. local volume = math.floor(players[user].settings.gain * 1000)
  106. local formspec = "size[5,2]" .. default.gui_bg .. default.gui_bg_img ..
  107. "textarea[0.3,0.06;2,1;;Volume:;]" ..
  108. "scrollbar[0,0.6;4.8,0.25;horizontal;volume;" .. tostring(volume) .. "]" ..
  109. "button[0,1.5;1,0.3;play;Play]" ..
  110. "button[0.9,1.5;1,0.3;stop;Stop]" ..
  111. "button_exit[3,1.5;2,0.3;accept;Accept]"
  112. minetest.show_formspec(user, "music_settings", formspec)
  113. end
  114. --Registrations
  115. minetest.register_on_player_receive_fields(function(player, formname, fields)
  116. if formname ~= "music_settings" then return end
  117. local name = player:get_player_name()
  118. if fields.volume then
  119. local params = minetest.explode_scrollbar_event(fields.volume)
  120. players[name].settings.gain = params.value / 1000
  121. end
  122. if fields.play then
  123. play_track(name)
  124. end
  125. if fields.stop then
  126. stop_track(name)
  127. end
  128. if fields.accept or fields.quit then
  129. save_player_settings(name)
  130. end
  131. end)
  132. minetest.register_on_joinplayer(function(player)
  133. local name = player:get_player_name()
  134. players[name] = {playing = false, playback_started = nil, track_handle = nil, track_def = nil, previous = nil, settings = {gain = 1}}
  135. load_player_settings(name)
  136. end
  137. )
  138. minetest.register_on_leaveplayer(function(player)
  139. local name = player:get_player_name()
  140. players[name] = nil
  141. end
  142. )
  143. minetest.register_chatcommand("musicsettings",{
  144. params = "",
  145. description = "Displays music settings menu",
  146. privs = {shout = true},
  147. func = display_music_settings
  148. })
  149. if minetest.get_modpath("sfinv_buttons") then
  150. sfinv_buttons.register_button("show_music_settings",
  151. {
  152. title = "Music Settings",
  153. action = display_music_settings,
  154. tooltip = "Show music settings",
  155. image = "music_sfinv_buttons_icon.png",
  156. })
  157. end
  158. local cleanup_timer = 0
  159. minetest.register_globalstep(function(dtime)
  160. cleanup_timer = cleanup_timer + dtime
  161. if cleanup_timer < cleanup_interval then return end
  162. cleanup_timer = 0
  163. for k,v in pairs(players) do
  164. if v.playing and os.time() > v.playback_started + v.track_def.length then
  165. stop_track(k)
  166. end
  167. end
  168. end)
  169. local track_timer = 0
  170. minetest.register_globalstep(function(dtime)
  171. --Increment timer, return if it doesn't, reset it if it does and continue with function execution
  172. track_timer = track_timer + dtime
  173. if track_timer < time_interval + random_delay then return end
  174. track_timer = 0
  175. --Return if no tracks are defined
  176. if next(tracks) == nil then return end
  177. --Play music for every player
  178. for k,_ in pairs(players) do
  179. play_track(k)
  180. end
  181. --Change random delay if enabled on each play attempt
  182. if add_random_delay then
  183. random_delay = math.random(maximum_random_delay)
  184. end
  185. end)
  186. --API function
  187. function music_api.register_track(def)
  188. if def.name == nil or def.length == nil then
  189. minetest.log("error", "[Music_api] Missing track definition parameters!")
  190. return
  191. end
  192. local track_def = {
  193. name = def.name,
  194. length = def.length,
  195. gain = def.gain or 1,
  196. day = def.day or false,
  197. night = def.night or false,
  198. ymin = def.ymin or -31000,
  199. ymax = def.ymax or 31000,
  200. }
  201. table.insert(tracks, track_def)
  202. end