Code cleanup and organization #4

Open
GreenXenith wants to merge 2 commits from GreenXenith/yl_cinema:cleanup into master
23 changed files with 583 additions and 646 deletions

View File

@ -35,7 +35,7 @@ The `save_path` is relative to the directory of the world that's using this mod.
The yl_cinema mod provides the following API functions that can be called from your own mod:
```lua
success, msg, movielist = yl_cinema.listmovies(searchterm)
success, msg, movielist = yl_cinema.list_movies(searchterm)
```
Parameters:
@ -47,7 +47,7 @@ Returns:
- `movielist`: An unordered table of movie IDs in the format `{"id1","id7","id27","id3"}`. It may be empty `{}` if no movies were found.
```lua
success, movie = yl_cinema.showmovie(name, movie_id, target_player_name)
success, movie = yl_cinema.show_movie(name, movie_id, target_player_name)
```
Parameters:
@ -60,7 +60,7 @@ Returns:
- `movie`: A string containing an error message if `success` is false, or a table representing the movie if `success` is true.
```lua
success, msg = yl_cinema.getmovie(movie_id)
success, msg = yl_cinema.get_movie(movie_id)
```
Parameters:
@ -108,4 +108,4 @@ This mod is licensed under the MIT License.
## Dedication
This mod is dedicated to daydream, whose determined love for the movie Princess Bride willed this mod into existence.
This mod is dedicated to daydream, whose determined love for the movie Princess Bride willed this mod into existence.

215
api.lua
View File

@ -0,0 +1,215 @@
function yl_cinema.warn(text)
minetest.log("warning", "[yl_cinema] " .. text)
end
function yl_cinema.action(text)
minetest.log("action", "[yl_cinema] " .. text)
end
-- JSON helpers
local parse_json
if minetest.global_exists("json_lua") then
parse_json = _G.json_lua.json.decode
else
parse_json = minetest.parse_json
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
-- Media helpers
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 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 mediacallback(name)
local target_name = name or ""
yl_cinema.action("mediacallback: File pushed to player " .. target_name)
end
local function load_image(path)
local options = {
filepath = path
}
return minetest.dynamic_add_media(options, mediacallback)
end
-- API
function yl_cinema.orderpages(a, b)
return a.order < b.order
end
function yl_cinema.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
function yl_cinema.path_of_movie(movie_id)
return yl_cinema.worldpath .. yl_cinema.settings.save_path .. DIR_DELIM .. movie_id .. DIR_DELIM .. movie_id ..
".json"
end
function yl_cinema.get_movie(movie_id)
return yl_cinema.movies[movie_id] or {}
end
function yl_cinema.load_images_of_movie(movie_id)
local movie = yl_cinema.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 = yl_cinema.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
yl_cinema.warn(movie)
end
return success, movie
end
function yl_cinema.load_movies(movies_directory)
local movies = {}
local loaded_count = 0
local folders = minetest.get_dir_list(movies_directory, true)
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
function yl_cinema.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
function yl_cinema.list_movies(searchterm)
return yl_cinema.search_movies(yl_cinema.movies, searchterm)
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 = yl_cinema.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
function yl_cinema.load(filename, ...)
return read_json_file(filename, ...)
end
function yl_cinema.get_movie_reel(movie_id)
if yl_cinema.settings.enable_movieitems ~= true then
return false, "Movie items not enabled"
end
if not yl_cinema.movies or not yl_cinema.movies[movie_id] then
return false, "Cannot find movie_id"
end
local stack = ItemStack(yl_cinema.movie_item_name_written)
local meta = stack:get_meta()
meta:set_string("_yl_cinema_movie_id", movie_id)
meta:set_string("description", movie_id)
return true, stack
end

View File

@ -1,11 +0,0 @@
local chatcommand_cmd = "movie_imprint"
local chatcommand_definition = {
params = "<movie>", -- Short parameter description
description = "Imprints the <movie> on the wielded reel", -- Full description
privs = {
[yl_cinema.settings.admin_priv] = true
}, -- Require the "privs" privilege to run
func = yl_cinema.cmd_movie_imprint
}
minetest.register_chatcommand(chatcommand_cmd, chatcommand_definition)

View File

@ -1,11 +0,0 @@
local chatcommand_cmd = "movie_list"
local chatcommand_definition = {
params = "[<searchstring>]", -- Short parameter description
description = "Shows the whole movielist to you that match the <searchstring>, if given", -- Full description
privs = {
[yl_cinema.settings.admin_priv] = true
}, -- Require the "privs" privilege to run
func = yl_cinema.cmd_list_movies
}
minetest.register_chatcommand(chatcommand_cmd, chatcommand_definition)

View File

@ -1,11 +0,0 @@
local chatcommand_cmd = "movie_reload"
local chatcommand_definition = {
params = "[<movie_id>]", -- Short parameter description
description = "Reload specific <movie_id> or all movies, but does not create movie reel items.",
privs = {
[yl_cinema.settings.admin_priv] = true
},
func = yl_cinema.cmd_movie_reload
}
minetest.register_chatcommand(chatcommand_cmd, chatcommand_definition)

View File

@ -1,11 +0,0 @@
local chatcommand_cmd = "movie_show"
local chatcommand_definition = {
params = "<name> <movie>", -- Short parameter description
description = "Shows to the <player> the <movie>", -- Full description
privs = {
[yl_cinema.settings.admin_priv] = true
}, -- Require the "privs" privilege to run
func = yl_cinema.cmd_show_movie
}
minetest.register_chatcommand(chatcommand_cmd, chatcommand_definition)

View File

@ -1,4 +0,0 @@
dofile(yl_cinema.modpath .. "chatcommand_movie_show.lua")
dofile(yl_cinema.modpath .. "chatcommand_movie_list.lua")
dofile(yl_cinema.modpath .. "chatcommand_movie_reload.lua")
dofile(yl_cinema.modpath .. "chatcommand_movie_imprint.lua")

View File

@ -0,0 +1,65 @@
if yl_cinema.settings.enable_movieitems ~= true then
return
end
local function imprint_movie(name, movie_id)
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 got_reel, stack_or_err = yl_cinema.get_movie_reel(movie_id)
if not got_reel then
return false, stack_or_err
end
-- Replace or add
if old_stack:get_count() == 1 then
-- replace
pobj:set_wielded_item(stack_or_err)
return true, ""
else
-- can we add?
local inv = pobj:get_inventory()
local listname = "main"
if not inv:room_for_item(listname, stack_or_err) 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, stack_or_err)
return true, ""
end
end
minetest.register_chatcommand("movie_imprint", {
params = "<movie>", -- Short parameter description
description = "Imprints the <movie> on the wielded reel", -- Full description
privs = {
[yl_cinema.settings.admin_priv] = true
}, -- Require the "privs" privilege to run
func = function(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 ""
yl_cinema.action("Player " .. name .. " imprints movie " .. movie_id)
return imprint_movie(name, movie_id)
end
})

View File

@ -0,0 +1,67 @@
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
minetest.register_chatcommand("movie_list", {
params = "[<searchstring>]", -- Short parameter description
description = "Shows the whole movielist to you that match the <searchstring>, if given", -- Full description
privs = {
[yl_cinema.settings.admin_priv] = true
}, -- Require the "privs" privilege to run
func = function(name, param)
local args = string.split(param, " ")
if (#args > 1) then
return false, "Usage: /listmovies [<searchstring>]"
end
local searchterm = args[1] or ""
yl_cinema.action("Player " .. name .. " searches for movie " .. searchterm)
local display_table = {{"movie_id","name","itemstring"}}
local success, t = yl_cinema.list_movies(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
})

View File

@ -0,0 +1,20 @@
minetest.register_chatcommand("movie_reload", {
params = "[<movie_id>]", -- Short parameter description
description = "Reload specific <movie_id> or all movies, but does not create movie reel items.",
privs = {
[yl_cinema.settings.admin_priv] = true
},
func = function(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 ""
yl_cinema.action("Player " .. name .. " reloads movie " .. movie_id)
return yl_cinema.load_all_movies()
end
})

View File

@ -0,0 +1,31 @@
minetest.register_chatcommand("movie_show", {
params = "<name> <movie>", -- Short parameter description
description = "Shows to the <player> the <movie>", -- Full description
privs = {
[yl_cinema.settings.admin_priv] = true
}, -- Require the "privs" privilege to run
func = function(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 ""
yl_cinema.action("Player " .. name .. " shows movie " .. movie_id .. " to " .. target_player_name)
local success, msg = yl_cinema.show_movie(movie_id, target_player_name)
if success then
return true, msg
else
return false, msg
end
end
})

View File

@ -1,7 +0,0 @@
yl_cinema.dependencies = {}
if minetest.get_modpath("json_lua") and json_lua then
yl_cinema.dependencies.json = json_lua.json
else
yl_cinema.dependencies.json = yl_cinema.error
end

View File

@ -1,4 +0,0 @@
dofile(yl_cinema.modpath .. "feature_bigscreen_items.lua")
dofile(yl_cinema.modpath .. "feature_bigscreen_entity.lua")
dofile(yl_cinema.modpath .. "feature_bigscreen_crafting.lua")
dofile(yl_cinema.modpath .. "feature_bigscreen_integration.lua")

View File

@ -1,16 +0,0 @@
if (yl_cinema.settings.enable_bigscreen and (yl_cinema.settings.enable_bigscreen == true) and
yl_cinema.settings.enable_craft_bigscreen and (yl_cinema.settings.enable_craft_bigscreen == true)) then
local recipe = yl_cinema.string_to_recipe(yl_cinema.settings.enable_craft_bigscreen_recipe)
if type(recipe) == "table" then
minetest.register_craft({
type = "shaped",
output = "yl_cinema:bigscreen_base",
recipe = recipe
})
else
yl_cinema.warn("Cannot register crafting recipe for bigscreen: " ..
dump(yl_cinema.settings.enable_craft_bigscreen_recipe))
end
end

View File

@ -1,10 +0,0 @@
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_stopper(yl_cinema.bigscreen_block_name)
mesecon.register_mvps_stopper(yl_cinema.flatscreen_block_name)
end
if minetest.get_modpath("replacer") and replacer and replacer.blacklist then
replacer.blacklist[yl_cinema.bigscreen_block_name] = true
replacer.blacklist[yl_cinema.flatscreen_block_name] = true
end

View File

@ -1,3 +1,23 @@
-- TODO: Move bigscreen node here
-- Craft the bigscreen
if (yl_cinema.settings.enable_bigscreen and (yl_cinema.settings.enable_bigscreen == true) and
yl_cinema.settings.enable_craft_bigscreen and (yl_cinema.settings.enable_craft_bigscreen == true)) then
local bigscreen_recipe = yl_cinema.string_to_recipe(yl_cinema.settings.enable_craft_bigscreen_recipe)
if type(bigscreen_recipe) == "table" then
minetest.register_craft({
type = "shaped",
output = "yl_cinema:bigscreen_base",
recipe = bigscreen_recipe
})
else
yl_cinema.warn("Cannot register crafting recipe for bigscreen: " ..
dump(yl_cinema.settings.enable_craft_bigscreen_recipe))
end
end
if yl_cinema.settings.enable_movieitems ~= true then
return
end
@ -10,6 +30,7 @@ local movie_item_definition_empty = {
movie = 1,
media = 1
},
description = "Blank Movie Reel",
inventory_image = "yl_cinema_icon_movie_empty_inv.png",
wield_image = "yl_cinema_icon_movie_empty_inv.png",
}
@ -25,6 +46,7 @@ local movie_item_definition = {
not_in_creative_inventory = 1,
media = 1
},
description = "[UNLABELED MOVIE PRINT]", -- This should get overridden
inventory_image = "yl_cinema_icon_movie_inv.png",
wield_image = "yl_cinema_icon_movie_inv.png",
stack_max = 1,
@ -34,13 +56,13 @@ minetest.register_craftitem(yl_cinema.movie_item_name_written, movie_item_defini
-- Craft the empty reel
local recipe = yl_cinema.string_to_recipe(yl_cinema.settings.enable_craft_moviereel_recipe)
local reel_recipe = yl_cinema.string_to_recipe(yl_cinema.settings.enable_craft_moviereel_recipe)
if type(recipe) == "table" then
if type(reel_recipe) == "table" then
minetest.register_craft({
type = "shaped",
output = yl_cinema.movie_item_name_empty,
recipe = recipe
recipe = reel_recipe
})
else
yl_cinema.warn("Cannot register crafting recipe for Reel Item: " ..

125
feature_formspec.lua Normal file
View File

@ -0,0 +1,125 @@
local F = minetest.formspec_escape
yl_cinema.watching = {}
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 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 = yl_cinema.get_movie(movie_id)
local pages = movie.pages
if pagenum <= #pages then
table.sort(pages, yl_cinema.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)
function yl_cinema.show_movie(movie_id, target_player_name)
local pagenum = 0
if not next(yl_cinema.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
minetest.register_on_leaveplayer(stops_watching)
minetest.register_on_dieplayer(stops_watching)

View File

@ -1 +0,0 @@
dofile(yl_cinema.modpath .. "feature_bigscreen.lua")

View File

@ -21,14 +21,39 @@ yl_cinema.information.name = "yl_cinema"
yl_cinema.information.source = "https://gitea.your-land.de/your-land/yl_cinema"
yl_cinema.information.additional = "This mod implements #9 of the Small Tasks: A way to display pictures to a player."
dofile(yl_cinema.modpath .. "dependencies.lua")
-- Load configuration
dofile(yl_cinema.modpath .. "config.lua")
dofile(yl_cinema.modpath .. "setup.lua")
dofile(yl_cinema.modpath .. "internal.lua")
-- Create movie path if needed
minetest.mkdir(yl_cinema.worldpath .. DIR_DELIM .. yl_cinema.settings.save_path)
-- Set up API
dofile(yl_cinema.modpath .. "api.lua")
dofile(yl_cinema.modpath .. "initialize.lua")
dofile(yl_cinema.modpath .. "features.lua")
dofile(yl_cinema.modpath .. "chatcommands.lua")
-- Initialize features
dofile(yl_cinema.modpath .. "feature_bigscreen_items.lua")
dofile(yl_cinema.modpath .. "feature_bigscreen_entity.lua")
dofile(yl_cinema.modpath .. "feature_formspec.lua")
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_stopper(yl_cinema.bigscreen_block_name)
mesecon.register_mvps_stopper(yl_cinema.flatscreen_block_name)
end
if minetest.get_modpath("replacer") and replacer and replacer.blacklist then
replacer.blacklist[yl_cinema.bigscreen_block_name] = true
replacer.blacklist[yl_cinema.flatscreen_block_name] = true
end
-- Register chatcommands
for _, filename in ipairs(minetest.get_dir_list(yl_cinema.modpath .. "/chatcommands", false)) do
if filename:sub(-4) == ".lua" then
dofile(yl_cinema.modpath .. "/chatcommands/" .. filename)
end
end
-- Load movies
minetest.after(0, yl_cinema.load_all_movies)
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
minetest.log("action", "[MOD] yl_cinema loaded in [" .. mod_end_time .. "s]")

View File

@ -1,6 +0,0 @@
local function run_each_serverstart()
minetest.after(0,yl_cinema.load_all_movies)
end
run_each_serverstart()

View File

@ -1,519 +0,0 @@
-- 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 parse_json
if json == yl_cinema.error or json == nil then
local function parse_json(json_str)
return minetest.parse_json(json_str)
end
else
local function parse_json(json_str)
return json.decode(json_str)
end
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

View File

@ -1,4 +0,0 @@
local function get_files_in_directory(folderpath)
return minetest.get_dir_list(folderpath, true)
end

View File

@ -1,18 +0,0 @@
local mkdir = minetest.mkdir
local save_path = yl_cinema.settings.save_path
local function run_once()
local path = yl_cinema.worldpath .. DIR_DELIM .. save_path
local file = io.open(path, "r")
if not file then
mkdir(path)
else
file:close()
end
end
run_once()