-- The functions and variables in this file are only for use in the mod itself. -- Those that do real work should be local and wrapped in public functions -- Minetest connector local json = yl_cinema.dependencies.json local F = minetest.formspec_escape yl_cinema.watching = {} local function warn(text) minetest.log("warning", "[yl_cinema] " .. text) end local function action(text) minetest.log("action", "[yl_cinema] " .. text) end -- JSON decoder local function parse_json(json_str) return json.decode(json_str) end local function get_files_in_directory(folderpath) return minetest.get_dir_list(folderpath, true) end -- internal functions local function format_table(t) -- Format of t must be {{row1,row2,row3, ...},{row1,row2,row3, ...},...} local blanks_between_rows = 3 local max_row_length = {} for linenumber = 1, #t do for rownumber = 1, #t[linenumber] do local row_length = #tostring(t[linenumber][rownumber]) if (max_row_length[rownumber] or 0) < row_length then max_row_length[rownumber] = row_length end end end local ret = {} for linenumber = 1, #t do local line_s = "" for rownumber = 1, #t[linenumber] do local text = t[linenumber][rownumber] local text_length = #tostring(text) local add_blanks = max_row_length[rownumber] - text_length local newtext = t[linenumber][rownumber] for add = 1, (add_blanks + blanks_between_rows) do newtext = newtext .. " " end line_s = line_s .. newtext end table.insert(ret,line_s) end return table.concat(ret, "\n") end local function remove_forbidden_characters(str) local pattern = "[^%w_]" return string.gsub(str, pattern, "_") end local function get_itemstring(movie_id) return yl_cinema.settings.movie_item_name .. "_" .. remove_forbidden_characters(movie_id) end local function remove_invisible_folders(folder_list) local filtered_list = {} for _, folder in ipairs(folder_list) do if not string.find(folder, "^%.") then table.insert(filtered_list, folder) end end return filtered_list or {} end local function watches(playername, movie_id) yl_cinema.watching[playername] = movie_id end local function watched_by(playername) return yl_cinema.watching[playername] or "" end local function stops_watching(player) local playername = player:get_player_name() yl_cinema.watching[playername] = nil end local function orderpages(a, b) return a.order < b.order end local function mediacallback(name) local target_name = name or "" action("mediacallback: File pushed to player " .. target_name) end local function string_to_recipe(s) local recipe = string.split(s, ",",true) if #recipe ~= 9 then return false end return {{recipe[1], recipe[2], recipe[3]}, {recipe[4], recipe[5], recipe[6]}, {recipe[7], recipe[8], recipe[9]}} end local function path_of_movie(movie_id) return yl_cinema.worldpath .. yl_cinema.settings.save_path .. DIR_DELIM .. movie_id .. DIR_DELIM .. movie_id .. ".json" end local function path_of_image(movie_id, name) return yl_cinema.worldpath .. yl_cinema.settings.save_path .. DIR_DELIM .. movie_id .. DIR_DELIM .. "textures" .. DIR_DELIM .. name end local function read_json_file(path) local file = io.open(path, "r") if not file then return false, "Error opening file: " .. path end local content = file:read("*all") file:close() if not content then return false, "Error reading file: " .. path end return true, parse_json(content) end local function get_movie(movie_id) return yl_cinema.movies[movie_id] or {} end local function load_image(path) local options = { filepath = path } return minetest.dynamic_add_media(options, mediacallback) end local function load_images_of_movie(movie_id) local movie = get_movie(movie_id) local loaded_count = 0 for _, file in ipairs(movie.files) do local path = path_of_image(movie_id, file) local success = load_image(path) if success then loaded_count = loaded_count + 1 end end return loaded_count, #movie.files end local function load_movie(movie_id) local file_path = path_of_movie(movie_id) local success, movie_data = read_json_file(file_path) local movie = "Error loading movie metadata from file " .. dump(file_path) .. ": " .. dump(movie_data) if success then movie = movie_data else warn(movie) end return success, movie end local function load_movies(movies_directory) local movies = {} local loaded_count = 0 local folders = get_files_in_directory(movies_directory) local movie_files = remove_invisible_folders(folders) for _, movie_id in ipairs(movie_files) do local success, movie = load_movie(movie_id) if success then movies[movie_id] = movie loaded_count = loaded_count + 1 end end return movies, loaded_count, #movie_files end function yl_cinema.load_movies() local movie_save_path = yl_cinema.worldpath .. yl_cinema.settings.save_path local movies, loaded_count, total_count = yl_cinema.load_movies(movie_save_path) if loaded_count ~= total_count then yl_cinema.warn(loaded_count .. "/" .. total_count .. " movies loaded.") else yl_cinema.action(loaded_count .. "/" .. total_count .. " movies loaded.") end return movies end local function search_movies(movies, search_term) local results = {} for movie_id, movie in pairs(movies) do if string.match(movie_id, search_term) or string.match(movie.name, search_term) or string.match(movie.description, search_term) then local item_string = "" if movie.item == true then item_string = "yes" end table.insert(results, {movie_id, movie.name, item_string or ""}) end end if #results > 0 then return true, results else return false, results end end local function get_movieformspec(movie_id, pagenum) local movie = yl_cinema.get_movie(movie_id) local screen_width = 12 local screen_height = 6.75 local screensize = screen_width .. "," .. 6.75 local formspec = {} table.insert(formspec, "formspec_version[4]size[" .. screensize .. "]bgcolor[#000000]") if (not movie) or (not pagenum) then warn("Some error happened. movie=" .. dump(movie) .. ", page=" .. dump(pagenum)) table.insert(formspec, "[0.5,0.5;Some Error happened.]") return table.concat(formspec, "") end if pagenum == 0 then table.insert(formspec, "background[0,0;" .. screensize .. ";" .. (F(movie.title_texture) or "") .. ";true]") table.insert(formspec, "label[0.5,0.5;" .. (F(movie.name) or "") .. "]") table.insert(formspec, "textarea[0.5,1.5;" .. screensize .. ";;" .. (F(movie.description) or "") .. ";]") else local page = movie.pages[pagenum] if not page then warn("Page empty = " .. dump(page)) return "" end table.insert(formspec, "background[0,0;" .. screensize .. ";" .. (F(page.texture) or "") .. ";true]") table.insert(formspec, "label[" .. (F(page.captionposx) or 0) .. "," .. (F(page.captionposy) or 0) .. ";" .. (F(page.caption) or "") .. "]") if pagenum == #movie.pages then table.insert(formspec, "button[3," .. (F(screen_height) - 1) .. ";2,1;replay;Replay]") table.insert(formspec, "button_exit[6," .. (F(screen_height) - 1) .. ";2,1;exit;Exit]") end end return table.concat(formspec, "") end local function showpage(target_player_name, movie_id, pagenum) if not minetest.get_player_by_name(target_player_name) then return end if watched_by(target_player_name) == "" then return end local formspec = get_movieformspec(movie_id, pagenum) local movie = get_movie(movie_id) local pages = movie.pages if pagenum <= #pages then table.sort(pages, orderpages) local page = pages[pagenum] or {} local delay = page.duration or 3 local nextpage = pagenum + 1 minetest.after(delay, showpage, target_player_name, movie_id, nextpage) watches(target_player_name, movie_id) minetest.show_formspec(target_player_name, "yl_cinema:movie", formspec) end end -- minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "yl_cinema:movie" then return end local playername = player:get_player_name() if fields.exit or fields.quit then stops_watching(player) end if player and fields.replay then local movie_id = watched_by(playername) showpage(player:get_player_name(), movie_id, 0) end return true end) minetest.register_on_leaveplayer(stops_watching) minetest.register_on_dieplayer(stops_watching) -- Chatcommands function yl_cinema.cmd_list_movies(name, param) local args = string.split(param, " ") if (#args > 1) then return false, "Usage: /listmovies []" end local searchterm = args[1] or "" minetest.log("action", "[yl_cinema] Player " .. name .. " searches for movie " .. searchterm) local display_table = {{"movie_id","name","itemstring"}} local success, t = yl_cinema.listmovies(searchterm) for _,movies in ipairs(t) do table.insert(display_table, movies) end local nice_format = format_table(display_table) if success then return true, nice_format else return false, "No movies matching your criteria were found." end end function yl_cinema.cmd_show_movie(name, param) local args = string.split(param, " ") if (#args ~= 2) then return false, "Usage: /showmovie " end if not minetest.get_player_by_name(args[1]) then return false, "Player not online" end local target_player_name = args[1] or "" local movie_id = args[2] or "" minetest.log("action", "[yl_cinema] Player " .. name .. " shows movie " .. movie_id .. " to " .. target_player_name) local success, msg = yl_cinema.startmovie(movie_id, target_player_name) if success then return true, msg else return false, msg end end function yl_cinema.cmd_movie_reload(name, param) local args = string.split(param, " ") if (#args >= 2) then return false, "Usage: /movie_reload []" end local movie_id = args[1] or "" action("[yl_cinema] Player " .. name .. " reloads movie " .. movie_id) end function yl_cinema.listmovies(searchterm) return search_movies(yl_cinema.movies, searchterm) end function yl_cinema.startmovie(movie_id, target_player_name) local pagenum = 0 if not next(get_movie(movie_id)) then return false, "Movie " .. movie_id .. " not found." end watches(target_player_name, movie_id) -- Start to show a movie if minetest.get_player_by_name(target_player_name) then showpage(target_player_name, movie_id, pagenum) return true, "Movie " .. movie_id .. " started for " .. target_player_name else return false, "Player " .. target_player_name .. " not found." end end function yl_cinema.load(filename, ...) return read_json_file(filename, ...) end function yl_cinema.load_movies(movies_directory, ...) return load_movies(movies_directory, ...) end function yl_cinema.search_movies(movies, search_term, ...) return search_movies(movies, search_term, ...) end function yl_cinema.path_of_movie(movie_id, ...) return path_of_movie(movie_id, ...) end function yl_cinema.load_images_of_movie(movie_id, ...) return load_images_of_movie(movie_id, ...) end function yl_cinema.get_movie(movie_id, ...) return get_movie(movie_id, ...) end function yl_cinema.string_to_recipe(recipestring, ...) return string_to_recipe(recipestring, ...) end function yl_cinema.orderpages(a, b, ...) return orderpages(a, b, ...) end function yl_cinema.get_itemstring(movie_id, ...) return get_itemstring(movie_id, ...) end yl_cinema.action = action yl_cinema.warn = warn