yl_cinema/internal.lua

513 lines
14 KiB
Lua

-- 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
-- Initialize files
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_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
local movie_files = movie.files or {}
if type(movie_files) ~= "table" then return 0, "no files" end
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.init_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 [<searchstring>]"
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 <playername> <movie>"
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.load_all_movies()
yl_cinema.movies = yl_cinema.init_movies()
for movie_id, _ in pairs(yl_cinema.movies) do
local loaded_count, total_count = load_images_of_movie(movie_id)
if loaded_count ~= total_count then
yl_cinema.warn(loaded_count .. "/" .. total_count .. " images loaded for movie " .. movie_id)
else
yl_cinema.action(loaded_count .. "/" .. total_count .. " images loaded for movie " .. movie_id)
end
end
return true, "Done."
end
local function movieid_to_item(movie_id)
local itemstack = ItemStack(yl_cinema.movie_item_name_written)
local meta = itemstack:get_meta()
meta:set_string("_yl_cinema_movie_id", movie_id)
meta:set_string("description", movie_id)
return itemstack
end
function yl_cinema.imprint_movie(name, movie_id)
-- Defense
if not yl_cinema.movies or not yl_cinema.movies[movie_id] then
return false, "Cannot find movie_id"
end
local pobj = minetest.get_player_by_name(name)
if not pobj then
return false, "Cannot find player"
end
local old_stack = pobj:get_wielded_item()
if (not old_stack) or (old_stack:get_name() ~= yl_cinema.movie_item_name_empty) then
return false, "Please wield an empty movie reel"
end
-- Prepare new itemstack
local new_stack = movieid_to_item(movie_id)
-- Replace or add
if old_stack:get_count() == 1 then
-- replace
pobj:set_wielded_item(new_stack)
return true, ""
else
-- can we add?
local inv = pobj:get_inventory()
local listname = "main"
if not inv:room_for_item(listname, new_stack) then
return false, "No space in inventory for new print."
end
-- remove old
old_stack:take_item(1)
pobj:set_wielded_item(old_stack)
-- add new
inv:add_item(listname, new_stack)
return true, ""
end
end
function yl_cinema.cmd_movie_reload(name, param)
local args = string.split(param, " ")
if (#args >= 2) then
return false, "Usage: /movie_reload [<movie_id>]"
end
local movie_id = args[1] or ""
action("[yl_cinema] Player " .. name .. " reloads movie " .. movie_id)
return yl_cinema.load_all_movies()
end
function yl_cinema.cmd_movie_imprint(name, param)
local args = string.split(param, " ")
if (#args > 1) then
return false, "Usage: /movie_imprint [<movie_id>]"
end
local movie_id = args[1] or ""
action("[yl_cinema] Player " .. name .. " imprints movie " .. movie_id)
return yl_cinema.imprint_movie(name, 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
yl_cinema.action = action
yl_cinema.warn = warn