Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad81c1ce3 | ||
|
|
25b1cca415 | ||
|
|
767b2e7b17 | ||
|
|
2902a29c2d | ||
|
|
829426c714 | ||
|
|
8e1d78e9de | ||
|
|
4b31a38ae9 | ||
|
|
bf9f50bc0e | ||
|
|
3aa28bc7a2 | ||
|
|
a5377251e2 | ||
|
|
a58b47d0f6 | ||
|
|
7ae0b90ff7 | ||
|
|
a50db0e824 | ||
|
|
e1f9ba435f | ||
|
|
ba8fa0bd42 | ||
|
|
1ed90c90c3 | ||
|
|
49cec3f782 | ||
|
|
38d112033b | ||
|
|
54dbd78f90 | ||
|
|
c9ac9992c7 | ||
|
|
3946d2a504 | ||
|
|
5fd92d67d5 | ||
|
|
c120ea57c9 | ||
|
|
848f80b2e5 | ||
|
|
e9e9fd7c3f | ||
|
|
9772322613 | ||
|
|
7a4c1e7327 | ||
|
|
15c316765d | ||
|
|
c3708b456e | ||
|
|
83853ccd41 | ||
|
|
e275b7099a | ||
|
|
0d990bd189 | ||
|
|
b03135548b | ||
|
|
0a903e69fb | ||
|
|
0404bbf671 | ||
|
|
0ea3e6dbe2 | ||
|
|
67bf7130ce | ||
|
|
d9ef072305 | ||
|
|
33de69a173 | ||
|
|
76036abdb0 | ||
|
|
4ccaa6d0af | ||
|
|
dfd1f87762 | ||
|
|
6c4a8766ab | ||
|
|
dae03382bf | ||
|
|
50b0e9f7a4 | ||
|
|
206565d965 | ||
|
|
3cc45fd8ad | ||
|
|
83cc882335 | ||
|
|
bbae8eb751 | ||
|
|
a0dce51af6 | ||
|
|
96f753a108 | ||
|
|
e15681080e | ||
|
|
35f88ac67c | ||
|
|
22dbbf0a6f | ||
|
|
000da6b25d | ||
|
|
e51ad5337f | ||
|
|
15be2659ea | ||
|
|
d9de9f23d9 | ||
|
|
2330267d22 | ||
|
|
60113bde74 | ||
|
|
1309e52198 | ||
|
|
5004f31575 | ||
|
|
6cbd1b8bf7 | ||
|
|
6fa3892a71 | ||
|
|
04e9a9d541 | ||
|
|
f3439c40d8 | ||
|
|
20e3d550fa | ||
|
|
de0cdbc01c | ||
|
|
e605d70256 | ||
|
|
4696c59a5f | ||
|
|
4594ba6522 | ||
|
|
06baf05c64 | ||
|
|
d879a539cd | ||
|
|
752e11e114 | ||
|
|
9a927476ca | ||
|
|
ff7d7080e3 | ||
|
|
769b2d7c05 | ||
|
|
5be786c804 | ||
|
|
747bc40840 | ||
|
|
d19a69cd0d | ||
|
|
d02ce1cf4d | ||
|
|
2e66aca357 | ||
|
|
b08d7558de | ||
|
|
ff25218374 | ||
|
|
7433d65d3e | ||
|
|
1fd9a11e30 | ||
|
|
4f246f0e22 | ||
|
|
f87c1c2410 |
@@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||
# Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
|
||||
set(VERSION_MAJOR 0)
|
||||
set(VERSION_MINOR 4)
|
||||
set(VERSION_PATCH 8)
|
||||
set(VERSION_PATCH 9)
|
||||
if(VERSION_EXTRA)
|
||||
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
|
||||
else()
|
||||
|
||||
19
builtin/async_env.lua
Normal file
19
builtin/async_env.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
engine.log("info","Initializing Asynchronous environment")
|
||||
|
||||
dofile(SCRIPTDIR .. DIR_DELIM .. "misc_helpers.lua")
|
||||
|
||||
function engine.job_processor(serialized_function, serialized_data)
|
||||
|
||||
local fct = marshal.decode(serialized_function)
|
||||
local params = marshal.decode(serialized_data)
|
||||
local retval = marshal.encode(nil)
|
||||
|
||||
if fct ~= nil and type(fct) == "function" then
|
||||
local result = fct(params)
|
||||
retval = marshal.encode(result)
|
||||
else
|
||||
engine.log("error","ASYNC WORKER: unable to deserialize function")
|
||||
end
|
||||
|
||||
return retval,retval:len()
|
||||
end
|
||||
59
builtin/async_event.lua
Normal file
59
builtin/async_event.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
local tbl = engine or minetest
|
||||
|
||||
tbl.async_jobs = {}
|
||||
|
||||
if engine ~= nil then
|
||||
function tbl.async_event_handler(jobid, serialized_retval)
|
||||
local retval = nil
|
||||
if serialized_retval ~= "ERROR" then
|
||||
retval= marshal.decode(serialized_retval)
|
||||
else
|
||||
tbl.log("error","Error fetching async result")
|
||||
end
|
||||
|
||||
assert(type(tbl.async_jobs[jobid]) == "function")
|
||||
tbl.async_jobs[jobid](retval)
|
||||
tbl.async_jobs[jobid] = nil
|
||||
end
|
||||
else
|
||||
|
||||
minetest.register_globalstep(
|
||||
function(dtime)
|
||||
local list = tbl.get_finished_jobs()
|
||||
|
||||
for i=1,#list,1 do
|
||||
local retval = marshal.decode(list[i].retval)
|
||||
|
||||
assert(type(tbl.async_jobs[jobid]) == "function")
|
||||
tbl.async_jobs[list[i].jobid](retval)
|
||||
tbl.async_jobs[list[i].jobid] = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function tbl.handle_async(fct, parameters, callback)
|
||||
|
||||
--serialize fct
|
||||
local serialized_fct = marshal.encode(fct)
|
||||
|
||||
assert(marshal.decode(serialized_fct) ~= nil)
|
||||
|
||||
--serialize parameters
|
||||
local serialized_params = marshal.encode(parameters)
|
||||
|
||||
if serialized_fct == nil or
|
||||
serialized_params == nil or
|
||||
serialized_fct:len() == 0 or
|
||||
serialized_params:len() == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
local jobid = tbl.do_async_callback( serialized_fct,
|
||||
serialized_fct:len(),
|
||||
serialized_params,
|
||||
serialized_params:len())
|
||||
|
||||
tbl.async_jobs[jobid] = callback
|
||||
|
||||
return true
|
||||
end
|
||||
@@ -515,34 +515,45 @@ minetest.register_on_punchnode(function(pos, node, puncher)
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("rollback_check", {
|
||||
params = "[<range>] [<seconds>]",
|
||||
params = "[<range>] [<seconds>] [limit]",
|
||||
description = "check who has last touched a node or near it, "..
|
||||
"max. <seconds> ago (default range=0, seconds=86400=24h)",
|
||||
"max. <seconds> ago (default range=0, seconds=86400=24h, limit=5)",
|
||||
privs = {rollback=true},
|
||||
func = function(name, param)
|
||||
local range, seconds = string.match(param, "(%d+) *(%d*)")
|
||||
local range, seconds, limit =
|
||||
param:match("(%d+) *(%d*) *(%d*)")
|
||||
range = tonumber(range) or 0
|
||||
seconds = tonumber(seconds) or 86400
|
||||
minetest.chat_send_player(name, "Punch a node (limits set: range="..
|
||||
dump(range).." seconds="..dump(seconds).."s)")
|
||||
limit = tonumber(limit) or 5
|
||||
if limit > 100 then
|
||||
minetest.chat_send_player(name, "That limit is too high!")
|
||||
return
|
||||
end
|
||||
minetest.chat_send_player(name, "Punch a node (range="..
|
||||
range..", seconds="..seconds.."s, limit="..limit..")")
|
||||
|
||||
minetest.rollback_punch_callbacks[name] = function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
minetest.chat_send_player(name, "Checking...")
|
||||
local actor, act_p, act_seconds =
|
||||
minetest.rollback_get_last_node_actor(pos, range, seconds)
|
||||
if actor == "" then
|
||||
minetest.chat_send_player(name, "Checking "..minetest.pos_to_string(pos).."...")
|
||||
local actions = minetest.rollback_get_node_actions(pos, range, seconds, limit)
|
||||
local num_actions = #actions
|
||||
if num_actions == 0 then
|
||||
minetest.chat_send_player(name, "Nobody has touched the "..
|
||||
"specified location in "..dump(seconds).." seconds")
|
||||
"specified location in "..seconds.." seconds")
|
||||
return
|
||||
end
|
||||
local nodedesc = "this node"
|
||||
if act_p.x ~= pos.x or act_p.y ~= pos.y or act_p.z ~= pos.z then
|
||||
nodedesc = minetest.pos_to_string(act_p)
|
||||
local time = os.time()
|
||||
for i = num_actions, 1, -1 do
|
||||
local action = actions[i]
|
||||
minetest.chat_send_player(name,
|
||||
("%s %s %s -> %s %d seconds ago.")
|
||||
:format(
|
||||
minetest.pos_to_string(action.pos),
|
||||
action.actor,
|
||||
action.oldnode.name,
|
||||
action.newnode.name,
|
||||
time - action.time))
|
||||
end
|
||||
local nodename = minetest.get_node(act_p).name
|
||||
minetest.chat_send_player(name, "Last actor on "..nodedesc..
|
||||
" was "..actor..", "..dump(act_seconds)..
|
||||
"s ago (node is now "..nodename..")")
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -554,7 +565,7 @@ minetest.register_chatcommand("rollback", {
|
||||
func = function(name, param)
|
||||
local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
|
||||
if not target_name then
|
||||
local player_name = nil;
|
||||
local player_name = nil
|
||||
player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
|
||||
if not player_name then
|
||||
minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check")
|
||||
@@ -564,13 +575,13 @@ minetest.register_chatcommand("rollback", {
|
||||
end
|
||||
seconds = tonumber(seconds) or 60
|
||||
minetest.chat_send_player(name, "Reverting actions of "..
|
||||
dump(target_name).." since "..dump(seconds).." seconds.")
|
||||
target_name.." since "..seconds.." seconds.")
|
||||
local success, log = minetest.rollback_revert_actions_by(
|
||||
target_name, seconds)
|
||||
if #log > 10 then
|
||||
if #log > 100 then
|
||||
minetest.chat_send_player(name, "(log is too long to show)")
|
||||
else
|
||||
for _,line in ipairs(log) do
|
||||
for _, line in pairs(log) do
|
||||
minetest.chat_send_player(name, line)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,3 +46,8 @@ setmetatable(minetest.env, {
|
||||
return rawget(table, key)
|
||||
end
|
||||
})
|
||||
|
||||
function minetest.rollback_get_last_node_actor(pos, range, seconds)
|
||||
return minetest.rollback_get_node_actions(pos, range, seconds, 1)[1]
|
||||
end
|
||||
|
||||
|
||||
@@ -17,6 +17,20 @@
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Generic implementation of a filter/sortable list --
|
||||
-- Usage: --
|
||||
-- Filterlist needs to be initialized on creation. To achieve this you need to --
|
||||
-- pass following functions: --
|
||||
-- raw_fct() (mandatory): --
|
||||
-- function returning a table containing the elements to be filtered --
|
||||
-- compare_fct(element1,element2) (mandatory): --
|
||||
-- function returning true/false if element1 is same element as element2 --
|
||||
-- uid_match_fct(element1,uid) (optional) --
|
||||
-- function telling if uid is attached to element1 --
|
||||
-- filter_fct(element,filtercriteria) (optional) --
|
||||
-- function returning true/false if filtercriteria met to element --
|
||||
-- fetch_param (optional) --
|
||||
-- parameter passed to raw_fct to aquire correct raw data --
|
||||
-- --
|
||||
--------------------------------------------------------------------------------
|
||||
filterlist = {}
|
||||
|
||||
@@ -157,7 +171,7 @@ function filterlist.process(this)
|
||||
this.m_processed_list = {}
|
||||
|
||||
for k,v in pairs(this.m_raw_list) do
|
||||
if this.m_filtercriteria == nil or
|
||||
if this.m_filtercriteria == nil or
|
||||
this.m_filter_fct(v,this.m_filtercriteria) then
|
||||
table.insert(this.m_processed_list,v)
|
||||
end
|
||||
@@ -167,7 +181,7 @@ function filterlist.process(this)
|
||||
return
|
||||
end
|
||||
|
||||
if this.m_sort_list[this.m_sortmode] ~= nil and
|
||||
if this.m_sort_list[this.m_sortmode] ~= nil and
|
||||
type(this.m_sort_list[this.m_sortmode]) == "function" then
|
||||
|
||||
this.m_sort_list[this.m_sortmode](this)
|
||||
@@ -237,7 +251,7 @@ function compare_worlds(world1,world2)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function sort_worlds_alphabetic(this)
|
||||
function sort_worlds_alphabetic(this)
|
||||
|
||||
table.sort(this.m_processed_list, function(a, b)
|
||||
--fixes issue #857 (crash due to sorting nil in worldlist)
|
||||
|
||||
@@ -98,7 +98,7 @@ function minetest.facedir_to_dir(facedir)
|
||||
|
||||
--indexed into by a table of correlating facedirs
|
||||
[({[0]=1, 2, 3, 4,
|
||||
5, 4, 6, 2,
|
||||
5, 2, 6, 4,
|
||||
6, 2, 5, 4,
|
||||
1, 5, 3, 6,
|
||||
1, 6, 3, 5,
|
||||
@@ -501,7 +501,7 @@ minetest.nodedef_default = {
|
||||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
paramtype2 = "none",
|
||||
is_ground_content = false,
|
||||
is_ground_content = true,
|
||||
sunlight_propagates = false,
|
||||
walkable = true,
|
||||
pointable = true,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,17 +24,15 @@ minetest.registered_aliases = {}
|
||||
|
||||
-- For tables that are indexed by item name:
|
||||
-- If table[X] does not exist, default to table[minetest.registered_aliases[X]]
|
||||
local function set_alias_metatable(table)
|
||||
setmetatable(table, {
|
||||
__index = function(name)
|
||||
return rawget(table, minetest.registered_aliases[name])
|
||||
end
|
||||
})
|
||||
end
|
||||
set_alias_metatable(minetest.registered_items)
|
||||
set_alias_metatable(minetest.registered_nodes)
|
||||
set_alias_metatable(minetest.registered_craftitems)
|
||||
set_alias_metatable(minetest.registered_tools)
|
||||
local alias_metatable = {
|
||||
__index = function(t, name)
|
||||
return rawget(t, minetest.registered_aliases[name])
|
||||
end
|
||||
}
|
||||
setmetatable(minetest.registered_items, alias_metatable)
|
||||
setmetatable(minetest.registered_nodes, alias_metatable)
|
||||
setmetatable(minetest.registered_craftitems, alias_metatable)
|
||||
setmetatable(minetest.registered_tools, alias_metatable)
|
||||
|
||||
-- These item names may not be used because they would interfere
|
||||
-- with legacy itemstrings
|
||||
@@ -106,6 +104,11 @@ function minetest.register_item(name, itemdef)
|
||||
-- Use the nodebox as selection box if it's not set manually
|
||||
if itemdef.drawtype == "nodebox" and not itemdef.selection_box then
|
||||
itemdef.selection_box = itemdef.node_box
|
||||
elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then
|
||||
itemdef.selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
|
||||
}
|
||||
end
|
||||
setmetatable(itemdef, {__index = minetest.nodedef_default})
|
||||
minetest.registered_nodes[itemdef.name] = itemdef
|
||||
@@ -311,6 +314,45 @@ minetest.register_item(":", {
|
||||
groups = {not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
|
||||
function minetest.run_callbacks(callbacks, mode, ...)
|
||||
assert(type(callbacks) == "table")
|
||||
local cb_len = #callbacks
|
||||
if cb_len == 0 then
|
||||
if mode == 2 or mode == 3 then
|
||||
return true
|
||||
elseif mode == 4 or mode == 5 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local ret = nil
|
||||
for i = 1, cb_len do
|
||||
local cb_ret = callbacks[i](...)
|
||||
|
||||
if mode == 0 and i == 1 then
|
||||
ret = cb_ret
|
||||
elseif mode == 1 and i == cb_len then
|
||||
ret = cb_ret
|
||||
elseif mode == 2 then
|
||||
if not cb_ret or i == 1 then
|
||||
ret = cb_ret
|
||||
end
|
||||
elseif mode == 3 then
|
||||
if cb_ret then
|
||||
return cb_ret
|
||||
end
|
||||
ret = cb_ret
|
||||
elseif mode == 4 then
|
||||
if (cb_ret and not ret) or i == 1 then
|
||||
ret = cb_ret
|
||||
end
|
||||
elseif mode == 5 and cb_ret then
|
||||
return cb_ret
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--
|
||||
-- Callback registration
|
||||
--
|
||||
@@ -338,6 +380,7 @@ minetest.registered_on_generateds, minetest.register_on_generated = make_registr
|
||||
minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration()
|
||||
minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration()
|
||||
minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration()
|
||||
minetest.registered_on_prejoinplayers, minetest.register_on_prejoinplayer = make_registration()
|
||||
minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration()
|
||||
minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
|
||||
minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse()
|
||||
|
||||
@@ -22,7 +22,7 @@ function get_mods(path,retval,modpack)
|
||||
for i=1,#mods,1 do
|
||||
local toadd = {}
|
||||
local modpackfile = nil
|
||||
|
||||
|
||||
toadd.name = mods[i]
|
||||
toadd.path = path .. DIR_DELIM .. mods[i] .. DIR_DELIM
|
||||
if modpack ~= nil and
|
||||
@@ -33,7 +33,7 @@ function get_mods(path,retval,modpack)
|
||||
local error = nil
|
||||
modpackfile,error = io.open(filename,"r")
|
||||
end
|
||||
|
||||
|
||||
if modpackfile ~= nil then
|
||||
modpackfile:close()
|
||||
toadd.is_modpack = true
|
||||
@@ -52,14 +52,16 @@ modmgr = {}
|
||||
function modmgr.extract(modfile)
|
||||
if modfile.type == "zip" then
|
||||
local tempfolder = os.tempfolder()
|
||||
|
||||
|
||||
if tempfolder ~= nil and
|
||||
tempfodler ~= "" then
|
||||
tempfolder ~= "" then
|
||||
engine.create_dir(tempfolder)
|
||||
engine.extract_zip(modfile.name,tempfolder)
|
||||
return tempfolder
|
||||
if engine.extract_zip(modfile.name,tempfolder) then
|
||||
return tempfolder
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -80,7 +82,7 @@ function modmgr.getbasefolder(temppath)
|
||||
path=temppath
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
@@ -89,9 +91,9 @@ function modmgr.getbasefolder(temppath)
|
||||
path=temppath
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local subdirs = engine.get_dirlist(temppath,true)
|
||||
|
||||
|
||||
--only single mod or modpack allowed
|
||||
if #subdirs ~= 1 then
|
||||
return {
|
||||
@@ -100,7 +102,7 @@ function modmgr.getbasefolder(temppath)
|
||||
}
|
||||
end
|
||||
|
||||
testfile =
|
||||
testfile =
|
||||
io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
@@ -109,8 +111,8 @@ function modmgr.getbasefolder(temppath)
|
||||
path= temppath .. DIR_DELIM .. subdirs[1]
|
||||
}
|
||||
end
|
||||
|
||||
testfile =
|
||||
|
||||
testfile =
|
||||
io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
@@ -131,7 +133,7 @@ function modmgr.isValidModname(modpath)
|
||||
if modpath:find("-") ~= nil then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -142,20 +144,20 @@ function modmgr.parse_register_line(line)
|
||||
if pos1 ~= nil then
|
||||
pos2 = line:find("\"",pos1+1)
|
||||
end
|
||||
|
||||
|
||||
if pos1 ~= nil and pos2 ~= nil then
|
||||
local item = line:sub(pos1+1,pos2-1)
|
||||
|
||||
|
||||
if item ~= nil and
|
||||
item ~= "" then
|
||||
local pos3 = item:find(":")
|
||||
|
||||
|
||||
if pos3 ~= nil then
|
||||
local retval = item:sub(1,pos3-1)
|
||||
if retval ~= nil and
|
||||
retval ~= "" then
|
||||
return retval
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -169,10 +171,10 @@ function modmgr.parse_dofile_line(modpath,line)
|
||||
if pos1 ~= nil then
|
||||
pos2 = line:find("\"",pos1+1)
|
||||
end
|
||||
|
||||
|
||||
if pos1 ~= nil and pos2 ~= nil then
|
||||
local filename = line:sub(pos1+1,pos2-1)
|
||||
|
||||
|
||||
if filename ~= nil and
|
||||
filename ~= "" and
|
||||
filename:find(".lua") then
|
||||
@@ -187,37 +189,37 @@ function modmgr.identify_modname(modpath,filename)
|
||||
local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
|
||||
if testfile ~= nil then
|
||||
local line = testfile:read()
|
||||
|
||||
|
||||
while line~= nil do
|
||||
local modname = nil
|
||||
|
||||
|
||||
if line:find("minetest.register_tool") then
|
||||
modname = modmgr.parse_register_line(line)
|
||||
end
|
||||
|
||||
|
||||
if line:find("minetest.register_craftitem") then
|
||||
modname = modmgr.parse_register_line(line)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
if line:find("minetest.register_node") then
|
||||
modname = modmgr.parse_register_line(line)
|
||||
end
|
||||
|
||||
|
||||
if line:find("dofile") then
|
||||
modname = modmgr.parse_dofile_line(modpath,line)
|
||||
end
|
||||
|
||||
|
||||
if modname ~= nil then
|
||||
testfile:close()
|
||||
return modname
|
||||
end
|
||||
|
||||
|
||||
line = testfile:read()
|
||||
end
|
||||
testfile:close()
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -231,29 +233,29 @@ function modmgr.tab()
|
||||
if modmgr.selected_mod == nil then
|
||||
modmgr.selected_mod = 1
|
||||
end
|
||||
|
||||
local retval =
|
||||
|
||||
local retval =
|
||||
"vertlabel[0,-0.25;".. fgettext("MODS") .. "]" ..
|
||||
"label[0.8,-0.25;".. fgettext("Installed Mods:") .. "]" ..
|
||||
"textlist[0.75,0.25;4.5,4;modlist;" ..
|
||||
modmgr.render_modlist(modmgr.global_mods) ..
|
||||
modmgr.render_modlist(modmgr.global_mods) ..
|
||||
";" .. modmgr.selected_mod .. "]"
|
||||
|
||||
retval = retval ..
|
||||
"label[0.8,4.2;" .. fgettext("Add mod:") .. "]" ..
|
||||
"label[0.8,4.2;" .. fgettext("Add mod:") .. "]" ..
|
||||
-- TODO Disabled due to upcoming release 0.4.8 and irrlicht messing up localization
|
||||
-- "button[0.75,4.85;1.8,0.5;btn_mod_mgr_install_local;".. fgettext("Local install") .. "]" ..
|
||||
"button[2.45,4.85;3.05,0.5;btn_mod_mgr_download;".. fgettext("Online mod repository") .. "]"
|
||||
|
||||
|
||||
local selected_mod = nil
|
||||
|
||||
|
||||
if filterlist.size(modmgr.global_mods) >= modmgr.selected_mod then
|
||||
selected_mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
|
||||
end
|
||||
|
||||
|
||||
if selected_mod ~= nil then
|
||||
local modscreenshot = nil
|
||||
|
||||
|
||||
--check for screenshot beeing available
|
||||
local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png"
|
||||
local error = nil
|
||||
@@ -262,40 +264,40 @@ function modmgr.tab()
|
||||
screenshotfile:close()
|
||||
modscreenshot = screenshotfilename
|
||||
end
|
||||
|
||||
|
||||
if modscreenshot == nil then
|
||||
modscreenshot = modstore.basetexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
retval = retval
|
||||
|
||||
retval = retval
|
||||
.. "image[5.5,0;3,2;" .. engine.formspec_escape(modscreenshot) .. "]"
|
||||
.. "label[8.25,0.6;" .. selected_mod.name .. "]"
|
||||
|
||||
|
||||
local descriptionlines = nil
|
||||
error = nil
|
||||
local descriptionfilename = selected_mod.path .. "description.txt"
|
||||
descriptionfile,error = io.open(descriptionfilename,"r")
|
||||
if error == nil then
|
||||
descriptiontext = descriptionfile:read("*all")
|
||||
|
||||
|
||||
descriptionlines = engine.splittext(descriptiontext,42)
|
||||
descriptionfile:close()
|
||||
else
|
||||
descriptionlines = {}
|
||||
table.insert(descriptionlines,fgettext("No mod description available"))
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
|
||||
retval = retval ..
|
||||
"label[5.5,1.7;".. fgettext("Mod information:") .. "]" ..
|
||||
"textlist[5.5,2.2;6.2,2.4;description;"
|
||||
|
||||
|
||||
for i=1,#descriptionlines,1 do
|
||||
retval = retval .. engine.formspec_escape(descriptionlines[i]) .. ","
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
if selected_mod.is_modpack then
|
||||
retval = retval .. ";0]" ..
|
||||
retval = retval .. ";0]" ..
|
||||
"button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;" ..
|
||||
fgettext("Rename") .. "]"
|
||||
retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
|
||||
@@ -304,11 +306,11 @@ function modmgr.tab()
|
||||
--show dependencies
|
||||
|
||||
retval = retval .. ",Depends:,"
|
||||
|
||||
|
||||
toadd = modmgr.get_dependencies(selected_mod.path)
|
||||
|
||||
|
||||
retval = retval .. toadd .. ";0]"
|
||||
|
||||
|
||||
retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
|
||||
.. fgettext("Uninstall selected mod") .. "]"
|
||||
end
|
||||
@@ -320,15 +322,15 @@ end
|
||||
function modmgr.dialog_rename_modpack()
|
||||
|
||||
local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
|
||||
|
||||
local retval =
|
||||
|
||||
local retval =
|
||||
"label[1.75,1;".. fgettext("Rename Modpack:") .. "]"..
|
||||
"field[4.5,1.4;6,0.5;te_modpack_name;;" ..
|
||||
mod.name ..
|
||||
"]" ..
|
||||
"button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;"..
|
||||
"button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;"..
|
||||
fgettext("Accept") .. "]" ..
|
||||
"button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;"..
|
||||
"button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;"..
|
||||
fgettext("Cancel") .. "]"
|
||||
|
||||
return retval
|
||||
@@ -340,15 +342,15 @@ function modmgr.precheck()
|
||||
if modmgr.world_config_selected_world == nil then
|
||||
modmgr.world_config_selected_world = 1
|
||||
end
|
||||
|
||||
|
||||
if modmgr.world_config_selected_mod == nil then
|
||||
modmgr.world_config_selected_mod = 1
|
||||
end
|
||||
|
||||
|
||||
if modmgr.hide_gamemods == nil then
|
||||
modmgr.hide_gamemods = true
|
||||
end
|
||||
|
||||
|
||||
if modmgr.hide_modpackcontents == nil then
|
||||
modmgr.hide_modpackcontents = true
|
||||
end
|
||||
@@ -357,27 +359,27 @@ end
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.render_modlist(render_list)
|
||||
local retval = ""
|
||||
|
||||
|
||||
if render_list == nil then
|
||||
if modmgr.global_mods == nil then
|
||||
modmgr.refresh_globals()
|
||||
end
|
||||
render_list = modmgr.global_mods
|
||||
end
|
||||
|
||||
|
||||
local list = filterlist.get_list(render_list)
|
||||
local last_modpack = nil
|
||||
|
||||
|
||||
for i,v in ipairs(list) do
|
||||
if retval ~= "" then
|
||||
retval = retval ..","
|
||||
end
|
||||
|
||||
local color = ""
|
||||
|
||||
|
||||
if v.is_modpack then
|
||||
local rawlist = filterlist.get_raw_list(render_list)
|
||||
|
||||
|
||||
local all_enabled = true
|
||||
for j=1,#rawlist,1 do
|
||||
if rawlist[j].modpack == list[i].name and
|
||||
@@ -386,14 +388,14 @@ function modmgr.render_modlist(render_list)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if all_enabled == false then
|
||||
color = mt_color_grey
|
||||
else
|
||||
color = mt_color_dark_green
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if v.typ == "game_mod" then
|
||||
color = mt_color_blue
|
||||
else
|
||||
@@ -408,34 +410,34 @@ function modmgr.render_modlist(render_list)
|
||||
end
|
||||
retval = retval .. v.name
|
||||
end
|
||||
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.dialog_configure_world()
|
||||
modmgr.precheck()
|
||||
|
||||
|
||||
local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
|
||||
local mod = filterlist.get_list(modmgr.modlist)[modmgr.world_config_selected_mod]
|
||||
|
||||
|
||||
local retval =
|
||||
"size[11,6.5]" ..
|
||||
"label[0.5,-0.25;" .. fgettext("World:") .. "]" ..
|
||||
"label[1.75,-0.25;" .. worldspec.name .. "]"
|
||||
|
||||
|
||||
if modmgr.hide_gamemods then
|
||||
retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]"
|
||||
else
|
||||
retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]"
|
||||
end
|
||||
|
||||
|
||||
if modmgr.hide_modpackcontents then
|
||||
retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]"
|
||||
else
|
||||
retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]"
|
||||
end
|
||||
|
||||
|
||||
if mod == nil then
|
||||
mod = {name=""}
|
||||
end
|
||||
@@ -447,11 +449,11 @@ function modmgr.dialog_configure_world()
|
||||
modmgr.get_dependencies(mod.path) .. ";0]" ..
|
||||
"button[9.25,6.35;2,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" ..
|
||||
"button[7.4,6.35;2,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]"
|
||||
|
||||
|
||||
if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then
|
||||
if mod.is_modpack then
|
||||
local rawlist = filterlist.get_raw_list(modmgr.modlist)
|
||||
|
||||
|
||||
local all_enabled = true
|
||||
for j=1,#rawlist,1 do
|
||||
if rawlist[j].modpack == mod.name and
|
||||
@@ -460,7 +462,7 @@ function modmgr.dialog_configure_world()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if all_enabled == false then
|
||||
retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]"
|
||||
else
|
||||
@@ -474,15 +476,15 @@ function modmgr.dialog_configure_world()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
retval = retval ..
|
||||
"button[8.5,-0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" ..
|
||||
"textlist[5.5,0.5;5.5,5.75;world_config_modlist;"
|
||||
|
||||
|
||||
retval = retval .. modmgr.render_modlist(modmgr.modlist)
|
||||
|
||||
|
||||
retval = retval .. ";" .. modmgr.world_config_selected_mod .."]"
|
||||
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
@@ -490,23 +492,23 @@ end
|
||||
function modmgr.handle_buttons(tab,fields)
|
||||
|
||||
local retval = nil
|
||||
|
||||
|
||||
if tab == "mod_mgr" then
|
||||
retval = modmgr.handle_modmgr_buttons(fields)
|
||||
end
|
||||
|
||||
|
||||
if tab == "dialog_rename_modpack" then
|
||||
retval = modmgr.handle_rename_modpack_buttons(fields)
|
||||
end
|
||||
|
||||
|
||||
if tab == "dialog_delete_mod" then
|
||||
retval = modmgr.handle_delete_mod_buttons(fields)
|
||||
end
|
||||
|
||||
|
||||
if tab == "dialog_configure_world" then
|
||||
retval = modmgr.handle_configure_world_buttons(fields)
|
||||
end
|
||||
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
@@ -516,13 +518,13 @@ function modmgr.get_dependencies(modfolder)
|
||||
if modfolder ~= nil then
|
||||
local filename = modfolder ..
|
||||
DIR_DELIM .. "depends.txt"
|
||||
|
||||
|
||||
local dependencyfile = io.open(filename,"r")
|
||||
|
||||
|
||||
if dependencyfile then
|
||||
local dependency = dependencyfile:read("*l")
|
||||
while dependency do
|
||||
if toadd ~= "" then
|
||||
if toadd ~= "" then
|
||||
toadd = toadd .. ","
|
||||
end
|
||||
toadd = toadd .. dependency
|
||||
@@ -542,11 +544,11 @@ function modmgr.get_worldconfig(worldpath)
|
||||
DIR_DELIM .. "world.mt"
|
||||
|
||||
local worldfile = Settings(filename)
|
||||
|
||||
|
||||
local worldconfig = {}
|
||||
worldconfig.global_mods = {}
|
||||
worldconfig.game_mods = {}
|
||||
|
||||
|
||||
for key,value in pairs(worldfile:to_table()) do
|
||||
if key == "gameid" then
|
||||
worldconfig.id = value
|
||||
@@ -554,7 +556,7 @@ function modmgr.get_worldconfig(worldpath)
|
||||
worldconfig.global_mods[key] = engine.is_yes(value)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--read gamemods
|
||||
local gamespec = gamemgr.find_by_gameid(worldconfig.id)
|
||||
gamemgr.get_game_mods(gamespec, worldconfig.game_mods)
|
||||
@@ -573,11 +575,11 @@ function modmgr.handle_modmgr_buttons(fields)
|
||||
local event = explode_textlist_event(fields["modlist"])
|
||||
modmgr.selected_mod = event.index
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_mod_mgr_install_local"] ~= nil then
|
||||
engine.show_file_open_dialog("mod_mgt_open_dlg",fgettext("Select Mod File:"))
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_mod_mgr_download"] ~= nil then
|
||||
modstore.update_modlist()
|
||||
retval.current_tab = "dialog_modstore_unsorted"
|
||||
@@ -585,26 +587,26 @@ function modmgr.handle_modmgr_buttons(fields)
|
||||
retval.show_buttons = false
|
||||
return retval
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_mod_mgr_rename_modpack"] ~= nil then
|
||||
retval.current_tab = "dialog_rename_modpack"
|
||||
retval.is_dialog = true
|
||||
retval.show_buttons = false
|
||||
return retval
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_mod_mgr_delete_mod"] ~= nil then
|
||||
retval.current_tab = "dialog_delete_mod"
|
||||
retval.is_dialog = true
|
||||
retval.show_buttons = false
|
||||
return retval
|
||||
end
|
||||
|
||||
|
||||
if fields["mod_mgt_open_dlg_accepted"] ~= nil and
|
||||
fields["mod_mgt_open_dlg_accepted"] ~= "" then
|
||||
modmgr.installmod(fields["mod_mgt_open_dlg_accepted"],nil)
|
||||
end
|
||||
|
||||
|
||||
return nil;
|
||||
end
|
||||
|
||||
@@ -612,27 +614,27 @@ end
|
||||
function modmgr.installmod(modfilename,basename)
|
||||
local modfile = modmgr.identify_filetype(modfilename)
|
||||
local modpath = modmgr.extract(modfile)
|
||||
|
||||
|
||||
if modpath == nil then
|
||||
gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
|
||||
fgettext("\nInstall Mod: unsupported filetype \"$1\"", modfile.type)
|
||||
fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local basefolder = modmgr.getbasefolder(modpath)
|
||||
|
||||
|
||||
if basefolder.type == "modpack" then
|
||||
local clean_path = nil
|
||||
|
||||
|
||||
if basename ~= nil then
|
||||
clean_path = "mp_" .. basename
|
||||
end
|
||||
|
||||
|
||||
if clean_path == nil then
|
||||
clean_path = get_last_folder(cleanup_path(basefolder.path))
|
||||
end
|
||||
|
||||
|
||||
if clean_path ~= nil then
|
||||
local targetpath = engine.get_modpath() .. DIR_DELIM .. clean_path
|
||||
if not engine.copy_dir(basefolder.path,targetpath) then
|
||||
@@ -642,19 +644,19 @@ function modmgr.installmod(modfilename,basename)
|
||||
gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if basefolder.type == "mod" then
|
||||
local targetfolder = basename
|
||||
|
||||
|
||||
if targetfolder == nil then
|
||||
targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
|
||||
end
|
||||
|
||||
|
||||
--if heuristic failed try to use current foldername
|
||||
if targetfolder == nil then
|
||||
targetfolder = get_last_folder(basefolder.path)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
|
||||
local targetpath = engine.get_modpath() .. DIR_DELIM .. targetfolder
|
||||
engine.copy_dir(basefolder.path,targetpath)
|
||||
@@ -662,7 +664,7 @@ function modmgr.installmod(modfilename,basename)
|
||||
gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
engine.delete_dir(modpath)
|
||||
|
||||
modmgr.refresh_globals()
|
||||
@@ -671,7 +673,7 @@ end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_rename_modpack_buttons(fields)
|
||||
|
||||
|
||||
if fields["dlg_rename_modpack_confirm"] ~= nil then
|
||||
local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
|
||||
local oldpath = engine.get_modpath() .. DIR_DELIM .. mod.name
|
||||
@@ -681,7 +683,7 @@ function modmgr.handle_rename_modpack_buttons(fields)
|
||||
modmgr.selected_mod = filterlist.get_current_index(modmgr.global_mods,
|
||||
filterlist.raw_index_by_uid(modmgr.global_mods, fields["te_modpack_name"]))
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
@@ -698,25 +700,25 @@ function modmgr.handle_configure_world_buttons(fields)
|
||||
modmgr.world_config_enable_mod(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if fields["key_enter"] ~= nil then
|
||||
modmgr.world_config_enable_mod(nil)
|
||||
end
|
||||
|
||||
|
||||
if fields["cb_mod_enable"] ~= nil then
|
||||
local toset = engine.is_yes(fields["cb_mod_enable"])
|
||||
modmgr.world_config_enable_mod(toset)
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_mp_enable"] ~= nil or
|
||||
fields["btn_mp_disable"] then
|
||||
local toset = (fields["btn_mp_enable"] ~= nil)
|
||||
modmgr.world_config_enable_mod(toset)
|
||||
end
|
||||
|
||||
|
||||
if fields["cb_hide_gamemods"] ~= nil then
|
||||
local current = filterlist.get_filtercriteria(modmgr.modlist)
|
||||
|
||||
|
||||
if current == nil then
|
||||
current = {}
|
||||
end
|
||||
@@ -728,13 +730,13 @@ function modmgr.handle_configure_world_buttons(fields)
|
||||
current.hide_game = false
|
||||
modmgr.hide_gamemods = false
|
||||
end
|
||||
|
||||
|
||||
filterlist.set_filtercriteria(modmgr.modlist,current)
|
||||
end
|
||||
|
||||
|
||||
if fields["cb_hide_mpcontent"] ~= nil then
|
||||
local current = filterlist.get_filtercriteria(modmgr.modlist)
|
||||
|
||||
|
||||
if current == nil then
|
||||
current = {}
|
||||
end
|
||||
@@ -746,21 +748,21 @@ function modmgr.handle_configure_world_buttons(fields)
|
||||
current.hide_modpackcontents = false
|
||||
modmgr.hide_modpackcontents = false
|
||||
end
|
||||
|
||||
|
||||
filterlist.set_filtercriteria(modmgr.modlist,current)
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_config_world_save"] then
|
||||
local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
|
||||
|
||||
|
||||
local filename = worldspec.path ..
|
||||
DIR_DELIM .. "world.mt"
|
||||
|
||||
|
||||
local worldfile = Settings(filename)
|
||||
local mods = worldfile:to_table()
|
||||
|
||||
|
||||
local rawlist = filterlist.get_raw_list(modmgr.modlist)
|
||||
|
||||
|
||||
local i,mod
|
||||
for i,mod in ipairs(rawlist) do
|
||||
if not mod.is_modpack and
|
||||
@@ -773,42 +775,42 @@ function modmgr.handle_configure_world_buttons(fields)
|
||||
mods["load_mod_"..mod.name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Remove mods that are not present anymore
|
||||
for key,value in pairs(mods) do
|
||||
if key:sub(1,9) == "load_mod_" then
|
||||
worldfile:remove(key)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if not worldfile:write() then
|
||||
engine.log("error", "Failed to write world config file")
|
||||
end
|
||||
|
||||
|
||||
modmgr.modlist = nil
|
||||
modmgr.worldconfig = nil
|
||||
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_config_world_cancel"] then
|
||||
|
||||
|
||||
modmgr.worldconfig = nil
|
||||
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_all_mods"] then
|
||||
local list = filterlist.get_raw_list(modmgr.modlist)
|
||||
|
||||
|
||||
for i=1,#list,1 do
|
||||
if list[i].typ ~= "game_mod" and
|
||||
not list[i].is_modpack then
|
||||
@@ -816,9 +818,9 @@ function modmgr.handle_configure_world_buttons(fields)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -849,9 +851,9 @@ end
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_delete_mod_buttons(fields)
|
||||
local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
|
||||
|
||||
|
||||
if fields["dlg_delete_mod_confirm"] ~= nil then
|
||||
|
||||
|
||||
if mod.path ~= nil and
|
||||
mod.path ~= "" and
|
||||
mod.path ~= engine.get_modpath() then
|
||||
@@ -863,7 +865,7 @@ function modmgr.handle_delete_mod_buttons(fields)
|
||||
gamedata.errormessage = fgettext("Modmgr: invalid modpath \"$1\"", mod.path)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
@@ -875,8 +877,8 @@ end
|
||||
function modmgr.dialog_delete_mod()
|
||||
|
||||
local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
|
||||
|
||||
local retval =
|
||||
|
||||
local retval =
|
||||
"field[1.75,1;10,3;;" .. fgettext("Are you sure you want to delete \"$1\"?", mod.name) .. ";]"..
|
||||
"button[4,4.2;1,0.5;dlg_delete_mod_confirm;" .. fgettext("Yes") .. "]" ..
|
||||
"button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;" .. fgettext("No of course not!") .. "]"
|
||||
@@ -887,10 +889,10 @@ end
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.preparemodlist(data)
|
||||
local retval = {}
|
||||
|
||||
|
||||
local global_mods = {}
|
||||
local game_mods = {}
|
||||
|
||||
|
||||
--read global mods
|
||||
local modpath = engine.get_modpath()
|
||||
|
||||
@@ -898,31 +900,31 @@ function modmgr.preparemodlist(data)
|
||||
modpath ~= "" then
|
||||
get_mods(modpath,global_mods)
|
||||
end
|
||||
|
||||
|
||||
for i=1,#global_mods,1 do
|
||||
global_mods[i].typ = "global_mod"
|
||||
table.insert(retval,global_mods[i])
|
||||
end
|
||||
|
||||
|
||||
--read game mods
|
||||
local gamespec = gamemgr.find_by_gameid(data.gameid)
|
||||
gamemgr.get_game_mods(gamespec, game_mods)
|
||||
|
||||
|
||||
for i=1,#game_mods,1 do
|
||||
game_mods[i].typ = "game_mod"
|
||||
table.insert(retval,game_mods[i])
|
||||
end
|
||||
|
||||
|
||||
if data.worldpath == nil then
|
||||
return retval
|
||||
end
|
||||
|
||||
|
||||
--read world mod configuration
|
||||
local filename = data.worldpath ..
|
||||
DIR_DELIM .. "world.mt"
|
||||
|
||||
local worldfile = Settings(filename)
|
||||
|
||||
|
||||
for key,value in pairs(worldfile:to_table()) do
|
||||
if key:sub(1, 9) == "load_mod_" then
|
||||
key = key:sub(10)
|
||||
@@ -948,17 +950,17 @@ end
|
||||
function modmgr.init_worldconfig()
|
||||
modmgr.precheck()
|
||||
local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
|
||||
|
||||
|
||||
if worldspec ~= nil then
|
||||
--read worldconfig
|
||||
modmgr.worldconfig = modmgr.get_worldconfig(worldspec.path)
|
||||
|
||||
|
||||
if modmgr.worldconfig.id == nil or
|
||||
modmgr.worldconfig.id == "" then
|
||||
modmgr.worldconfig = nil
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
modmgr.modlist = filterlist.create(
|
||||
modmgr.preparemodlist, --refresh
|
||||
modmgr.comparemod, --compare
|
||||
@@ -966,13 +968,13 @@ function modmgr.init_worldconfig()
|
||||
if element.name == uid then
|
||||
return true
|
||||
end
|
||||
end,
|
||||
end,
|
||||
function(element,criteria)
|
||||
if criteria.hide_game and
|
||||
element.typ == "game_mod" then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
if criteria.hide_modpackcontents and
|
||||
element.modpack ~= nil then
|
||||
return false
|
||||
@@ -982,15 +984,15 @@ function modmgr.init_worldconfig()
|
||||
{ worldpath= worldspec.path,
|
||||
gameid = worldspec.gameid }
|
||||
)
|
||||
|
||||
|
||||
filterlist.set_filtercriteria(modmgr.modlist, {
|
||||
hide_game=modmgr.hide_gamemods,
|
||||
hide_modpackcontents= modmgr.hide_modpackcontents
|
||||
})
|
||||
filterlist.add_sort_mechanism(modmgr.modlist, "alphabetic", sort_mod_list)
|
||||
filterlist.set_sortmode(modmgr.modlist, "alphabetic")
|
||||
|
||||
return true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
@@ -1013,34 +1015,34 @@ function modmgr.comparemod(elem1,elem2)
|
||||
if elem1.modpack ~= elem2.modpack then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
if elem1.path ~= elem2.path then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.gettab(name)
|
||||
local retval = ""
|
||||
|
||||
|
||||
if name == "mod_mgr" then
|
||||
retval = retval .. modmgr.tab()
|
||||
end
|
||||
|
||||
|
||||
if name == "dialog_rename_modpack" then
|
||||
retval = retval .. modmgr.dialog_rename_modpack()
|
||||
end
|
||||
|
||||
|
||||
if name == "dialog_delete_mod" then
|
||||
retval = retval .. modmgr.dialog_delete_mod()
|
||||
end
|
||||
|
||||
|
||||
if name == "dialog_configure_world" then
|
||||
retval = retval .. modmgr.dialog_configure_world()
|
||||
end
|
||||
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
@@ -1054,7 +1056,7 @@ function modmgr.mod_exists(basename)
|
||||
if filterlist.raw_index_by_uid(modmgr.global_mods,basename) > 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -1064,7 +1066,7 @@ function modmgr.get_global_mod(idx)
|
||||
if modmgr.global_mods == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
if idx < 1 or idx > filterlist.size(modmgr.global_mods) then
|
||||
return nil
|
||||
end
|
||||
@@ -1081,7 +1083,7 @@ function modmgr.refresh_globals()
|
||||
if element.name == uid then
|
||||
return true
|
||||
end
|
||||
end,
|
||||
end,
|
||||
nil, --filter
|
||||
{}
|
||||
)
|
||||
@@ -1098,7 +1100,7 @@ function modmgr.identify_filetype(name)
|
||||
type = "zip"
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
if name:sub(-6):lower() == "tar.gz" or
|
||||
name:sub(-3):lower() == "tgz"then
|
||||
return {
|
||||
@@ -1106,14 +1108,14 @@ function modmgr.identify_filetype(name)
|
||||
type = "tgz"
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
if name:sub(-6):lower() == "tar.bz2" then
|
||||
return {
|
||||
name = name,
|
||||
type = "tbz"
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
if name:sub(-2):lower() == "7z" then
|
||||
return {
|
||||
name = name,
|
||||
|
||||
@@ -21,22 +21,68 @@
|
||||
modstore = {}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] init
|
||||
function modstore.init()
|
||||
modstore.tabnames = {}
|
||||
|
||||
|
||||
table.insert(modstore.tabnames,"dialog_modstore_unsorted")
|
||||
table.insert(modstore.tabnames,"dialog_modstore_search")
|
||||
|
||||
|
||||
modstore.modsperpage = 5
|
||||
|
||||
modstore.basetexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" ..
|
||||
|
||||
modstore.basetexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" ..
|
||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||
|
||||
modstore.lastmodtitle = ""
|
||||
modstore.last_search = ""
|
||||
|
||||
modstore.searchlist = filterlist.create(
|
||||
function()
|
||||
if modstore.modlist_unsorted ~= nil and
|
||||
modstore.modlist_unsorted.data ~= nil then
|
||||
return modstore.modlist_unsorted.data
|
||||
end
|
||||
return {}
|
||||
end,
|
||||
function(element,modid)
|
||||
if element.id == modid then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end, --compare fct
|
||||
nil, --uid match fct
|
||||
function(element,substring)
|
||||
if substring == nil or
|
||||
substring == "" then
|
||||
return false
|
||||
end
|
||||
substring = substring:upper()
|
||||
|
||||
if element.title ~= nil and
|
||||
element.title:upper():find(substring) ~= nil then
|
||||
return true
|
||||
end
|
||||
|
||||
if element.details ~= nil and
|
||||
element.details.author ~= nil and
|
||||
element.details.author:upper():find(substring) ~= nil then
|
||||
return true
|
||||
end
|
||||
|
||||
if element.details ~= nil and
|
||||
element.details.description ~= nil and
|
||||
element.details.description:upper():find(substring) ~= nil then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end --filter fct
|
||||
)
|
||||
|
||||
modstore.current_list = nil
|
||||
|
||||
modstore.details_cache = {}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] nametoindex
|
||||
function modstore.nametoindex(name)
|
||||
|
||||
for i=1,#modstore.tabnames,1 do
|
||||
@@ -49,55 +95,94 @@ function modstore.nametoindex(name)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.gettab(tabname)
|
||||
-- @function [parent=#modstore] getsuccessfuldialog
|
||||
function modstore.getsuccessfuldialog()
|
||||
local retval = ""
|
||||
retval = retval .. "size[6,2]"
|
||||
if modstore.lastmodentry ~= nil then
|
||||
retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
|
||||
retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
|
||||
|
||||
local is_modstore_tab = false
|
||||
|
||||
if tabname == "dialog_modstore_unsorted" then
|
||||
retval = modstore.getmodlist(modstore.modlist_unsorted)
|
||||
is_modstore_tab = true
|
||||
end
|
||||
|
||||
if tabname == "dialog_modstore_search" then
|
||||
|
||||
|
||||
is_modstore_tab = true
|
||||
end
|
||||
|
||||
if is_modstore_tab then
|
||||
return modstore.tabheader(tabname) .. retval
|
||||
end
|
||||
|
||||
if tabname == "modstore_mod_installed" then
|
||||
return "size[6,2]label[0.25,0.25;Mod: " .. modstore.lastmodtitle ..
|
||||
" installed successfully]" ..
|
||||
"button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;ok]"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
|
||||
retval = retval .. "label[3,0.75;" .. engine.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.tabheader(tabname)
|
||||
local retval = "size[12,9.25]"
|
||||
retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
|
||||
"Unsorted,Search;" ..
|
||||
modstore.nametoindex(tabname) .. ";true;false]"
|
||||
end
|
||||
retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]"
|
||||
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] gettab
|
||||
function modstore.gettab(tabname)
|
||||
local retval = ""
|
||||
|
||||
local is_modstore_tab = false
|
||||
|
||||
if tabname == "dialog_modstore_unsorted" then
|
||||
modstore.modsperpage = 5
|
||||
retval = modstore.getmodlist(modstore.modlist_unsorted)
|
||||
is_modstore_tab = true
|
||||
end
|
||||
|
||||
if tabname == "dialog_modstore_search" then
|
||||
retval = modstore.getsearchpage()
|
||||
is_modstore_tab = true
|
||||
end
|
||||
|
||||
if is_modstore_tab then
|
||||
return modstore.tabheader(tabname) .. retval
|
||||
end
|
||||
|
||||
if tabname == "modstore_mod_installed" then
|
||||
return modstore.getsuccessfuldialog()
|
||||
end
|
||||
|
||||
if tabname == "modstore_downloading" then
|
||||
return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") ..
|
||||
" " .. modstore.lastmodtitle .. " " ..
|
||||
fgettext("please wait...") .. "]"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] tabheader
|
||||
function modstore.tabheader(tabname)
|
||||
local retval = "size[12,10.25]"
|
||||
retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
|
||||
"Unsorted,Search;" ..
|
||||
modstore.nametoindex(tabname) .. ";true;false]" ..
|
||||
"button[4,9.9;4,0.5;btn_modstore_close;" ..
|
||||
fgettext("Close modstore") .. "]"
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] handle_buttons
|
||||
function modstore.handle_buttons(current_tab,fields)
|
||||
|
||||
modstore.lastmodtitle = ""
|
||||
|
||||
if fields["modstore_tab"] then
|
||||
local index = tonumber(fields["modstore_tab"])
|
||||
|
||||
|
||||
if index > 0 and
|
||||
index <= #modstore.tabnames then
|
||||
if modstore.tabnames[index] == "dialog_modstore_search" then
|
||||
filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search)
|
||||
filterlist.refresh(modstore.searchlist)
|
||||
modstore.modsperpage = 4
|
||||
modstore.currentlist = {
|
||||
page = 0,
|
||||
pagecount =
|
||||
math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
|
||||
data = filterlist.get_list(modstore.searchlist),
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
current_tab = modstore.tabnames[index],
|
||||
is_dialog = true,
|
||||
@@ -105,170 +190,426 @@ function modstore.handle_buttons(current_tab,fields)
|
||||
}
|
||||
end
|
||||
|
||||
modstore.modlist_page = 0
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_modstore_page_up"] then
|
||||
if modstore.current_list ~= nil and modstore.current_list.page > 0 then
|
||||
modstore.current_list.page = modstore.current_list.page - 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_modstore_page_down"] then
|
||||
if modstore.current_list ~= nil and
|
||||
if modstore.current_list ~= nil and
|
||||
modstore.current_list.page <modstore.current_list.pagecount-1 then
|
||||
modstore.current_list.page = modstore.current_list.page +1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if fields["btn_hidden_close_download"] ~= nil then
|
||||
if fields["btn_hidden_close_download"].successfull then
|
||||
modstore.lastmodentry = fields["btn_hidden_close_download"]
|
||||
return {
|
||||
current_tab = "modstore_mod_installed",
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
else
|
||||
modstore.lastmodtitle = ""
|
||||
return {
|
||||
current_tab = modstore.tabnames[1],
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if fields["btn_confirm_mod_successfull"] then
|
||||
modstore.lastmodentry = nil
|
||||
modstore.lastmodtitle = ""
|
||||
return {
|
||||
current_tab = modstore.tabnames[1],
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
end
|
||||
|
||||
if fields["btn_modstore_search"] or
|
||||
(fields["key_enter"] and fields["te_modstore_search"] ~= nil) then
|
||||
modstore.last_search = fields["te_modstore_search"]
|
||||
filterlist.set_filtercriteria(modstore.searchlist,fields["te_modstore_search"])
|
||||
filterlist.refresh(modstore.searchlist)
|
||||
modstore.currentlist = {
|
||||
page = 0,
|
||||
pagecount = math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
|
||||
data = filterlist.get_list(modstore.searchlist),
|
||||
}
|
||||
end
|
||||
|
||||
for i=1, modstore.modsperpage, 1 do
|
||||
local installbtn = "btn_install_mod_" .. i
|
||||
|
||||
if fields[installbtn] then
|
||||
local modlistentry =
|
||||
modstore.current_list.page * modstore.modsperpage + i
|
||||
|
||||
local moddetails = modstore.get_details(modstore.current_list.data[modlistentry].id)
|
||||
|
||||
local fullurl = engine.setting_get("modstore_download_url") ..
|
||||
moddetails.download_url
|
||||
local modfilename = os.tempfolder() .. ".zip"
|
||||
|
||||
if engine.download_file(fullurl,modfilename) then
|
||||
|
||||
modmgr.installmod(modfilename,moddetails.basename)
|
||||
|
||||
os.remove(modfilename)
|
||||
modstore.lastmodtitle = modstore.current_list.data[modlistentry].title
|
||||
|
||||
return {
|
||||
current_tab = "modstore_mod_installed",
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
else
|
||||
gamedata.errormessage = "Unable to download " ..
|
||||
moddetails.download_url .. " (internet connection?)"
|
||||
|
||||
if fields["btn_modstore_close"] then
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
for key,value in pairs(fields) do
|
||||
local foundat = key:find("btn_install_mod_")
|
||||
if ( foundat == 1) then
|
||||
local modid = tonumber(key:sub(17))
|
||||
for i=1,#modstore.modlist_unsorted.data,1 do
|
||||
if modstore.modlist_unsorted.data[i].id == modid then
|
||||
local moddetails = modstore.modlist_unsorted.data[i].details
|
||||
|
||||
if modstore.lastmodtitle ~= "" then
|
||||
modstore.lastmodtitle = modstore.lastmodtitle .. ", "
|
||||
end
|
||||
|
||||
modstore.lastmodtitle = modstore.lastmodtitle .. moddetails.title
|
||||
|
||||
engine.handle_async(
|
||||
function(param)
|
||||
|
||||
local fullurl = engine.setting_get("modstore_download_url") ..
|
||||
param.moddetails.download_url
|
||||
|
||||
if param.version ~= nil then
|
||||
local found = false
|
||||
for i=1,#param.moddetails.versions, 1 do
|
||||
if param.moddetails.versions[i].date:sub(1,10) == param.version then
|
||||
fullurl = engine.setting_get("modstore_download_url") ..
|
||||
param.moddetails.versions[i].download_url
|
||||
found = true
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
return {
|
||||
moddetails = param.moddetails,
|
||||
successfull = false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if engine.download_file(fullurl,param.filename) then
|
||||
return {
|
||||
texturename = param.texturename,
|
||||
moddetails = param.moddetails,
|
||||
filename = param.filename,
|
||||
successfull = true
|
||||
}
|
||||
else
|
||||
return {
|
||||
modtitle = param.title,
|
||||
successfull = false
|
||||
}
|
||||
end
|
||||
end,
|
||||
{
|
||||
moddetails = moddetails,
|
||||
version = fields["dd_version" .. modid],
|
||||
filename = os.tempfolder() .. "_MODNAME_" .. moddetails.basename .. ".zip",
|
||||
texturename = modstore.modlist_unsorted.data[i].texturename
|
||||
},
|
||||
function(result)
|
||||
if result.successfull then
|
||||
modmgr.installmod(result.filename,result.moddetails.basename)
|
||||
os.remove(result.filename)
|
||||
else
|
||||
gamedata.errormessage = "Failed to download " .. result.moddetails.title
|
||||
end
|
||||
|
||||
if gamedata.errormessage == nil then
|
||||
engine.button_handler({btn_hidden_close_download=result})
|
||||
else
|
||||
engine.button_handler({btn_hidden_close_download={successfull=false}})
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return {
|
||||
current_tab = "modstore_downloading",
|
||||
is_dialog = true,
|
||||
show_buttons = false,
|
||||
ignore_menu_quit = true
|
||||
}
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] update_modlist
|
||||
function modstore.update_modlist()
|
||||
modstore.modlist_unsorted = {}
|
||||
modstore.modlist_unsorted.data = engine.get_modstore_list()
|
||||
|
||||
if modstore.modlist_unsorted.data ~= nil then
|
||||
modstore.modlist_unsorted.pagecount =
|
||||
math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
|
||||
else
|
||||
modstore.modlist_unsorted.data = {}
|
||||
modstore.modlist_unsorted.pagecount = 1
|
||||
end
|
||||
modstore.modlist_unsorted.data = {}
|
||||
modstore.modlist_unsorted.pagecount = 1
|
||||
modstore.modlist_unsorted.page = 0
|
||||
|
||||
engine.handle_async(
|
||||
function(param)
|
||||
return engine.get_modstore_list()
|
||||
end,
|
||||
nil,
|
||||
function(result)
|
||||
if result ~= nil then
|
||||
modstore.modlist_unsorted = {}
|
||||
modstore.modlist_unsorted.data = result
|
||||
|
||||
if modstore.modlist_unsorted.data ~= nil then
|
||||
modstore.modlist_unsorted.pagecount =
|
||||
math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
|
||||
else
|
||||
modstore.modlist_unsorted.data = {}
|
||||
modstore.modlist_unsorted.pagecount = 1
|
||||
end
|
||||
modstore.modlist_unsorted.page = 0
|
||||
modstore.fetchdetails()
|
||||
engine.event_handler("Refresh")
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.getmodlist(list)
|
||||
local retval = ""
|
||||
retval = retval .. "label[10,-0.4;" .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
|
||||
|
||||
retval = retval .. "button[11.6,-0.1;0.5,0.5;btn_modstore_page_up;^]"
|
||||
retval = retval .. "box[11.6,0.35;0.28,8.6;#000000]"
|
||||
local scrollbarpos = 0.35 + (8.1/(list.pagecount-1)) * list.page
|
||||
retval = retval .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
|
||||
retval = retval .. "button[11.6,9.0;0.5,0.5;btn_modstore_page_down;v]"
|
||||
|
||||
|
||||
if #list.data < (list.page * modstore.modsperpage) then
|
||||
return retval
|
||||
-- @function [parent=#modstore] fetchdetails
|
||||
function modstore.fetchdetails()
|
||||
|
||||
for i=1,#modstore.modlist_unsorted.data,1 do
|
||||
engine.handle_async(
|
||||
function(param)
|
||||
param.details = engine.get_modstore_details(tostring(param.modid))
|
||||
return param
|
||||
end,
|
||||
{
|
||||
modid=modstore.modlist_unsorted.data[i].id,
|
||||
listindex=i
|
||||
},
|
||||
function(result)
|
||||
if result ~= nil and
|
||||
modstore.modlist_unsorted ~= nil
|
||||
and modstore.modlist_unsorted.data ~= nil and
|
||||
modstore.modlist_unsorted.data[result.listindex] ~= nil and
|
||||
modstore.modlist_unsorted.data[result.listindex].id ~= nil then
|
||||
|
||||
modstore.modlist_unsorted.data[result.listindex].details = result.details
|
||||
engine.event_handler("Refresh")
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @function [parent=#modstore] getscreenshot
|
||||
function modstore.getscreenshot(ypos,listentry)
|
||||
|
||||
if listentry.details ~= nil and
|
||||
(listentry.details.screenshot_url == nil or
|
||||
listentry.details.screenshot_url == "") then
|
||||
|
||||
if listentry.texturename == nil then
|
||||
listentry.texturename = modstore.basetexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
return "image[0,".. ypos .. ";3,2;" ..
|
||||
engine.formspec_escape(listentry.texturename) .. "]"
|
||||
end
|
||||
|
||||
local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
|
||||
if listentry.details ~= nil and
|
||||
listentry.texturename == nil then
|
||||
--make sure we don't download multiple times
|
||||
listentry.texturename = "in progress"
|
||||
|
||||
--prepare url and filename
|
||||
local fullurl = engine.setting_get("modstore_download_url") ..
|
||||
listentry.details.screenshot_url
|
||||
local filename = os.tempfolder() .. "_MID_" .. listentry.id
|
||||
|
||||
--trigger download
|
||||
engine.handle_async(
|
||||
--first param is downloadfct
|
||||
function(param)
|
||||
param.successfull = engine.download_file(param.fullurl,param.filename)
|
||||
return param
|
||||
end,
|
||||
--second parameter is data passed to async job
|
||||
{
|
||||
fullurl = fullurl,
|
||||
filename = filename,
|
||||
modid = listentry.id
|
||||
},
|
||||
--integrate result to raw list
|
||||
function(result)
|
||||
if result.successfull then
|
||||
local found = false
|
||||
for i=1,#modstore.modlist_unsorted.data,1 do
|
||||
if modstore.modlist_unsorted.data[i].id == result.modid then
|
||||
found = true
|
||||
modstore.modlist_unsorted.data[i].texturename = result.filename
|
||||
break
|
||||
end
|
||||
end
|
||||
if found then
|
||||
engine.event_handler("Refresh")
|
||||
else
|
||||
engine.log("error","got screenshot but didn't find matching mod: " .. result.modid)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
if listentry.texturename ~= nil and
|
||||
listentry.texturename ~= "in progress" then
|
||||
return "image[0,".. ypos .. ";3,2;" ..
|
||||
engine.formspec_escape(listentry.texturename) .. "]"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--@function [parent=#modstore] getshortmodinfo
|
||||
function modstore.getshortmodinfo(ypos,listentry,details)
|
||||
local retval = ""
|
||||
|
||||
retval = retval .. "box[0," .. ypos .. ";11.4,1.75;#FFFFFF]"
|
||||
|
||||
--screenshot
|
||||
retval = retval .. modstore.getscreenshot(ypos,listentry)
|
||||
|
||||
--title + author
|
||||
retval = retval .."label[2.75," .. ypos .. ";" ..
|
||||
engine.formspec_escape(details.title) .. " (" .. details.author .. ")]"
|
||||
|
||||
--description
|
||||
local descriptiony = ypos + 0.5
|
||||
retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
|
||||
engine.formspec_escape(details.description) .. ";]"
|
||||
|
||||
--rating
|
||||
local ratingy = ypos
|
||||
retval = retval .."label[7," .. ratingy .. ";" ..
|
||||
fgettext("Rating") .. ":]"
|
||||
retval = retval .. "label[8.7," .. ratingy .. ";" .. details.rating .."]"
|
||||
|
||||
--versions (IMPORTANT has to be defined AFTER rating)
|
||||
if details.versions ~= nil and
|
||||
#details.versions > 1 then
|
||||
local versiony = ypos + 0.05
|
||||
retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";"
|
||||
local versions = ""
|
||||
for i=1,#details.versions , 1 do
|
||||
if versions ~= "" then
|
||||
versions = versions .. ","
|
||||
end
|
||||
|
||||
versions = versions .. details.versions[i].date:sub(1,10)
|
||||
end
|
||||
retval = retval .. versions .. ";1]"
|
||||
end
|
||||
|
||||
if details.basename then
|
||||
--install button
|
||||
local buttony = ypos + 1.2
|
||||
retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";"
|
||||
|
||||
if modmgr.mod_exists(details.basename) then
|
||||
retval = retval .. fgettext("re-Install") .."]"
|
||||
else
|
||||
retval = retval .. fgettext("Install") .."]"
|
||||
end
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--@function [parent=#modstore] getmodlist
|
||||
function modstore.getmodlist(list,yoffset)
|
||||
|
||||
modstore.current_list = list
|
||||
|
||||
if #list.data == 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
if yoffset == nil then
|
||||
yoffset = 0
|
||||
end
|
||||
|
||||
local scrollbar = ""
|
||||
scrollbar = scrollbar .. "label[0.1,9.5;"
|
||||
.. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
|
||||
scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28,"
|
||||
.. (8.6 - yoffset) .. ";#000000]"
|
||||
local scrollbarpos = (yoffset + 0.75) +
|
||||
((7.7 -yoffset)/(list.pagecount-1)) * list.page
|
||||
scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
|
||||
scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3))
|
||||
.. ";0.5,0.5;btn_modstore_page_up;^]"
|
||||
scrollbar = scrollbar .. "button[11.6," .. 9.0
|
||||
.. ";0.5,0.5;btn_modstore_page_down;v]"
|
||||
|
||||
local retval = ""
|
||||
|
||||
local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
|
||||
|
||||
if (endmod > #list.data) then
|
||||
endmod = #list.data
|
||||
end
|
||||
|
||||
for i=(list.page * modstore.modsperpage) +1, endmod, 1 do
|
||||
--getmoddetails
|
||||
local details = modstore.get_details(list.data[i].id)
|
||||
|
||||
local details = list.data[i].details
|
||||
|
||||
if details == nil then
|
||||
details = {}
|
||||
details.title = list.data[i].title
|
||||
details.author = ""
|
||||
details.rating = -1
|
||||
details.description = ""
|
||||
end
|
||||
|
||||
if details ~= nil then
|
||||
local screenshot_ypos = (i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
|
||||
|
||||
retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;#FFFFFF]"
|
||||
|
||||
--screenshot
|
||||
if details.screenshot_url ~= nil and
|
||||
details.screenshot_url ~= "" then
|
||||
if list.data[i].texturename == nil then
|
||||
local fullurl = engine.setting_get("modstore_download_url") ..
|
||||
details.screenshot_url
|
||||
local filename = os.tempfolder()
|
||||
|
||||
if engine.download_file(fullurl,filename) then
|
||||
list.data[i].texturename = filename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if list.data[i].texturename == nil then
|
||||
list.data[i].texturename = modstore.basetexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
retval = retval .. "image[0,".. screenshot_ypos .. ";3,2;" ..
|
||||
engine.formspec_escape(list.data[i].texturename) .. "]"
|
||||
|
||||
--title + author
|
||||
retval = retval .."label[2.75," .. screenshot_ypos .. ";" ..
|
||||
engine.formspec_escape(details.title) .. " (" .. details.author .. ")]"
|
||||
|
||||
--description
|
||||
local descriptiony = screenshot_ypos + 0.5
|
||||
retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
|
||||
engine.formspec_escape(details.description) .. ";]"
|
||||
--rating
|
||||
local ratingy = screenshot_ypos + 0.6
|
||||
retval = retval .."label[10.1," .. ratingy .. ";" ..
|
||||
fgettext("Rating") .. ": " .. details.rating .."]"
|
||||
|
||||
--install button
|
||||
local buttony = screenshot_ypos + 1.2
|
||||
local buttonnumber = (i - (list.page * modstore.modsperpage))
|
||||
retval = retval .."button[9.6," .. buttony .. ";2,0.5;btn_install_mod_" .. buttonnumber .. ";"
|
||||
|
||||
if modmgr.mod_exists(details.basename) then
|
||||
retval = retval .. fgettext("re-Install") .."]"
|
||||
else
|
||||
retval = retval .. fgettext("Install") .."]"
|
||||
end
|
||||
local screenshot_ypos =
|
||||
yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
|
||||
|
||||
retval = retval .. modstore.getshortmodinfo(screenshot_ypos,
|
||||
list.data[i],
|
||||
details)
|
||||
end
|
||||
end
|
||||
|
||||
modstore.current_list = list
|
||||
|
||||
return retval
|
||||
return retval .. scrollbar
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.get_details(modid)
|
||||
|
||||
if modstore.details_cache[modid] ~= nil then
|
||||
return modstore.details_cache[modid]
|
||||
end
|
||||
--@function [parent=#modstore] getsearchpage
|
||||
function modstore.getsearchpage()
|
||||
local retval = ""
|
||||
local search = ""
|
||||
|
||||
local retval = engine.get_modstore_details(tostring(modid))
|
||||
modstore.details_cache[modid] = retval
|
||||
return retval
|
||||
if modstore.last_search ~= nil then
|
||||
search = modstore.last_search
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
"button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" ..
|
||||
"field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]"
|
||||
|
||||
|
||||
--show 4 mods only
|
||||
modstore.modsperpage = 4
|
||||
retval = retval ..
|
||||
modstore.getmodlist(
|
||||
modstore.currentlist,
|
||||
1.75)
|
||||
|
||||
return retval;
|
||||
end
|
||||
|
||||
|
||||
71
client/shaders/alpha_shader/opengl_fragment.glsl
Normal file
71
client/shaders/alpha_shader/opengl_fragment.glsl
Normal file
@@ -0,0 +1,71 @@
|
||||
uniform sampler2D baseTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
uniform sampler2D useNormalmap;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
varying vec3 tsEyeVec;
|
||||
#endif
|
||||
|
||||
const float e = 2.718281828459;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec3 color;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
|
||||
#ifdef USE_NORMALMAPS
|
||||
float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
float height;
|
||||
vec2 tsEye = vec2(tsEyeVec.x,-tsEyeVec.y);
|
||||
|
||||
if (use_normalmap > 0.0) {
|
||||
float map_height = texture2D(normalTexture, uv).a;
|
||||
if (map_height < 1.0){
|
||||
float height = PARALLAX_OCCLUSION_SCALE * map_height - PARALLAX_OCCLUSION_BIAS;
|
||||
uv = uv + height * tsEye;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BUMPMAPPING
|
||||
if (use_normalmap > 0.0) {
|
||||
vec3 base = texture2D(baseTexture, uv).rgb;
|
||||
vec3 vVec = normalize(eyeVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vVec);
|
||||
float diffuse = max(dot(lVec, bump), 0.0);
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse;
|
||||
} else {
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
}
|
||||
#else
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
#endif
|
||||
|
||||
float alpha = texture2D(baseTexture, uv).a;
|
||||
vec4 col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / e;
|
||||
col.g = 1.0 - exp(1.0 - col.g) / e;
|
||||
col.b = 1.0 - exp(1.0 - col.b) / e;
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
col = mix(col, skyBgColor, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
102
client/shaders/alpha_shader/opengl_vertex.glsl
Normal file
102
client/shaders/alpha_shader/opengl_vertex.glsl
Normal file
@@ -0,0 +1,102 @@
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
varying vec3 tsEyeVec;
|
||||
#endif
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
vec3 normal,tangent,binormal;
|
||||
normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
|
||||
if (gl_Normal.x > 0.5) {
|
||||
// 1.0, 0.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, -1.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
} else if (gl_Normal.x < -0.5) {
|
||||
// -1.0, 0.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
} else if (gl_Normal.y > 0.5) {
|
||||
// 0.0, 1.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
|
||||
} else if (gl_Normal.y < -0.5) {
|
||||
// 0.0, -1.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
|
||||
} else if (gl_Normal.z > 0.5) {
|
||||
// 0.0, 0.0, 1.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
} else if (gl_Normal.z < -0.5) {
|
||||
// 0.0, 0.0, -1.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3(-1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
}
|
||||
|
||||
mat3 tbnMatrix = mat3( tangent.x, binormal.x, normal.x,
|
||||
tangent.y, binormal.y, normal.y,
|
||||
tangent.z, binormal.z, normal.z);
|
||||
|
||||
tsEyeVec = normalize(eyeVec * tbnMatrix);
|
||||
#endif
|
||||
|
||||
vec4 color;
|
||||
//color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
float day = gl_Color.r;
|
||||
float night = gl_Color.g;
|
||||
float light_source = gl_Color.b;
|
||||
|
||||
/*color.r = mix(night, day, dayNightRatio);
|
||||
color.g = color.r;
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 2.5; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
b += (day - night) / 13.0;
|
||||
rg -= (day - night) / 13.0;
|
||||
|
||||
// Emphase blue a bit in darker places
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
|
||||
|
||||
// Artificial light is yellow-ish
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = clamp(rg,0.0,1.0);
|
||||
color.g = clamp(rg,0.0,1.0);
|
||||
color.b = clamp(b,0.0,1.0);
|
||||
|
||||
// Make sides and bottom darker than the top
|
||||
color = color * color; // SRGB -> Linear
|
||||
if(gl_Normal.y <= 0.5)
|
||||
color *= 0.6;
|
||||
//color *= 0.7;
|
||||
color = sqrt(color); // Linear -> SRGB
|
||||
|
||||
color.a = gl_Color.a;
|
||||
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
uniform sampler2D myTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
|
||||
varying vec3 vPosition;
|
||||
|
||||
varying vec3 viewVec;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
|
||||
float alpha = col.a;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
vec4 base = texture2D(myTexture, uv);
|
||||
vec4 final_color = vec4(0.2, 0.2, 0.2, 1.0) * base;
|
||||
vec3 vVec = normalize(viewVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vec3(0.0, -0.4, 0.5));
|
||||
float diffuse = max(dot(lVec, bump), 0.0);
|
||||
|
||||
vec3 color = diffuse * texture2D(myTexture, gl_TexCoord[0].st).rgb;
|
||||
|
||||
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color += vec3(0.2*specular*diffuse);
|
||||
|
||||
|
||||
col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
|
||||
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
|
||||
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
alpha = mix(alpha, 0.0, d);
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
uniform sampler2D myTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
|
||||
varying vec3 vPosition;
|
||||
|
||||
varying vec3 viewVec;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
|
||||
float alpha = col.a;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
vec4 base = texture2D(myTexture, uv);
|
||||
vec4 final_color = vec4(0.2, 0.2, 0.2, 1.0) * base;
|
||||
vec3 vVec = normalize(viewVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vec3(0.0, -0.4, 0.5));
|
||||
float diffuse = max(dot(lVec, bump), 0.0);
|
||||
|
||||
vec3 color = diffuse * texture2D(myTexture, gl_TexCoord[0].st).rgb;
|
||||
|
||||
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color += vec3(0.2*specular*diffuse);
|
||||
|
||||
|
||||
col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
|
||||
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
|
||||
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
col = mix(col, skyBgColor, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 viewVec;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
|
||||
vec3 tangent;
|
||||
vec3 binormal;
|
||||
|
||||
vec3 c1 = cross( gl_Normal, vec3(0.0, 0.0, 1.0) );
|
||||
vec3 c2 = cross( gl_Normal, vec3(0.0, 1.0, 0.0) );
|
||||
|
||||
if( length(c1)>length(c2) )
|
||||
{
|
||||
tangent = c1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tangent = c2;
|
||||
}
|
||||
|
||||
tangent = normalize(tangent);
|
||||
|
||||
//binormal = cross(gl_Normal, tangent);
|
||||
//binormal = normalize(binormal);
|
||||
|
||||
vec4 color;
|
||||
//color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
float day = gl_Color.r;
|
||||
float night = gl_Color.g;
|
||||
float light_source = gl_Color.b;
|
||||
|
||||
/*color.r = mix(night, day, dayNightRatio);
|
||||
color.g = color.r;
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 1.5; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
b += (day - night) / 13.0;
|
||||
rg -= (day - night) / 13.0;
|
||||
|
||||
// Emphase blue a bit in darker places
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
|
||||
|
||||
// Artificial light is yellow-ish
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = rg;
|
||||
color.g = rg;
|
||||
color.b = b;
|
||||
|
||||
// Make sides and bottom darker than the top
|
||||
color = color * color; // SRGB -> Linear
|
||||
if(gl_Normal.y <= 0.5)
|
||||
color *= 0.6;
|
||||
//color *= 0.7;
|
||||
color = sqrt(color); // Linear -> SRGB
|
||||
|
||||
color.a = gl_Color.a;
|
||||
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
vec3 n1 = normalize(gl_NormalMatrix * gl_Normal);
|
||||
vec4 tangent1 = vec4(tangent.x, tangent.y, tangent.z, 0);
|
||||
//vec3 t1 = normalize(gl_NormalMatrix * tangent1);
|
||||
//vec3 b1 = cross(n1, t1);
|
||||
|
||||
vec3 v;
|
||||
vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
|
||||
vec3 vVec = -vVertex;
|
||||
//v.x = dot(vVec, t1);
|
||||
//v.y = dot(vVec, b1);
|
||||
//v.z = dot(vVec, n1);
|
||||
//viewVec = vVec;
|
||||
viewVec = normalize(vec3(0.0, -0.4, 0.5));
|
||||
//Vector representing the 0th texture coordinate passed to fragment shader
|
||||
//gl_TexCoord[0] = vec2(gl_MultiTexCoord0);
|
||||
|
||||
// Transform the current vertex
|
||||
//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
54
client/shaders/leaves_shader/opengl_fragment.glsl
Normal file
54
client/shaders/leaves_shader/opengl_fragment.glsl
Normal file
@@ -0,0 +1,54 @@
|
||||
uniform sampler2D baseTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
uniform sampler2D useNormalmap;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
const float e = 2.718281828459;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec3 color;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
|
||||
#ifdef USE_NORMALMAPS
|
||||
float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BUMPMAPPING
|
||||
if (use_normalmap > 0.0) {
|
||||
vec3 base = texture2D(baseTexture, uv).rgb;
|
||||
vec3 vVec = normalize(eyeVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vVec);
|
||||
float diffuse = max(dot(lVec, bump), 0.0);
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse;
|
||||
} else {
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
}
|
||||
#else
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
#endif
|
||||
|
||||
float alpha = texture2D(baseTexture, uv).a;
|
||||
vec4 col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / e;
|
||||
col.g = 1.0 - exp(1.0 - col.g) / e;
|
||||
col.b = 1.0 - exp(1.0 - col.b) / e;
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
col = mix(col, skyBgColor, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
@@ -1,37 +1,44 @@
|
||||
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
uniform float animationTimer;
|
||||
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 viewVec;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
#ifdef ENABLE_WAVING_LEAVES
|
||||
float smoothCurve( float x ) {
|
||||
return x * x *( 3.0 - 2.0 * x );
|
||||
}
|
||||
float triangleWave( float x ) {
|
||||
return abs( fract( x + 0.5 ) * 2.0 - 1.0 );
|
||||
}
|
||||
float smoothTriangleWave( float x ) {
|
||||
return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
#ifdef ENABLE_WAVING_LEAVES
|
||||
vec4 pos = gl_Vertex;
|
||||
vec4 pos2 = mTransWorld*gl_Vertex;
|
||||
pos.x += (smoothTriangleWave(animationTimer*10.0 + pos2.x * 0.01 + pos2.z * 0.01) * 2.0 - 1.0) * 0.4;
|
||||
pos.y += (smoothTriangleWave(animationTimer*15.0 + pos2.x * -0.01 + pos2.z * -0.01) * 2.0 - 1.0) * 0.2;
|
||||
pos.z += (smoothTriangleWave(animationTimer*10.0 + pos2.x * -0.01 + pos2.z * -0.01) * 2.0 - 1.0) * 0.4;
|
||||
gl_Position = mWorldViewProj * pos;
|
||||
#else
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
#endif
|
||||
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
|
||||
vec3 tangent;
|
||||
vec3 binormal;
|
||||
|
||||
vec3 c1 = cross( gl_Normal, vec3(0.0, 0.0, 1.0) );
|
||||
vec3 c2 = cross( gl_Normal, vec3(0.0, 1.0, 0.0) );
|
||||
|
||||
if( length(c1)>length(c2) )
|
||||
{
|
||||
tangent = c1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tangent = c2;
|
||||
}
|
||||
|
||||
tangent = normalize(tangent);
|
||||
|
||||
//binormal = cross(gl_Normal, tangent);
|
||||
//binormal = normalize(binormal);
|
||||
eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
|
||||
vec4 color;
|
||||
//color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
@@ -45,7 +52,7 @@ void main(void)
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 1.5; // Make light sources brighter
|
||||
rg += light_source * 2.5; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
@@ -60,9 +67,9 @@ void main(void)
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = rg;
|
||||
color.g = rg;
|
||||
color.b = b;
|
||||
color.r = clamp(rg,0.0,1.0);
|
||||
color.g = clamp(rg,0.0,1.0);
|
||||
color.b = clamp(b,0.0,1.0);
|
||||
|
||||
// Make sides and bottom darker than the top
|
||||
color = color * color; // SRGB -> Linear
|
||||
@@ -76,23 +83,5 @@ void main(void)
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
vec3 n1 = normalize(gl_NormalMatrix * gl_Normal);
|
||||
vec4 tangent1 = vec4(tangent.x, tangent.y, tangent.z, 0);
|
||||
//vec3 t1 = normalize(gl_NormalMatrix * tangent1);
|
||||
//vec3 b1 = cross(n1, t1);
|
||||
|
||||
vec3 v;
|
||||
vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
|
||||
vec3 vVec = -vVertex;
|
||||
//v.x = dot(vVec, t1);
|
||||
//v.y = dot(vVec, b1);
|
||||
//v.z = dot(vVec, n1);
|
||||
//viewVec = vVec;
|
||||
viewVec = normalize(vec3(0.0, -0.4, 0.5));
|
||||
//Vector representing the 0th texture coordinate passed to fragment shader
|
||||
//gl_TexCoord[0] = vec2(gl_MultiTexCoord0);
|
||||
|
||||
// Transform the current vertex
|
||||
//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
54
client/shaders/liquids_shader/opengl_fragment.glsl
Normal file
54
client/shaders/liquids_shader/opengl_fragment.glsl
Normal file
@@ -0,0 +1,54 @@
|
||||
uniform sampler2D baseTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
uniform sampler2D useNormalmap;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
const float e = 2.718281828459;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec3 color;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
|
||||
#ifdef USE_NORMALMAPS
|
||||
float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BUMPMAPPING
|
||||
if (use_normalmap > 0.0) {
|
||||
vec3 base = texture2D(baseTexture, uv).rgb;
|
||||
vec3 vVec = normalize(eyeVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vVec);
|
||||
float diffuse = max(dot(vec3(-1.0, -0.4, 0.5), bump), 0.0);
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse;
|
||||
} else {
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
}
|
||||
#else
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
#endif
|
||||
|
||||
float alpha = gl_Color.a;
|
||||
vec4 col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / e;
|
||||
col.g = 1.0 - exp(1.0 - col.g) / e;
|
||||
col.b = 1.0 - exp(1.0 - col.b) / e;
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
alpha = mix(alpha, 0.0, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
@@ -1,15 +1,27 @@
|
||||
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
uniform float animationTimer;
|
||||
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
#ifdef ENABLE_WAVING_WATER
|
||||
vec4 pos2 = gl_Vertex;
|
||||
pos2.y -= 2.0;
|
||||
pos2.y -= sin (pos2.z/WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH) * WATER_WAVE_HEIGHT
|
||||
+ sin ((pos2.z/WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH) / 7.0) * WATER_WAVE_HEIGHT;
|
||||
gl_Position = mWorldViewProj * pos2;
|
||||
#else
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
#endif
|
||||
|
||||
eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
|
||||
vec4 color;
|
||||
@@ -24,7 +36,7 @@ void main(void)
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 1.0; // Make light sources brighter
|
||||
rg += light_source * 2.5; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
@@ -39,13 +51,13 @@ void main(void)
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = rg;
|
||||
color.g = rg;
|
||||
color.b = b;
|
||||
|
||||
color.r = clamp(rg,0.0,1.0);
|
||||
color.g = clamp(rg,0.0,1.0);
|
||||
color.b = clamp(b,0.0,1.0);
|
||||
color.a = gl_Color.a;
|
||||
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
}
|
||||
54
client/shaders/plants_shader/opengl_fragment.glsl
Normal file
54
client/shaders/plants_shader/opengl_fragment.glsl
Normal file
@@ -0,0 +1,54 @@
|
||||
uniform sampler2D baseTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
uniform sampler2D useNormalmap;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
const float e = 2.718281828459;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec3 color;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
|
||||
#ifdef USE_NORMALMAPS
|
||||
float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BUMPMAPPING
|
||||
if (use_normalmap > 0.0) {
|
||||
vec3 base = texture2D(baseTexture, uv).rgb;
|
||||
vec3 vVec = normalize(eyeVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vVec);
|
||||
float diffuse = max(dot(lVec, bump), 0.0);
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse;
|
||||
} else {
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
}
|
||||
#else
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
#endif
|
||||
|
||||
float alpha = texture2D(baseTexture, uv).a;
|
||||
vec4 col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / e;
|
||||
col.g = 1.0 - exp(1.0 - col.g) / e;
|
||||
col.b = 1.0 - exp(1.0 - col.b) / e;
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
col = mix(col, skyBgColor, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
@@ -1,16 +1,45 @@
|
||||
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
uniform float animationTimer;
|
||||
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
#ifdef ENABLE_WAVING_PLANTS
|
||||
float smoothCurve( float x ) {
|
||||
return x * x *( 3.0 - 2.0 * x );
|
||||
}
|
||||
float triangleWave( float x ) {
|
||||
return abs( fract( x + 0.5 ) * 2.0 - 1.0 );
|
||||
}
|
||||
float smoothTriangleWave( float x ) {
|
||||
return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main(void)
|
||||
{
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
#ifdef ENABLE_WAVING_PLANTS
|
||||
vec4 pos = gl_Vertex;
|
||||
vec4 pos2 = mTransWorld * gl_Vertex;
|
||||
if (gl_TexCoord[0].y < 0.05) {
|
||||
pos.x += (smoothTriangleWave(animationTimer * 20.0 + pos2.x * 0.1 + pos2.z * 0.1) * 2.0 - 1.0) * 0.8;
|
||||
pos.y -= (smoothTriangleWave(animationTimer * 10.0 + pos2.x * -0.5 + pos2.z * -0.5) * 2.0 - 1.0) * 0.4;
|
||||
}
|
||||
gl_Position = mWorldViewProj * pos;
|
||||
#else
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
#endif
|
||||
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
|
||||
vec4 color;
|
||||
//color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
@@ -24,7 +53,7 @@ void main(void)
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 1.0; // Make light sources brighter
|
||||
rg += light_source * 2.5; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
@@ -39,9 +68,9 @@ void main(void)
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = rg;
|
||||
color.g = rg;
|
||||
color.b = b;
|
||||
color.r = clamp(rg,0.0,1.0);
|
||||
color.g = clamp(rg,0.0,1.0);
|
||||
color.b = clamp(b,0.0,1.0);
|
||||
|
||||
// Make sides and bottom darker than the top
|
||||
color = color * color; // SRGB -> Linear
|
||||
@@ -54,5 +83,4 @@ void main(void)
|
||||
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
}
|
||||
1
client/shaders/solids_shader/base.txt
Normal file
1
client/shaders/solids_shader/base.txt
Normal file
@@ -0,0 +1 @@
|
||||
trans_alphach_ref
|
||||
90
client/shaders/solids_shader/opengl_fragment.glsl
Normal file
90
client/shaders/solids_shader/opengl_fragment.glsl
Normal file
@@ -0,0 +1,90 @@
|
||||
uniform sampler2D baseTexture;
|
||||
uniform sampler2D normalTexture;
|
||||
uniform sampler2D useNormalmap;
|
||||
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
varying vec3 tsEyeVec;
|
||||
#endif
|
||||
|
||||
const float e = 2.718281828459;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec3 color;
|
||||
vec2 uv = gl_TexCoord[0].st;
|
||||
|
||||
#ifdef USE_NORMALMAPS
|
||||
float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
float height;
|
||||
vec2 tsEye = vec2(tsEyeVec.x,-tsEyeVec.y);
|
||||
|
||||
if (use_normalmap > 0.0) {
|
||||
float map_height = texture2D(normalTexture, uv).a;
|
||||
if (map_height < 1.0){
|
||||
float height = PARALLAX_OCCLUSION_SCALE * map_height - PARALLAX_OCCLUSION_BIAS;
|
||||
uv = uv + height * tsEye;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Steep parallax code, for future use
|
||||
if ((parallaxMappingMode == 2.0) && (use_normalmap > 0.0)) {
|
||||
const float numSteps = 40.0;
|
||||
float height = 1.0;
|
||||
float step = 1.0 / numSteps;
|
||||
vec4 NB = texture2D(normalTexture, uv);
|
||||
vec2 delta = tsEye * parallaxMappingScale / numSteps;
|
||||
for (float i = 0.0; i < numSteps; i++) {
|
||||
if (NB.a < height) {
|
||||
height -= step;
|
||||
uv += delta;
|
||||
NB = texture2D(normalTexture, uv);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_BUMPMAPPING
|
||||
if (use_normalmap > 0.0) {
|
||||
vec3 base = texture2D(baseTexture, uv).rgb;
|
||||
vec3 vVec = normalize(eyeVec);
|
||||
vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0);
|
||||
vec3 R = reflect(-vVec, bump);
|
||||
vec3 lVec = normalize(vVec);
|
||||
float diffuse = max(dot(lVec, bump), 0.0);
|
||||
float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0);
|
||||
color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse;
|
||||
} else {
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
}
|
||||
#else
|
||||
color = texture2D(baseTexture, uv).rgb;
|
||||
#endif
|
||||
|
||||
float alpha = texture2D(baseTexture, uv).a;
|
||||
vec4 col = vec4(color.r, color.g, color.b, alpha);
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / e;
|
||||
col.g = 1.0 - exp(1.0 - col.g) / e;
|
||||
col.b = 1.0 - exp(1.0 - col.b) / e;
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
col = mix(col, skyBgColor, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
||||
102
client/shaders/solids_shader/opengl_vertex.glsl
Normal file
102
client/shaders/solids_shader/opengl_vertex.glsl
Normal file
@@ -0,0 +1,102 @@
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
|
||||
uniform vec3 eyePosition;
|
||||
|
||||
varying vec3 vPosition;
|
||||
varying vec3 eyeVec;
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
varying vec3 tsEyeVec;
|
||||
#endif
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
|
||||
#ifdef ENABLE_PARALLAX_OCCLUSION
|
||||
vec3 normal,tangent,binormal;
|
||||
normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
|
||||
if (gl_Normal.x > 0.5) {
|
||||
// 1.0, 0.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, -1.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
} else if (gl_Normal.x < -0.5) {
|
||||
// -1.0, 0.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
} else if (gl_Normal.y > 0.5) {
|
||||
// 0.0, 1.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
|
||||
} else if (gl_Normal.y < -0.5) {
|
||||
// 0.0, -1.0, 0.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
|
||||
} else if (gl_Normal.z > 0.5) {
|
||||
// 0.0, 0.0, 1.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
} else if (gl_Normal.z < -0.5) {
|
||||
// 0.0, 0.0, -1.0
|
||||
tangent = normalize(gl_NormalMatrix * vec3(-1.0, 0.0, 0.0));
|
||||
binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
|
||||
}
|
||||
|
||||
mat3 tbnMatrix = mat3( tangent.x, binormal.x, normal.x,
|
||||
tangent.y, binormal.y, normal.y,
|
||||
tangent.z, binormal.z, normal.z);
|
||||
|
||||
tsEyeVec = normalize(eyeVec * tbnMatrix);
|
||||
#endif
|
||||
|
||||
vec4 color;
|
||||
//color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
float day = gl_Color.r;
|
||||
float night = gl_Color.g;
|
||||
float light_source = gl_Color.b;
|
||||
|
||||
/*color.r = mix(night, day, dayNightRatio);
|
||||
color.g = color.r;
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 2.5; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
b += (day - night) / 13.0;
|
||||
rg -= (day - night) / 13.0;
|
||||
|
||||
// Emphase blue a bit in darker places
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
|
||||
|
||||
// Artificial light is yellow-ish
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = clamp(rg,0.0,1.0);
|
||||
color.g = clamp(rg,0.0,1.0);
|
||||
color.b = clamp(b,0.0,1.0);
|
||||
|
||||
// Make sides and bottom darker than the top
|
||||
color = color * color; // SRGB -> Linear
|
||||
if(gl_Normal.y <= 0.5)
|
||||
color *= 0.6;
|
||||
//color *= 0.7;
|
||||
color = sqrt(color); // Linear -> SRGB
|
||||
|
||||
color.a = gl_Color.a;
|
||||
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
uniform sampler2D myTexture;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform float fogDistance;
|
||||
|
||||
varying vec3 vPosition;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
//vec4 col = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
|
||||
float a = col.a;
|
||||
col *= gl_Color;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
|
||||
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
|
||||
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
col = mix(col, skyBgColor, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, a);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
uniform sampler2D myTexture;
|
||||
uniform float fogDistance;
|
||||
|
||||
varying vec3 vPosition;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
|
||||
col *= gl_Color;
|
||||
float a = gl_Color.a;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
|
||||
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
|
||||
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
a = mix(a, 0.0, d);
|
||||
}
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, a);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
trans_alphach
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
uniform sampler2D myTexture;
|
||||
uniform float fogDistance;
|
||||
|
||||
varying vec3 vPosition;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
|
||||
col *= gl_Color;
|
||||
float a = col.a;
|
||||
col = col * col; // SRGB -> Linear
|
||||
col *= 1.8;
|
||||
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
|
||||
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
|
||||
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
|
||||
col = sqrt(col); // Linear -> SRGB
|
||||
|
||||
if(fogDistance != 0.0){
|
||||
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
|
||||
a = mix(a, 0.0, d);
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(col.r, col.g, col.b, a);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
uniform mat4 mWorldViewProj;
|
||||
uniform mat4 mInvWorld;
|
||||
uniform mat4 mTransWorld;
|
||||
uniform float dayNightRatio;
|
||||
|
||||
varying vec3 vPosition;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = mWorldViewProj * gl_Vertex;
|
||||
|
||||
vPosition = (mWorldViewProj * gl_Vertex).xyz;
|
||||
|
||||
vec4 color;
|
||||
//color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
float day = gl_Color.r;
|
||||
float night = gl_Color.g;
|
||||
float light_source = gl_Color.b;
|
||||
|
||||
/*color.r = mix(night, day, dayNightRatio);
|
||||
color.g = color.r;
|
||||
color.b = color.r;*/
|
||||
|
||||
float rg = mix(night, day, dayNightRatio);
|
||||
rg += light_source * 1.0; // Make light sources brighter
|
||||
float b = rg;
|
||||
|
||||
// Moonlight is blue
|
||||
b += (day - night) / 13.0;
|
||||
rg -= (day - night) / 13.0;
|
||||
|
||||
// Emphase blue a bit in darker places
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
|
||||
|
||||
// Artificial light is yellow-ish
|
||||
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
|
||||
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
|
||||
|
||||
color.r = rg;
|
||||
color.g = rg;
|
||||
color.b = b;
|
||||
|
||||
color.a = gl_Color.a;
|
||||
|
||||
gl_FrontColor = gl_BackColor = color;
|
||||
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
Minetest Lua Modding API Reference 0.4.8
|
||||
Minetest Lua Modding API Reference 0.4.9
|
||||
========================================
|
||||
More information at http://www.minetest.net/
|
||||
Developer Wiki: http://dev.minetest.net/
|
||||
@@ -129,13 +129,6 @@ screenshot.png:
|
||||
description.txt:
|
||||
File containing desctiption to be shown within mainmenu.
|
||||
|
||||
optdepends.txt:
|
||||
An alternative way of specifying optional dependencies.
|
||||
Like depends.txt, a single line contains a single modname.
|
||||
|
||||
NOTE: This file exists for compatibility purposes only and
|
||||
support for it will be removed from the engine by the end of 2013.
|
||||
|
||||
init.lua:
|
||||
The main Lua script. Running this script should register everything it
|
||||
wants to register. Subsequent execution depends on minetest calling the
|
||||
@@ -1105,6 +1098,7 @@ minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)"
|
||||
^ Convert position to a printable string
|
||||
minetest.string_to_pos(string) -> position
|
||||
^ Same but in reverse
|
||||
minetest.formspec_escape(string) -> string
|
||||
^ escapes characters [ ] \ , ; that can not be used in formspecs
|
||||
minetest.is_yes(arg)
|
||||
^ returns whether arg can be interpreted as yes
|
||||
@@ -1174,6 +1168,9 @@ minetest.register_on_respawnplayer(func(ObjectRef))
|
||||
^ Called when player is to be respawned
|
||||
^ Called _before_ repositioning of player occurs
|
||||
^ return true in func to disable regular player placement
|
||||
minetest.register_on_prejoinplayer(func(name, ip))
|
||||
^ Called before a player joins the game
|
||||
^ If it returns a string, the player is disconnected with that string as reason
|
||||
minetest.register_on_joinplayer(func(ObjectRef))
|
||||
^ Called when a player joins the game
|
||||
minetest.register_on_leaveplayer(func(ObjectRef))
|
||||
@@ -1254,6 +1251,8 @@ Environment access:
|
||||
minetest.set_node(pos, node)
|
||||
minetest.add_node(pos, node): alias set_node(pos, node)
|
||||
^ Set node at position (node = {name="foo", param1=0, param2=0})
|
||||
minetest.swap_node(pos, node)
|
||||
^ Set node at position, but don't remove metadata
|
||||
minetest.remove_node(pos)
|
||||
^ Equivalent to set_node(pos, "air")
|
||||
minetest.get_node(pos)
|
||||
@@ -1290,6 +1289,10 @@ minetest.get_perlin(seeddiff, octaves, persistence, scale)
|
||||
^ Return world-specific perlin noise (int(worldseed)+seeddiff)
|
||||
minetest.get_voxel_manip()
|
||||
^ Return voxel manipulator object
|
||||
minetest.set_gen_notify(flags)
|
||||
^ Set the types of on-generate notifications that should be collected
|
||||
^ flags is a comma-delimited combination of:
|
||||
^ dungeon, temple, cave_begin, cave_end, large_cave_begin, large_cave_end
|
||||
minetest.get_mapgen_object(objectname)
|
||||
^ Return requested mapgen object if available (see Mapgen objects)
|
||||
minetest.set_mapgen_params(MapgenParams)
|
||||
@@ -1302,8 +1305,9 @@ minetest.set_mapgen_params(MapgenParams)
|
||||
^ flags and flagmask are in the same format and have the same options as 'mgflags' in minetest.conf
|
||||
minetest.clear_objects()
|
||||
^ clear all objects in the environments
|
||||
minetest.line_of_sight(pos1,pos2,stepsize) ->true/false
|
||||
^ checkif there is a direct line of sight between pos1 and pos2
|
||||
minetest.line_of_sight(pos1, pos2, stepsize) -> true/false, pos
|
||||
^ Check if there is a direct line of sight between pos1 and pos2
|
||||
^ Returns the position of the blocking node when false
|
||||
^ pos1 First position
|
||||
^ pos2 Second position
|
||||
^ stepsize smaller gives more accurate results but requires more computing
|
||||
@@ -1402,8 +1406,8 @@ minetest.handle_node_drops(pos, drops, digger)
|
||||
^ Can be overridden to get different functionality (eg. dropping items on
|
||||
ground)
|
||||
|
||||
Rollbacks:
|
||||
minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
|
||||
Rollback:
|
||||
minetest.rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...}
|
||||
^ Find who has done something to a node, or near a node
|
||||
^ actor: "player:<name>", also "liquid".
|
||||
minetest.rollback_revert_actions_by(actor, seconds) -> bool, log messages
|
||||
@@ -1489,7 +1493,7 @@ minetest.delete_particlespawner(id, player)
|
||||
^ otherwise on all clients
|
||||
|
||||
Schematics:
|
||||
minetest.create_schematic(p1, p2, probability_list, filename)
|
||||
minetest.create_schematic(p1, p2, probability_list, filename, slice_prob_list)
|
||||
^ Create a schematic from the volume of map specified by the box formed by p1 and p2.
|
||||
^ Apply the specified probability values to the specified nodes in probability_list.
|
||||
^ probability_list is an array of tables containing two fields, pos and prob.
|
||||
@@ -1498,6 +1502,9 @@ minetest.create_schematic(p1, p2, probability_list, filename)
|
||||
^ If there are two or more entries with the same pos value, the last occuring in the array is used.
|
||||
^ If pos is not inside the box formed by p1 and p2, it is ignored.
|
||||
^ If probability_list is nil, no probabilities are applied.
|
||||
^ Slice probability works in the same manner, except takes a field called ypos instead which indicates
|
||||
^ the y position of the slice with a probability applied.
|
||||
^ If slice probability list is nil, no slice probabilities are applied.
|
||||
^ Saves schematic in the Minetest Schematic format to filename.
|
||||
|
||||
minetest.place_schematic(pos, schematic, rotation, replacements)
|
||||
@@ -1523,7 +1530,16 @@ minetest.parse_json(string[, nullvalue]) -> something
|
||||
^ nullvalue: returned in place of the JSON null; defaults to nil
|
||||
^ On success returns a table, a string, a number, a boolean or nullvalue
|
||||
^ On failure outputs an error message and returns nil
|
||||
^ Example: parse_json("[10, {\"a\":false}]") -> {[1] = 10, [2] = {a = false}}
|
||||
^ Example: parse_json("[10, {\"a\":false}]") -> {10, {a = false}}
|
||||
minetest.write_json(data[, styled]) -> string or nil and error message
|
||||
^ Convert a Lua table into a JSON string
|
||||
^ styled: Outputs in a human-readable format if this is set, defaults to false
|
||||
^ Un-serializable things like functions and userdata are saved as null.
|
||||
^ Warning: JSON is more strict than the Lua table format.
|
||||
1. You can only use strings and positive integers of at least one as keys.
|
||||
2. You can not mix string and integer keys.
|
||||
This is due to the fact that Javascript has two distinct array and object values.
|
||||
^ Example: write_json({10, {a = false}}) -> "[10, {\"a\": false}]"
|
||||
minetest.serialize(table) -> string
|
||||
^ Convert a table containing tables, strings, numbers, booleans and nils
|
||||
into string form readable by minetest.deserialize
|
||||
@@ -1701,10 +1717,13 @@ Player-only: (no-op for other objects)
|
||||
{jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool}
|
||||
- get_player_control_bits(): returns integer with bit packed player pressed keys
|
||||
bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB
|
||||
- set_physics_override(speed, jump, gravity)
|
||||
modifies per-player walking speed, jump height, and gravity.
|
||||
Values default to 1 and act as offsets to the physics settings
|
||||
in minetest.conf. nil will keep the current setting.
|
||||
- set_physics_override({
|
||||
speed = 1.0, -- multiplier to default value
|
||||
jump = 1.0, -- multiplier to default value
|
||||
gravity = 1.0, -- multiplier to default value
|
||||
sneak = true, -- whether player can sneak
|
||||
sneak_glitch = true, -- whether player can use the sneak glitch
|
||||
})
|
||||
- hud_add(hud definition): add a HUD element described by HUD def, returns ID number on success
|
||||
- hud_remove(id): remove the HUD element of the specified id
|
||||
- hud_change(id, stat, value): change a value of a previously added HUD element
|
||||
@@ -1815,11 +1834,19 @@ methods:
|
||||
- update_map(): Update map after writing chunk back to map.
|
||||
^ To be used only by VoxelManip objects created by the mod itself; not a VoxelManip that was
|
||||
^ retrieved from minetest.get_mapgen_object
|
||||
- set_lighting(light): Set the lighting within the VoxelManip
|
||||
- set_lighting(light, p1, p2): Set the lighting within the VoxelManip to a uniform value
|
||||
^ light is a table, {day=<0...15>, night=<0...15>}
|
||||
^ To be used only by a VoxelManip object from minetest.get_mapgen_object
|
||||
- calc_lighting(): Calculate lighting within the VoxelManip
|
||||
^ (p1, p2) is the area in which lighting is set; defaults to the whole area if left out
|
||||
- get_light_data(): Gets the light data read into the VoxelManip object
|
||||
^ Returns an array (indicies 1 to volume) of integers ranging from 0 to 255
|
||||
^ Each value is the bitwise combination of day and night light values (0..15 each)
|
||||
^ light = day + (night * 16)
|
||||
- set_light_data(light_data): Sets the param1 (light) contents of each node in the VoxelManip
|
||||
^ expects lighting data in the same format that get_light_data() returns
|
||||
- calc_lighting(p1, p2): Calculate lighting within the VoxelManip
|
||||
^ To be used only by a VoxelManip object from minetest.get_mapgen_object
|
||||
^ (p1, p2) is the area in which lighting is set; defaults to the whole area if left out
|
||||
- update_liquids(): Update liquid flow
|
||||
|
||||
VoxelArea: A helper class for voxel areas
|
||||
@@ -1880,6 +1907,12 @@ the current mapgen.
|
||||
Returns an array containing the humidity values of nodes in the most recently generated chunk by the
|
||||
current mapgen.
|
||||
|
||||
- gennotify
|
||||
Returns a table mapping requested generation notification types to arrays of positions at which the
|
||||
corresponding generated structures are located at within the current chunk. To set the capture of positions
|
||||
of interest to be recorded on generate, use minetest.set_gen_notify().
|
||||
Possible fields of the table returned are: dungeon, temple, cave_begin, cave_end, large_cave_begin, large_cave_end
|
||||
|
||||
Registered entities
|
||||
--------------------
|
||||
- Functions receive a "luaentity" as self:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Minetest Lua Mainmenu API Reference 0.4.6
|
||||
Minetest Lua Mainmenu API Reference 0.4.9
|
||||
========================================
|
||||
|
||||
Introduction
|
||||
@@ -33,9 +33,9 @@ engine.close()
|
||||
Filesystem:
|
||||
engine.get_scriptdir()
|
||||
^ returns directory of script
|
||||
engine.get_modpath()
|
||||
engine.get_modpath() (possible in async calls)
|
||||
^ returns path to global modpath
|
||||
engine.get_modstore_details(modid)
|
||||
engine.get_modstore_details(modid) (possible in async calls)
|
||||
^ modid numeric id of mod in modstore
|
||||
^ returns {
|
||||
id = <numeric id of mod in modstore>,
|
||||
@@ -47,7 +47,7 @@ engine.get_modstore_details(modid)
|
||||
license = <short description of license>,
|
||||
rating = <float value of current rating>
|
||||
}
|
||||
engine.get_modstore_list()
|
||||
engine.get_modstore_list() (possible in async calls)
|
||||
^ returns {
|
||||
[1] = {
|
||||
id = <numeric id of mod in modstore>,
|
||||
@@ -55,19 +55,21 @@ engine.get_modstore_list()
|
||||
basename = <basename for mod>
|
||||
}
|
||||
}
|
||||
engine.get_gamepath()
|
||||
engine.get_gamepath() (possible in async calls)
|
||||
^ returns path to global gamepath
|
||||
engine.get_dirlist(path,onlydirs)
|
||||
engine.get_texturepath() (possible in async calls)
|
||||
^ returns path to default textures
|
||||
engine.get_dirlist(path,onlydirs) (possible in async calls)
|
||||
^ path to get subdirs from
|
||||
^ onlydirs should result contain only dirs?
|
||||
^ returns list of folders within path
|
||||
engine.create_dir(absolute_path)
|
||||
engine.create_dir(absolute_path) (possible in async calls)
|
||||
^ absolute_path to directory to create (needs to be absolute)
|
||||
^ returns true/false
|
||||
engine.delete_dir(absolute_path)
|
||||
engine.delete_dir(absolute_path) (possible in async calls)
|
||||
^ absolute_path to directory to delete (needs to be absolute)
|
||||
^ returns true/false
|
||||
engine.copy_dir(source,destination,keep_soure)
|
||||
engine.copy_dir(source,destination,keep_soure) (possible in async calls)
|
||||
^ source folder
|
||||
^ destination folder
|
||||
^ keep_source DEFAULT true --> if set to false source is deleted after copying
|
||||
@@ -76,11 +78,11 @@ engine.extract_zip(zipfile,destination) [unzip within path required]
|
||||
^ zipfile to extract
|
||||
^ destination folder to extract to
|
||||
^ returns true/false
|
||||
engine.download_file(url,target)
|
||||
engine.download_file(url,target) (possible in async calls)
|
||||
^ url to download
|
||||
^ target to store to
|
||||
^ returns true/false
|
||||
engine.get_version()
|
||||
engine.get_version() (possible in async calls)
|
||||
^ returns current minetest version
|
||||
engine.sound_play(spec, looped) -> handle
|
||||
^ spec = SimpleSoundSpec (see lua-api.txt)
|
||||
@@ -105,10 +107,10 @@ engine.get_game(index)
|
||||
DEPRECATED:
|
||||
addon_mods_paths = {[1] = <path>,},
|
||||
}
|
||||
engine.get_games() -> table of all games in upper format
|
||||
engine.get_games() -> table of all games in upper format (possible in async calls)
|
||||
|
||||
Favorites:
|
||||
engine.get_favorites(location) -> list of favorites
|
||||
engine.get_favorites(location) -> list of favorites (possible in async calls)
|
||||
^ location: "local" or "online"
|
||||
^ returns {
|
||||
[1] = {
|
||||
@@ -128,21 +130,21 @@ engine.get_favorites(location) -> list of favorites
|
||||
engine.delete_favorite(id, location) -> success
|
||||
|
||||
Logging:
|
||||
engine.debug(line)
|
||||
engine.debug(line) (possible in async calls)
|
||||
^ Always printed to stderr and logfile (print() is redirected here)
|
||||
engine.log(line)
|
||||
engine.log(loglevel, line)
|
||||
engine.log(line) (possible in async calls)
|
||||
engine.log(loglevel, line) (possible in async calls)
|
||||
^ loglevel one of "error", "action", "info", "verbose"
|
||||
|
||||
Settings:
|
||||
engine.setting_set(name, value)
|
||||
engine.setting_get(name) -> string or nil
|
||||
engine.setting_get(name) -> string or nil (possible in async calls)
|
||||
engine.setting_setbool(name, value)
|
||||
engine.setting_getbool(name) -> bool or nil
|
||||
engine.setting_getbool(name) -> bool or nil (possible in async calls)
|
||||
engine.setting_save() -> nil, save all settings to config file
|
||||
|
||||
Worlds:
|
||||
engine.get_worlds() -> list of worlds
|
||||
engine.get_worlds() -> list of worlds (possible in async calls)
|
||||
^ returns {
|
||||
[1] = {
|
||||
path = <full path to world>,
|
||||
@@ -174,7 +176,7 @@ engine.gettext(string) -> string
|
||||
fgettext(string, ...) -> string
|
||||
^ call engine.gettext(string), replace "$1"..."$9" with the given
|
||||
^ extra arguments, call engine.formspec_escape and return the result
|
||||
engine.parse_json(string[, nullvalue]) -> something
|
||||
engine.parse_json(string[, nullvalue]) -> something (possible in async calls)
|
||||
^ see minetest.parse_json (lua_api.txt)
|
||||
dump(obj, dumped={})
|
||||
^ Return object serialized as a string
|
||||
@@ -182,9 +184,24 @@ string:split(separator)
|
||||
^ eg. string:split("a,b", ",") == {"a","b"}
|
||||
string:trim()
|
||||
^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
|
||||
minetest.is_yes(arg)
|
||||
minetest.is_yes(arg) (possible in async calls)
|
||||
^ returns whether arg can be interpreted as yes
|
||||
|
||||
Async:
|
||||
engine.handle_async(async_job,parameters,finished)
|
||||
^ execute a function asynchronously
|
||||
^ async_job is a function receiving one parameter and returning one parameter
|
||||
^ parameters parameter table passed to async_job
|
||||
^ finished function to be called once async_job has finished
|
||||
^ the result of async_job is passed to this function
|
||||
|
||||
Limitations of Async operations
|
||||
-No access to global lua variables, don't even try
|
||||
-Limited set of available functions
|
||||
e.g. No access to functions modifying menu like engine.start,engine.close,
|
||||
engine.file_open_dialog
|
||||
|
||||
|
||||
Class reference
|
||||
----------------
|
||||
Settings: see lua_api.txt
|
||||
|
||||
@@ -712,7 +712,6 @@ end
|
||||
minetest.register_node("default:stone", {
|
||||
description = "Stone",
|
||||
tiles ={"default_stone.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:cobble',
|
||||
legacy_mineral = true,
|
||||
@@ -722,7 +721,6 @@ minetest.register_node("default:stone", {
|
||||
minetest.register_node("default:stone_with_coal", {
|
||||
description = "Stone with coal",
|
||||
tiles ={"default_stone.png^default_mineral_coal.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:coal_lump',
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -731,7 +729,6 @@ minetest.register_node("default:stone_with_coal", {
|
||||
minetest.register_node("default:stone_with_iron", {
|
||||
description = "Stone with iron",
|
||||
tiles ={"default_stone.png^default_mineral_iron.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:iron_lump',
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -740,7 +737,6 @@ minetest.register_node("default:stone_with_iron", {
|
||||
minetest.register_node("default:dirt_with_grass", {
|
||||
description = "Dirt with grass",
|
||||
tiles ={"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3, soil=1},
|
||||
drop = 'default:dirt',
|
||||
sounds = default.node_sound_dirt_defaults({
|
||||
@@ -751,7 +747,6 @@ minetest.register_node("default:dirt_with_grass", {
|
||||
minetest.register_node("default:dirt_with_grass_footsteps", {
|
||||
description = "Dirt with grass and footsteps",
|
||||
tiles ={"default_grass_footsteps.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3, soil=1},
|
||||
drop = 'default:dirt',
|
||||
sounds = default.node_sound_dirt_defaults({
|
||||
@@ -762,7 +757,6 @@ minetest.register_node("default:dirt_with_grass_footsteps", {
|
||||
minetest.register_node("default:dirt", {
|
||||
description = "Dirt",
|
||||
tiles ={"default_dirt.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3, soil=1},
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
})
|
||||
@@ -770,7 +764,6 @@ minetest.register_node("default:dirt", {
|
||||
minetest.register_node("default:sand", {
|
||||
description = "Sand",
|
||||
tiles ={"default_sand.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3, falling_node=1},
|
||||
sounds = default.node_sound_sand_defaults(),
|
||||
})
|
||||
@@ -778,7 +771,6 @@ minetest.register_node("default:sand", {
|
||||
minetest.register_node("default:gravel", {
|
||||
description = "Gravel",
|
||||
tiles ={"default_gravel.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=2, falling_node=1},
|
||||
sounds = default.node_sound_dirt_defaults({
|
||||
footstep = {name="default_gravel_footstep", gain=0.45},
|
||||
@@ -788,7 +780,6 @@ minetest.register_node("default:gravel", {
|
||||
minetest.register_node("default:sandstone", {
|
||||
description = "Sandstone",
|
||||
tiles ={"default_sandstone.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=2,cracky=2},
|
||||
drop = 'default:sand',
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -797,7 +788,6 @@ minetest.register_node("default:sandstone", {
|
||||
minetest.register_node("default:clay", {
|
||||
description = "Clay",
|
||||
tiles ={"default_clay.png"},
|
||||
is_ground_content = true,
|
||||
groups = {crumbly=3},
|
||||
drop = 'default:clay_lump 4',
|
||||
sounds = default.node_sound_dirt_defaults({
|
||||
@@ -808,7 +798,6 @@ minetest.register_node("default:clay", {
|
||||
minetest.register_node("default:brick", {
|
||||
description = "Brick",
|
||||
tiles ={"default_brick.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'default:clay_brick 4',
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
@@ -817,7 +806,6 @@ minetest.register_node("default:brick", {
|
||||
minetest.register_node("default:tree", {
|
||||
description = "Tree",
|
||||
tiles ={"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
@@ -825,7 +813,6 @@ minetest.register_node("default:tree", {
|
||||
minetest.register_node("default:jungletree", {
|
||||
description = "Jungle Tree",
|
||||
tiles ={"default_jungletree_top.png", "default_jungletree_top.png", "default_jungletree.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
@@ -849,6 +836,7 @@ minetest.register_node("default:leaves", {
|
||||
visual_scale = 1.3,
|
||||
tiles ={"default_leaves.png"},
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
groups = {snappy=3},
|
||||
drop = {
|
||||
max_items = 1,
|
||||
@@ -871,7 +859,6 @@ minetest.register_node("default:leaves", {
|
||||
minetest.register_node("default:cactus", {
|
||||
description = "Cactus",
|
||||
tiles ={"default_cactus_top.png", "default_cactus_top.png", "default_cactus_side.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
@@ -883,7 +870,6 @@ minetest.register_node("default:papyrus", {
|
||||
inventory_image = "default_papyrus.png",
|
||||
wield_image = "default_papyrus.png",
|
||||
paramtype = "light",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
groups = {snappy=3},
|
||||
sounds = default.node_sound_leaves_defaults(),
|
||||
@@ -892,7 +878,6 @@ minetest.register_node("default:papyrus", {
|
||||
minetest.register_node("default:bookshelf", {
|
||||
description = "Bookshelf",
|
||||
tiles ={"default_wood.png", "default_wood.png", "default_bookshelf.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=3,oddly_breakable_by_hand=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
@@ -904,7 +889,6 @@ minetest.register_node("default:glass", {
|
||||
inventory_image = minetest.inventorycube("default_glass.png"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,cracky=3,oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
@@ -916,7 +900,6 @@ minetest.register_node("default:fence_wood", {
|
||||
inventory_image = "default_fence.png",
|
||||
wield_image = "default_fence.png",
|
||||
paramtype = "light",
|
||||
is_ground_content = true,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
|
||||
@@ -932,7 +915,6 @@ minetest.register_node("default:rail", {
|
||||
inventory_image = "default_rail.png",
|
||||
wield_image = "default_rail.png",
|
||||
paramtype = "light",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
@@ -949,7 +931,6 @@ minetest.register_node("default:ladder", {
|
||||
wield_image = "default_ladder.png",
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
@@ -966,7 +947,6 @@ minetest.register_node("default:ladder", {
|
||||
minetest.register_node("default:wood", {
|
||||
description = "Wood",
|
||||
tiles ={"default_wood.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
@@ -974,7 +954,6 @@ minetest.register_node("default:wood", {
|
||||
minetest.register_node("default:mese", {
|
||||
description = "Mese",
|
||||
tiles ={"default_mese.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=1,level=2},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
@@ -982,7 +961,6 @@ minetest.register_node("default:mese", {
|
||||
minetest.register_node("default:cloud", {
|
||||
description = "Cloud",
|
||||
tiles ={"default_cloud.png"},
|
||||
is_ground_content = true,
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
@@ -1104,6 +1082,7 @@ minetest.register_node("default:torch", {
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
light_source = LIGHT_MAX-1,
|
||||
selection_box = {
|
||||
@@ -1126,6 +1105,7 @@ minetest.register_node("default:sign_wall", {
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
@@ -1160,6 +1140,7 @@ minetest.register_node("default:chest", {
|
||||
paramtype2 = "facedir",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
legacy_facedir_simple = true,
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
@@ -1192,6 +1173,7 @@ minetest.register_node("default:chest_locked", {
|
||||
paramtype2 = "facedir",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
legacy_facedir_simple = true,
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
@@ -1277,6 +1259,7 @@ minetest.register_node("default:furnace", {
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2},
|
||||
legacy_facedir_simple = true,
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
@@ -1310,6 +1293,7 @@ minetest.register_node("default:furnace_active", {
|
||||
drop = "default:furnace",
|
||||
groups = {cracky=2},
|
||||
legacy_facedir_simple = true,
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
@@ -1334,18 +1318,13 @@ minetest.register_node("default:furnace_active", {
|
||||
end,
|
||||
})
|
||||
|
||||
function hacky_swap_node(pos,name)
|
||||
function swap_node(pos,name)
|
||||
local node = minetest.get_node(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local meta0 = meta:to_table()
|
||||
if node.name == name then
|
||||
return
|
||||
end
|
||||
node.name = name
|
||||
local meta0 = meta:to_table()
|
||||
minetest.set_node(pos,node)
|
||||
meta = minetest.get_meta(pos)
|
||||
meta:from_table(meta0)
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
@@ -1400,7 +1379,7 @@ minetest.register_abm({
|
||||
local percent = math.floor(meta:get_float("fuel_time") /
|
||||
meta:get_float("fuel_totaltime") * 100)
|
||||
meta:set_string("infotext","Furnace active: "..percent.."%")
|
||||
hacky_swap_node(pos,"default:furnace_active")
|
||||
swap_node(pos,"default:furnace_active")
|
||||
meta:set_string("formspec",
|
||||
"size[8,9]"..
|
||||
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
|
||||
@@ -1426,7 +1405,7 @@ minetest.register_abm({
|
||||
|
||||
if fuel.time <= 0 then
|
||||
meta:set_string("infotext","Furnace out of fuel")
|
||||
hacky_swap_node(pos,"default:furnace")
|
||||
swap_node(pos,"default:furnace")
|
||||
meta:set_string("formspec", default.furnace_inactive_formspec)
|
||||
return
|
||||
end
|
||||
@@ -1434,7 +1413,7 @@ minetest.register_abm({
|
||||
if cooked.item:is_empty() then
|
||||
if was_active then
|
||||
meta:set_string("infotext","Furnace is empty")
|
||||
hacky_swap_node(pos,"default:furnace")
|
||||
swap_node(pos,"default:furnace")
|
||||
meta:set_string("formspec", default.furnace_inactive_formspec)
|
||||
end
|
||||
return
|
||||
@@ -1452,7 +1431,6 @@ minetest.register_abm({
|
||||
minetest.register_node("default:cobble", {
|
||||
description = "Cobble",
|
||||
tiles ={"default_cobble.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
@@ -1460,7 +1438,6 @@ minetest.register_node("default:cobble", {
|
||||
minetest.register_node("default:mossycobble", {
|
||||
description = "Mossy Cobble",
|
||||
tiles ={"default_mossycobble.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
@@ -1468,7 +1445,6 @@ minetest.register_node("default:mossycobble", {
|
||||
minetest.register_node("default:steelblock", {
|
||||
description = "Steel Block",
|
||||
tiles ={"default_steel_block.png"},
|
||||
is_ground_content = true,
|
||||
groups = {snappy=1,bendy=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
@@ -1481,6 +1457,7 @@ minetest.register_node("default:nyancat", {
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2},
|
||||
legacy_facedir_simple = true,
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
@@ -1488,6 +1465,7 @@ minetest.register_node("default:nyancat_rainbow", {
|
||||
description = "Nyancat Rainbow",
|
||||
tiles ={"default_nc_rb.png"},
|
||||
inventory_image = "default_nc_rb.png",
|
||||
is_ground_content = false,
|
||||
groups = {cracky=2},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
# Client and server
|
||||
#
|
||||
|
||||
# Network port (UDP)
|
||||
#port =
|
||||
# Name of player; on a server this is the main admin
|
||||
#name =
|
||||
#name =
|
||||
|
||||
#
|
||||
# Client stuff
|
||||
#
|
||||
|
||||
# Port to connect to (UDP)
|
||||
#remote_port =
|
||||
# Key mappings
|
||||
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
|
||||
#keymap_forward = KEY_KEY_W
|
||||
@@ -82,7 +82,7 @@
|
||||
#vsync = false
|
||||
#fov = 72
|
||||
# Address to connect to (#blank = start local server)
|
||||
#address =
|
||||
#address =
|
||||
# Enable random user input, for testing
|
||||
#random_input = false
|
||||
# Timeout for client to remove unused map data from memory
|
||||
@@ -115,7 +115,7 @@
|
||||
# disable for speed or for different looks.
|
||||
#smooth_lighting = true
|
||||
# Path to texture directory. All textures are first searched from here.
|
||||
#texture_path =
|
||||
#texture_path =
|
||||
# Video back-end.
|
||||
# Possible values: null, software, burningsvideo, direct3d8, direct3d9, opengl
|
||||
#video_driver = opengl
|
||||
@@ -170,13 +170,37 @@
|
||||
#enable_shaders = true
|
||||
# Set to true to enable textures bumpmapping. Requires shaders enabled.
|
||||
#enable_bumpmapping = false
|
||||
# Set to true enables parallax occlusion mapping. Requires shaders enabled.
|
||||
#enable_parallax_occlusion = false
|
||||
# Scale of parallax occlusion effect
|
||||
#parallax_occlusion_scale = 0.08
|
||||
# Bias of parallax occlusion effect, usually scale/2
|
||||
#parallax_occlusion_bias = 0.04
|
||||
# Set to true enables waving water. Requires shaders enabled.
|
||||
#enable_waving_water = false
|
||||
# Parameters for waving water:
|
||||
#water_wave_height = 1.0
|
||||
#water_wave_length = 20.0
|
||||
#water_wave_speed = 5.0
|
||||
# Set to true enables waving leaves. Requires shaders enabled.
|
||||
#enable_waving_leaves = false
|
||||
# Set to true enables waving plants. Requires shaders enabled.
|
||||
#enable_waving_plants = false
|
||||
# The time in seconds it takes between repeated
|
||||
# right clicks when holding the right mouse button
|
||||
#repeat_rightclick_time = 0.25
|
||||
# Make fog and sky colors depend on daytime (dawn/sunset) and view direction
|
||||
#directional_colored_fog = true
|
||||
|
||||
# will only work for servers which use remote_media setting
|
||||
# and only for clients compiled with cURL
|
||||
#media_fetch_threads = 8
|
||||
# Default timeout for cURL, in milliseconds
|
||||
# Only has an effect if compiled with cURL
|
||||
#curl_timeout = 5000
|
||||
# Limits number of parallel HTTP requests. Affects:
|
||||
# - Media fetch if server uses remote_media setting
|
||||
# - Serverlist download and server announcement
|
||||
# - Downloads performed by main menu (e.g. mod manager)
|
||||
# Only has an effect if compiled with cURL
|
||||
#curl_parallel_limit = 8
|
||||
|
||||
# Url to the server list displayed in the Multiplayer Tab
|
||||
#serverlist_url = servers.minetest.net
|
||||
@@ -188,17 +212,24 @@
|
||||
# Path to TrueTypeFont or bitmap
|
||||
#font_path = fonts/liberationsans.ttf
|
||||
#font_size = 13
|
||||
# Font shadow offset, if 0 then shadow will not be drawn.
|
||||
#font_shadow = 1
|
||||
# Font shadow alpha (opaqueness, between 0 and 255)
|
||||
#font_shadow_alpha = 128
|
||||
#mono_font_path = fonts/liberationmono.ttf
|
||||
#mono_font_size = 13
|
||||
|
||||
# This font will be used for certain languages
|
||||
#fallback_font_path = fonts/DroidSansFallbackFull.ttf
|
||||
#fallback_font_size = 13
|
||||
#fallback_font_shadow = 1
|
||||
#fallback_font_shadow_alpha = 128
|
||||
|
||||
#
|
||||
# Server stuff
|
||||
#
|
||||
|
||||
# Network port to listen (UDP)
|
||||
#port =
|
||||
# Name of server
|
||||
#server_name = Minetest server
|
||||
# Description of server
|
||||
@@ -232,7 +263,7 @@
|
||||
# Gives some stuff to players at the beginning
|
||||
#give_initial_stuff = false
|
||||
# New users need to input this password
|
||||
#default_password =
|
||||
#default_password =
|
||||
# Available privileges: interact, shout, teleport, settime, privs, ...
|
||||
# See /privs in game for a full list on your server and mod configuration.
|
||||
#default_privs = interact, shout
|
||||
@@ -248,6 +279,10 @@
|
||||
#disable_anticheat = false
|
||||
# If true, actions are recorded for rollback
|
||||
#enable_rollback_recording = false
|
||||
# If true, blocks are cached (and generated if not before) before a player is spawned.
|
||||
#cache_block_before_spawn = true
|
||||
# Defines the maximum height a player can spawn in a map, above water level
|
||||
#max_spawn_height = 50
|
||||
|
||||
# Profiler data print interval. #0 = disable.
|
||||
#profiler_print_interval = 0
|
||||
@@ -308,7 +343,7 @@
|
||||
#emergequeue_limit_diskonly =
|
||||
# Maximum number of blocks to be queued that are to be generated.
|
||||
# Leave blank for an appropriate amount to be chosen automatically.
|
||||
#emergequeue_limit_generate =
|
||||
#emergequeue_limit_generate =
|
||||
# Number of emerge threads to use. Make this field blank, or increase this number, to use multiple threads.
|
||||
# On multiprocessor systems, this will improve mapgen speed greatly, at the cost of slightly buggy caves.
|
||||
#num_emerge_threads = 1
|
||||
|
||||
968
po/lt/minetest.po
Normal file
968
po/lt/minetest.po
Normal file
@@ -0,0 +1,968 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: minetest\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-11-23 17:37+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: builtin/gamemgr.lua:23
|
||||
msgid "Game Name"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:25 builtin/mainmenu.lua:310
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:26 builtin/mainmenu.lua:311 builtin/modmgr.lua:331
|
||||
#: builtin/modmgr.lua:448 src/guiKeyChangeMenu.cpp:195 src/keycode.cpp:223
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:118
|
||||
msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:216
|
||||
msgid "GAMES"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:217 builtin/mainmenu.lua:1076
|
||||
msgid "Games"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:234
|
||||
msgid "Mods:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:235
|
||||
msgid "edit game"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:238
|
||||
msgid "new game"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:248
|
||||
msgid "EDIT GAME"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:269
|
||||
msgid "Remove selected mod"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/gamemgr.lua:272
|
||||
msgid "<<-- Add mod"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:158
|
||||
msgid "Ok"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:297
|
||||
msgid "World name"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:300
|
||||
msgid "Seed"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:303
|
||||
msgid "Mapgen"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:306
|
||||
msgid "Game"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:319
|
||||
msgid "Delete World \"$1\"?"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:320 builtin/modmgr.lua:877
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:321
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:364
|
||||
msgid "A world named \"$1\" already exists"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:381
|
||||
msgid "No worldname given or no game selected"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:650
|
||||
msgid "To enable shaders the OpenGL driver needs to be used."
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:818
|
||||
msgid "CLIENT"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:819
|
||||
msgid "Favorites:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:820
|
||||
msgid "Address/Port"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:821
|
||||
msgid "Name/Password"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:824
|
||||
msgid "Public Serverlist"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:829 builtin/mainmenu.lua:874 builtin/mainmenu.lua:937
|
||||
#: src/keycode.cpp:229
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:833
|
||||
msgid "Connect"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:875 builtin/mainmenu.lua:938
|
||||
msgid "New"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:876 builtin/mainmenu.lua:939
|
||||
msgid "Configure"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:877
|
||||
msgid "Start Game"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:878 builtin/mainmenu.lua:941
|
||||
msgid "Select World:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:879
|
||||
msgid "START SERVER"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:880 builtin/mainmenu.lua:943
|
||||
msgid "Creative Mode"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:882 builtin/mainmenu.lua:945
|
||||
msgid "Enable Damage"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:884
|
||||
msgid "Public"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:886
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:888
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:889
|
||||
msgid "Server Port"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:899
|
||||
msgid "SETTINGS"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:900
|
||||
msgid "Fancy trees"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:902
|
||||
msgid "Smooth Lighting"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:904
|
||||
msgid "3D Clouds"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:906
|
||||
msgid "Opaque Water"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:909
|
||||
msgid "Mip-Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:911
|
||||
msgid "Anisotropic Filtering"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:913
|
||||
msgid "Bi-Linear Filtering"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:915
|
||||
msgid "Tri-Linear Filtering"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:918
|
||||
msgid "Shaders"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:920
|
||||
msgid "Preload item visuals"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:922
|
||||
msgid "Enable Particles"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:924
|
||||
msgid "Finite Liquid"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:927
|
||||
msgid "Change keys"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:940 src/keycode.cpp:248
|
||||
msgid "Play"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:942
|
||||
msgid "SINGLE PLAYER"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:955
|
||||
msgid "Select texture pack:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:956
|
||||
msgid "TEXTURE PACKS"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:976
|
||||
msgid "No information available"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1005
|
||||
msgid "Core Developers"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1020
|
||||
msgid "Active Contributors"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1028
|
||||
msgid "Previous Contributors"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1069
|
||||
msgid "Singleplayer"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1070
|
||||
msgid "Client"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1071
|
||||
msgid "Server"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1072
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1073
|
||||
msgid "Texture Packs"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1080
|
||||
msgid "Mods"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/mainmenu.lua:1082
|
||||
msgid "Credits"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:236
|
||||
msgid "MODS"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:237
|
||||
msgid "Installed Mods:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:243
|
||||
msgid "Add mod:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:244
|
||||
msgid "Local install"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:245
|
||||
msgid "Online mod repository"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:284
|
||||
msgid "No mod description available"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:288
|
||||
msgid "Mod information:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:299
|
||||
msgid "Rename"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:301
|
||||
msgid "Uninstall selected modpack"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:312
|
||||
msgid "Uninstall selected mod"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:324
|
||||
msgid "Rename Modpack:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:329 src/keycode.cpp:227
|
||||
msgid "Accept"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:423
|
||||
msgid "World:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:427 builtin/modmgr.lua:429
|
||||
msgid "Hide Game"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:433 builtin/modmgr.lua:435
|
||||
msgid "Hide mp content"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:442
|
||||
msgid "Mod:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:444
|
||||
msgid "Depends:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:447 src/guiKeyChangeMenu.cpp:187
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:464
|
||||
msgid "Enable MP"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:466
|
||||
msgid "Disable MP"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:470 builtin/modmgr.lua:472
|
||||
msgid "enabled"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:478
|
||||
msgid "Enable all"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:577
|
||||
msgid "Select Mod File:"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:616
|
||||
msgid "Install Mod: file: \"$1\""
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:617
|
||||
msgid ""
|
||||
"\n"
|
||||
"Install Mod: unsupported filetype \"$1\""
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:638
|
||||
msgid "Failed to install $1 to $2"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:641
|
||||
msgid "Install Mod: unable to find suitable foldername for modpack $1"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:661
|
||||
msgid "Install Mod: unable to find real modname for: $1"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:855
|
||||
msgid "Modmgr: failed to delete \"$1\""
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:859
|
||||
msgid "Modmgr: invalid modpath \"$1\""
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:876
|
||||
msgid "Are you sure you want to delete \"$1\"?"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modmgr.lua:878
|
||||
msgid "No of course not!"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modstore.lua:183
|
||||
msgid "Page $1 of $2"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modstore.lua:243
|
||||
msgid "Rating"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modstore.lua:251
|
||||
msgid "re-Install"
|
||||
msgstr ""
|
||||
|
||||
#: builtin/modstore.lua:253
|
||||
msgid "Install"
|
||||
msgstr ""
|
||||
|
||||
#: src/client.cpp:2917
|
||||
msgid "Item textures..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:940
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1000
|
||||
msgid "Creating server...."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1016
|
||||
msgid "Creating client..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1025
|
||||
msgid "Resolving address..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1122
|
||||
msgid "Connecting to server..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1219
|
||||
msgid "Item definitions..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1226
|
||||
msgid "Node definitions..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:1233
|
||||
msgid "Media..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:3409
|
||||
msgid "Shutting down stuff..."
|
||||
msgstr ""
|
||||
|
||||
#: src/game.cpp:3439
|
||||
msgid ""
|
||||
"\n"
|
||||
"Check debug.txt for details."
|
||||
msgstr ""
|
||||
|
||||
#: src/guiDeathScreen.cpp:96
|
||||
msgid "You died."
|
||||
msgstr ""
|
||||
|
||||
#: src/guiDeathScreen.cpp:104
|
||||
msgid "Respawn"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiFormSpecMenu.cpp:1656 src/guiMessageMenu.cpp:107
|
||||
#: src/guiTextInputMenu.cpp:139
|
||||
msgid "Proceed"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:121
|
||||
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:161
|
||||
msgid "\"Use\" = climb down"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:176
|
||||
msgid "Double tap \"jump\" to toggle fly"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:288
|
||||
msgid "Key already in use"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:363
|
||||
msgid "press key"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:389
|
||||
msgid "Forward"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:390
|
||||
msgid "Backward"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:391 src/keycode.cpp:228
|
||||
msgid "Left"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:392 src/keycode.cpp:228
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:393
|
||||
msgid "Use"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:394
|
||||
msgid "Jump"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:395
|
||||
msgid "Sneak"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:396
|
||||
msgid "Drop"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:397
|
||||
msgid "Inventory"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:398
|
||||
msgid "Chat"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:399
|
||||
msgid "Command"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:400
|
||||
msgid "Console"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:401
|
||||
msgid "Toggle fly"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:402
|
||||
msgid "Toggle fast"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:403
|
||||
msgid "Toggle noclip"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:404
|
||||
msgid "Range select"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiKeyChangeMenu.cpp:405
|
||||
msgid "Print stacks"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPasswordChange.cpp:106
|
||||
msgid "Old Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPasswordChange.cpp:122
|
||||
msgid "New Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPasswordChange.cpp:137
|
||||
msgid "Confirm Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPasswordChange.cpp:153
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPasswordChange.cpp:162
|
||||
msgid "Passwords do not match!"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPauseMenu.cpp:122
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPauseMenu.cpp:133
|
||||
msgid "Change Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPauseMenu.cpp:143
|
||||
msgid "Sound Volume"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPauseMenu.cpp:152
|
||||
msgid "Exit to Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPauseMenu.cpp:161
|
||||
msgid "Exit to OS"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiPauseMenu.cpp:170
|
||||
msgid ""
|
||||
"Default Controls:\n"
|
||||
"- WASD: move\n"
|
||||
"- Space: jump/climb\n"
|
||||
"- Shift: sneak/go down\n"
|
||||
"- Q: drop item\n"
|
||||
"- I: inventory\n"
|
||||
"- Mouse: turn/look\n"
|
||||
"- Mouse left: dig/punch\n"
|
||||
"- Mouse right: place/use\n"
|
||||
"- Mouse wheel: select item\n"
|
||||
"- T: chat\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/guiVolumeChange.cpp:107
|
||||
msgid "Sound Volume: "
|
||||
msgstr ""
|
||||
|
||||
#: src/guiVolumeChange.cpp:121
|
||||
msgid "Exit"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:223
|
||||
msgid "Left Button"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:223
|
||||
msgid "Middle Button"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:223
|
||||
msgid "Right Button"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:223
|
||||
msgid "X Button 1"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:224
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:224
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:224
|
||||
msgid "Return"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:224
|
||||
msgid "Tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:224
|
||||
msgid "X Button 2"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:225
|
||||
msgid "Capital"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:225
|
||||
msgid "Control"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:225
|
||||
msgid "Kana"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:225
|
||||
msgid "Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:225
|
||||
msgid "Pause"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:225
|
||||
msgid "Shift"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:226
|
||||
msgid "Convert"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:226
|
||||
msgid "Escape"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:226
|
||||
msgid "Final"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:226
|
||||
msgid "Junja"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:226
|
||||
msgid "Kanji"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:226
|
||||
msgid "Nonconvert"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:227
|
||||
msgid "End"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:227
|
||||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:227
|
||||
msgid "Mode Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:227
|
||||
msgid "Next"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:227
|
||||
msgid "Prior"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:227
|
||||
msgid "Space"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:228
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:228
|
||||
msgid "Execute"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:228
|
||||
msgid "Print"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:228
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:228
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:229
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:229
|
||||
msgid "Insert"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:229
|
||||
msgid "Snapshot"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:232
|
||||
msgid "Left Windows"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:233
|
||||
msgid "Apps"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:233
|
||||
msgid "Numpad 0"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:233
|
||||
msgid "Numpad 1"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:233
|
||||
msgid "Right Windows"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:233
|
||||
msgid "Sleep"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:234
|
||||
msgid "Numpad 2"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:234
|
||||
msgid "Numpad 3"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:234
|
||||
msgid "Numpad 4"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:234
|
||||
msgid "Numpad 5"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:234
|
||||
msgid "Numpad 6"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:234
|
||||
msgid "Numpad 7"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:235
|
||||
msgid "Numpad *"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:235
|
||||
msgid "Numpad +"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:235
|
||||
msgid "Numpad -"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:235
|
||||
msgid "Numpad /"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:235
|
||||
msgid "Numpad 8"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:235
|
||||
msgid "Numpad 9"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:239
|
||||
msgid "Num Lock"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:239
|
||||
msgid "Scroll Lock"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:240
|
||||
msgid "Left Shift"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:240
|
||||
msgid "Right Shift"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:241
|
||||
msgid "Left Control"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:241
|
||||
msgid "Left Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:241
|
||||
msgid "Right Control"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:241
|
||||
msgid "Right Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:243
|
||||
msgid "Comma"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:243
|
||||
msgid "Minus"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:243
|
||||
msgid "Period"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:243
|
||||
msgid "Plus"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:247
|
||||
msgid "Attn"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:247
|
||||
msgid "CrSel"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:248
|
||||
msgid "Erase OEF"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:248
|
||||
msgid "ExSel"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:248
|
||||
msgid "OEM Clear"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:248
|
||||
msgid "PA1"
|
||||
msgstr ""
|
||||
|
||||
#: src/keycode.cpp:248
|
||||
msgid "Zoom"
|
||||
msgstr ""
|
||||
|
||||
#: src/main.cpp:1472
|
||||
msgid "needs_fallback_font"
|
||||
msgstr ""
|
||||
|
||||
#: src/main.cpp:1547
|
||||
msgid "Main Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/main.cpp:1723
|
||||
msgid "No world selected and no address provided. Nothing to do."
|
||||
msgstr ""
|
||||
|
||||
#: src/main.cpp:1731
|
||||
msgid "Could not find or load game \""
|
||||
msgstr ""
|
||||
|
||||
#: src/main.cpp:1745
|
||||
msgid "Invalid gamespec."
|
||||
msgstr ""
|
||||
|
||||
#: src/main.cpp:1790
|
||||
msgid "Connection error (timed out?)"
|
||||
msgstr ""
|
||||
@@ -188,7 +188,7 @@ message (STATUS "LuaJIT library: ${LUA_LIBRARY}")
|
||||
message (STATUS "LuaJIT headers: ${LUA_INCLUDE_DIR}")
|
||||
|
||||
set(USE_LUAJIT 0)
|
||||
if(LUA_LIBRARY AND LUA_INCLUDE_DIR)
|
||||
if(LUA_LIBRARY AND LUA_INCLUDE_DIR)
|
||||
message (STATUS "LuaJIT found.")
|
||||
set(USE_LUAJIT 1)
|
||||
else(LUA_LIBRARY AND LUA_INCLUDE_DIR)
|
||||
@@ -307,6 +307,7 @@ set(common_SRCS
|
||||
pathfinder.cpp
|
||||
convert_json.cpp
|
||||
gettext.cpp
|
||||
httpfetch.cpp
|
||||
${JTHREAD_SRCS}
|
||||
${common_SCRIPT_SRCS}
|
||||
${UTIL_SRCS}
|
||||
@@ -359,6 +360,7 @@ set(minetest_SRCS
|
||||
guiDeathScreen.cpp
|
||||
guiChatConsole.cpp
|
||||
client.cpp
|
||||
clientmedia.cpp
|
||||
filecache.cpp
|
||||
tile.cpp
|
||||
shader.cpp
|
||||
@@ -500,7 +502,7 @@ if(MSVC)
|
||||
# Flags for C files (sqlite)
|
||||
# /MT = Link statically with standard library stuff
|
||||
set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MT")
|
||||
|
||||
|
||||
if(BUILD_SERVER)
|
||||
set_target_properties(${PROJECT_NAME}server PROPERTIES
|
||||
COMPILE_DEFINITIONS "SERVER")
|
||||
@@ -508,13 +510,13 @@ if(MSVC)
|
||||
|
||||
else()
|
||||
# Probably GCC
|
||||
|
||||
|
||||
if(WARN_ALL)
|
||||
set(RELEASE_WARNING_FLAGS "-Wall")
|
||||
else()
|
||||
set(RELEASE_WARNING_FLAGS "")
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT APPLE AND NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
CHECK_CXX_COMPILER_FLAG("-Wno-unused-but-set-variable" HAS_UNUSED_BUT_SET_VARIABLE_WARNING)
|
||||
if(HAS_UNUSED_BUT_SET_VARIABLE_WARNING)
|
||||
@@ -537,7 +539,7 @@ else()
|
||||
if(USE_GPROF)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
|
||||
endif()
|
||||
|
||||
|
||||
if(BUILD_SERVER)
|
||||
set_target_properties(${PROJECT_NAME}server PROPERTIES
|
||||
COMPILE_DEFINITIONS "SERVER")
|
||||
|
||||
@@ -31,7 +31,6 @@ BanManager::BanManager(const std::string &banfilepath):
|
||||
m_banfilepath(banfilepath),
|
||||
m_modified(false)
|
||||
{
|
||||
m_mutex.Init();
|
||||
try{
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -110,9 +110,21 @@ void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
|
||||
(float)(ps->next() % ar.Z) + 0.5
|
||||
);
|
||||
|
||||
int notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
|
||||
if (mg->gennotify & (1 << notifytype)) {
|
||||
std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
|
||||
nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
|
||||
}
|
||||
|
||||
// Generate some tunnel starting from orp
|
||||
for (u16 j = 0; j < tunnel_routepoints; j++)
|
||||
makeTunnel(j % dswitchint == 0);
|
||||
|
||||
notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
|
||||
if (mg->gennotify & (1 << notifytype)) {
|
||||
std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
|
||||
nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +248,9 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
|
||||
continue;
|
||||
|
||||
u32 i = vm->m_area.index(p);
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (!ndef->get(c).is_ground_content)
|
||||
continue;
|
||||
|
||||
if (large_cave) {
|
||||
int full_ymin = node_min.Y - MAP_BLOCKSIZE;
|
||||
@@ -250,7 +265,6 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
|
||||
}
|
||||
} else {
|
||||
// Don't replace air or water or lava or ignore
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (c == CONTENT_IGNORE || c == CONTENT_AIR ||
|
||||
c == c_water_source || c == c_lava_source)
|
||||
continue;
|
||||
@@ -345,9 +359,21 @@ void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
|
||||
(float)(ps->next() % ar.Z) + 0.5
|
||||
);
|
||||
|
||||
int notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
|
||||
if (mg->gennotify & (1 << notifytype)) {
|
||||
std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
|
||||
nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
|
||||
}
|
||||
|
||||
// Generate some tunnel starting from orp
|
||||
for (u16 j = 0; j < tunnel_routepoints; j++)
|
||||
makeTunnel(j % dswitchint == 0);
|
||||
|
||||
notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
|
||||
if (mg->gennotify & (1 << notifytype)) {
|
||||
std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
|
||||
nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -516,7 +542,8 @@ void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
|
||||
v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
|
||||
p += of;
|
||||
|
||||
if (!is_ravine && mg->heightmap && should_make_cave_hole) {
|
||||
if (!is_ravine && mg->heightmap && should_make_cave_hole &&
|
||||
p.X <= node_max.X && p.Z <= node_max.Z) {
|
||||
int maplen = node_max.X - node_min.X + 1;
|
||||
int idx = (p.Z - node_min.Z) * maplen + (p.X - node_min.X);
|
||||
if (p.Y >= mg->heightmap[idx] - 2)
|
||||
@@ -530,8 +557,8 @@ void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
|
||||
|
||||
// Don't replace air, water, lava, or ice
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (c == CONTENT_AIR || c == c_water_source ||
|
||||
c == c_lava_source || c == c_ice)
|
||||
if (!ndef->get(c).is_ground_content || c == CONTENT_AIR ||
|
||||
c == c_water_source || c == c_lava_source || c == c_ice)
|
||||
continue;
|
||||
|
||||
if (large_cave) {
|
||||
|
||||
@@ -199,7 +199,7 @@ void SGUITTGlyph::unload()
|
||||
|
||||
//////////////////////
|
||||
|
||||
CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
|
||||
CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency, const u32 shadow, const u32 shadow_alpha)
|
||||
{
|
||||
if (!c_libraryLoaded)
|
||||
{
|
||||
@@ -216,6 +216,9 @@ CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filen
|
||||
return 0;
|
||||
}
|
||||
|
||||
font->shadow_offset = shadow;
|
||||
font->shadow_alpha = shadow_alpha;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
@@ -625,6 +628,14 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
|
||||
CGUITTGlyphPage* page = n->getValue();
|
||||
|
||||
if (!use_transparency) color.color |= 0xff000000;
|
||||
|
||||
if (shadow_offset) {
|
||||
for (size_t i = 0; i < page->render_positions.size(); ++i)
|
||||
page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
|
||||
Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, video::SColor(shadow_alpha,0,0,0), true);
|
||||
for (size_t i = 0; i < page->render_positions.size(); ++i)
|
||||
page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
|
||||
}
|
||||
Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace gui
|
||||
//! \param antialias set the use_monochrome (opposite to antialias) flag
|
||||
//! \param transparency set the use_transparency flag
|
||||
//! \return Returns a pointer to a CGUITTFont. Will return 0 if the font failed to load.
|
||||
static CGUITTFont* createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
|
||||
static CGUITTFont* createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true, const u32 shadow = 0, const u32 shadow_alpha = 255);
|
||||
static CGUITTFont* createTTFont(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
|
||||
static CGUITTFont* create(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
|
||||
static CGUITTFont* create(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
|
||||
@@ -369,6 +369,8 @@ namespace gui
|
||||
s32 GlobalKerningWidth;
|
||||
s32 GlobalKerningHeight;
|
||||
core::ustring Invisible;
|
||||
u32 shadow_offset;
|
||||
u32 shadow_alpha;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
|
||||
352
src/client.cpp
352
src/client.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "client.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "clientserver.h"
|
||||
#include "jthread/jmutexautolock.h"
|
||||
#include "main.h"
|
||||
@@ -37,28 +38,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "itemdef.h"
|
||||
#include "shader.h"
|
||||
#include <IFileSystem.h>
|
||||
#include "sha1.h"
|
||||
#include "base64.h"
|
||||
#include "clientmap.h"
|
||||
#include "filecache.h"
|
||||
#include "clientmedia.h"
|
||||
#include "sound.h"
|
||||
#include "util/string.h"
|
||||
#include "hex.h"
|
||||
#include "IMeshCache.h"
|
||||
#include "serialization.h"
|
||||
#include "util/serialize.h"
|
||||
#include "config.h"
|
||||
#include "util/directiontables.h"
|
||||
#include "util/pointedthing.h"
|
||||
#include "version.h"
|
||||
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
static std::string getMediaCacheDir()
|
||||
{
|
||||
return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
|
||||
}
|
||||
|
||||
/*
|
||||
QueuedMeshUpdate
|
||||
*/
|
||||
@@ -82,7 +74,6 @@ QueuedMeshUpdate::~QueuedMeshUpdate()
|
||||
|
||||
MeshUpdateQueue::MeshUpdateQueue()
|
||||
{
|
||||
m_mutex.Init();
|
||||
}
|
||||
|
||||
MeshUpdateQueue::~MeshUpdateQueue()
|
||||
@@ -177,7 +168,7 @@ void * MeshUpdateThread::Thread()
|
||||
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
while(getRun())
|
||||
while(!StopRequested())
|
||||
{
|
||||
/*// Wait for output queue to flush.
|
||||
// Allow 2 in queue, this makes less frametime jitter.
|
||||
@@ -223,46 +214,9 @@ void * MeshUpdateThread::Thread()
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void * MediaFetchThread::Thread()
|
||||
{
|
||||
ThreadStarted();
|
||||
|
||||
log_register_thread("MediaFetchThread");
|
||||
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
#if USE_CURL
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
|
||||
i != m_file_requests.end(); ++i) {
|
||||
curl = curl_easy_init();
|
||||
assert(curl);
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
|
||||
std::ostringstream stream;
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, (std::string("Minetest ")+minetest_version_hash).c_str());
|
||||
res = curl_easy_perform(curl);
|
||||
if (res == CURLE_OK) {
|
||||
std::string data = stream.str();
|
||||
m_file_data.push_back(make_pair(i->name, data));
|
||||
} else {
|
||||
m_failed.push_back(*i);
|
||||
infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl;
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
#endif
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
Client
|
||||
*/
|
||||
|
||||
Client::Client(
|
||||
IrrlichtDevice *device,
|
||||
@@ -304,12 +258,9 @@ Client::Client(
|
||||
m_map_seed(0),
|
||||
m_password(password),
|
||||
m_access_denied(false),
|
||||
m_media_cache(getMediaCacheDir()),
|
||||
m_media_receive_started(false),
|
||||
m_media_count(0),
|
||||
m_media_received_count(0),
|
||||
m_itemdef_received(false),
|
||||
m_nodedef_received(false),
|
||||
m_media_downloader(new ClientMediaDownloader()),
|
||||
m_time_of_day_set(false),
|
||||
m_last_time_of_day_f(-1),
|
||||
m_time_of_day_update_timer(0),
|
||||
@@ -333,9 +284,6 @@ Client::Client(
|
||||
|
||||
m_env.addPlayer(player);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
|
||||
m_media_fetch_threads.push_back(new MediaFetchThread(this));
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
@@ -345,9 +293,8 @@ Client::~Client()
|
||||
m_con.Disconnect();
|
||||
}
|
||||
|
||||
m_mesh_update_thread.setRun(false);
|
||||
while(m_mesh_update_thread.IsRunning())
|
||||
sleep_ms(100);
|
||||
m_mesh_update_thread.Stop();
|
||||
m_mesh_update_thread.Wait();
|
||||
while(!m_mesh_update_thread.m_queue_out.empty()) {
|
||||
MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
|
||||
delete r.mesh;
|
||||
@@ -365,10 +312,6 @@ Client::~Client()
|
||||
}
|
||||
}
|
||||
|
||||
for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
|
||||
i != m_media_fetch_threads.end(); ++i)
|
||||
delete *i;
|
||||
|
||||
// cleanup 3d model meshes on client shutdown
|
||||
while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
|
||||
scene::IAnimatedMesh * mesh =
|
||||
@@ -474,7 +417,7 @@ void Client::step(float dtime)
|
||||
|
||||
core::list<v3s16> deleted_blocks;
|
||||
|
||||
float delete_unused_sectors_timeout =
|
||||
float delete_unused_sectors_timeout =
|
||||
g_settings->getFloat("client_delete_unused_sectors_timeout");
|
||||
|
||||
// Delete sector blocks
|
||||
@@ -798,57 +741,11 @@ void Client::step(float dtime)
|
||||
/*
|
||||
Load fetched media
|
||||
*/
|
||||
if (m_media_receive_started) {
|
||||
bool all_stopped = true;
|
||||
for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
|
||||
thread != m_media_fetch_threads.end(); ++thread) {
|
||||
all_stopped &= !(*thread)->IsRunning();
|
||||
while (!(*thread)->m_file_data.empty()) {
|
||||
std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
|
||||
if(m_media_received_count < m_media_count)
|
||||
m_media_received_count++;
|
||||
|
||||
bool success = loadMedia(out.second, out.first);
|
||||
if(success){
|
||||
verbosestream<<"Client: Loaded received media: "
|
||||
<<"\""<<out.first<<"\". Caching."<<std::endl;
|
||||
} else{
|
||||
infostream<<"Client: Failed to load received media: "
|
||||
<<"\""<<out.first<<"\". Not caching."<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool did = fs::CreateAllDirs(getMediaCacheDir());
|
||||
if(!did){
|
||||
errorstream<<"Could not create media cache directory"
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::map<std::string, std::string>::iterator n;
|
||||
n = m_media_name_sha1_map.find(out.first);
|
||||
if(n == m_media_name_sha1_map.end())
|
||||
errorstream<<"The server sent a file that has not "
|
||||
<<"been announced."<<std::endl;
|
||||
else
|
||||
m_media_cache.update_sha1(out.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (all_stopped) {
|
||||
std::list<MediaRequest> fetch_failed;
|
||||
for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
|
||||
thread != m_media_fetch_threads.end(); ++thread) {
|
||||
for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
|
||||
request != (*thread)->m_failed.end(); ++request)
|
||||
fetch_failed.push_back(*request);
|
||||
(*thread)->m_failed.clear();
|
||||
}
|
||||
if (fetch_failed.size() > 0) {
|
||||
infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
|
||||
<< "Requesting them the usual way." << std::endl;
|
||||
request_media(fetch_failed);
|
||||
}
|
||||
if (m_media_downloader && m_media_downloader->isStarted()) {
|
||||
m_media_downloader->step(this);
|
||||
if (m_media_downloader->isDone()) {
|
||||
delete m_media_downloader;
|
||||
m_media_downloader = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,15 +946,15 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
|
||||
string name
|
||||
}
|
||||
*/
|
||||
void Client::request_media(const std::list<MediaRequest> &file_requests)
|
||||
void Client::request_media(const std::list<std::string> &file_requests)
|
||||
{
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
writeU16(os, TOSERVER_REQUEST_MEDIA);
|
||||
writeU16(os, file_requests.size());
|
||||
|
||||
for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
|
||||
for(std::list<std::string>::const_iterator i = file_requests.begin();
|
||||
i != file_requests.end(); ++i) {
|
||||
os<<serializeString(i->name);
|
||||
os<<serializeString(*i);
|
||||
}
|
||||
|
||||
// Make data buffer
|
||||
@@ -1069,6 +966,19 @@ void Client::request_media(const std::list<MediaRequest> &file_requests)
|
||||
<<file_requests.size()<<" files)"<<std::endl;
|
||||
}
|
||||
|
||||
void Client::received_media()
|
||||
{
|
||||
// notify server we received everything
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
writeU16(os, TOSERVER_RECEIVED_MEDIA);
|
||||
std::string s = os.str();
|
||||
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
||||
// Send as reliable
|
||||
Send(0, data, true);
|
||||
infostream<<"Client: Notifying server that we received all media"
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
void Client::ReceiveAll()
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
@@ -1262,7 +1172,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
MapNode n;
|
||||
n.deSerialize(&data[8], ser_version);
|
||||
|
||||
addNode(p, n);
|
||||
bool remove_metadata = true;
|
||||
u32 index = 8 + MapNode::serializedLength(ser_version);
|
||||
if ((datasize >= index+1) && data[index]){
|
||||
remove_metadata = false;
|
||||
}
|
||||
|
||||
addNode(p, n, remove_metadata);
|
||||
}
|
||||
else if(command == TOCLIENT_BLOCKDATA)
|
||||
{
|
||||
@@ -1655,96 +1571,54 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
std::string datastring((char*)&data[2], datasize-2);
|
||||
std::istringstream is(datastring, std::ios_base::binary);
|
||||
|
||||
// Mesh update thread must be stopped while
|
||||
// updating content definitions
|
||||
assert(!m_mesh_update_thread.IsRunning());
|
||||
|
||||
int num_files = readU16(is);
|
||||
|
||||
infostream<<"Client: Received media announcement: packet size: "
|
||||
<<datasize<<std::endl;
|
||||
|
||||
std::list<MediaRequest> file_requests;
|
||||
if (m_media_downloader == NULL ||
|
||||
m_media_downloader->isStarted()) {
|
||||
const char *problem = m_media_downloader ?
|
||||
"we already saw another announcement" :
|
||||
"all media has been received already";
|
||||
errorstream<<"Client: Received media announcement but "
|
||||
<<problem<<"! "
|
||||
<<" files="<<num_files
|
||||
<<" size="<<datasize<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Mesh update thread must be stopped while
|
||||
// updating content definitions
|
||||
assert(!m_mesh_update_thread.IsRunning());
|
||||
|
||||
for(int i=0; i<num_files; i++)
|
||||
{
|
||||
//read file from cache
|
||||
std::string name = deSerializeString(is);
|
||||
std::string sha1_base64 = deSerializeString(is);
|
||||
|
||||
// if name contains illegal characters, ignore the file
|
||||
if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
|
||||
errorstream<<"Client: ignoring illegal file name "
|
||||
<<"sent by server: \""<<name<<"\""<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string sha1_raw = base64_decode(sha1_base64);
|
||||
std::string sha1_hex = hex_encode(sha1_raw);
|
||||
std::ostringstream tmp_os(std::ios_base::binary);
|
||||
bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
|
||||
m_media_name_sha1_map[name] = sha1_raw;
|
||||
|
||||
// If found in cache, try to load it from there
|
||||
if(found_in_cache)
|
||||
{
|
||||
bool success = loadMedia(tmp_os.str(), name);
|
||||
if(success){
|
||||
verbosestream<<"Client: Loaded cached media: "
|
||||
<<sha1_hex<<" \""<<name<<"\""<<std::endl;
|
||||
continue;
|
||||
} else{
|
||||
infostream<<"Client: Failed to load cached media: "
|
||||
<<sha1_hex<<" \""<<name<<"\""<<std::endl;
|
||||
}
|
||||
}
|
||||
// Didn't load from cache; queue it to be requested
|
||||
verbosestream<<"Client: Adding file to request list: \""
|
||||
<<sha1_hex<<" \""<<name<<"\""<<std::endl;
|
||||
file_requests.push_back(MediaRequest(name));
|
||||
m_media_downloader->addFile(name, sha1_raw);
|
||||
}
|
||||
|
||||
std::string remote_media = "";
|
||||
std::vector<std::string> remote_media;
|
||||
try {
|
||||
remote_media = deSerializeString(is);
|
||||
Strfnd sf(deSerializeString(is));
|
||||
while(!sf.atend()) {
|
||||
std::string baseurl = trim(sf.next(","));
|
||||
if(baseurl != "")
|
||||
m_media_downloader->addRemoteServer(baseurl);
|
||||
}
|
||||
}
|
||||
catch(SerializationError) {
|
||||
// not supported by server or turned off
|
||||
}
|
||||
|
||||
m_media_count = file_requests.size();
|
||||
m_media_receive_started = true;
|
||||
|
||||
if (remote_media == "" || !USE_CURL) {
|
||||
request_media(file_requests);
|
||||
} else {
|
||||
#if USE_CURL
|
||||
std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
|
||||
for(std::list<MediaRequest>::iterator i = file_requests.begin();
|
||||
i != file_requests.end(); ++i) {
|
||||
(*cur)->m_file_requests.push_back(*i);
|
||||
cur++;
|
||||
if (cur == m_media_fetch_threads.end())
|
||||
cur = m_media_fetch_threads.begin();
|
||||
}
|
||||
for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
|
||||
i != m_media_fetch_threads.end(); ++i) {
|
||||
(*i)->m_remote_url = remote_media;
|
||||
(*i)->Start();
|
||||
}
|
||||
#endif
|
||||
|
||||
// notify server we received everything
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
writeU16(os, TOSERVER_RECEIVED_MEDIA);
|
||||
std::string s = os.str();
|
||||
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
||||
// Send as reliable
|
||||
Send(0, data, true);
|
||||
m_media_downloader->step(this);
|
||||
if (m_media_downloader->isDone()) {
|
||||
// might be done already if all media is in the cache
|
||||
delete m_media_downloader;
|
||||
m_media_downloader = NULL;
|
||||
}
|
||||
ClientEvent event;
|
||||
event.type = CE_TEXTURES_UPDATED;
|
||||
m_client_event_queue.push_back(event);
|
||||
}
|
||||
else if(command == TOCLIENT_MEDIA)
|
||||
{
|
||||
@@ -1770,67 +1644,37 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
<<num_bunches<<" files="<<num_files
|
||||
<<" size="<<datasize<<std::endl;
|
||||
|
||||
// Check total and received media count
|
||||
assert(m_media_received_count <= m_media_count);
|
||||
if (num_files > m_media_count - m_media_received_count) {
|
||||
errorstream<<"Client: Received more files than requested:"
|
||||
<<" total count="<<m_media_count
|
||||
<<" total received="<<m_media_received_count
|
||||
if (num_files == 0)
|
||||
return;
|
||||
|
||||
if (m_media_downloader == NULL ||
|
||||
!m_media_downloader->isStarted()) {
|
||||
const char *problem = m_media_downloader ?
|
||||
"media has not been requested" :
|
||||
"all media has been received already";
|
||||
errorstream<<"Client: Received media but "
|
||||
<<problem<<"! "
|
||||
<<" bunch "<<bunch_i<<"/"<<num_bunches
|
||||
<<" files="<<num_files
|
||||
<<" size="<<datasize<<std::endl;
|
||||
num_files = m_media_count - m_media_received_count;
|
||||
}
|
||||
if (num_files == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Mesh update thread must be stopped while
|
||||
// updating content definitions
|
||||
assert(!m_mesh_update_thread.IsRunning());
|
||||
|
||||
for(u32 i=0; i<num_files; i++){
|
||||
assert(m_media_received_count < m_media_count);
|
||||
m_media_received_count++;
|
||||
std::string name = deSerializeString(is);
|
||||
std::string data = deSerializeLongString(is);
|
||||
|
||||
// if name contains illegal characters, ignore the file
|
||||
if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
|
||||
errorstream<<"Client: ignoring illegal file name "
|
||||
<<"sent by server: \""<<name<<"\""<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool success = loadMedia(data, name);
|
||||
if(success){
|
||||
verbosestream<<"Client: Loaded received media: "
|
||||
<<"\""<<name<<"\". Caching."<<std::endl;
|
||||
} else{
|
||||
infostream<<"Client: Failed to load received media: "
|
||||
<<"\""<<name<<"\". Not caching."<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool did = fs::CreateAllDirs(getMediaCacheDir());
|
||||
if(!did){
|
||||
errorstream<<"Could not create media cache directory"
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::map<std::string, std::string>::iterator n;
|
||||
n = m_media_name_sha1_map.find(name);
|
||||
if(n == m_media_name_sha1_map.end())
|
||||
errorstream<<"The server sent a file that has not "
|
||||
<<"been announced."<<std::endl;
|
||||
else
|
||||
m_media_cache.update_sha1(data);
|
||||
}
|
||||
m_media_downloader->conventionalTransferDone(
|
||||
name, data, this);
|
||||
}
|
||||
|
||||
ClientEvent event;
|
||||
event.type = CE_TEXTURES_UPDATED;
|
||||
m_client_event_queue.push_back(event);
|
||||
if (m_media_downloader->isDone()) {
|
||||
delete m_media_downloader;
|
||||
m_media_downloader = NULL;
|
||||
}
|
||||
}
|
||||
else if(command == TOCLIENT_TOOLDEF)
|
||||
{
|
||||
@@ -2120,7 +1964,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
m_client_event_queue.push_back(event);
|
||||
}
|
||||
else if(command == TOCLIENT_HUDCHANGE)
|
||||
{
|
||||
{
|
||||
std::string sdata;
|
||||
v2f v2fdata;
|
||||
u32 intdata = 0;
|
||||
@@ -2149,7 +1993,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
m_client_event_queue.push_back(event);
|
||||
}
|
||||
else if(command == TOCLIENT_HUD_SET_FLAGS)
|
||||
{
|
||||
{
|
||||
std::string datastring((char *)&data[2], datasize - 2);
|
||||
std::istringstream is(datastring, std::ios_base::binary);
|
||||
|
||||
@@ -2258,7 +2102,7 @@ void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
|
||||
Send(0, data, true);
|
||||
}
|
||||
|
||||
void Client::sendInventoryFields(const std::string &formname,
|
||||
void Client::sendInventoryFields(const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields)
|
||||
{
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
@@ -2462,7 +2306,7 @@ void Client::sendPlayerPos()
|
||||
writeV3S32(&data[2], position);
|
||||
writeV3S32(&data[2+12], speed);
|
||||
writeS32(&data[2+12+12], pitch);
|
||||
writeS32(&data[2+12+12+4], yaw);
|
||||
writeS32(&data[2+12+12+4], yaw);
|
||||
writeU32(&data[2+12+12+4+4], keyPressed);
|
||||
// Send as unreliable
|
||||
Send(0, data, false);
|
||||
@@ -2514,7 +2358,7 @@ void Client::removeNode(v3s16 p)
|
||||
}
|
||||
}
|
||||
|
||||
void Client::addNode(v3s16 p, MapNode n)
|
||||
void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
|
||||
{
|
||||
TimeTaker timer1("Client::addNode()");
|
||||
|
||||
@@ -2523,7 +2367,7 @@ void Client::addNode(v3s16 p, MapNode n)
|
||||
try
|
||||
{
|
||||
//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
|
||||
m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
|
||||
m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{}
|
||||
@@ -2880,6 +2724,14 @@ ClientEvent Client::getClientEvent()
|
||||
return m_client_event_queue.pop_front();
|
||||
}
|
||||
|
||||
float Client::mediaReceiveProgress()
|
||||
{
|
||||
if (m_media_downloader)
|
||||
return m_media_downloader->getProgress();
|
||||
else
|
||||
return 1.0; // downloader only exists when not yet done
|
||||
}
|
||||
|
||||
void draw_load_screen(const std::wstring &text,
|
||||
IrrlichtDevice* device, gui::IGUIFont* font,
|
||||
float dtime=0 ,int percent=0, bool clouds=true);
|
||||
@@ -2888,12 +2740,8 @@ void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
|
||||
infostream<<"Client::afterContentReceived() started"<<std::endl;
|
||||
assert(m_itemdef_received);
|
||||
assert(m_nodedef_received);
|
||||
assert(texturesReceived());
|
||||
assert(mediaReceived());
|
||||
|
||||
// remove the information about which checksum each texture
|
||||
// ought to have
|
||||
m_media_name_sha1_map.clear();
|
||||
|
||||
// Rebuild inherited images and recreate textures
|
||||
infostream<<"- Rebuilding images and textures"<<std::endl;
|
||||
m_tsrc->rebuildImagesAndTextures();
|
||||
|
||||
71
src/client.h
71
src/client.h
@@ -31,32 +31,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "clientobject.h"
|
||||
#include "gamedef.h"
|
||||
#include "inventorymanager.h"
|
||||
#include "filecache.h"
|
||||
#include "localplayer.h"
|
||||
#include "server.h"
|
||||
#include "hud.h"
|
||||
#include "particles.h"
|
||||
#include "util/pointedthing.h"
|
||||
#include <algorithm>
|
||||
|
||||
struct MeshMakeData;
|
||||
class MapBlockMesh;
|
||||
class IGameDef;
|
||||
class IWritableTextureSource;
|
||||
class IWritableShaderSource;
|
||||
class IWritableItemDefManager;
|
||||
class IWritableNodeDefManager;
|
||||
//class IWritableCraftDefManager;
|
||||
class ClientEnvironment;
|
||||
class ClientMediaDownloader;
|
||||
struct MapDrawControl;
|
||||
class MtEventManager;
|
||||
|
||||
class ClientNotReadyException : public BaseException
|
||||
{
|
||||
public:
|
||||
ClientNotReadyException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
};
|
||||
struct PointedThing;
|
||||
|
||||
struct QueuedMeshUpdate
|
||||
{
|
||||
@@ -114,7 +103,7 @@ struct MeshUpdateResult
|
||||
}
|
||||
};
|
||||
|
||||
class MeshUpdateThread : public SimpleThread
|
||||
class MeshUpdateThread : public JThread
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -132,31 +121,12 @@ class MeshUpdateThread : public SimpleThread
|
||||
IGameDef *m_gamedef;
|
||||
};
|
||||
|
||||
class MediaFetchThread : public SimpleThread
|
||||
{
|
||||
public:
|
||||
|
||||
MediaFetchThread(IGameDef *gamedef):
|
||||
m_gamedef(gamedef)
|
||||
{
|
||||
}
|
||||
|
||||
void * Thread();
|
||||
|
||||
std::list<MediaRequest> m_file_requests;
|
||||
MutexedQueue<std::pair<std::string, std::string> > m_file_data;
|
||||
std::list<MediaRequest> m_failed;
|
||||
std::string m_remote_url;
|
||||
IGameDef *m_gamedef;
|
||||
};
|
||||
|
||||
enum ClientEventType
|
||||
{
|
||||
CE_NONE,
|
||||
CE_PLAYER_DAMAGE,
|
||||
CE_PLAYER_FORCE_MOVE,
|
||||
CE_DEATHSCREEN,
|
||||
CE_TEXTURES_UPDATED,
|
||||
CE_SHOW_FORMSPEC,
|
||||
CE_SPAWN_PARTICLE,
|
||||
CE_ADD_PARTICLESPAWNER,
|
||||
@@ -365,7 +335,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
|
||||
// Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent)
|
||||
void removeNode(v3s16 p);
|
||||
void addNode(v3s16 p, MapNode n);
|
||||
void addNode(v3s16 p, MapNode n, bool remove_metadata = true);
|
||||
|
||||
void setPlayerControl(PlayerControl &control);
|
||||
|
||||
@@ -426,19 +396,15 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
std::wstring accessDeniedReason()
|
||||
{ return m_access_denied_reason; }
|
||||
|
||||
float mediaReceiveProgress()
|
||||
{
|
||||
if (!m_media_receive_started) return 0;
|
||||
return 1.0 * m_media_received_count / m_media_count;
|
||||
}
|
||||
|
||||
bool texturesReceived()
|
||||
{ return m_media_receive_started && m_media_received_count == m_media_count; }
|
||||
bool itemdefReceived()
|
||||
{ return m_itemdef_received; }
|
||||
bool nodedefReceived()
|
||||
{ return m_nodedef_received; }
|
||||
|
||||
bool mediaReceived()
|
||||
{ return m_media_downloader == NULL; }
|
||||
|
||||
float mediaReceiveProgress();
|
||||
|
||||
void afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font);
|
||||
|
||||
float getRTT(void);
|
||||
@@ -455,12 +421,15 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
virtual bool checkLocalPrivilege(const std::string &priv)
|
||||
{ return checkPrivilege(priv); }
|
||||
|
||||
private:
|
||||
|
||||
// The following set of functions is used by ClientMediaDownloader
|
||||
// Insert a media file appropriately into the appropriate manager
|
||||
bool loadMedia(const std::string &data, const std::string &filename);
|
||||
// Send a request for conventional media transfer
|
||||
void request_media(const std::list<std::string> &file_requests);
|
||||
// Send a notification that no conventional media transfer is needed
|
||||
void received_media();
|
||||
|
||||
void request_media(const std::list<MediaRequest> &file_requests);
|
||||
private:
|
||||
|
||||
// Virtual methods from con::PeerHandler
|
||||
void peerAdded(con::Peer *peer);
|
||||
@@ -488,7 +457,6 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
MtEventManager *m_event;
|
||||
|
||||
MeshUpdateThread m_mesh_update_thread;
|
||||
std::list<MediaFetchThread*> m_media_fetch_threads;
|
||||
ClientEnvironment m_env;
|
||||
con::Connection m_con;
|
||||
IrrlichtDevice *m_device;
|
||||
@@ -514,14 +482,9 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
bool m_access_denied;
|
||||
std::wstring m_access_denied_reason;
|
||||
Queue<ClientEvent> m_client_event_queue;
|
||||
FileCache m_media_cache;
|
||||
// Mapping from media file name to SHA1 checksum
|
||||
std::map<std::string, std::string> m_media_name_sha1_map;
|
||||
bool m_media_receive_started;
|
||||
u32 m_media_count;
|
||||
u32 m_media_received_count;
|
||||
bool m_itemdef_received;
|
||||
bool m_nodedef_received;
|
||||
ClientMediaDownloader *m_media_downloader;
|
||||
|
||||
// time_of_day speed approximation for old protocol
|
||||
bool m_time_of_day_set;
|
||||
|
||||
@@ -50,9 +50,6 @@ ClientMap::ClientMap(
|
||||
m_camera_direction(0,0,1),
|
||||
m_camera_fov(M_PI)
|
||||
{
|
||||
m_camera_mutex.Init();
|
||||
assert(m_camera_mutex.IsInitialized());
|
||||
|
||||
m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
|
||||
BS*1000000,BS*1000000,BS*1000000);
|
||||
}
|
||||
|
||||
656
src/clientmedia.cpp
Normal file
656
src/clientmedia.cpp
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "clientmedia.h"
|
||||
#include "httpfetch.h"
|
||||
#include "client.h"
|
||||
#include "clientserver.h"
|
||||
#include "filecache.h"
|
||||
#include "hex.h"
|
||||
#include "sha1.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "porting.h"
|
||||
#include "settings.h"
|
||||
#include "main.h"
|
||||
#include "util/serialize.h"
|
||||
#include "util/string.h"
|
||||
|
||||
static std::string getMediaCacheDir()
|
||||
{
|
||||
return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
|
||||
}
|
||||
|
||||
/*
|
||||
ClientMediaDownloader
|
||||
*/
|
||||
|
||||
ClientMediaDownloader::ClientMediaDownloader():
|
||||
m_media_cache(getMediaCacheDir())
|
||||
{
|
||||
m_initial_step_done = false;
|
||||
m_name_bound = ""; // works because "" is an invalid file name
|
||||
m_uncached_count = 0;
|
||||
m_uncached_received_count = 0;
|
||||
m_httpfetch_caller = HTTPFETCH_DISCARD;
|
||||
m_httpfetch_active = 0;
|
||||
m_httpfetch_active_limit = 0;
|
||||
m_httpfetch_next_id = 0;
|
||||
m_httpfetch_timeout = 0;
|
||||
m_outstanding_hash_sets = 0;
|
||||
}
|
||||
|
||||
ClientMediaDownloader::~ClientMediaDownloader()
|
||||
{
|
||||
if (m_httpfetch_caller != HTTPFETCH_DISCARD)
|
||||
httpfetch_caller_free(m_httpfetch_caller);
|
||||
|
||||
for (std::map<std::string, FileStatus*>::iterator it = m_files.begin();
|
||||
it != m_files.end(); ++it)
|
||||
delete it->second;
|
||||
|
||||
for (u32 i = 0; i < m_remotes.size(); ++i)
|
||||
delete m_remotes[i];
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::addFile(std::string name, std::string sha1)
|
||||
{
|
||||
assert(!m_initial_step_done);
|
||||
|
||||
// if name was already announced, ignore the new announcement
|
||||
if (m_files.count(name) != 0) {
|
||||
errorstream << "Client: ignoring duplicate media announcement "
|
||||
<< "sent by server: \"" << name << "\""
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// if name is empty or contains illegal characters, ignore the file
|
||||
if (name.empty() || !string_allowed(name, TEXTURENAME_ALLOWED_CHARS)) {
|
||||
errorstream << "Client: ignoring illegal file name "
|
||||
<< "sent by server: \"" << name << "\""
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// length of sha1 must be exactly 20 (160 bits), else ignore the file
|
||||
if (sha1.size() != 20) {
|
||||
errorstream << "Client: ignoring illegal SHA1 sent by server: "
|
||||
<< hex_encode(sha1) << " \"" << name << "\""
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
FileStatus *filestatus = new FileStatus;
|
||||
filestatus->received = false;
|
||||
filestatus->sha1 = sha1;
|
||||
filestatus->current_remote = -1;
|
||||
m_files.insert(std::make_pair(name, filestatus));
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::addRemoteServer(std::string baseurl)
|
||||
{
|
||||
assert(!m_initial_step_done);
|
||||
|
||||
#ifdef USE_CURL
|
||||
|
||||
infostream << "Client: Adding remote server \""
|
||||
<< baseurl << "\" for media download" << std::endl;
|
||||
|
||||
RemoteServerStatus *remote = new RemoteServerStatus;
|
||||
remote->baseurl = baseurl;
|
||||
remote->active_count = 0;
|
||||
remote->request_by_filename = false;
|
||||
m_remotes.push_back(remote);
|
||||
|
||||
#else
|
||||
|
||||
infostream << "Client: Ignoring remote server \""
|
||||
<< baseurl << "\" because cURL support is not compiled in"
|
||||
<< std::endl;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::step(Client *client)
|
||||
{
|
||||
if (!m_initial_step_done) {
|
||||
initialStep(client);
|
||||
m_initial_step_done = true;
|
||||
}
|
||||
|
||||
// Remote media: check for completion of fetches
|
||||
if (m_httpfetch_active) {
|
||||
bool fetched_something = false;
|
||||
HTTPFetchResult fetchresult;
|
||||
|
||||
while (httpfetch_async_get(m_httpfetch_caller, fetchresult)) {
|
||||
m_httpfetch_active--;
|
||||
fetched_something = true;
|
||||
|
||||
// Is this a hashset (index.mth) or a media file?
|
||||
if (fetchresult.request_id < m_remotes.size())
|
||||
remoteHashSetReceived(fetchresult);
|
||||
else
|
||||
remoteMediaReceived(fetchresult, client);
|
||||
}
|
||||
|
||||
if (fetched_something)
|
||||
startRemoteMediaTransfers();
|
||||
|
||||
// Did all remote transfers end and no new ones can be started?
|
||||
// If so, request still missing files from the minetest server
|
||||
// (Or report that we have all files.)
|
||||
if (m_httpfetch_active == 0) {
|
||||
if (m_uncached_received_count < m_uncached_count) {
|
||||
infostream << "Client: Failed to remote-fetch "
|
||||
<< (m_uncached_count-m_uncached_received_count)
|
||||
<< " files. Requesting them"
|
||||
<< " the usual way." << std::endl;
|
||||
}
|
||||
startConventionalTransfers(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::initialStep(Client *client)
|
||||
{
|
||||
// Check media cache
|
||||
m_uncached_count = m_files.size();
|
||||
for (std::map<std::string, FileStatus*>::iterator
|
||||
it = m_files.begin();
|
||||
it != m_files.end(); ++it) {
|
||||
std::string name = it->first;
|
||||
FileStatus *filestatus = it->second;
|
||||
const std::string &sha1 = filestatus->sha1;
|
||||
|
||||
std::ostringstream tmp_os(std::ios_base::binary);
|
||||
bool found_in_cache = m_media_cache.load(hex_encode(sha1), tmp_os);
|
||||
|
||||
// If found in cache, try to load it from there
|
||||
if (found_in_cache) {
|
||||
bool success = checkAndLoad(name, sha1,
|
||||
tmp_os.str(), true, client);
|
||||
if (success) {
|
||||
filestatus->received = true;
|
||||
m_uncached_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(m_uncached_received_count == 0);
|
||||
|
||||
// Create the media cache dir if we are likely to write to it
|
||||
if (m_uncached_count != 0) {
|
||||
bool did = fs::CreateAllDirs(getMediaCacheDir());
|
||||
if (!did) {
|
||||
errorstream << "Client: "
|
||||
<< "Could not create media cache directory: "
|
||||
<< getMediaCacheDir()
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found all files in the cache, report this fact to the server.
|
||||
// If the server reported no remote servers, immediately start
|
||||
// conventional transfers. Note: if cURL support is not compiled in,
|
||||
// m_remotes is always empty, so "!USE_CURL" is redundant but may
|
||||
// reduce the size of the compiled code
|
||||
if (!USE_CURL || m_uncached_count == 0 || m_remotes.empty()) {
|
||||
startConventionalTransfers(client);
|
||||
}
|
||||
else {
|
||||
// Otherwise start off by requesting each server's sha1 set
|
||||
|
||||
// This is the first time we use httpfetch, so alloc a caller ID
|
||||
m_httpfetch_caller = httpfetch_caller_alloc();
|
||||
m_httpfetch_timeout = g_settings->getS32("curl_timeout");
|
||||
|
||||
// Set the active fetch limit to curl_parallel_limit or 84,
|
||||
// whichever is greater. This gives us some leeway so that
|
||||
// inefficiencies in communicating with the httpfetch thread
|
||||
// don't slow down fetches too much. (We still want some limit
|
||||
// so that when the first remote server returns its hash set,
|
||||
// not all files are requested from that server immediately.)
|
||||
// One such inefficiency is that ClientMediaDownloader::step()
|
||||
// is only called a couple times per second, while httpfetch
|
||||
// might return responses much faster than that.
|
||||
// Note that httpfetch strictly enforces curl_parallel_limit
|
||||
// but at no inter-thread communication cost. This however
|
||||
// doesn't help with the aforementioned inefficiencies.
|
||||
// The signifance of 84 is that it is 2*6*9 in base 13.
|
||||
m_httpfetch_active_limit = g_settings->getS32("curl_parallel_limit");
|
||||
m_httpfetch_active_limit = MYMAX(m_httpfetch_active_limit, 84);
|
||||
|
||||
// Write a list of hashes that we need. This will be POSTed
|
||||
// to the server using Content-Type: application/octet-stream
|
||||
std::string required_hash_set = serializeRequiredHashSet();
|
||||
|
||||
// minor fixme: this loop ignores m_httpfetch_active_limit
|
||||
|
||||
// another minor fixme, unlikely to matter in normal usage:
|
||||
// these index.mth fetches do (however) count against
|
||||
// m_httpfetch_active_limit when starting actual media file
|
||||
// requests, so if there are lots of remote servers that are
|
||||
// not responding, those will stall new media file transfers.
|
||||
|
||||
for (u32 i = 0; i < m_remotes.size(); ++i) {
|
||||
assert(m_httpfetch_next_id == i);
|
||||
|
||||
RemoteServerStatus *remote = m_remotes[i];
|
||||
actionstream << "Client: Contacting remote server \""
|
||||
<< remote->baseurl << "\"" << std::endl;
|
||||
|
||||
HTTPFetchRequest fetchrequest;
|
||||
fetchrequest.url =
|
||||
remote->baseurl + MTHASHSET_FILE_NAME;
|
||||
fetchrequest.caller = m_httpfetch_caller;
|
||||
fetchrequest.request_id = m_httpfetch_next_id; // == i
|
||||
fetchrequest.timeout = m_httpfetch_timeout;
|
||||
fetchrequest.connect_timeout = m_httpfetch_timeout;
|
||||
fetchrequest.post_fields = required_hash_set;
|
||||
fetchrequest.extra_headers.push_back(
|
||||
"Content-Type: application/octet-stream");
|
||||
httpfetch_async(fetchrequest);
|
||||
|
||||
m_httpfetch_active++;
|
||||
m_httpfetch_next_id++;
|
||||
m_outstanding_hash_sets++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::remoteHashSetReceived(
|
||||
const HTTPFetchResult &fetchresult)
|
||||
{
|
||||
u32 remote_id = fetchresult.request_id;
|
||||
assert(remote_id < m_remotes.size());
|
||||
RemoteServerStatus *remote = m_remotes[remote_id];
|
||||
|
||||
m_outstanding_hash_sets--;
|
||||
|
||||
if (fetchresult.succeeded) {
|
||||
try {
|
||||
// Server sent a list of file hashes that are
|
||||
// available on it, try to parse the list
|
||||
|
||||
std::set<std::string> sha1_set;
|
||||
deSerializeHashSet(fetchresult.data, sha1_set);
|
||||
|
||||
// Parsing succeeded: For every file that is
|
||||
// available on this server, add this server
|
||||
// to the available_remotes array
|
||||
|
||||
for(std::map<std::string, FileStatus*>::iterator
|
||||
it = m_files.upper_bound(m_name_bound);
|
||||
it != m_files.end(); ++it) {
|
||||
FileStatus *f = it->second;
|
||||
if (!f->received && sha1_set.count(f->sha1))
|
||||
f->available_remotes.push_back(remote_id);
|
||||
}
|
||||
}
|
||||
catch (SerializationError &e) {
|
||||
infostream << "Client: Remote server \""
|
||||
<< remote->baseurl << "\" sent invalid hash set: "
|
||||
<< e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// For compatibility: If index.mth is not found, assume that the
|
||||
// server contains files named like the original files (not their sha1)
|
||||
|
||||
if (!fetchresult.succeeded && !fetchresult.timeout &&
|
||||
fetchresult.response_code == 404) {
|
||||
infostream << "Client: Enabling compatibility mode for remote "
|
||||
<< "server \"" << remote->baseurl << "\"" << std::endl;
|
||||
remote->request_by_filename = true;
|
||||
|
||||
// Assume every file is available on this server
|
||||
|
||||
for(std::map<std::string, FileStatus*>::iterator
|
||||
it = m_files.upper_bound(m_name_bound);
|
||||
it != m_files.end(); ++it) {
|
||||
FileStatus *f = it->second;
|
||||
if (!f->received)
|
||||
f->available_remotes.push_back(remote_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::remoteMediaReceived(
|
||||
const HTTPFetchResult &fetchresult,
|
||||
Client *client)
|
||||
{
|
||||
// Some remote server sent us a file.
|
||||
// -> decrement number of active fetches
|
||||
// -> mark file as received if fetch succeeded
|
||||
// -> try to load media
|
||||
|
||||
std::string name;
|
||||
{
|
||||
std::map<unsigned long, std::string>::iterator it =
|
||||
m_remote_file_transfers.find(fetchresult.request_id);
|
||||
assert(it != m_remote_file_transfers.end());
|
||||
name = it->second;
|
||||
m_remote_file_transfers.erase(it);
|
||||
}
|
||||
|
||||
assert(m_files.count(name) != 0);
|
||||
|
||||
FileStatus *filestatus = m_files[name];
|
||||
assert(!filestatus->received);
|
||||
assert(filestatus->current_remote >= 0);
|
||||
|
||||
RemoteServerStatus *remote = m_remotes[filestatus->current_remote];
|
||||
|
||||
filestatus->current_remote = -1;
|
||||
remote->active_count--;
|
||||
|
||||
// If fetch succeeded, try to load media file
|
||||
|
||||
if (fetchresult.succeeded) {
|
||||
bool success = checkAndLoad(name, filestatus->sha1,
|
||||
fetchresult.data, false, client);
|
||||
if (success) {
|
||||
filestatus->received = true;
|
||||
assert(m_uncached_received_count < m_uncached_count);
|
||||
m_uncached_received_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 ClientMediaDownloader::selectRemoteServer(FileStatus *filestatus)
|
||||
{
|
||||
assert(filestatus != NULL);
|
||||
assert(!filestatus->received);
|
||||
assert(filestatus->current_remote < 0);
|
||||
|
||||
if (filestatus->available_remotes.empty())
|
||||
return -1;
|
||||
else {
|
||||
// Of all servers that claim to provide the file (and haven't
|
||||
// been unsuccessfully tried before), find the one with the
|
||||
// smallest number of currently active transfers
|
||||
|
||||
s32 best = 0;
|
||||
s32 best_remote_id = filestatus->available_remotes[best];
|
||||
s32 best_active_count = m_remotes[best_remote_id]->active_count;
|
||||
|
||||
for (u32 i = 1; i < filestatus->available_remotes.size(); ++i) {
|
||||
s32 remote_id = filestatus->available_remotes[i];
|
||||
s32 active_count = m_remotes[remote_id]->active_count;
|
||||
if (active_count < best_active_count) {
|
||||
best = i;
|
||||
best_remote_id = remote_id;
|
||||
best_active_count = active_count;
|
||||
}
|
||||
}
|
||||
|
||||
filestatus->available_remotes.erase(
|
||||
filestatus->available_remotes.begin() + best);
|
||||
|
||||
return best_remote_id;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::startRemoteMediaTransfers()
|
||||
{
|
||||
bool changing_name_bound = true;
|
||||
|
||||
for (std::map<std::string, FileStatus*>::iterator
|
||||
files_iter = m_files.upper_bound(m_name_bound);
|
||||
files_iter != m_files.end(); ++files_iter) {
|
||||
|
||||
// Abort if active fetch limit is exceeded
|
||||
if (m_httpfetch_active >= m_httpfetch_active_limit)
|
||||
break;
|
||||
|
||||
const std::string &name = files_iter->first;
|
||||
FileStatus *filestatus = files_iter->second;
|
||||
|
||||
if (!filestatus->received && filestatus->current_remote < 0) {
|
||||
// File has not been received yet and is not currently
|
||||
// being transferred. Choose a server for it.
|
||||
s32 remote_id = selectRemoteServer(filestatus);
|
||||
if (remote_id >= 0) {
|
||||
// Found a server, so start fetching
|
||||
RemoteServerStatus *remote =
|
||||
m_remotes[remote_id];
|
||||
|
||||
std::string url = remote->baseurl +
|
||||
(remote->request_by_filename ? name :
|
||||
hex_encode(filestatus->sha1));
|
||||
verbosestream << "Client: "
|
||||
<< "Requesting remote media file "
|
||||
<< "\"" << name << "\" "
|
||||
<< "\"" << url << "\"" << std::endl;
|
||||
|
||||
HTTPFetchRequest fetchrequest;
|
||||
fetchrequest.url = url;
|
||||
fetchrequest.caller = m_httpfetch_caller;
|
||||
fetchrequest.request_id = m_httpfetch_next_id;
|
||||
fetchrequest.timeout = 0; // no data timeout!
|
||||
fetchrequest.connect_timeout =
|
||||
m_httpfetch_timeout;
|
||||
httpfetch_async(fetchrequest);
|
||||
|
||||
m_remote_file_transfers.insert(std::make_pair(
|
||||
m_httpfetch_next_id,
|
||||
name));
|
||||
|
||||
filestatus->current_remote = remote_id;
|
||||
remote->active_count++;
|
||||
m_httpfetch_active++;
|
||||
m_httpfetch_next_id++;
|
||||
}
|
||||
}
|
||||
|
||||
if (filestatus->received ||
|
||||
(filestatus->current_remote < 0 &&
|
||||
!m_outstanding_hash_sets)) {
|
||||
// If we arrive here, we conclusively know that we
|
||||
// won't fetch this file from a remote server in the
|
||||
// future. So update the name bound if possible.
|
||||
if (changing_name_bound)
|
||||
m_name_bound = name;
|
||||
}
|
||||
else
|
||||
changing_name_bound = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::startConventionalTransfers(Client *client)
|
||||
{
|
||||
assert(m_httpfetch_active == 0);
|
||||
|
||||
if (m_uncached_received_count == m_uncached_count) {
|
||||
// In this case all media was found in the cache or
|
||||
// has been downloaded from some remote server;
|
||||
// report this fact to the server
|
||||
client->received_media();
|
||||
}
|
||||
else {
|
||||
// Some media files have not been received yet, use the
|
||||
// conventional slow method (minetest protocol) to get them
|
||||
std::list<std::string> file_requests;
|
||||
for (std::map<std::string, FileStatus*>::iterator
|
||||
it = m_files.begin();
|
||||
it != m_files.end(); ++it) {
|
||||
if (!it->second->received)
|
||||
file_requests.push_back(it->first);
|
||||
}
|
||||
assert((s32) file_requests.size() ==
|
||||
m_uncached_count - m_uncached_received_count);
|
||||
client->request_media(file_requests);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::conventionalTransferDone(
|
||||
const std::string &name,
|
||||
const std::string &data,
|
||||
Client *client)
|
||||
{
|
||||
// Check that file was announced
|
||||
std::map<std::string, FileStatus*>::iterator
|
||||
file_iter = m_files.find(name);
|
||||
if (file_iter == m_files.end()) {
|
||||
errorstream << "Client: server sent media file that was"
|
||||
<< "not announced, ignoring it: \"" << name << "\""
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
FileStatus *filestatus = file_iter->second;
|
||||
assert(filestatus != NULL);
|
||||
|
||||
// Check that file hasn't already been received
|
||||
if (filestatus->received) {
|
||||
errorstream << "Client: server sent media file that we already"
|
||||
<< "received, ignoring it: \"" << name << "\""
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark file as received, regardless of whether loading it works and
|
||||
// whether the checksum matches (because at this point there is no
|
||||
// other server that could send a replacement)
|
||||
filestatus->received = true;
|
||||
assert(m_uncached_received_count < m_uncached_count);
|
||||
m_uncached_received_count++;
|
||||
|
||||
// Check that received file matches announced checksum
|
||||
// If so, load it
|
||||
checkAndLoad(name, filestatus->sha1, data, false, client);
|
||||
}
|
||||
|
||||
bool ClientMediaDownloader::checkAndLoad(
|
||||
const std::string &name, const std::string &sha1,
|
||||
const std::string &data, bool is_from_cache, Client *client)
|
||||
{
|
||||
const char *cached_or_received = is_from_cache ? "cached" : "received";
|
||||
const char *cached_or_received_uc = is_from_cache ? "Cached" : "Received";
|
||||
std::string sha1_hex = hex_encode(sha1);
|
||||
|
||||
// Compute actual checksum of data
|
||||
std::string data_sha1;
|
||||
{
|
||||
SHA1 data_sha1_calculator;
|
||||
data_sha1_calculator.addBytes(data.c_str(), data.size());
|
||||
unsigned char *data_tmpdigest = data_sha1_calculator.getDigest();
|
||||
data_sha1.assign((char*) data_tmpdigest, 20);
|
||||
free(data_tmpdigest);
|
||||
}
|
||||
|
||||
// Check that received file matches announced checksum
|
||||
if (data_sha1 != sha1) {
|
||||
std::string data_sha1_hex = hex_encode(data_sha1);
|
||||
infostream << "Client: "
|
||||
<< cached_or_received_uc << " media file "
|
||||
<< sha1_hex << " \"" << name << "\" "
|
||||
<< "mismatches actual checksum " << data_sha1_hex
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checksum is ok, try loading the file
|
||||
bool success = client->loadMedia(data, name);
|
||||
if (!success) {
|
||||
infostream << "Client: "
|
||||
<< "Failed to load " << cached_or_received << " media: "
|
||||
<< sha1_hex << " \"" << name << "\""
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
verbosestream << "Client: "
|
||||
<< "Loaded " << cached_or_received << " media: "
|
||||
<< sha1_hex << " \"" << name << "\""
|
||||
<< std::endl;
|
||||
|
||||
// Update cache (unless we just loaded the file from the cache)
|
||||
if (!is_from_cache)
|
||||
m_media_cache.update(sha1_hex, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Minetest Hashset File Format
|
||||
|
||||
All values are stored in big-endian byte order.
|
||||
[u32] signature: 'MTHS'
|
||||
[u16] version: 1
|
||||
For each hash in set:
|
||||
[u8*20] SHA1 hash
|
||||
|
||||
Version changes:
|
||||
1 - Initial version
|
||||
*/
|
||||
|
||||
std::string ClientMediaDownloader::serializeRequiredHashSet()
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
|
||||
writeU32(os, MTHASHSET_FILE_SIGNATURE); // signature
|
||||
writeU16(os, 1); // version
|
||||
|
||||
// Write list of hashes of files that have not been
|
||||
// received (found in cache) yet
|
||||
for (std::map<std::string, FileStatus*>::iterator
|
||||
it = m_files.begin();
|
||||
it != m_files.end(); ++it) {
|
||||
if (!it->second->received) {
|
||||
assert(it->second->sha1.size() == 20);
|
||||
os << it->second->sha1;
|
||||
}
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void ClientMediaDownloader::deSerializeHashSet(const std::string &data,
|
||||
std::set<std::string> &result)
|
||||
{
|
||||
if (data.size() < 6 || data.size() % 20 != 6) {
|
||||
throw SerializationError(
|
||||
"ClientMediaDownloader::deSerializeHashSet: "
|
||||
"invalid hash set file size");
|
||||
}
|
||||
|
||||
const u8 *data_cstr = (const u8*) data.c_str();
|
||||
|
||||
u32 signature = readU32(&data_cstr[0]);
|
||||
if (signature != MTHASHSET_FILE_SIGNATURE) {
|
||||
throw SerializationError(
|
||||
"ClientMediaDownloader::deSerializeHashSet: "
|
||||
"invalid hash set file signature");
|
||||
}
|
||||
|
||||
u16 version = readU16(&data_cstr[4]);
|
||||
if (version != 1) {
|
||||
throw SerializationError(
|
||||
"ClientMediaDownloader::deSerializeHashSet: "
|
||||
"unsupported hash set file version");
|
||||
}
|
||||
|
||||
for (u32 pos = 6; pos < data.size(); pos += 20) {
|
||||
result.insert(data.substr(pos, 20));
|
||||
}
|
||||
}
|
||||
150
src/clientmedia.h
Normal file
150
src/clientmedia.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef CLIENTMEDIA_HEADER
|
||||
#define CLIENTMEDIA_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "filecache.h"
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class Client;
|
||||
struct HTTPFetchResult;
|
||||
|
||||
#define MTHASHSET_FILE_SIGNATURE 0x4d544853 // 'MTHS'
|
||||
#define MTHASHSET_FILE_NAME "index.mth"
|
||||
|
||||
class ClientMediaDownloader
|
||||
{
|
||||
public:
|
||||
ClientMediaDownloader();
|
||||
~ClientMediaDownloader();
|
||||
|
||||
float getProgress() const {
|
||||
if (m_uncached_count >= 1)
|
||||
return 1.0 * m_uncached_received_count /
|
||||
m_uncached_count;
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bool isStarted() const {
|
||||
return m_initial_step_done;
|
||||
}
|
||||
|
||||
// If this returns true, the downloader is done and can be deleted
|
||||
bool isDone() const {
|
||||
return m_initial_step_done &&
|
||||
m_uncached_received_count == m_uncached_count;
|
||||
}
|
||||
|
||||
// Add a file to the list of required file (but don't fetch it yet)
|
||||
void addFile(std::string name, std::string sha1);
|
||||
|
||||
// Add a remote server to the list; ignored if not built with cURL
|
||||
void addRemoteServer(std::string baseurl);
|
||||
|
||||
// Steps the media downloader:
|
||||
// - May load media into client by calling client->loadMedia()
|
||||
// - May check media cache for files
|
||||
// - May add files to media cache
|
||||
// - May start remote transfers by calling httpfetch_async
|
||||
// - May check for completion of current remote transfers
|
||||
// - May start conventional transfers by calling client->request_media()
|
||||
// - May inform server that all media has been loaded
|
||||
// by calling client->received_media()
|
||||
// After step has been called once, don't call addFile/addRemoteServer.
|
||||
void step(Client *client);
|
||||
|
||||
// Must be called for each file received through TOCLIENT_MEDIA
|
||||
void conventionalTransferDone(
|
||||
const std::string &name,
|
||||
const std::string &data,
|
||||
Client *client);
|
||||
|
||||
private:
|
||||
struct FileStatus {
|
||||
bool received;
|
||||
std::string sha1;
|
||||
s32 current_remote;
|
||||
std::vector<s32> available_remotes;
|
||||
};
|
||||
|
||||
struct RemoteServerStatus {
|
||||
std::string baseurl;
|
||||
s32 active_count;
|
||||
bool request_by_filename;
|
||||
};
|
||||
|
||||
void initialStep(Client *client);
|
||||
void remoteHashSetReceived(const HTTPFetchResult &fetchresult);
|
||||
void remoteMediaReceived(const HTTPFetchResult &fetchresult,
|
||||
Client *client);
|
||||
s32 selectRemoteServer(FileStatus *filestatus);
|
||||
void startRemoteMediaTransfers();
|
||||
void startConventionalTransfers(Client *client);
|
||||
|
||||
bool checkAndLoad(const std::string &name, const std::string &sha1,
|
||||
const std::string &data, bool is_from_cache,
|
||||
Client *client);
|
||||
|
||||
std::string serializeRequiredHashSet();
|
||||
static void deSerializeHashSet(const std::string &data,
|
||||
std::set<std::string> &result);
|
||||
|
||||
// Maps filename to file status
|
||||
std::map<std::string, FileStatus*> m_files;
|
||||
|
||||
// Array of remote media servers
|
||||
std::vector<RemoteServerStatus*> m_remotes;
|
||||
|
||||
// Filesystem-based media cache
|
||||
FileCache m_media_cache;
|
||||
|
||||
// Has an attempt been made to load media files from the file cache?
|
||||
// Have hash sets been requested from remote servers?
|
||||
bool m_initial_step_done;
|
||||
|
||||
// Total number of media files to load
|
||||
s32 m_uncached_count;
|
||||
|
||||
// Number of media files that have been received
|
||||
s32 m_uncached_received_count;
|
||||
|
||||
// Status of remote transfers
|
||||
unsigned long m_httpfetch_caller;
|
||||
unsigned long m_httpfetch_next_id;
|
||||
long m_httpfetch_timeout;
|
||||
s32 m_httpfetch_active;
|
||||
s32 m_httpfetch_active_limit;
|
||||
s32 m_outstanding_hash_sets;
|
||||
std::map<unsigned long, std::string> m_remote_file_transfers;
|
||||
|
||||
// All files up to this name have either been received from a
|
||||
// remote server or failed on all remote servers, so those files
|
||||
// don't need to be looked at again
|
||||
// (use m_files.upper_bound(m_name_bound) to get an iterator)
|
||||
std::string m_name_bound;
|
||||
|
||||
};
|
||||
|
||||
#endif // !CLIENTMEDIA_HEADER
|
||||
@@ -102,7 +102,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
added to object properties
|
||||
*/
|
||||
|
||||
#define LATEST_PROTOCOL_VERSION 21
|
||||
#define LATEST_PROTOCOL_VERSION 22
|
||||
|
||||
// Server's supported network protocol range
|
||||
#define SERVER_PROTOCOL_VERSION_MIN 13
|
||||
@@ -139,6 +139,12 @@ enum ToClientCommand
|
||||
|
||||
TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
|
||||
TOCLIENT_ADDNODE = 0x21,
|
||||
/*
|
||||
u16 command
|
||||
v3s16 position
|
||||
serialized mapnode
|
||||
u8 keep_metadata // Added in protocol version 22
|
||||
*/
|
||||
TOCLIENT_REMOVENODE = 0x22,
|
||||
|
||||
TOCLIENT_PLAYERPOS = 0x23, // Obsolete
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define CMAKE_USE_FREETYPE @USE_FREETYPE@
|
||||
#define CMAKE_STATIC_SHAREDIR "@SHAREDIR@"
|
||||
#define CMAKE_USE_LEVELDB @USE_LEVELDB@
|
||||
#define CMAKE_USE_LUAJIT @USE_LUAJIT@
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define CMAKE_BUILD_TYPE "Release"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define USE_FREETYPE 0
|
||||
#define STATIC_SHAREDIR ""
|
||||
#define USE_LEVELDB 0
|
||||
#define USE_LUAJIT 0
|
||||
|
||||
#ifdef USE_CMAKE_CONFIG_H
|
||||
#include "cmake_config.h"
|
||||
@@ -33,6 +34,8 @@
|
||||
#define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
|
||||
#undef USE_LEVELDB
|
||||
#define USE_LEVELDB CMAKE_USE_LEVELDB
|
||||
#undef USE_LUAJIT
|
||||
#define USE_LUAJIT CMAKE_USE_LUAJIT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -556,7 +556,7 @@ Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout,
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
stop();
|
||||
Stop();
|
||||
// Delete peers
|
||||
for(std::map<u16, Peer*>::iterator
|
||||
j = m_peers.begin();
|
||||
@@ -578,7 +578,7 @@ void * Connection::Thread()
|
||||
u32 curtime = porting::getTimeMs();
|
||||
u32 lasttime = curtime;
|
||||
|
||||
while(getRun())
|
||||
while(!StopRequested())
|
||||
{
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
|
||||
@@ -450,11 +450,11 @@ struct ConnectionEvent
|
||||
return "CONNEVENT_NONE";
|
||||
case CONNEVENT_DATA_RECEIVED:
|
||||
return "CONNEVENT_DATA_RECEIVED";
|
||||
case CONNEVENT_PEER_ADDED:
|
||||
case CONNEVENT_PEER_ADDED:
|
||||
return "CONNEVENT_PEER_ADDED";
|
||||
case CONNEVENT_PEER_REMOVED:
|
||||
case CONNEVENT_PEER_REMOVED:
|
||||
return "CONNEVENT_PEER_REMOVED";
|
||||
case CONNEVENT_BIND_FAILED:
|
||||
case CONNEVENT_BIND_FAILED:
|
||||
return "CONNEVENT_BIND_FAILED";
|
||||
}
|
||||
return "Invalid ConnectionEvent";
|
||||
@@ -544,7 +544,7 @@ struct ConnectionCommand
|
||||
}
|
||||
};
|
||||
|
||||
class Connection: public SimpleThread
|
||||
class Connection: public JThread
|
||||
{
|
||||
public:
|
||||
Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6);
|
||||
|
||||
@@ -1649,6 +1649,8 @@ class GenericCAO : public ClientActiveObject
|
||||
m_acceleration = readV3F1000(is);
|
||||
if(fabs(m_prop.automatic_rotate) < 0.001)
|
||||
m_yaw = readF1000(is);
|
||||
else
|
||||
readF1000(is);
|
||||
bool do_interpolate = readU8(is);
|
||||
bool is_end_position = readU8(is);
|
||||
float update_interval = readF1000(is);
|
||||
@@ -1693,12 +1695,18 @@ class GenericCAO : public ClientActiveObject
|
||||
float override_speed = readF1000(is);
|
||||
float override_jump = readF1000(is);
|
||||
float override_gravity = readF1000(is);
|
||||
// these are sent inverted so we get true when the server sends nothing
|
||||
bool sneak = !readU8(is);
|
||||
bool sneak_glitch = !readU8(is);
|
||||
|
||||
if(m_is_local_player)
|
||||
{
|
||||
LocalPlayer *player = m_env->getLocalPlayer();
|
||||
player->physics_override_speed = override_speed;
|
||||
player->physics_override_jump = override_jump;
|
||||
player->physics_override_gravity = override_gravity;
|
||||
player->physics_override_sneak = sneak;
|
||||
player->physics_override_sneak_glitch = sneak_glitch;
|
||||
}
|
||||
}
|
||||
else if(cmd == GENERIC_CMD_SET_ANIMATION)
|
||||
|
||||
@@ -969,6 +969,8 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
|
||||
m_physics_override_speed(1),
|
||||
m_physics_override_jump(1),
|
||||
m_physics_override_gravity(1),
|
||||
m_physics_override_sneak(true),
|
||||
m_physics_override_sneak_glitch(true),
|
||||
m_physics_override_sent(false)
|
||||
{
|
||||
assert(m_player);
|
||||
@@ -1060,7 +1062,9 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
|
||||
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
|
||||
}
|
||||
os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
|
||||
os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity)); // 5
|
||||
os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
|
||||
m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
|
||||
m_physics_override_sneak_glitch)); // 5
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1187,7 +1191,9 @@ void PlayerSAO::step(float dtime, bool send_recommended)
|
||||
|
||||
if(m_physics_override_sent == false){
|
||||
m_physics_override_sent = true;
|
||||
std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity);
|
||||
std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
|
||||
m_physics_override_jump, m_physics_override_gravity,
|
||||
m_physics_override_sneak, m_physics_override_sneak_glitch);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
|
||||
@@ -330,6 +330,8 @@ class PlayerSAO : public ServerActiveObject
|
||||
float m_physics_override_speed;
|
||||
float m_physics_override_jump;
|
||||
float m_physics_override_gravity;
|
||||
bool m_physics_override_sneak;
|
||||
bool m_physics_override_sneak_glitch;
|
||||
bool m_physics_override_sent;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,58 +28,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "main.h" // for g_settings
|
||||
#include "settings.h"
|
||||
#include "version.h"
|
||||
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
#endif
|
||||
#include "httpfetch.h"
|
||||
|
||||
Json::Value fetchJsonValue(const std::string url,
|
||||
struct curl_slist *chunk) {
|
||||
#if USE_CURL
|
||||
std::string liststring;
|
||||
CURL *curl;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
CURLcode res;
|
||||
HTTPFetchRequest fetchrequest;
|
||||
HTTPFetchResult fetchresult;
|
||||
fetchrequest.url = url;
|
||||
fetchrequest.useragent = std::string("Minetest ")+minetest_version_hash;
|
||||
fetchrequest.timeout = g_settings->getS32("curl_timeout");
|
||||
fetchrequest.caller = HTTPFETCH_SYNC;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, g_settings->getS32("curl_timeout"));
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, g_settings->getS32("curl_timeout"));
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, (std::string("Minetest ")+minetest_version_hash).c_str());
|
||||
|
||||
if (chunk != 0)
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
errorstream<<"Jsonreader: "<< url <<" not found (" << curl_easy_strerror(res) << ")" <<std::endl;
|
||||
curl_easy_cleanup(curl);
|
||||
struct curl_slist* runptr = chunk;
|
||||
while(runptr) {
|
||||
fetchrequest.extra_headers.push_back(runptr->data);
|
||||
runptr = runptr->next;
|
||||
}
|
||||
httpfetch_sync(fetchrequest,fetchresult);
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
std::istringstream stream(liststring);
|
||||
if (!liststring.size()) {
|
||||
if (!fetchresult.succeeded) {
|
||||
return Json::Value();
|
||||
}
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
std::istringstream stream(fetchresult.data);
|
||||
|
||||
if (!reader.parse( stream, root ) )
|
||||
{
|
||||
errorstream << "URL: " << url << std::endl;
|
||||
errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
|
||||
errorstream << "data: \"" << liststring << "\"" << std::endl;
|
||||
errorstream << "data: \"" << fetchresult.data << "\"" << std::endl;
|
||||
return Json::Value();
|
||||
}
|
||||
|
||||
@@ -210,7 +190,7 @@ ModStoreModDetails readModStoreModDetails(Json::Value& details) {
|
||||
}
|
||||
|
||||
if (retval.versions.size() < 1) {
|
||||
errorstream << "readModStoreModDetails: not a single version specified!" << std::endl;
|
||||
infostream << "readModStoreModDetails: not a single version specified!" << std::endl;
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ MapBlock* Database_LevelDB::loadBlock(v3s16 blockpos)
|
||||
std::string datastr;
|
||||
leveldb::Status s = m_database->Get(leveldb::ReadOptions(),
|
||||
i64tos(getBlockAsInteger(blockpos)), &datastr);
|
||||
if (datastr.length() == 0) {
|
||||
if (datastr.length() == 0 && s.ok()) {
|
||||
errorstream << "Blank block data in database (datastr.length() == 0) ("
|
||||
<< blockpos.X << "," << blockpos.Y << "," << blockpos.Z << ")" << std::endl;
|
||||
|
||||
|
||||
@@ -206,7 +206,6 @@ JMutex g_debug_stacks_mutex;
|
||||
|
||||
void debug_stacks_init()
|
||||
{
|
||||
g_debug_stacks_mutex.Init();
|
||||
}
|
||||
|
||||
void debug_stacks_print_to(std::ostream &os)
|
||||
|
||||
@@ -25,11 +25,10 @@ void set_default_settings(Settings *settings)
|
||||
{
|
||||
// Client and server
|
||||
|
||||
settings->setDefault("port", "");
|
||||
settings->setDefault("name", "");
|
||||
|
||||
// Client stuff
|
||||
|
||||
settings->setDefault("remote_port", "30000");
|
||||
settings->setDefault("keymap_forward", "KEY_KEY_W");
|
||||
settings->setDefault("keymap_backward", "KEY_KEY_S");
|
||||
settings->setDefault("keymap_left", "KEY_KEY_A");
|
||||
@@ -60,6 +59,7 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("aux1_descends", "false");
|
||||
settings->setDefault("doubletap_jump", "false");
|
||||
settings->setDefault("always_fly_fast", "true");
|
||||
settings->setDefault("directional_colored_fog", "true");
|
||||
|
||||
// Some (temporary) keys for debugging
|
||||
settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
|
||||
@@ -127,11 +127,21 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("trilinear_filter", "false");
|
||||
settings->setDefault("preload_item_visuals", "true");
|
||||
settings->setDefault("enable_bumpmapping", "false");
|
||||
settings->setDefault("enable_parallax_occlusion", "false");
|
||||
settings->setDefault("parallax_occlusion_scale", "0.08");
|
||||
settings->setDefault("parallax_occlusion_bias", "0.04");
|
||||
settings->setDefault("enable_waving_water", "false");
|
||||
settings->setDefault("water_wave_height", "1.0");
|
||||
settings->setDefault("water_wave_length", "20.0");
|
||||
settings->setDefault("water_wave_speed", "5.0");
|
||||
settings->setDefault("enable_waving_leaves", "false");
|
||||
settings->setDefault("enable_waving_plants", "false");
|
||||
settings->setDefault("enable_shaders", "true");
|
||||
settings->setDefault("repeat_rightclick_time", "0.25");
|
||||
settings->setDefault("enable_particles", "true");
|
||||
|
||||
settings->setDefault("media_fetch_threads", "8");
|
||||
settings->setDefault("curl_timeout", "5000");
|
||||
settings->setDefault("curl_parallel_limit", "8");
|
||||
|
||||
settings->setDefault("serverlist_url", "servers.minetest.net");
|
||||
settings->setDefault("serverlist_file", "favoriteservers.txt");
|
||||
@@ -145,10 +155,14 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("freetype", "true");
|
||||
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf"));
|
||||
settings->setDefault("font_size", "13");
|
||||
settings->setDefault("font_shadow", "1");
|
||||
settings->setDefault("font_shadow_alpha", "128");
|
||||
settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "liberationmono.ttf"));
|
||||
settings->setDefault("mono_font_size", "13");
|
||||
settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "DroidSansFallbackFull.ttf"));
|
||||
settings->setDefault("fallback_font_size", "13");
|
||||
settings->setDefault("fallback_font_shadow", "1");
|
||||
settings->setDefault("fallback_font_shadow_alpha", "128");
|
||||
#else
|
||||
settings->setDefault("freetype", "false");
|
||||
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "fontlucida.png"));
|
||||
@@ -157,6 +171,7 @@ void set_default_settings(Settings *settings)
|
||||
|
||||
// Server stuff
|
||||
// "map-dir" doesn't exist by default.
|
||||
settings->setDefault("port", "30000");
|
||||
settings->setDefault("default_game", "minetest");
|
||||
settings->setDefault("motd", "");
|
||||
settings->setDefault("max_users", "15");
|
||||
@@ -173,6 +188,8 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("disallow_empty_password", "false");
|
||||
settings->setDefault("disable_anticheat", "false");
|
||||
settings->setDefault("enable_rollback_recording", "false");
|
||||
settings->setDefault("cache_block_before_spawn", "true");
|
||||
settings->setDefault("max_spawn_height", "50");
|
||||
|
||||
settings->setDefault("profiler_print_interval", "0");
|
||||
settings->setDefault("enable_mapgen_debug_info", "false");
|
||||
@@ -252,7 +269,7 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("mgv7_np_terrain_alt", "4, 25, (600, 600, 600), 5934, 5, 0.6");
|
||||
settings->setDefault("mgv7_np_terrain_persist", "0.6, 0.1, (500, 500, 500), 539, 3, 0.6");
|
||||
settings->setDefault("mgv7_np_height_select", "-0.5, 1, (250, 250, 250), 4213, 5, 0.69");
|
||||
settings->setDefault("mgv7_np_filler_depth", "0, 1.2, (150, 150, 150), 261, 4, 0.7");
|
||||
settings->setDefault("mgv7_np_filler_depth", "0, 1.2, (150, 150, 150), 261, 4, 0.7");
|
||||
settings->setDefault("mgv7_np_mount_height", "100, 30, (500, 500, 500), 72449, 4, 0.6");
|
||||
settings->setDefault("mgv7_np_ridge_uwater", "0, 1, (500, 500, 500), 85039, 4, 0.6");
|
||||
settings->setDefault("mgv7_np_mountain", "0, 1, (250, 350, 250), 5333, 5, 0.68");
|
||||
@@ -270,7 +287,6 @@ void set_default_settings(Settings *settings)
|
||||
|
||||
settings->setDefault("mgmath_generator", "mandelbox");
|
||||
|
||||
settings->setDefault("curl_timeout", "5000");
|
||||
|
||||
// IPv6
|
||||
settings->setDefault("enable_ipv6", "true");
|
||||
|
||||
@@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "settings.h" // For g_settings
|
||||
#include "main.h" // For g_profiler
|
||||
|
||||
//#define DGEN_USE_TORCHES
|
||||
|
||||
NoiseParams nparams_dungeon_rarity =
|
||||
{0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8};
|
||||
NoiseParams nparams_dungeon_wetness =
|
||||
@@ -40,54 +42,58 @@ NoiseParams nparams_dungeon_density =
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
DungeonGen::DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel) {
|
||||
this->ndef = ndef;
|
||||
this->mapseed = seed;
|
||||
this->water_level = waterlevel;
|
||||
DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) {
|
||||
this->mg = mapgen;
|
||||
this->vm = mapgen->vm;
|
||||
|
||||
#ifdef DGEN_USE_TORCHES
|
||||
c_torch = ndef->getId("default:torch");
|
||||
#endif
|
||||
|
||||
np_rarity = &nparams_dungeon_rarity;
|
||||
np_wetness = &nparams_dungeon_wetness;
|
||||
np_density = &nparams_dungeon_density;
|
||||
/*
|
||||
cid_water_source = ndef->getId("mapgen_water_source");
|
||||
cid_cobble = ndef->getId("mapgen_cobble");
|
||||
cid_mossycobble = ndef->getId("mapgen_mossycobble");
|
||||
cid_torch = ndef->getId("default:torch");
|
||||
*/
|
||||
if (dparams) {
|
||||
memcpy(&dp, dparams, sizeof(dp));
|
||||
} else {
|
||||
dp.c_water = mg->ndef->getId("mapgen_water_source");
|
||||
dp.c_cobble = mg->ndef->getId("mapgen_cobble");
|
||||
dp.c_moss = mg->ndef->getId("mapgen_mossycobble");
|
||||
dp.c_stair = mg->ndef->getId("mapgen_stair_cobble");
|
||||
|
||||
dp.diagonal_dirs = false;
|
||||
dp.mossratio = 3.0;
|
||||
dp.holesize = v3s16(1, 2, 1);
|
||||
dp.roomsize = v3s16(0,0,0);
|
||||
dp.notifytype = GENNOTIFY_DUNGEON;
|
||||
|
||||
dp.np_rarity = nparams_dungeon_rarity;
|
||||
dp.np_wetness = nparams_dungeon_wetness;
|
||||
dp.np_density = nparams_dungeon_density;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
|
||||
v3s16 nmin, v3s16 nmax) {
|
||||
void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) {
|
||||
//TimeTaker t("gen dungeons");
|
||||
int approx_groundlevel = 10 + water_level;
|
||||
int approx_groundlevel = 10 + mg->water_level;
|
||||
|
||||
if ((nmin.Y + nmax.Y) / 2 >= approx_groundlevel ||
|
||||
NoisePerlin3D(np_rarity, nmin.X, nmin.Y, nmin.Z, mapseed) < 0.2)
|
||||
NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, mg->seed) < 0.2)
|
||||
return;
|
||||
|
||||
this->vmanip = vm;
|
||||
|
||||
this->blockseed = bseed;
|
||||
random.seed(bseed + 2);
|
||||
|
||||
cid_water_source = ndef->getId("mapgen_water_source");
|
||||
cid_cobble = ndef->getId("mapgen_cobble");
|
||||
cid_mossycobble = ndef->getId("mapgen_mossycobble");
|
||||
//cid_torch = ndef->getId("default:torch");
|
||||
cid_cobblestair = ndef->getId("mapgen_stair_cobble");
|
||||
|
||||
// Dungeon generator doesn't modify places which have this set
|
||||
vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
|
||||
vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
|
||||
|
||||
// Set all air and water to be untouchable to make dungeons open
|
||||
// to caves and open air
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++) {
|
||||
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
|
||||
u32 i = vmanip->m_area.index(nmin.X, y, z);
|
||||
u32 i = vm->m_area.index(nmin.X, y, z);
|
||||
for (s16 x = nmin.X; x <= nmax.X; x++) {
|
||||
content_t c = vmanip->m_data[i].getContent();
|
||||
if (c == CONTENT_AIR || c == cid_water_source)
|
||||
vmanip->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (c == CONTENT_AIR || c == dp.c_water)
|
||||
vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -95,17 +101,18 @@ void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
|
||||
|
||||
// Add it
|
||||
makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE);
|
||||
|
||||
|
||||
// Convert some cobble to mossy cobble
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++) {
|
||||
if (dp.mossratio != 0.0) {
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
|
||||
u32 i = vmanip->m_area.index(nmin.X, y, z);
|
||||
u32 i = vm->m_area.index(nmin.X, y, z);
|
||||
for (s16 x = nmin.X; x <= nmax.X; x++) {
|
||||
if (vmanip->m_data[i].getContent() == cid_cobble) {
|
||||
float wetness = NoisePerlin3D(np_wetness, x, y, z, mapseed);
|
||||
float density = NoisePerlin3D(np_density, x, y, z, blockseed);
|
||||
if (density < wetness / 3.0)
|
||||
vmanip->m_data[i].setContent(cid_mossycobble);
|
||||
if (vm->m_data[i].getContent() == dp.c_cobble) {
|
||||
float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, mg->seed);
|
||||
float density = NoisePerlin3D(&dp.np_density, x, y, z, blockseed);
|
||||
if (density < wetness / dp.mossratio)
|
||||
vm->m_data[i].setContent(dp.c_moss);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -118,7 +125,7 @@ void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
|
||||
|
||||
void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
{
|
||||
v3s16 areasize = vmanip->m_area.getExtent();
|
||||
v3s16 areasize = vm->m_area.getExtent();
|
||||
v3s16 roomsize;
|
||||
v3s16 roomplace;
|
||||
|
||||
@@ -126,16 +133,17 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
Find place for first room
|
||||
*/
|
||||
bool fits = false;
|
||||
for (u32 i = 0; i < 100; i++)
|
||||
for (u32 i = 0; i < 100 && !fits; i++)
|
||||
{
|
||||
bool is_large_room = ((random.next() & 3) == 1);
|
||||
roomsize = is_large_room ?
|
||||
v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) :
|
||||
v3s16(random.range(4, 8),random.range(4, 6),random.range(4, 8));
|
||||
|
||||
roomsize += dp.roomsize;
|
||||
|
||||
// start_padding is used to disallow starting the generation of
|
||||
// a dungeon in a neighboring generation chunk
|
||||
roomplace = vmanip->m_area.MinEdge + start_padding + v3s16(
|
||||
roomplace = vm->m_area.MinEdge + start_padding + v3s16(
|
||||
random.range(0,areasize.X-roomsize.X-1-start_padding.X),
|
||||
random.range(0,areasize.Y-roomsize.Y-1-start_padding.Y),
|
||||
random.range(0,areasize.Z-roomsize.Z-1-start_padding.Z));
|
||||
@@ -150,20 +158,13 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
for (s16 x = 1; x < roomsize.X - 1; x++)
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, z);
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
|
||||
{
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
if (vmanip->m_data[vi].getContent() == CONTENT_IGNORE)
|
||||
{
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) ||
|
||||
vm->m_data[vi].getContent() == CONTENT_IGNORE) {
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fits)
|
||||
break;
|
||||
}
|
||||
// No place found
|
||||
if (fits == false)
|
||||
@@ -183,9 +184,15 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
makeRoom(roomsize, roomplace);
|
||||
|
||||
v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
|
||||
if (mg->gennotify & (1 << dp.notifytype)) {
|
||||
std::vector <v3s16> *nvec = mg->gen_notifications[dp.notifytype];
|
||||
nvec->push_back(room_center);
|
||||
}
|
||||
|
||||
#ifdef DGEN_USE_TORCHES
|
||||
// Place torch at room center (for testing)
|
||||
//vmanip->m_data[vmanip->m_area.index(room_center)] = MapNode(cid_torch);
|
||||
vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch);
|
||||
#endif
|
||||
|
||||
// Quit if last room
|
||||
if (i == room_count - 1)
|
||||
@@ -197,12 +204,9 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
|
||||
v3s16 walker_start_place;
|
||||
|
||||
if(start_in_last_room)
|
||||
{
|
||||
if (start_in_last_room) {
|
||||
walker_start_place = last_room_center;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
walker_start_place = room_center;
|
||||
// Store center of current room as the last one
|
||||
last_room_center = room_center;
|
||||
@@ -213,8 +217,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
v3s16 doordir;
|
||||
|
||||
m_pos = walker_start_place;
|
||||
bool r = findPlaceForDoor(doorplace, doordir);
|
||||
if (r == false)
|
||||
if (!findPlaceForDoor(doorplace, doordir))
|
||||
return;
|
||||
|
||||
if (random.range(0,1) == 0)
|
||||
@@ -231,10 +234,11 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
|
||||
// Find a place for a random sized room
|
||||
roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
|
||||
roomsize += dp.roomsize;
|
||||
|
||||
m_pos = corridor_end;
|
||||
m_dir = corridor_end_dir;
|
||||
r = findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
|
||||
if (r == false)
|
||||
if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
|
||||
return;
|
||||
|
||||
if (random.range(0,1) == 0)
|
||||
@@ -250,7 +254,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
|
||||
void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
|
||||
{
|
||||
MapNode n_cobble(cid_cobble);
|
||||
MapNode n_cobble(dp.c_cobble);
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
|
||||
// Make +-X walls
|
||||
@@ -259,21 +263,21 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
|
||||
{
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(0, y, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vmanip->m_data[vi] = n_cobble;
|
||||
vm->m_data[vi] = n_cobble;
|
||||
}
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vmanip->m_data[vi] = n_cobble;
|
||||
vm->m_data[vi] = n_cobble;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,21 +287,21 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
|
||||
{
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, 0);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vmanip->m_data[vi] = n_cobble;
|
||||
vm->m_data[vi] = n_cobble;
|
||||
}
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vmanip->m_data[vi] = n_cobble;
|
||||
vm->m_data[vi] = n_cobble;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,21 +311,21 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
|
||||
{
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, 0, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vmanip->m_data[vi] = n_cobble;
|
||||
vm->m_data[vi] = n_cobble;
|
||||
}
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vmanip->m_data[vi] = n_cobble;
|
||||
vm->m_data[vi] = n_cobble;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,11 +335,11 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
|
||||
for (s16 x = 1; x < roomsize.X - 1; x++)
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
vmanip->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
|
||||
vmanip->m_data[vi] = n_air;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
|
||||
vm->m_data[vi] = n_air;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,29 +352,32 @@ void DungeonGen::makeFill(v3s16 place, v3s16 size,
|
||||
for (s16 x = 0; x < size.X; x++)
|
||||
{
|
||||
v3s16 p = place + v3s16(x, y, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
continue;
|
||||
u32 vi = vmanip->m_area.index(p);
|
||||
if (vmanip->m_flags[vi] & avoid_flags)
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & avoid_flags)
|
||||
continue;
|
||||
vmanip->m_flags[vi] |= or_flags;
|
||||
vmanip->m_data[vi] = n;
|
||||
vm->m_flags[vi] |= or_flags;
|
||||
vm->m_data[vi] = n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeHole(v3s16 place)
|
||||
{
|
||||
makeFill(place, v3s16(1, 2, 1), 0, MapNode(CONTENT_AIR),
|
||||
VMANIP_FLAG_DUNGEON_INSIDE);
|
||||
makeFill(place, dp.holesize, 0,
|
||||
MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
|
||||
{
|
||||
makeHole(doorplace);
|
||||
|
||||
#ifdef DGEN_USE_TORCHES
|
||||
// Place torch (for testing)
|
||||
//vmanip->m_data[vmanip->m_area.index(doorplace)] = MapNode(cid_torch);
|
||||
vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -398,33 +405,35 @@ void DungeonGen::makeCorridor(v3s16 doorplace,
|
||||
if (partcount != 0)
|
||||
p.Y += make_stairs;
|
||||
|
||||
if (vmanip->m_area.contains(p) == true &&
|
||||
vmanip->m_area.contains(p + v3s16(0, 1, 0)) == true) {
|
||||
if (vm->m_area.contains(p) == true &&
|
||||
vm->m_area.contains(p + v3s16(0, 1, 0)) == true) {
|
||||
if (make_stairs) {
|
||||
makeFill(p + v3s16(-1, -1, -1), v3s16(3, 5, 3),
|
||||
VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0);
|
||||
makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2),
|
||||
VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
|
||||
makeHole(p);
|
||||
makeHole(p - dir);
|
||||
|
||||
// TODO: fix stairs code so it works 100% (quite difficult)
|
||||
|
||||
// exclude stairs from the bottom step
|
||||
if (((make_stairs == 1) && i != 0) ||
|
||||
((make_stairs == -1) && i != length - 1)) {
|
||||
// exclude stairs from diagonal steps
|
||||
if (((dir.X ^ dir.Z) & 1) &&
|
||||
(((make_stairs == 1) && i != 0) ||
|
||||
((make_stairs == -1) && i != length - 1))) {
|
||||
// rotate face 180 deg if making stairs backwards
|
||||
int facedir = dir_to_facedir(dir * make_stairs);
|
||||
|
||||
u32 vi = vmanip->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z);
|
||||
if (vmanip->m_data[vi].getContent() == cid_cobble)
|
||||
vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir);
|
||||
u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z);
|
||||
if (vm->m_data[vi].getContent() == dp.c_cobble)
|
||||
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
|
||||
|
||||
vi = vmanip->m_area.index(p.X, p.Y, p.Z);
|
||||
if (vmanip->m_data[vi].getContent() == cid_cobble)
|
||||
vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir);
|
||||
vi = vm->m_area.index(p.X, p.Y, p.Z);
|
||||
if (vm->m_data[vi].getContent() == dp.c_cobble)
|
||||
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
|
||||
}
|
||||
} else {
|
||||
makeFill(p + v3s16(-1, -1, -1), v3s16(3, 4, 3),
|
||||
VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0);
|
||||
makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2),
|
||||
VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
|
||||
makeHole(p);
|
||||
}
|
||||
|
||||
@@ -462,15 +471,15 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
|
||||
{
|
||||
v3s16 p = m_pos + m_dir;
|
||||
v3s16 p1 = p + v3s16(0, 1, 0);
|
||||
if (vmanip->m_area.contains(p) == false
|
||||
|| vmanip->m_area.contains(p1) == false
|
||||
if (vm->m_area.contains(p) == false
|
||||
|| vm->m_area.contains(p1) == false
|
||||
|| i % 4 == 0)
|
||||
{
|
||||
randomizeDir();
|
||||
continue;
|
||||
}
|
||||
if (vmanip->getNodeNoExNoEmerge(p).getContent() == cid_cobble
|
||||
&& vmanip->getNodeNoExNoEmerge(p1).getContent() == cid_cobble)
|
||||
if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_cobble
|
||||
&& vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_cobble)
|
||||
{
|
||||
// Found wall, this is a good place!
|
||||
result_place = p;
|
||||
@@ -483,18 +492,18 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
|
||||
Determine where to move next
|
||||
*/
|
||||
// Jump one up if the actual space is there
|
||||
if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == cid_cobble
|
||||
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR
|
||||
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR)
|
||||
if (vm->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == dp.c_cobble
|
||||
&& vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR
|
||||
&& vm->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR)
|
||||
p += v3s16(0,1,0);
|
||||
// Jump one down if the actual space is there
|
||||
if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == cid_cobble
|
||||
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR
|
||||
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR)
|
||||
if (vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == dp.c_cobble
|
||||
&& vm->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR
|
||||
&& vm->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR)
|
||||
p += v3s16(0,-1,0);
|
||||
// Check if walking is now possible
|
||||
if (vmanip->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR
|
||||
|| vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() != CONTENT_AIR)
|
||||
if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR
|
||||
|| vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() != CONTENT_AIR)
|
||||
{
|
||||
// Cannot continue walking here
|
||||
randomizeDir();
|
||||
@@ -551,12 +560,12 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
|
||||
for (s16 x = 1; x < roomsize.X - 1; x++)
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, z);
|
||||
if (vmanip->m_area.contains(p) == false)
|
||||
if (vm->m_area.contains(p) == false)
|
||||
{
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
if (vmanip->m_flags[vmanip->m_area.index(p)]
|
||||
if (vm->m_flags[vm->m_area.index(p)]
|
||||
& VMANIP_FLAG_DUNGEON_INSIDE)
|
||||
{
|
||||
fits = false;
|
||||
@@ -577,12 +586,25 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
|
||||
}
|
||||
|
||||
|
||||
v3s16 rand_ortho_dir(PseudoRandom &random)
|
||||
v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
|
||||
{
|
||||
if (random.next() % 2 == 0)
|
||||
return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
|
||||
else
|
||||
return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
|
||||
// Make diagonal directions somewhat rare
|
||||
if (diagonal_dirs && (random.next() % 4 == 0)) {
|
||||
v3s16 dir;
|
||||
int trycount = 0;
|
||||
|
||||
do {
|
||||
trycount++;
|
||||
dir = v3s16(random.next() % 3 - 1, 0, random.next() % 3 - 1);
|
||||
} while ((dir.X == 0 && dir.Z == 0) && trycount < 10);
|
||||
|
||||
return dir;
|
||||
} else {
|
||||
if (random.next() % 2 == 0)
|
||||
return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
|
||||
else
|
||||
return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,40 +30,48 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
class ManualMapVoxelManipulator;
|
||||
class INodeDefManager;
|
||||
class Mapgen;
|
||||
|
||||
v3s16 rand_ortho_dir(PseudoRandom &random);
|
||||
v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs);
|
||||
v3s16 turn_xz(v3s16 olddir, int t);
|
||||
v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
|
||||
int dir_to_facedir(v3s16 d);
|
||||
|
||||
|
||||
struct DungeonParams {
|
||||
content_t c_water;
|
||||
content_t c_cobble;
|
||||
content_t c_moss;
|
||||
content_t c_stair;
|
||||
|
||||
int notifytype;
|
||||
bool diagonal_dirs;
|
||||
float mossratio;
|
||||
v3s16 holesize;
|
||||
v3s16 roomsize;
|
||||
|
||||
NoiseParams np_rarity;
|
||||
NoiseParams np_wetness;
|
||||
NoiseParams np_density;
|
||||
};
|
||||
|
||||
class DungeonGen {
|
||||
public:
|
||||
ManualMapVoxelManipulator *vm;
|
||||
Mapgen *mg;
|
||||
u32 blockseed;
|
||||
u64 mapseed;
|
||||
ManualMapVoxelManipulator *vmanip;
|
||||
INodeDefManager *ndef;
|
||||
PseudoRandom random;
|
||||
v3s16 csize;
|
||||
s16 water_level;
|
||||
|
||||
NoiseParams *np_rarity;
|
||||
NoiseParams *np_wetness;
|
||||
NoiseParams *np_density;
|
||||
|
||||
content_t cid_water_source;
|
||||
content_t cid_cobble;
|
||||
content_t cid_mossycobble;
|
||||
content_t cid_torch;
|
||||
content_t cid_cobblestair;
|
||||
|
||||
content_t c_torch;
|
||||
DungeonParams dp;
|
||||
|
||||
//RoomWalker
|
||||
v3s16 m_pos;
|
||||
v3s16 m_dir;
|
||||
|
||||
DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel);
|
||||
void generate(ManualMapVoxelManipulator *vm, u32 bseed,
|
||||
v3s16 full_node_min, v3s16 full_node_max);
|
||||
//void generate(v3s16 full_node_min, v3s16 full_node_max, u32 bseed);
|
||||
DungeonGen(Mapgen *mg, DungeonParams *dparams);
|
||||
void generate(u32 bseed, v3s16 full_node_min, v3s16 full_node_max);
|
||||
|
||||
void makeDungeon(v3s16 start_padding);
|
||||
void makeRoom(v3s16 roomsize, v3s16 roomplace);
|
||||
@@ -79,50 +87,12 @@ class DungeonGen {
|
||||
|
||||
void randomizeDir()
|
||||
{
|
||||
m_dir = rand_ortho_dir(random);
|
||||
m_dir = rand_ortho_dir(random, dp.diagonal_dirs);
|
||||
}
|
||||
};
|
||||
|
||||
class RoomWalker
|
||||
{
|
||||
public:
|
||||
|
||||
RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
|
||||
INodeDefManager *ndef):
|
||||
vmanip(vmanip_),
|
||||
m_pos(pos),
|
||||
m_random(random),
|
||||
m_ndef(ndef)
|
||||
{
|
||||
randomizeDir();
|
||||
}
|
||||
|
||||
void randomizeDir()
|
||||
{
|
||||
m_dir = rand_ortho_dir(m_random);
|
||||
}
|
||||
|
||||
void setPos(v3s16 pos)
|
||||
{
|
||||
m_pos = pos;
|
||||
}
|
||||
|
||||
void setDir(v3s16 dir)
|
||||
{
|
||||
m_dir = dir;
|
||||
}
|
||||
|
||||
//bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
|
||||
//bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
|
||||
// v3s16 &result_doordir, v3s16 &result_roomplace);
|
||||
|
||||
private:
|
||||
VoxelManipulator &vmanip;
|
||||
v3s16 m_pos;
|
||||
v3s16 m_dir;
|
||||
PseudoRandom &m_random;
|
||||
INodeDefManager *m_ndef;
|
||||
};
|
||||
|
||||
extern NoiseParams nparams_dungeon_rarity;
|
||||
extern NoiseParams nparams_dungeon_wetness;
|
||||
extern NoiseParams nparams_dungeon_density;
|
||||
|
||||
#endif
|
||||
|
||||
197
src/emerge.cpp
197
src/emerge.cpp
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "server.h"
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include "jthread/jevent.h"
|
||||
#include "map.h"
|
||||
#include "environment.h"
|
||||
#include "util/container.h"
|
||||
@@ -46,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "mapgen_math.h"
|
||||
|
||||
|
||||
class EmergeThread : public SimpleThread
|
||||
class EmergeThread : public JThread
|
||||
{
|
||||
public:
|
||||
Server *m_server;
|
||||
@@ -55,31 +56,22 @@ class EmergeThread : public SimpleThread
|
||||
Mapgen *mapgen;
|
||||
bool enable_mapgen_debug_info;
|
||||
int id;
|
||||
|
||||
|
||||
Event qevent;
|
||||
std::queue<v3s16> blockqueue;
|
||||
|
||||
|
||||
EmergeThread(Server *server, int ethreadid):
|
||||
SimpleThread(),
|
||||
JThread(),
|
||||
m_server(server),
|
||||
map(NULL),
|
||||
emerge(NULL),
|
||||
mapgen(NULL),
|
||||
enable_mapgen_debug_info(false),
|
||||
id(ethreadid)
|
||||
{
|
||||
}
|
||||
|
||||
void *Thread();
|
||||
|
||||
void trigger()
|
||||
{
|
||||
setRun(true);
|
||||
if(IsRunning() == false)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
bool popBlockEmerge(v3s16 *pos, u8 *flags);
|
||||
bool getBlockOrStartGen(v3s16 p, MapBlock **b,
|
||||
BlockMakeData *data, bool allow_generate);
|
||||
@@ -90,24 +82,24 @@ class EmergeThread : public SimpleThread
|
||||
|
||||
EmergeManager::EmergeManager(IGameDef *gamedef) {
|
||||
//register built-in mapgens
|
||||
registerMapgen("v6", new MapgenFactoryV6());
|
||||
registerMapgen("v7", new MapgenFactoryV7());
|
||||
registerMapgen("indev", new MapgenFactoryIndev());
|
||||
registerMapgen("v6", new MapgenFactoryV6());
|
||||
registerMapgen("v7", new MapgenFactoryV7());
|
||||
registerMapgen("indev", new MapgenFactoryIndev());
|
||||
registerMapgen("singlenode", new MapgenFactorySinglenode());
|
||||
registerMapgen("math", new MapgenFactoryMath());
|
||||
registerMapgen("math", new MapgenFactoryMath());
|
||||
|
||||
this->ndef = gamedef->getNodeDefManager();
|
||||
this->biomedef = new BiomeDefManager();
|
||||
this->params = NULL;
|
||||
|
||||
|
||||
this->luaoverride_params = NULL;
|
||||
this->luaoverride_params_modified = 0;
|
||||
this->luaoverride_flagmask = 0;
|
||||
|
||||
|
||||
this->gennotify = 0;
|
||||
|
||||
mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
|
||||
|
||||
queuemutex.Init();
|
||||
|
||||
int nthreads;
|
||||
if (g_settings->get("num_emerge_threads").empty()) {
|
||||
int nprocs = porting::getNumberOfProcessors();
|
||||
@@ -118,7 +110,7 @@ EmergeManager::EmergeManager(IGameDef *gamedef) {
|
||||
}
|
||||
if (nthreads < 1)
|
||||
nthreads = 1;
|
||||
|
||||
|
||||
qlimit_total = g_settings->getU16("emergequeue_limit_total");
|
||||
qlimit_diskonly = g_settings->get("emergequeue_limit_diskonly").empty() ?
|
||||
nthreads * 5 + 1 :
|
||||
@@ -126,19 +118,19 @@ EmergeManager::EmergeManager(IGameDef *gamedef) {
|
||||
qlimit_generate = g_settings->get("emergequeue_limit_generate").empty() ?
|
||||
nthreads + 1 :
|
||||
g_settings->getU16("emergequeue_limit_generate");
|
||||
|
||||
|
||||
for (int i = 0; i != nthreads; i++)
|
||||
emergethread.push_back(new EmergeThread((Server *)gamedef, i));
|
||||
|
||||
|
||||
infostream << "EmergeManager: using " << nthreads << " threads" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
EmergeManager::~EmergeManager() {
|
||||
for (unsigned int i = 0; i != emergethread.size(); i++) {
|
||||
emergethread[i]->setRun(false);
|
||||
emergethread[i]->Stop();
|
||||
emergethread[i]->qevent.signal();
|
||||
emergethread[i]->stop();
|
||||
emergethread[i]->Wait();
|
||||
delete emergethread[i];
|
||||
delete mapgen[i];
|
||||
}
|
||||
@@ -152,7 +144,7 @@ EmergeManager::~EmergeManager() {
|
||||
for (unsigned int i = 0; i < decorations.size(); i++)
|
||||
delete decorations[i];
|
||||
decorations.clear();
|
||||
|
||||
|
||||
for (std::map<std::string, MapgenFactory *>::iterator iter = mglist.begin();
|
||||
iter != mglist.end(); iter ++) {
|
||||
delete iter->second;
|
||||
@@ -165,83 +157,120 @@ EmergeManager::~EmergeManager() {
|
||||
|
||||
void EmergeManager::initMapgens(MapgenParams *mgparams) {
|
||||
Mapgen *mg;
|
||||
|
||||
|
||||
if (mapgen.size())
|
||||
return;
|
||||
|
||||
|
||||
// Resolve names of nodes for things that were registered
|
||||
// (at this point, the registration period is over)
|
||||
biomedef->resolveNodeNames(ndef);
|
||||
|
||||
|
||||
for (size_t i = 0; i != ores.size(); i++)
|
||||
ores[i]->resolveNodeNames(ndef);
|
||||
|
||||
|
||||
for (size_t i = 0; i != decorations.size(); i++)
|
||||
decorations[i]->resolveNodeNames(ndef);
|
||||
|
||||
|
||||
// Apply mapgen parameter overrides from Lua
|
||||
if (luaoverride_params) {
|
||||
if (luaoverride_params_modified & MGPARAMS_SET_MGNAME)
|
||||
mgparams->mg_name = luaoverride_params->mg_name;
|
||||
|
||||
if (luaoverride_params_modified & MGPARAMS_SET_MGNAME) {
|
||||
MapgenParams *mgp = setMapgenType(mgparams, luaoverride_params->mg_name);
|
||||
if (!mgp) {
|
||||
errorstream << "EmergeManager: Failed to set new mapgen name"
|
||||
<< std::endl;
|
||||
} else {
|
||||
mgparams = mgp;
|
||||
}
|
||||
}
|
||||
|
||||
if (luaoverride_params_modified & MGPARAMS_SET_SEED)
|
||||
mgparams->seed = luaoverride_params->seed;
|
||||
|
||||
|
||||
if (luaoverride_params_modified & MGPARAMS_SET_WATER_LEVEL)
|
||||
mgparams->water_level = luaoverride_params->water_level;
|
||||
|
||||
|
||||
if (luaoverride_params_modified & MGPARAMS_SET_FLAGS) {
|
||||
mgparams->flags &= ~luaoverride_flagmask;
|
||||
mgparams->flags |= luaoverride_params->flags;
|
||||
}
|
||||
|
||||
|
||||
delete luaoverride_params;
|
||||
luaoverride_params = NULL;
|
||||
}
|
||||
|
||||
|
||||
// Create the mapgens
|
||||
this->params = mgparams;
|
||||
for (size_t i = 0; i != emergethread.size(); i++) {
|
||||
mg = createMapgen(params->mg_name, 0, params);
|
||||
mg = createMapgen(params->mg_name, i, params);
|
||||
if (!mg) {
|
||||
infostream << "EmergeManager: falling back to mapgen v6" << std::endl;
|
||||
delete params;
|
||||
params = createMapgenParams("v6");
|
||||
mg = createMapgen("v6", 0, params);
|
||||
infostream << "EmergeManager: Falling back to Mapgen V6" << std::endl;
|
||||
|
||||
params = setMapgenType(params, "v6");
|
||||
mg = createMapgen(params->mg_name, i, params);
|
||||
if (!mg) {
|
||||
errorstream << "EmergeManager: CRITICAL ERROR: Failed to fall"
|
||||
"back to Mapgen V6, not generating map" << std::endl;
|
||||
}
|
||||
}
|
||||
mapgen.push_back(mg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MapgenParams *EmergeManager::setMapgenType(MapgenParams *mgparams,
|
||||
std::string newname) {
|
||||
MapgenParams *newparams = createMapgenParams(newname);
|
||||
if (!newparams) {
|
||||
errorstream << "EmergeManager: Mapgen override failed" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newparams->mg_name = newname;
|
||||
newparams->seed = mgparams->seed;
|
||||
newparams->water_level = mgparams->water_level;
|
||||
newparams->chunksize = mgparams->chunksize;
|
||||
newparams->flags = mgparams->flags;
|
||||
|
||||
if (!newparams->readParams(g_settings)) {
|
||||
errorstream << "EmergeManager: Mapgen override failed" << std::endl;
|
||||
delete newparams;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
delete mgparams;
|
||||
return newparams;
|
||||
}
|
||||
|
||||
|
||||
Mapgen *EmergeManager::getCurrentMapgen() {
|
||||
for (unsigned int i = 0; i != emergethread.size(); i++) {
|
||||
if (emergethread[i]->IsSameThread())
|
||||
return emergethread[i]->mapgen;
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void EmergeManager::triggerAllThreads() {
|
||||
void EmergeManager::startAllThreads() {
|
||||
for (unsigned int i = 0; i != emergethread.size(); i++)
|
||||
emergethread[i]->trigger();
|
||||
emergethread[i]->Start();
|
||||
}
|
||||
|
||||
|
||||
bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate) {
|
||||
std::map<v3s16, BlockEmergeData *>::const_iterator iter;
|
||||
BlockEmergeData *bedata;
|
||||
u16 count;
|
||||
u8 flags = 0;
|
||||
int idx = 0;
|
||||
|
||||
|
||||
if (allow_generate)
|
||||
flags |= BLOCK_EMERGE_ALLOWGEN;
|
||||
|
||||
{
|
||||
JMutexAutoLock queuelock(queuemutex);
|
||||
|
||||
|
||||
count = blocks_enqueued.size();
|
||||
if (count >= qlimit_total)
|
||||
return false;
|
||||
@@ -250,7 +279,7 @@ bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate
|
||||
u16 qlimit_peer = allow_generate ? qlimit_generate : qlimit_diskonly;
|
||||
if (count >= qlimit_peer)
|
||||
return false;
|
||||
|
||||
|
||||
iter = blocks_enqueued.find(p);
|
||||
if (iter != blocks_enqueued.end()) {
|
||||
bedata = iter->second;
|
||||
@@ -262,9 +291,9 @@ bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate
|
||||
bedata->flags = flags;
|
||||
bedata->peer_requested = peer_id;
|
||||
blocks_enqueued.insert(std::make_pair(p, bedata));
|
||||
|
||||
|
||||
peer_queue_count[peer_id] = count + 1;
|
||||
|
||||
|
||||
// insert into the EmergeThread queue with the least items
|
||||
int lowestitems = emergethread[0]->blockqueue.size();
|
||||
for (unsigned int i = 1; i != emergethread.size(); i++) {
|
||||
@@ -274,11 +303,11 @@ bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate
|
||||
lowestitems = nitems;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
emergethread[idx]->blockqueue.push(p);
|
||||
}
|
||||
emergethread[idx]->qevent.signal();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -286,10 +315,10 @@ bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate
|
||||
int EmergeManager::getGroundLevelAtPoint(v2s16 p) {
|
||||
if (mapgen.size() == 0 || !mapgen[0]) {
|
||||
errorstream << "EmergeManager: getGroundLevelAtPoint() called"
|
||||
" before mapgen initialized" << std::endl;
|
||||
" before mapgen initialized" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return mapgen[0]->getGroundLevelAtPoint(p);
|
||||
}
|
||||
|
||||
@@ -325,7 +354,7 @@ Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid,
|
||||
" not registered" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
MapgenFactory *mgfactory = iter->second;
|
||||
return mgfactory->createMapgen(mgid, mgparams, this);
|
||||
}
|
||||
@@ -339,7 +368,7 @@ MapgenParams *EmergeManager::createMapgenParams(std::string mgname) {
|
||||
" not registered" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
MapgenFactory *mgfactory = iter->second;
|
||||
return mgfactory->createMapgenParams();
|
||||
}
|
||||
@@ -350,10 +379,10 @@ MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) {
|
||||
MapgenParams *mgparams = createMapgenParams(mg_name);
|
||||
if (!mgparams)
|
||||
return NULL;
|
||||
|
||||
|
||||
std::string seedstr = settings->get(settings == g_settings ?
|
||||
"fixed_map_seed" : "seed");
|
||||
|
||||
|
||||
mgparams->mg_name = mg_name;
|
||||
mgparams->seed = read_seed(seedstr.c_str());
|
||||
mgparams->water_level = settings->getS16("water_level");
|
||||
@@ -385,7 +414,7 @@ void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory)
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////// Emerge Thread //////////////////////////////////
|
||||
////////////////////////////// Emerge Thread //////////////////////////////////
|
||||
|
||||
bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) {
|
||||
std::map<v3s16, BlockEmergeData *>::iterator iter;
|
||||
@@ -395,31 +424,31 @@ bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) {
|
||||
return false;
|
||||
v3s16 p = blockqueue.front();
|
||||
blockqueue.pop();
|
||||
|
||||
|
||||
*pos = p;
|
||||
|
||||
|
||||
iter = emerge->blocks_enqueued.find(p);
|
||||
if (iter == emerge->blocks_enqueued.end())
|
||||
if (iter == emerge->blocks_enqueued.end())
|
||||
return false; //uh oh, queue and map out of sync!!
|
||||
|
||||
BlockEmergeData *bedata = iter->second;
|
||||
*flags = bedata->flags;
|
||||
|
||||
|
||||
emerge->peer_queue_count[bedata->peer_requested]--;
|
||||
|
||||
delete bedata;
|
||||
emerge->blocks_enqueued.erase(iter);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b,
|
||||
bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b,
|
||||
BlockMakeData *data, bool allow_gen) {
|
||||
v2s16 p2d(p.X, p.Z);
|
||||
//envlock: usually takes <=1ms, sometimes 90ms or ~400ms to acquire
|
||||
JMutexAutoLock envlock(m_server->m_env_mutex);
|
||||
|
||||
JMutexAutoLock envlock(m_server->m_env_mutex);
|
||||
|
||||
// Load sector if it isn't loaded
|
||||
if (map->getSectorNoGenerateNoEx(p2d) == NULL)
|
||||
map->loadSectorMeta(p2d);
|
||||
@@ -440,7 +469,7 @@ bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b,
|
||||
*b = block;
|
||||
return map->initBlockMake(data, p);
|
||||
}
|
||||
|
||||
|
||||
*b = block;
|
||||
return false;
|
||||
}
|
||||
@@ -455,13 +484,13 @@ void *EmergeThread::Thread() {
|
||||
v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
|
||||
v3s16 p;
|
||||
u8 flags;
|
||||
|
||||
|
||||
map = (ServerMap *)&(m_server->m_env->getMap());
|
||||
emerge = m_server->m_emerge;
|
||||
mapgen = emerge->mapgen[id];
|
||||
enable_mapgen_debug_info = emerge->mapgen_debug_info;
|
||||
|
||||
while (getRun())
|
||||
|
||||
while (!StopRequested())
|
||||
try {
|
||||
if (!popBlockEmerge(&p, &flags)) {
|
||||
qevent.wait();
|
||||
@@ -474,7 +503,7 @@ void *EmergeThread::Thread() {
|
||||
|
||||
bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN;
|
||||
EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate);
|
||||
|
||||
|
||||
/*
|
||||
Try to fetch block from memory or disk.
|
||||
If not found and asked to generate, initialize generator.
|
||||
@@ -482,8 +511,8 @@ void *EmergeThread::Thread() {
|
||||
BlockMakeData data;
|
||||
MapBlock *block = NULL;
|
||||
std::map<v3s16, MapBlock *> modified_blocks;
|
||||
|
||||
if (getBlockOrStartGen(p, &block, &data, allow_generate)) {
|
||||
|
||||
if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) {
|
||||
{
|
||||
ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG);
|
||||
TimeTaker t("mapgen::make_block()");
|
||||
@@ -496,12 +525,12 @@ void *EmergeThread::Thread() {
|
||||
|
||||
{
|
||||
//envlock: usually 0ms, but can take either 30 or 400ms to acquire
|
||||
JMutexAutoLock envlock(m_server->m_env_mutex);
|
||||
JMutexAutoLock envlock(m_server->m_env_mutex);
|
||||
ScopeProfiler sp(g_profiler, "EmergeThread: after "
|
||||
"Mapgen::makeChunk (envlock)", SPT_AVG);
|
||||
|
||||
map->finishBlockMake(&data, modified_blocks);
|
||||
|
||||
|
||||
block = map->getBlockNoCreateNoEx(p);
|
||||
if (block) {
|
||||
/*
|
||||
@@ -513,16 +542,18 @@ void *EmergeThread::Thread() {
|
||||
|
||||
// Ignore map edit events, they will not need to be sent
|
||||
// to anybody because the block hasn't been sent to anybody
|
||||
MapEditEventAreaIgnorer
|
||||
MapEditEventAreaIgnorer
|
||||
ign(&m_server->m_ignore_map_edit_events_area,
|
||||
VoxelArea(minp, maxp));
|
||||
{ // takes about 90ms with -O1 on an e3-1230v2
|
||||
try { // takes about 90ms with -O1 on an e3-1230v2
|
||||
m_server->getScriptIface()->environment_OnGenerated(
|
||||
minp, maxp, emerge->getBlockSeed(minp));
|
||||
} catch(LuaError &e) {
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
}
|
||||
|
||||
EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
|
||||
|
||||
|
||||
m_server->m_env->activateBlock(block, 0);
|
||||
}
|
||||
}
|
||||
@@ -568,7 +599,7 @@ void *EmergeThread::Thread() {
|
||||
err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
|
||||
m_server->setAsyncFatalError(err.str());
|
||||
}
|
||||
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
||||
log_deregister_thread();
|
||||
return NULL;
|
||||
|
||||
17
src/emerge.h
17
src/emerge.h
@@ -83,21 +83,23 @@ class EmergeManager : public IBackgroundBlockEmerger {
|
||||
INodeDefManager *ndef;
|
||||
|
||||
std::map<std::string, MapgenFactory *> mglist;
|
||||
|
||||
|
||||
std::vector<Mapgen *> mapgen;
|
||||
std::vector<EmergeThread *> emergethread;
|
||||
|
||||
|
||||
//settings
|
||||
MapgenParams *params;
|
||||
bool mapgen_debug_info;
|
||||
u16 qlimit_total;
|
||||
u16 qlimit_diskonly;
|
||||
u16 qlimit_generate;
|
||||
|
||||
|
||||
u32 gennotify;
|
||||
|
||||
MapgenParams *luaoverride_params;
|
||||
u32 luaoverride_params_modified;
|
||||
u32 luaoverride_flagmask;
|
||||
|
||||
|
||||
//block emerge queue data structures
|
||||
JMutex queuemutex;
|
||||
std::map<v3s16, BlockEmergeData *> blocks_enqueued;
|
||||
@@ -112,17 +114,18 @@ class EmergeManager : public IBackgroundBlockEmerger {
|
||||
~EmergeManager();
|
||||
|
||||
void initMapgens(MapgenParams *mgparams);
|
||||
MapgenParams *setMapgenType(MapgenParams *mgparams, std::string newname);
|
||||
Mapgen *getCurrentMapgen();
|
||||
Mapgen *createMapgen(std::string mgname, int mgid,
|
||||
MapgenParams *mgparams);
|
||||
MapgenParams *createMapgenParams(std::string mgname);
|
||||
void triggerAllThreads();
|
||||
void startAllThreads();
|
||||
bool enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate);
|
||||
|
||||
|
||||
void registerMapgen(std::string name, MapgenFactory *mgfactory);
|
||||
MapgenParams *getParamsFromSettings(Settings *settings);
|
||||
void setParamsToSettings(Settings *settings);
|
||||
|
||||
|
||||
//mapgen helper methods
|
||||
Biome *getBiomeAtPoint(v3s16 p);
|
||||
int getGroundLevelAtPoint(v2s16 p);
|
||||
|
||||
@@ -354,7 +354,7 @@ ServerMap & ServerEnvironment::getServerMap()
|
||||
return *m_map;
|
||||
}
|
||||
|
||||
bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
|
||||
bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
|
||||
{
|
||||
float distance = pos1.getDistanceFrom(pos2);
|
||||
|
||||
@@ -372,6 +372,9 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
|
||||
MapNode n = getMap().getNodeNoEx(pos);
|
||||
|
||||
if(n.param0 != CONTENT_AIR) {
|
||||
if (p) {
|
||||
*p = pos;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -874,6 +877,11 @@ bool ServerEnvironment::removeNode(v3s16 p)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
|
||||
{
|
||||
return m_map->addNodeWithEvent(p, n, false);
|
||||
}
|
||||
|
||||
std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
|
||||
{
|
||||
std::set<u16> objects;
|
||||
|
||||
@@ -283,6 +283,7 @@ class ServerEnvironment : public Environment
|
||||
// Script-aware node setters
|
||||
bool setNode(v3s16 p, const MapNode &n);
|
||||
bool removeNode(v3s16 p);
|
||||
bool swapNode(v3s16 p, const MapNode &n);
|
||||
|
||||
// Find all active objects inside a radius around a point
|
||||
std::set<u16> getObjectsInsideRadius(v3f pos, float radius);
|
||||
@@ -294,7 +295,7 @@ class ServerEnvironment : public Environment
|
||||
void step(f32 dtime);
|
||||
|
||||
//check if there's a line of sight between two positions
|
||||
bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0);
|
||||
bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0, v3s16 *p=NULL);
|
||||
|
||||
u32 getGameTime() { return m_game_time; }
|
||||
|
||||
|
||||
115
src/exceptions.h
115
src/exceptions.h
@@ -21,132 +21,99 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#define EXCEPTIONS_HEADER
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
|
||||
class BaseException : public std::exception
|
||||
{
|
||||
public:
|
||||
BaseException(const char *s)
|
||||
BaseException(const std::string s) throw()
|
||||
{
|
||||
m_s = s;
|
||||
}
|
||||
~BaseException() throw() {}
|
||||
virtual const char * what() const throw()
|
||||
{
|
||||
return m_s;
|
||||
return m_s.c_str();
|
||||
}
|
||||
const char *m_s;
|
||||
protected:
|
||||
std::string m_s;
|
||||
};
|
||||
|
||||
class AsyncQueuedException : public BaseException
|
||||
{
|
||||
class AsyncQueuedException : public BaseException {
|
||||
public:
|
||||
AsyncQueuedException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
AsyncQueuedException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class NotImplementedException : public BaseException
|
||||
{
|
||||
class NotImplementedException : public BaseException {
|
||||
public:
|
||||
NotImplementedException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
NotImplementedException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class AlreadyExistsException : public BaseException
|
||||
{
|
||||
class AlreadyExistsException : public BaseException {
|
||||
public:
|
||||
AlreadyExistsException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
AlreadyExistsException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class VersionMismatchException : public BaseException
|
||||
{
|
||||
class VersionMismatchException : public BaseException {
|
||||
public:
|
||||
VersionMismatchException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
VersionMismatchException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class FileNotGoodException : public BaseException
|
||||
{
|
||||
class FileNotGoodException : public BaseException {
|
||||
public:
|
||||
FileNotGoodException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
FileNotGoodException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class SerializationError : public BaseException
|
||||
{
|
||||
class SerializationError : public BaseException {
|
||||
public:
|
||||
SerializationError(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
SerializationError(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class LoadError : public BaseException
|
||||
{
|
||||
class LoadError : public BaseException {
|
||||
public:
|
||||
LoadError(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
LoadError(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class ContainerFullException : public BaseException
|
||||
{
|
||||
class ContainerFullException : public BaseException {
|
||||
public:
|
||||
ContainerFullException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
ContainerFullException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class SettingNotFoundException : public BaseException
|
||||
{
|
||||
class SettingNotFoundException : public BaseException {
|
||||
public:
|
||||
SettingNotFoundException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
SettingNotFoundException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class InvalidFilenameException : public BaseException
|
||||
{
|
||||
class InvalidFilenameException : public BaseException {
|
||||
public:
|
||||
InvalidFilenameException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
InvalidFilenameException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class ProcessingLimitException : public BaseException
|
||||
{
|
||||
class ProcessingLimitException : public BaseException {
|
||||
public:
|
||||
ProcessingLimitException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
ProcessingLimitException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class CommandLineError : public BaseException
|
||||
{
|
||||
class CommandLineError : public BaseException {
|
||||
public:
|
||||
CommandLineError(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
CommandLineError(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class ItemNotFoundException : public BaseException
|
||||
{
|
||||
class ItemNotFoundException : public BaseException {
|
||||
public:
|
||||
ItemNotFoundException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
ItemNotFoundException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
class ServerError : public BaseException {
|
||||
public:
|
||||
ServerError(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
// Only used on Windows (SEH)
|
||||
class FatalSystemException : public BaseException
|
||||
{
|
||||
class FatalSystemException : public BaseException {
|
||||
public:
|
||||
FatalSystemException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
FatalSystemException(std::string s): BaseException(s) {}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -159,7 +126,7 @@ class InvalidPositionException : public BaseException
|
||||
InvalidPositionException():
|
||||
BaseException("Somebody tried to get/set something in a nonexistent position.")
|
||||
{}
|
||||
InvalidPositionException(const char *s):
|
||||
InvalidPositionException(std::string s):
|
||||
BaseException(s)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -23,12 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "clientserver.h"
|
||||
#include "log.h"
|
||||
#include "filesys.h"
|
||||
#include "hex.h"
|
||||
#include "sha1.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
||||
bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
@@ -85,36 +82,8 @@ bool FileCache::update(const std::string &name, const std::string &data)
|
||||
std::string path = m_dir + DIR_DELIM + name;
|
||||
return updateByPath(path, data);
|
||||
}
|
||||
bool FileCache::update_sha1(const std::string &data)
|
||||
{
|
||||
SHA1 sha1;
|
||||
sha1.addBytes(data.c_str(), data.size());
|
||||
unsigned char *digest = sha1.getDigest();
|
||||
std::string sha1_raw((char*)digest, 20);
|
||||
free(digest);
|
||||
std::string sha1_hex = hex_encode(sha1_raw);
|
||||
return update(sha1_hex, data);
|
||||
}
|
||||
bool FileCache::load(const std::string &name, std::ostream &os)
|
||||
{
|
||||
std::string path = m_dir + DIR_DELIM + name;
|
||||
return loadByPath(path, os);
|
||||
}
|
||||
bool FileCache::load_sha1(const std::string &sha1_raw, std::ostream &os)
|
||||
{
|
||||
std::ostringstream tmp_os(std::ios_base::binary);
|
||||
if(!load(hex_encode(sha1_raw), tmp_os))
|
||||
return false;
|
||||
SHA1 sha1;
|
||||
sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
|
||||
unsigned char *digest = sha1.getDigest();
|
||||
std::string sha1_real_raw((char*)digest, 20);
|
||||
free(digest);
|
||||
if(sha1_real_raw != sha1_raw){
|
||||
verbosestream<<"FileCache["<<m_dir<<"]: filename "<<sha1_real_raw
|
||||
<<" mismatches actual checksum"<<std::endl;
|
||||
return false;
|
||||
}
|
||||
os<<tmp_os.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@ class FileCache
|
||||
}
|
||||
|
||||
bool update(const std::string &name, const std::string &data);
|
||||
bool update_sha1(const std::string &data);
|
||||
bool load(const std::string &name, std::ostream &os);
|
||||
bool load_sha1(const std::string &sha1_raw, std::ostream &os);
|
||||
private:
|
||||
std::string m_dir;
|
||||
|
||||
|
||||
71
src/game.cpp
71
src/game.cpp
@@ -69,6 +69,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
#include "util/directiontables.h"
|
||||
#include "util/pointedthing.h"
|
||||
|
||||
/*
|
||||
Text input system
|
||||
@@ -805,13 +806,26 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
||||
float daynight_ratio_f = (float)daynight_ratio / 1000.0;
|
||||
services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1);
|
||||
|
||||
u32 animation_timer = porting::getTimeMs() % 100000;
|
||||
float animation_timer_f = (float)animation_timer / 100000.0;
|
||||
services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1);
|
||||
services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1);
|
||||
|
||||
LocalPlayer* player = m_client->getEnv().getLocalPlayer();
|
||||
v3f eye_position = player->getEyePosition();
|
||||
services->setPixelShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
|
||||
services->setVertexShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
|
||||
|
||||
// Normal map texture layer
|
||||
int layer = 1;
|
||||
int layer1 = 1;
|
||||
int layer2 = 2;
|
||||
// before 1.8 there isn't a "integer interface", only float
|
||||
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
|
||||
services->setPixelShaderConstant("normalTexture" , (irr::f32*)&layer, 1);
|
||||
services->setPixelShaderConstant("normalTexture" , (irr::f32*)&layer1, 1);
|
||||
services->setPixelShaderConstant("useNormalmap" , (irr::f32*)&layer2, 1);
|
||||
#else
|
||||
services->setPixelShaderConstant("normalTexture" , (irr::s32*)&layer, 1);
|
||||
services->setPixelShaderConstant("normalTexture" , (irr::s32*)&layer1, 1);
|
||||
services->setPixelShaderConstant("useNormalmap" , (irr::s32*)&layer2, 1);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@@ -1194,13 +1208,19 @@ void the_game(
|
||||
server->step(dtime);
|
||||
|
||||
// End condition
|
||||
if(client.texturesReceived() &&
|
||||
if(client.mediaReceived() &&
|
||||
client.itemdefReceived() &&
|
||||
client.nodedefReceived()){
|
||||
got_content = true;
|
||||
break;
|
||||
}
|
||||
// Break conditions
|
||||
if(client.accessDenied()){
|
||||
error_message = L"Access denied. Reason: "
|
||||
+client.accessDeniedReason();
|
||||
errorstream<<wide_to_narrow(error_message)<<std::endl;
|
||||
break;
|
||||
}
|
||||
if(!client.connectedAndInitialized()){
|
||||
error_message = L"Client disconnected";
|
||||
errorstream<<wide_to_narrow(error_message)<<std::endl;
|
||||
@@ -1304,7 +1324,7 @@ void the_game(
|
||||
*/
|
||||
|
||||
Sky *sky = NULL;
|
||||
sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
|
||||
sky = new Sky(smgr->getRootSceneNode(), smgr, -1, client.getEnv().getLocalPlayer());
|
||||
|
||||
/*
|
||||
A copy of the local inventory
|
||||
@@ -1409,7 +1429,7 @@ void the_game(
|
||||
bool invert_mouse = g_settings->getBool("invert_mouse");
|
||||
|
||||
bool respawn_menu_active = false;
|
||||
bool update_wielded_item_trigger = false;
|
||||
bool update_wielded_item_trigger = true;
|
||||
|
||||
bool show_hud = true;
|
||||
bool show_chat = true;
|
||||
@@ -1459,6 +1479,11 @@ void the_game(
|
||||
|
||||
bool use_weather = g_settings->getBool("weather");
|
||||
|
||||
core::stringw str = L"Minetest [";
|
||||
str += driver->getName();
|
||||
str += "]";
|
||||
device->setWindowCaption(str.c_str());
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(device->run() == false || kill == true)
|
||||
@@ -2277,10 +2302,6 @@ void the_game(
|
||||
delete(event.show_formspec.formspec);
|
||||
delete(event.show_formspec.formname);
|
||||
}
|
||||
else if(event.type == CE_TEXTURES_UPDATED)
|
||||
{
|
||||
update_wielded_item_trigger = true;
|
||||
}
|
||||
else if(event.type == CE_SPAWN_PARTICLE)
|
||||
{
|
||||
LocalPlayer* player = client.getEnv().getLocalPlayer();
|
||||
@@ -2974,10 +2995,13 @@ void the_game(
|
||||
scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
|
||||
static float endscenetime_avg = 0;
|
||||
endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/
|
||||
|
||||
|
||||
u16 fps = (1.0/dtime_avg1);
|
||||
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
os<<std::fixed
|
||||
<<"Minetest "<<minetest_version_hash
|
||||
<<" FPS = "<<fps
|
||||
<<" (R: range_all="<<draw_control.range_all<<")"
|
||||
<<std::setprecision(0)
|
||||
<<" drawtime = "<<drawtime_avg
|
||||
@@ -3366,21 +3390,6 @@ void the_game(
|
||||
End of drawing
|
||||
*/
|
||||
|
||||
static s16 lastFPS = 0;
|
||||
//u16 fps = driver->getFPS();
|
||||
u16 fps = (1.0/dtime_avg1);
|
||||
|
||||
if (lastFPS != fps)
|
||||
{
|
||||
core::stringw str = L"Minetest [";
|
||||
str += driver->getName();
|
||||
str += "] FPS=";
|
||||
str += fps;
|
||||
|
||||
device->setWindowCaption(str.c_str());
|
||||
lastFPS = fps;
|
||||
}
|
||||
|
||||
/*
|
||||
Log times and stuff for visualization
|
||||
*/
|
||||
@@ -3428,14 +3437,12 @@ void the_game(
|
||||
L" running a different version of Minetest.";
|
||||
errorstream<<wide_to_narrow(error_message)<<std::endl;
|
||||
}
|
||||
catch(ServerError &e)
|
||||
{
|
||||
catch(ServerError &e) {
|
||||
error_message = narrow_to_wide(e.what());
|
||||
errorstream<<wide_to_narrow(error_message)<<std::endl;
|
||||
errorstream << "ServerError: " << e.what() << std::endl;
|
||||
}
|
||||
catch(ModError &e)
|
||||
{
|
||||
errorstream<<e.what()<<std::endl;
|
||||
catch(ModError &e) {
|
||||
errorstream << "ModError: " << e.what() << std::endl;
|
||||
error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,8 @@ std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups)
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_update_physics_override(float physics_override_speed, float physics_override_jump, float physics_override_gravity)
|
||||
std::string gob_cmd_update_physics_override(float physics_override_speed, float physics_override_jump,
|
||||
float physics_override_gravity, bool sneak, bool sneak_glitch)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
@@ -126,6 +127,9 @@ std::string gob_cmd_update_physics_override(float physics_override_speed, float
|
||||
writeF1000(os, physics_override_speed);
|
||||
writeF1000(os, physics_override_jump);
|
||||
writeF1000(os, physics_override_gravity);
|
||||
// these are sent inverted so we get true when the server sends nothing
|
||||
writeU8(os, !sneak);
|
||||
writeU8(os, !sneak_glitch);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ std::string gob_cmd_punched(s16 damage, s16 result_hp);
|
||||
#include "itemgroup.h"
|
||||
std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups);
|
||||
|
||||
std::string gob_cmd_update_physics_override(float physics_override_speed, float physics_override_jump, float physics_override_gravity);
|
||||
std::string gob_cmd_update_physics_override(float physics_override_speed,
|
||||
float physics_override_jump, float physics_override_gravity, bool sneak, bool sneak_glitch);
|
||||
|
||||
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "sound.h"
|
||||
#include "sound_openal.h"
|
||||
#include "clouds.h"
|
||||
#include "httpfetch.h"
|
||||
|
||||
#include <IGUIStaticText.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
@@ -156,7 +157,7 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
|
||||
m_sound_manager = &dummySoundManager;
|
||||
|
||||
//create topleft header
|
||||
core::rect<s32> rect(0, 0, 500, 40);
|
||||
core::rect<s32> rect(0, 0, 500, 20);
|
||||
rect += v2s32(4, 0);
|
||||
std::string t = std::string("Minetest ") + minetest_version_hash;
|
||||
|
||||
@@ -286,6 +287,8 @@ void GUIEngine::run()
|
||||
cloudPostProcess();
|
||||
else
|
||||
sleep_ms(25);
|
||||
|
||||
m_script->Step();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,51 +508,39 @@ bool GUIEngine::setTexture(texture_layer layer,std::string texturepath) {
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
#if USE_CURL
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
FILE* targetfile = (FILE*) userp;
|
||||
fwrite(contents,size,nmemb,targetfile);
|
||||
return size * nmemb;
|
||||
}
|
||||
#endif
|
||||
bool GUIEngine::downloadFile(std::string url,std::string target) {
|
||||
#if USE_CURL
|
||||
//download file via curl
|
||||
CURL *curl;
|
||||
bool retval = true;
|
||||
|
||||
curl = curl_easy_init();
|
||||
FILE* targetfile = fopen(target.c_str(),"wb");
|
||||
|
||||
if (curl)
|
||||
{
|
||||
CURLcode res;
|
||||
bool retval = true;
|
||||
if (targetfile) {
|
||||
HTTPFetchRequest fetchrequest;
|
||||
HTTPFetchResult fetchresult;
|
||||
fetchrequest.url = url;
|
||||
fetchrequest.useragent = std::string("Minetest ")+minetest_version_hash;
|
||||
fetchrequest.timeout = g_settings->getS32("curl_timeout");
|
||||
fetchrequest.caller = HTTPFETCH_SYNC;
|
||||
httpfetch_sync(fetchrequest,fetchresult);
|
||||
|
||||
FILE* targetfile = fopen(target.c_str(),"wb");
|
||||
|
||||
if (targetfile) {
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, targetfile);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, (std::string("Minetest ")+minetest_version_hash).c_str());
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
errorstream << "File at url \"" << url
|
||||
<<"\" not found (" << curl_easy_strerror(res) << ")" <<std::endl;
|
||||
if (fetchresult.succeeded) {
|
||||
if (fwrite(fetchresult.data.c_str(),1,fetchresult.data.size(),targetfile) != fetchresult.data.size()) {
|
||||
retval = false;
|
||||
}
|
||||
fclose(targetfile);
|
||||
}
|
||||
else {
|
||||
retval = false;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
return retval;
|
||||
fclose(targetfile);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
retval = false;
|
||||
}
|
||||
|
||||
return retval;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -576,3 +567,9 @@ void GUIEngine::stopSound(s32 handle)
|
||||
{
|
||||
m_sound_manager->stopSound(handle);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
unsigned int GUIEngine::DoAsync(std::string serialized_fct,
|
||||
std::string serialized_params) {
|
||||
return m_script->DoAsync(serialized_fct,serialized_params);
|
||||
}
|
||||
|
||||
@@ -166,6 +166,9 @@ class GUIEngine {
|
||||
return m_scriptdir;
|
||||
}
|
||||
|
||||
/** pass async callback to scriptengine **/
|
||||
unsigned int DoAsync(std::string serialized_fct,std::string serialized_params);
|
||||
|
||||
private:
|
||||
|
||||
/** find and run the main menu script */
|
||||
@@ -244,7 +247,7 @@ class GUIEngine {
|
||||
* @param url url to download
|
||||
* @param target file to store to
|
||||
*/
|
||||
bool downloadFile(std::string url,std::string target);
|
||||
static bool downloadFile(std::string url,std::string target);
|
||||
|
||||
/** array containing pointers to current specified texture layers */
|
||||
video::ITexture* m_textures[TEX_LAYER_MAX];
|
||||
|
||||
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "log.h"
|
||||
#include "tile.h" // ITextureSource
|
||||
#include "hud.h" // drawItemStack
|
||||
#include "hex.h"
|
||||
#include "util/string.h"
|
||||
#include "util/numeric.h"
|
||||
#include "filesys.h"
|
||||
@@ -421,6 +422,12 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element) {
|
||||
s32 start_i = 0;
|
||||
if(startindex != "")
|
||||
start_i = stoi(startindex);
|
||||
|
||||
if (geom.X < 0 || geom.Y < 0 || start_i < 0) {
|
||||
errorstream<< "Invalid list element: '" << element << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if(data->bp_set != 2)
|
||||
errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
|
||||
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
|
||||
@@ -836,7 +843,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) {
|
||||
Environment->setFocus(e);
|
||||
}
|
||||
|
||||
if (label.length() > 1)
|
||||
if (label.length() >= 1)
|
||||
{
|
||||
rect.UpperLeftCorner.Y -= 15;
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
|
||||
@@ -927,7 +934,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,std::vector<std::string>
|
||||
evt.KeyInput.PressedDown = true;
|
||||
e->OnEvent(evt);
|
||||
|
||||
if (label.length() > 1)
|
||||
if (label.length() >= 1)
|
||||
{
|
||||
rect.UpperLeftCorner.Y -= 15;
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
|
||||
@@ -1019,7 +1026,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,std::vector<std::string>& p
|
||||
e->OnEvent(evt);
|
||||
}
|
||||
|
||||
if (label.length() > 1)
|
||||
if (label.length() >= 1)
|
||||
{
|
||||
rect.UpperLeftCorner.Y -= 15;
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
|
||||
@@ -1850,7 +1857,7 @@ void GUIFormSpecMenu::drawMenu()
|
||||
|
||||
v2u32 screenSize = driver->getScreenSize();
|
||||
core::rect<s32> allbg(0, 0, screenSize.X , screenSize.Y);
|
||||
if (m_bgfullscreen)
|
||||
if (m_bgfullscreen)
|
||||
driver->draw2DRectangle(m_bgcolor, allbg, &allbg);
|
||||
else
|
||||
driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
||||
@@ -1953,7 +1960,7 @@ void GUIFormSpecMenu::drawMenu()
|
||||
IItemDefManager *idef = m_gamedef->idef();
|
||||
ItemStack item;
|
||||
item.deSerialize(spec.name, idef);
|
||||
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
|
||||
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
|
||||
// Image size on screen
|
||||
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
|
||||
// Image rectangle on screen
|
||||
@@ -1992,7 +1999,7 @@ void GUIFormSpecMenu::drawMenu()
|
||||
if (spec.tooltip != "")
|
||||
{
|
||||
core::rect<s32> rect = spec.rect;
|
||||
if (rect.isPointInside(m_pointer))
|
||||
if (rect.isPointInside(m_pointer))
|
||||
{
|
||||
m_tooltip_element->setVisible(true);
|
||||
this->bringToFront(m_tooltip_element);
|
||||
@@ -2162,7 +2169,7 @@ void GUIFormSpecMenu::acceptInput(bool quit=false)
|
||||
for(u32 i=0; i<m_fields.size(); i++)
|
||||
{
|
||||
const FieldSpec &s = m_fields[i];
|
||||
if(s.send)
|
||||
if(s.send)
|
||||
{
|
||||
if(s.ftype == f_Button)
|
||||
{
|
||||
@@ -2188,8 +2195,11 @@ void GUIFormSpecMenu::acceptInput(bool quit=false)
|
||||
if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
|
||||
e = static_cast<gui::IGUIComboBox*>(element);
|
||||
}
|
||||
fields[wide_to_narrow(s.fname.c_str())] =
|
||||
wide_to_narrow(e->getItem(e->getSelected()));
|
||||
s32 selected = e->getSelected();
|
||||
if (selected >= 0) {
|
||||
fields[wide_to_narrow(s.fname.c_str())] =
|
||||
wide_to_narrow(e->getItem(selected));
|
||||
}
|
||||
}
|
||||
else if (s.ftype == f_TabHeader) {
|
||||
// no dynamic cast possible due to some distributions shipped
|
||||
@@ -2462,12 +2472,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
move_amount = 0;
|
||||
}
|
||||
}
|
||||
else if(getAbsoluteClippingRect().isPointInside(m_pointer))
|
||||
{
|
||||
// Clicked somewhere else: deselect
|
||||
m_selected_amount = 0;
|
||||
}
|
||||
else
|
||||
else if (!getAbsoluteClippingRect().isPointInside(m_pointer))
|
||||
{
|
||||
// Clicked outside of the window: drop
|
||||
if(button == 1) // right
|
||||
@@ -2661,7 +2666,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
for(u32 i=0; i<m_fields.size(); i++)
|
||||
{
|
||||
FieldSpec &s = m_fields[i];
|
||||
// if its a button, set the send field so
|
||||
// if its a button, set the send field so
|
||||
// lua knows which button was pressed
|
||||
if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
|
||||
(s.fid == event.GUIEvent.Caller->getID()))
|
||||
@@ -2732,19 +2737,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
||||
static inline bool hex_digit_decode(char hexdigit, unsigned char &value)
|
||||
{
|
||||
if(hexdigit >= '0' && hexdigit <= '9')
|
||||
value = hexdigit - '0';
|
||||
else if(hexdigit >= 'A' && hexdigit <= 'F')
|
||||
value = hexdigit - 'A' + 10;
|
||||
else if(hexdigit >= 'a' && hexdigit <= 'f')
|
||||
value = hexdigit - 'a' + 10;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GUIFormSpecMenu::parseColor(std::string &value, video::SColor &color, bool quiet)
|
||||
{
|
||||
const char *hexpattern = NULL;
|
||||
|
||||
13
src/hex.h
13
src/hex.h
@@ -46,4 +46,17 @@ static inline std::string hex_encode(const std::string &data)
|
||||
return hex_encode(data.c_str(), data.size());
|
||||
}
|
||||
|
||||
static inline bool hex_digit_decode(char hexdigit, unsigned char &value)
|
||||
{
|
||||
if(hexdigit >= '0' && hexdigit <= '9')
|
||||
value = hexdigit - '0';
|
||||
else if(hexdigit >= 'A' && hexdigit <= 'F')
|
||||
value = hexdigit - 'A' + 10;
|
||||
else if(hexdigit >= 'a' && hexdigit <= 'f')
|
||||
value = hexdigit - 'a' + 10;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
731
src/httpfetch.cpp
Normal file
731
src/httpfetch.cpp
Normal file
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "socket.h" // for select()
|
||||
#include "porting.h" // for sleep_ms()
|
||||
#include "httpfetch.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <errno.h>
|
||||
#include "jthread/jevent.h"
|
||||
#include "config.h"
|
||||
#include "exceptions.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "util/container.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
JMutex g_httpfetch_mutex;
|
||||
std::map<unsigned long, std::list<HTTPFetchResult> > g_httpfetch_results;
|
||||
|
||||
static void httpfetch_deliver_result(const HTTPFetchResult &fetchresult)
|
||||
{
|
||||
unsigned long caller = fetchresult.caller;
|
||||
if (caller != HTTPFETCH_DISCARD) {
|
||||
JMutexAutoLock lock(g_httpfetch_mutex);
|
||||
g_httpfetch_results[caller].push_back(fetchresult);
|
||||
}
|
||||
}
|
||||
|
||||
static void httpfetch_request_clear(unsigned long caller);
|
||||
|
||||
unsigned long httpfetch_caller_alloc()
|
||||
{
|
||||
JMutexAutoLock lock(g_httpfetch_mutex);
|
||||
|
||||
// Check each caller ID except HTTPFETCH_DISCARD
|
||||
const unsigned long discard = HTTPFETCH_DISCARD;
|
||||
for (unsigned long caller = discard + 1; caller != discard; ++caller) {
|
||||
std::map<unsigned long, std::list<HTTPFetchResult> >::iterator
|
||||
it = g_httpfetch_results.find(caller);
|
||||
if (it == g_httpfetch_results.end()) {
|
||||
verbosestream<<"httpfetch_caller_alloc: allocating "
|
||||
<<caller<<std::endl;
|
||||
// Access element to create it
|
||||
g_httpfetch_results[caller];
|
||||
return caller;
|
||||
}
|
||||
}
|
||||
|
||||
assert("httpfetch_caller_alloc: ran out of caller IDs" == 0);
|
||||
return discard;
|
||||
}
|
||||
|
||||
void httpfetch_caller_free(unsigned long caller)
|
||||
{
|
||||
verbosestream<<"httpfetch_caller_free: freeing "
|
||||
<<caller<<std::endl;
|
||||
|
||||
httpfetch_request_clear(caller);
|
||||
if (caller != HTTPFETCH_DISCARD) {
|
||||
JMutexAutoLock lock(g_httpfetch_mutex);
|
||||
g_httpfetch_results.erase(caller);
|
||||
}
|
||||
}
|
||||
|
||||
bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetchresult)
|
||||
{
|
||||
JMutexAutoLock lock(g_httpfetch_mutex);
|
||||
|
||||
// Check that caller exists
|
||||
std::map<unsigned long, std::list<HTTPFetchResult> >::iterator
|
||||
it = g_httpfetch_results.find(caller);
|
||||
if (it == g_httpfetch_results.end())
|
||||
return false;
|
||||
|
||||
// Check that result queue is nonempty
|
||||
std::list<HTTPFetchResult> &callerresults = it->second;
|
||||
if (callerresults.empty())
|
||||
return false;
|
||||
|
||||
// Pop first result
|
||||
fetchresult = callerresults.front();
|
||||
callerresults.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
|
||||
/*
|
||||
USE_CURL is on: use cURL based httpfetch implementation
|
||||
*/
|
||||
|
||||
static size_t httpfetch_writefunction(
|
||||
char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
std::ostringstream *stream = (std::ostringstream*)userdata;
|
||||
size_t count = size * nmemb;
|
||||
stream->write(ptr, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static size_t httpfetch_discardfunction(
|
||||
char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
class CurlHandlePool
|
||||
{
|
||||
std::list<CURL*> handles;
|
||||
|
||||
public:
|
||||
CurlHandlePool() {}
|
||||
~CurlHandlePool()
|
||||
{
|
||||
for (std::list<CURL*>::iterator it = handles.begin();
|
||||
it != handles.end(); ++it) {
|
||||
curl_easy_cleanup(*it);
|
||||
}
|
||||
}
|
||||
CURL * alloc()
|
||||
{
|
||||
CURL *curl;
|
||||
if (handles.empty()) {
|
||||
curl = curl_easy_init();
|
||||
if (curl == NULL) {
|
||||
errorstream<<"curl_easy_init returned NULL"<<std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
curl = handles.front();
|
||||
handles.pop_front();
|
||||
}
|
||||
return curl;
|
||||
}
|
||||
void free(CURL *handle)
|
||||
{
|
||||
if (handle)
|
||||
handles.push_back(handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct HTTPFetchOngoing
|
||||
{
|
||||
CurlHandlePool *pool;
|
||||
CURL *curl;
|
||||
CURLM *multi;
|
||||
HTTPFetchRequest request;
|
||||
HTTPFetchResult result;
|
||||
std::ostringstream oss;
|
||||
char *post_fields;
|
||||
struct curl_slist *httpheader;
|
||||
|
||||
HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_):
|
||||
pool(pool_),
|
||||
curl(NULL),
|
||||
multi(NULL),
|
||||
request(request_),
|
||||
result(request_),
|
||||
oss(std::ios::binary),
|
||||
httpheader(NULL)
|
||||
{
|
||||
curl = pool->alloc();
|
||||
if (curl != NULL) {
|
||||
// Set static cURL options
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x071304
|
||||
// Restrict protocols so that curl vulnerabilities in
|
||||
// other protocols don't affect us.
|
||||
// These settings were introduced in curl 7.19.4.
|
||||
long protocols =
|
||||
CURLPROTO_HTTP |
|
||||
CURLPROTO_HTTPS |
|
||||
CURLPROTO_FTP |
|
||||
CURLPROTO_FTPS;
|
||||
curl_easy_setopt(curl, CURLOPT_PROTOCOLS, protocols);
|
||||
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, protocols);
|
||||
#endif
|
||||
|
||||
// Set cURL options based on HTTPFetchRequest
|
||||
curl_easy_setopt(curl, CURLOPT_URL,
|
||||
request.url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS,
|
||||
request.timeout);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS,
|
||||
request.connect_timeout);
|
||||
|
||||
if (request.useragent != "")
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, request.useragent.c_str());
|
||||
|
||||
// Set up a write callback that writes to the
|
||||
// ostringstream ongoing->oss, unless the data
|
||||
// is to be discarded
|
||||
if (request.caller == HTTPFETCH_DISCARD) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
|
||||
httpfetch_discardfunction);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
|
||||
}
|
||||
else {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
|
||||
httpfetch_writefunction);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
|
||||
}
|
||||
// Set POST (or GET) data
|
||||
if (request.post_fields.empty()) {
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
|
||||
}
|
||||
else {
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
|
||||
request.post_fields.size());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
|
||||
request.post_fields.c_str());
|
||||
// request.post_fields must now *never* be
|
||||
// modified until CURLOPT_POSTFIELDS is cleared
|
||||
}
|
||||
// Set additional HTTP headers
|
||||
for (size_t i = 0; i < request.extra_headers.size(); ++i) {
|
||||
httpheader = curl_slist_append(
|
||||
httpheader,
|
||||
request.extra_headers[i].c_str());
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, httpheader);
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode start(CURLM *multi_)
|
||||
{
|
||||
if (curl == NULL)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
if (multi_) {
|
||||
// Multi interface (async)
|
||||
CURLMcode mres = curl_multi_add_handle(multi_, curl);
|
||||
if (mres != CURLM_OK) {
|
||||
errorstream<<"curl_multi_add_handle"
|
||||
<<" returned error code "<<mres
|
||||
<<std::endl;
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
multi = multi_; // store for curl_multi_remove_handle
|
||||
return CURLE_OK;
|
||||
}
|
||||
else {
|
||||
// Easy interface (sync)
|
||||
return curl_easy_perform(curl);
|
||||
}
|
||||
}
|
||||
|
||||
void complete(CURLcode res)
|
||||
{
|
||||
result.succeeded = (res == CURLE_OK);
|
||||
result.timeout = (res == CURLE_OPERATION_TIMEDOUT);
|
||||
result.data = oss.str();
|
||||
|
||||
// Get HTTP/FTP response code
|
||||
result.response_code = 0;
|
||||
if (curl != NULL) {
|
||||
if (curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE,
|
||||
&result.response_code) != CURLE_OK) {
|
||||
//we failed to get a return code make sure it is still 0
|
||||
result.response_code = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
infostream<<request.url<<" not found ("
|
||||
<<curl_easy_strerror(res)<<")"
|
||||
<<" (response code "<<result.response_code<<")"
|
||||
<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
~HTTPFetchOngoing()
|
||||
{
|
||||
if (multi != NULL) {
|
||||
CURLMcode mres = curl_multi_remove_handle(multi, curl);
|
||||
if (mres != CURLM_OK) {
|
||||
errorstream<<"curl_multi_remove_handle"
|
||||
<<" returned error code "<<mres
|
||||
<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Set safe options for the reusable cURL handle
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
|
||||
httpfetch_discardfunction);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
|
||||
if (httpheader != NULL) {
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
|
||||
curl_slist_free_all(httpheader);
|
||||
}
|
||||
|
||||
// Store the cURL handle for reuse
|
||||
pool->free(curl);
|
||||
}
|
||||
};
|
||||
|
||||
class CurlFetchThread : public JThread
|
||||
{
|
||||
protected:
|
||||
enum RequestType {
|
||||
RT_FETCH,
|
||||
RT_CLEAR,
|
||||
RT_WAKEUP,
|
||||
};
|
||||
|
||||
struct Request {
|
||||
RequestType type;
|
||||
HTTPFetchRequest fetchrequest;
|
||||
Event *event;
|
||||
};
|
||||
|
||||
CURLM *m_multi;
|
||||
MutexedQueue<Request> m_requests;
|
||||
size_t m_parallel_limit;
|
||||
|
||||
// Variables exclusively used within thread
|
||||
std::vector<HTTPFetchOngoing*> m_all_ongoing;
|
||||
std::list<HTTPFetchRequest> m_queued_fetches;
|
||||
|
||||
public:
|
||||
CurlFetchThread(int parallel_limit)
|
||||
{
|
||||
if (parallel_limit >= 1)
|
||||
m_parallel_limit = parallel_limit;
|
||||
else
|
||||
m_parallel_limit = 1;
|
||||
}
|
||||
|
||||
void requestFetch(const HTTPFetchRequest &fetchrequest)
|
||||
{
|
||||
Request req;
|
||||
req.type = RT_FETCH;
|
||||
req.fetchrequest = fetchrequest;
|
||||
req.event = NULL;
|
||||
m_requests.push_back(req);
|
||||
}
|
||||
|
||||
void requestClear(unsigned long caller, Event *event)
|
||||
{
|
||||
Request req;
|
||||
req.type = RT_CLEAR;
|
||||
req.fetchrequest.caller = caller;
|
||||
req.event = event;
|
||||
m_requests.push_back(req);
|
||||
}
|
||||
|
||||
void requestWakeUp()
|
||||
{
|
||||
Request req;
|
||||
req.type = RT_WAKEUP;
|
||||
req.event = NULL;
|
||||
m_requests.push_back(req);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Handle a request from some other thread
|
||||
// E.g. new fetch; clear fetches for one caller; wake up
|
||||
void processRequest(const Request &req)
|
||||
{
|
||||
if (req.type == RT_FETCH) {
|
||||
// New fetch, queue until there are less
|
||||
// than m_parallel_limit ongoing fetches
|
||||
m_queued_fetches.push_back(req.fetchrequest);
|
||||
|
||||
// see processQueued() for what happens next
|
||||
|
||||
}
|
||||
else if (req.type == RT_CLEAR) {
|
||||
unsigned long caller = req.fetchrequest.caller;
|
||||
|
||||
// Abort all ongoing fetches for the caller
|
||||
for (std::vector<HTTPFetchOngoing*>::iterator
|
||||
it = m_all_ongoing.begin();
|
||||
it != m_all_ongoing.end();) {
|
||||
if ((*it)->request.caller == caller) {
|
||||
delete (*it);
|
||||
it = m_all_ongoing.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// Also abort all queued fetches for the caller
|
||||
for (std::list<HTTPFetchRequest>::iterator
|
||||
it = m_queued_fetches.begin();
|
||||
it != m_queued_fetches.end();) {
|
||||
if ((*it).caller == caller)
|
||||
it = m_queued_fetches.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
else if (req.type == RT_WAKEUP) {
|
||||
// Wakeup: Nothing to do, thread is awake at this point
|
||||
}
|
||||
|
||||
if (req.event != NULL)
|
||||
req.event->signal();
|
||||
}
|
||||
|
||||
// Start new ongoing fetches if m_parallel_limit allows
|
||||
void processQueued(CurlHandlePool *pool)
|
||||
{
|
||||
while (m_all_ongoing.size() < m_parallel_limit &&
|
||||
!m_queued_fetches.empty()) {
|
||||
HTTPFetchRequest request = m_queued_fetches.front();
|
||||
m_queued_fetches.pop_front();
|
||||
|
||||
// Create ongoing fetch data and make a cURL handle
|
||||
// Set cURL options based on HTTPFetchRequest
|
||||
HTTPFetchOngoing *ongoing =
|
||||
new HTTPFetchOngoing(request, pool);
|
||||
|
||||
// Initiate the connection (curl_multi_add_handle)
|
||||
CURLcode res = ongoing->start(m_multi);
|
||||
if (res == CURLE_OK) {
|
||||
m_all_ongoing.push_back(ongoing);
|
||||
}
|
||||
else {
|
||||
ongoing->complete(res);
|
||||
httpfetch_deliver_result(ongoing->result);
|
||||
delete ongoing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process CURLMsg (indicates completion of a fetch)
|
||||
void processCurlMessage(CURLMsg *msg)
|
||||
{
|
||||
// Determine which ongoing fetch the message pertains to
|
||||
size_t i = 0;
|
||||
bool found = false;
|
||||
for (i = 0; i < m_all_ongoing.size(); ++i) {
|
||||
if (m_all_ongoing[i]->curl == msg->easy_handle) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (msg->msg == CURLMSG_DONE && found) {
|
||||
// m_all_ongoing[i] succeeded or failed.
|
||||
HTTPFetchOngoing *ongoing = m_all_ongoing[i];
|
||||
ongoing->complete(msg->data.result);
|
||||
httpfetch_deliver_result(ongoing->result);
|
||||
delete ongoing;
|
||||
m_all_ongoing.erase(m_all_ongoing.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a request from another thread, or timeout elapses
|
||||
void waitForRequest(long timeout)
|
||||
{
|
||||
if (m_queued_fetches.empty()) {
|
||||
try {
|
||||
Request req = m_requests.pop_front(timeout);
|
||||
processRequest(req);
|
||||
}
|
||||
catch (ItemNotFoundException &e) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until some IO happens, or timeout elapses
|
||||
void waitForIO(long timeout)
|
||||
{
|
||||
fd_set read_fd_set;
|
||||
fd_set write_fd_set;
|
||||
fd_set exc_fd_set;
|
||||
int max_fd;
|
||||
long select_timeout = -1;
|
||||
struct timeval select_tv;
|
||||
CURLMcode mres;
|
||||
|
||||
FD_ZERO(&read_fd_set);
|
||||
FD_ZERO(&write_fd_set);
|
||||
FD_ZERO(&exc_fd_set);
|
||||
|
||||
mres = curl_multi_fdset(m_multi, &read_fd_set,
|
||||
&write_fd_set, &exc_fd_set, &max_fd);
|
||||
if (mres != CURLM_OK) {
|
||||
errorstream<<"curl_multi_fdset"
|
||||
<<" returned error code "<<mres
|
||||
<<std::endl;
|
||||
select_timeout = 0;
|
||||
}
|
||||
|
||||
mres = curl_multi_timeout(m_multi, &select_timeout);
|
||||
if (mres != CURLM_OK) {
|
||||
errorstream<<"curl_multi_timeout"
|
||||
<<" returned error code "<<mres
|
||||
<<std::endl;
|
||||
select_timeout = 0;
|
||||
}
|
||||
|
||||
// Limit timeout so new requests get through
|
||||
if (select_timeout < 0 || select_timeout > timeout)
|
||||
select_timeout = timeout;
|
||||
|
||||
if (select_timeout > 0) {
|
||||
// in Winsock it is forbidden to pass three empty
|
||||
// fd_sets to select(), so in that case use sleep_ms
|
||||
if (max_fd == -1) {
|
||||
select_tv.tv_sec = select_timeout / 1000;
|
||||
select_tv.tv_usec = (select_timeout % 1000) * 1000;
|
||||
int retval = select(max_fd + 1, &read_fd_set,
|
||||
&write_fd_set, &exc_fd_set,
|
||||
&select_tv);
|
||||
if (retval == -1) {
|
||||
#ifdef _WIN32
|
||||
errorstream<<"select returned error code "
|
||||
<<WSAGetLastError()<<std::endl;
|
||||
#else
|
||||
errorstream<<"select returned error code "
|
||||
<<errno<<std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
sleep_ms(select_timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void * Thread()
|
||||
{
|
||||
ThreadStarted();
|
||||
log_register_thread("CurlFetchThread");
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
|
||||
CurlHandlePool pool;
|
||||
|
||||
m_multi = curl_multi_init();
|
||||
if (m_multi == NULL) {
|
||||
errorstream<<"curl_multi_init returned NULL\n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(m_all_ongoing.empty());
|
||||
|
||||
while (!StopRequested()) {
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
/*
|
||||
Handle new async requests
|
||||
*/
|
||||
|
||||
while (!m_requests.empty()) {
|
||||
Request req = m_requests.pop_front();
|
||||
processRequest(req);
|
||||
}
|
||||
processQueued(&pool);
|
||||
|
||||
/*
|
||||
Handle ongoing async requests
|
||||
*/
|
||||
|
||||
int still_ongoing = 0;
|
||||
while (curl_multi_perform(m_multi, &still_ongoing) ==
|
||||
CURLM_CALL_MULTI_PERFORM)
|
||||
/* noop */;
|
||||
|
||||
/*
|
||||
Handle completed async requests
|
||||
*/
|
||||
if (still_ongoing < (int) m_all_ongoing.size()) {
|
||||
CURLMsg *msg;
|
||||
int msgs_in_queue;
|
||||
msg = curl_multi_info_read(m_multi, &msgs_in_queue);
|
||||
while (msg != NULL) {
|
||||
processCurlMessage(msg);
|
||||
msg = curl_multi_info_read(m_multi, &msgs_in_queue);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If there are ongoing requests, wait for data
|
||||
(with a timeout of 100ms so that new requests
|
||||
can be processed).
|
||||
|
||||
If no ongoing requests, wait for a new request.
|
||||
(Possibly an empty request that signals
|
||||
that the thread should be stopped.)
|
||||
*/
|
||||
if (m_all_ongoing.empty())
|
||||
waitForRequest(100000000);
|
||||
else
|
||||
waitForIO(100);
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
||||
}
|
||||
|
||||
// Call curl_multi_remove_handle and cleanup easy handles
|
||||
for (size_t i = 0; i < m_all_ongoing.size(); ++i) {
|
||||
delete m_all_ongoing[i];
|
||||
}
|
||||
m_all_ongoing.clear();
|
||||
|
||||
m_queued_fetches.clear();
|
||||
|
||||
CURLMcode mres = curl_multi_cleanup(m_multi);
|
||||
if (mres != CURLM_OK) {
|
||||
errorstream<<"curl_multi_cleanup"
|
||||
<<" returned error code "<<mres
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
CurlFetchThread *g_httpfetch_thread = NULL;
|
||||
|
||||
void httpfetch_init(int parallel_limit)
|
||||
{
|
||||
verbosestream<<"httpfetch_init: parallel_limit="<<parallel_limit
|
||||
<<std::endl;
|
||||
|
||||
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
assert(res == CURLE_OK);
|
||||
|
||||
g_httpfetch_thread = new CurlFetchThread(parallel_limit);
|
||||
}
|
||||
|
||||
void httpfetch_cleanup()
|
||||
{
|
||||
verbosestream<<"httpfetch_cleanup: cleaning up"<<std::endl;
|
||||
|
||||
g_httpfetch_thread->Stop();
|
||||
g_httpfetch_thread->requestWakeUp();
|
||||
g_httpfetch_thread->Wait();
|
||||
delete g_httpfetch_thread;
|
||||
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
void httpfetch_async(const HTTPFetchRequest &fetchrequest)
|
||||
{
|
||||
g_httpfetch_thread->requestFetch(fetchrequest);
|
||||
if (!g_httpfetch_thread->IsRunning())
|
||||
g_httpfetch_thread->Start();
|
||||
}
|
||||
|
||||
static void httpfetch_request_clear(unsigned long caller)
|
||||
{
|
||||
if (g_httpfetch_thread->IsRunning()) {
|
||||
Event event;
|
||||
g_httpfetch_thread->requestClear(caller, &event);
|
||||
event.wait();
|
||||
}
|
||||
else {
|
||||
g_httpfetch_thread->requestClear(caller, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void httpfetch_sync(const HTTPFetchRequest &fetchrequest,
|
||||
HTTPFetchResult &fetchresult)
|
||||
{
|
||||
// Create ongoing fetch data and make a cURL handle
|
||||
// Set cURL options based on HTTPFetchRequest
|
||||
CurlHandlePool pool;
|
||||
HTTPFetchOngoing ongoing(fetchrequest, &pool);
|
||||
// Do the fetch (curl_easy_perform)
|
||||
CURLcode res = ongoing.start(NULL);
|
||||
// Update fetchresult
|
||||
ongoing.complete(res);
|
||||
fetchresult = ongoing.result;
|
||||
}
|
||||
|
||||
#else // USE_CURL
|
||||
|
||||
/*
|
||||
USE_CURL is off:
|
||||
|
||||
Dummy httpfetch implementation that always returns an error.
|
||||
*/
|
||||
|
||||
void httpfetch_init(int parallel_limit)
|
||||
{
|
||||
}
|
||||
|
||||
void httpfetch_cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void httpfetch_async(const HTTPFetchRequest &fetchrequest)
|
||||
{
|
||||
errorstream<<"httpfetch_async: unable to fetch "<<fetchrequest.url
|
||||
<<" because USE_CURL=0"<<std::endl;
|
||||
|
||||
HTTPFetchResult fetchresult(fetchrequest); // sets succeeded = false etc.
|
||||
httpfetch_deliver_result(fetchresult);
|
||||
}
|
||||
|
||||
static void httpfetch_request_clear(unsigned long caller)
|
||||
{
|
||||
}
|
||||
|
||||
void httpfetch_sync(const HTTPFetchRequest &fetchrequest,
|
||||
HTTPFetchResult &fetchresult)
|
||||
{
|
||||
errorstream<<"httpfetch_sync: unable to fetch "<<fetchrequest.url
|
||||
<<" because USE_CURL=0"<<std::endl;
|
||||
|
||||
fetchresult = HTTPFetchResult(fetchrequest); // sets succeeded = false etc.
|
||||
}
|
||||
|
||||
#endif // USE_CURL
|
||||
131
src/httpfetch.h
Normal file
131
src/httpfetch.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef HTTPFETCH_HEADER
|
||||
#define HTTPFETCH_HEADER
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "config.h"
|
||||
|
||||
// Can be used in place of "caller" in asynchronous transfers to discard result
|
||||
// (used as default value of "caller")
|
||||
#define HTTPFETCH_DISCARD 0
|
||||
#define HTTPFETCH_SYNC 1
|
||||
|
||||
struct HTTPFetchRequest
|
||||
{
|
||||
std::string url;
|
||||
|
||||
// Identifies the caller (for asynchronous requests)
|
||||
// Ignored by httpfetch_sync
|
||||
unsigned long caller;
|
||||
|
||||
// Some number that identifies the request
|
||||
// (when the same caller issues multiple httpfetch_async calls)
|
||||
unsigned long request_id;
|
||||
|
||||
// Timeout for the whole transfer, in milliseconds
|
||||
long timeout;
|
||||
|
||||
// Timeout for the connection phase, in milliseconds
|
||||
long connect_timeout;
|
||||
|
||||
// POST data (should be application/x-www-form-urlencoded
|
||||
// unless a Content-Type header is specified in extra_headers)
|
||||
// If this is empty a GET request is done instead.
|
||||
std::string post_fields;
|
||||
|
||||
// If not empty, should contain entries such as "Accept: text/html"
|
||||
std::vector<std::string> extra_headers;
|
||||
|
||||
//useragent to use
|
||||
std::string useragent;
|
||||
|
||||
HTTPFetchRequest()
|
||||
{
|
||||
url = "";
|
||||
caller = HTTPFETCH_DISCARD;
|
||||
request_id = 0;
|
||||
timeout = 0;
|
||||
connect_timeout = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct HTTPFetchResult
|
||||
{
|
||||
bool succeeded;
|
||||
bool timeout;
|
||||
long response_code;
|
||||
std::string data;
|
||||
// The caller and request_id from the corresponding HTTPFetchRequest.
|
||||
unsigned long caller;
|
||||
unsigned long request_id;
|
||||
|
||||
HTTPFetchResult()
|
||||
{
|
||||
succeeded = false;
|
||||
timeout = false;
|
||||
response_code = 0;
|
||||
data = "";
|
||||
caller = HTTPFETCH_DISCARD;
|
||||
request_id = 0;
|
||||
}
|
||||
|
||||
HTTPFetchResult(const HTTPFetchRequest &fetchrequest)
|
||||
{
|
||||
succeeded = false;
|
||||
timeout = false;
|
||||
response_code = 0;
|
||||
data = "";
|
||||
caller = fetchrequest.caller;
|
||||
request_id = fetchrequest.request_id;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Initializes the httpfetch module
|
||||
void httpfetch_init(int parallel_limit);
|
||||
|
||||
// Stops the httpfetch thread and cleans up resources
|
||||
void httpfetch_cleanup();
|
||||
|
||||
// Starts an asynchronous HTTP fetch request
|
||||
void httpfetch_async(const HTTPFetchRequest &fetchrequest);
|
||||
|
||||
// If any fetch for the given caller ID is complete, removes it from the
|
||||
// result queue, sets fetchresult and returns true. Otherwise returns false.
|
||||
bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetchresult);
|
||||
|
||||
// Allocates a caller ID for httpfetch_async
|
||||
// Not required if you want to set caller = HTTPFETCH_DISCARD
|
||||
unsigned long httpfetch_caller_alloc();
|
||||
|
||||
// Frees a caller ID allocated with httpfetch_caller_alloc
|
||||
// Note: This can be expensive, because the httpfetch thread is told
|
||||
// to stop any ongoing fetches for the given caller.
|
||||
void httpfetch_caller_free(unsigned long caller);
|
||||
|
||||
// Performs a synchronous HTTP request. This blocks and therefore should
|
||||
// only be used from background threads.
|
||||
void httpfetch_sync(const HTTPFetchRequest &fetchrequest,
|
||||
HTTPFetchResult &fetchresult);
|
||||
|
||||
|
||||
#endif // !HTTPFETCH_HEADER
|
||||
@@ -390,16 +390,18 @@ class CItemDefManager: public IWritableItemDefManager
|
||||
/*
|
||||
Make a mesh from the node
|
||||
*/
|
||||
bool reenable_shaders = false;
|
||||
if(g_settings->getBool("enable_shaders")){
|
||||
reenable_shaders = true;
|
||||
g_settings->setBool("enable_shaders",false);
|
||||
}
|
||||
MeshMakeData mesh_make_data(gamedef);
|
||||
MapNode mesh_make_node(id, param1, 0);
|
||||
mesh_make_data.fillSingleNode(&mesh_make_node);
|
||||
MapBlockMesh mapblock_mesh(&mesh_make_data);
|
||||
|
||||
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
|
||||
assert(node_mesh);
|
||||
video::SColor c(255, 255, 255, 255);
|
||||
if(g_settings->getBool("enable_shaders"))
|
||||
c = MapBlock_LightColor(255, 0xffff, decode_light(f.light_source));
|
||||
setMeshColor(node_mesh, c);
|
||||
|
||||
/*
|
||||
@@ -455,6 +457,9 @@ class CItemDefManager: public IWritableItemDefManager
|
||||
|
||||
//no way reference count can be smaller than 2 in this place!
|
||||
assert(cc->wield_mesh->getReferenceCount() >= 2);
|
||||
|
||||
if (reenable_shaders)
|
||||
g_settings->setBool("enable_shaders",true);
|
||||
}
|
||||
|
||||
// Put in cache
|
||||
|
||||
@@ -2,10 +2,14 @@ if( UNIX )
|
||||
set(JTHREAD_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jmutex.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jthread.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jsemaphore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pthread/jevent.cpp
|
||||
PARENT_SCOPE)
|
||||
else( UNIX )
|
||||
set(JTHREAD_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/win32/jmutex.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/win32/jthread.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/win32/jsemaphore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/win32/jevent.cpp
|
||||
PARENT_SCOPE)
|
||||
endif( UNIX )
|
||||
|
||||
52
src/jthread/jevent.h
Normal file
52
src/jthread/jevent.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
|
||||
This file is a part of the JThread package, which contains some object-
|
||||
oriented thread wrappers for different thread implementations.
|
||||
|
||||
Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef JEVENT_H_
|
||||
#define JEVENT_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
|
||||
class Event {
|
||||
#ifdef _WIN32
|
||||
HANDLE hEvent;
|
||||
#else
|
||||
sem_t sem;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Event();
|
||||
~Event();
|
||||
void wait();
|
||||
void signal();
|
||||
};
|
||||
|
||||
#endif /* JEVENT_H_ */
|
||||
@@ -53,10 +53,8 @@ class JMutex
|
||||
public:
|
||||
JMutex();
|
||||
~JMutex();
|
||||
int Init();
|
||||
int Lock();
|
||||
int Unlock();
|
||||
bool IsInitialized() { return initialized; }
|
||||
|
||||
private:
|
||||
#if (defined(WIN32) || defined(_WIN32_WCE))
|
||||
@@ -76,57 +74,6 @@ class JMutex
|
||||
return false;
|
||||
}
|
||||
#endif // WIN32
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
class Event {
|
||||
HANDLE hEvent;
|
||||
|
||||
public:
|
||||
Event() {
|
||||
hEvent = CreateEvent(NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
~Event() {
|
||||
CloseHandle(hEvent);
|
||||
}
|
||||
|
||||
void wait() {
|
||||
WaitForSingleObject(hEvent, INFINITE);
|
||||
}
|
||||
|
||||
void signal() {
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <semaphore.h>
|
||||
|
||||
class Event {
|
||||
sem_t sem;
|
||||
|
||||
public:
|
||||
Event() {
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
~Event() {
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
|
||||
void wait() {
|
||||
sem_wait(&sem);
|
||||
}
|
||||
|
||||
void signal() {
|
||||
sem_post(&sem);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // JMUTEX_H
|
||||
|
||||
50
src/jthread/jsemaphore.h
Normal file
50
src/jthread/jsemaphore.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef JSEMAPHORE_H_
|
||||
#define JSEMAPHORE_H_
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <windows.h>
|
||||
#define MAX_SEMAPHORE_COUNT 1024
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
class JSemaphore {
|
||||
public:
|
||||
JSemaphore();
|
||||
~JSemaphore();
|
||||
JSemaphore(int initval);
|
||||
|
||||
void Post();
|
||||
void Wait();
|
||||
|
||||
int GetValue();
|
||||
|
||||
private:
|
||||
#if defined(WIN32)
|
||||
HANDLE m_hSemaphore;
|
||||
#else
|
||||
sem_t m_semaphore;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* JSEMAPHORE_H_ */
|
||||
@@ -43,11 +43,20 @@ class JThread
|
||||
JThread();
|
||||
virtual ~JThread();
|
||||
int Start();
|
||||
void Stop();
|
||||
int Kill();
|
||||
virtual void *Thread() = 0;
|
||||
bool IsRunning();
|
||||
bool StopRequested();
|
||||
void *GetReturnValue();
|
||||
bool IsSameThread();
|
||||
|
||||
/*
|
||||
* Wait for thread to finish
|
||||
* Note: this does not stop a thread you have to do this on your own
|
||||
* WARNING: never ever call this on a thread not started or already killed!
|
||||
*/
|
||||
void Wait();
|
||||
protected:
|
||||
void ThreadStarted();
|
||||
private:
|
||||
@@ -63,15 +72,17 @@ class JThread
|
||||
HANDLE threadhandle;
|
||||
#else // pthread type threads
|
||||
static void *TheThread(void *param);
|
||||
|
||||
|
||||
pthread_t threadid;
|
||||
|
||||
bool started;
|
||||
#endif // WIN32
|
||||
void *retval;
|
||||
bool running;
|
||||
|
||||
bool requeststop;
|
||||
|
||||
JMutex runningmutex;
|
||||
JMutex continuemutex,continuemutex2;
|
||||
bool mutexinit;
|
||||
};
|
||||
|
||||
#endif // JTHREAD_H
|
||||
|
||||
54
src/jthread/pthread/jevent.cpp
Normal file
54
src/jthread/pthread/jevent.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
|
||||
This file is a part of the JThread package, which contains some object-
|
||||
oriented thread wrappers for different thread implementations.
|
||||
|
||||
Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include "jthread/jevent.h"
|
||||
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
|
||||
Event::Event() {
|
||||
int sem_init_retval = sem_init(&sem, 0, 0);
|
||||
assert(sem_init_retval == 0);
|
||||
UNUSED(sem_init_retval);
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
int sem_destroy_retval = sem_destroy(&sem);
|
||||
assert(sem_destroy_retval == 0);
|
||||
UNUSED(sem_destroy_retval);
|
||||
}
|
||||
|
||||
void Event::wait() {
|
||||
int sem_wait_retval = sem_wait(&sem);
|
||||
assert(sem_wait_retval == 0);
|
||||
UNUSED(sem_wait_retval);
|
||||
}
|
||||
|
||||
void Event::signal() {
|
||||
int sem_post_retval = sem_post(&sem);
|
||||
assert(sem_post_retval == 0);
|
||||
UNUSED(sem_post_retval);
|
||||
}
|
||||
@@ -24,44 +24,35 @@
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "jthread/jmutex.h"
|
||||
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
JMutex::JMutex()
|
||||
{
|
||||
initialized = false;
|
||||
int mutex_init_retval = pthread_mutex_init(&mutex,NULL);
|
||||
assert( mutex_init_retval == 0 );
|
||||
UNUSED(mutex_init_retval);
|
||||
}
|
||||
|
||||
JMutex::~JMutex()
|
||||
{
|
||||
if (initialized)
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
int JMutex::Init()
|
||||
{
|
||||
if (initialized)
|
||||
return ERR_JMUTEX_ALREADYINIT;
|
||||
|
||||
pthread_mutex_init(&mutex,NULL);
|
||||
initialized = true;
|
||||
return 0;
|
||||
int mutex_dextroy_retval = pthread_mutex_destroy(&mutex);
|
||||
assert( mutex_dextroy_retval == 0 );
|
||||
UNUSED(mutex_dextroy_retval);
|
||||
}
|
||||
|
||||
int JMutex::Lock()
|
||||
{
|
||||
if (!initialized)
|
||||
return ERR_JMUTEX_NOTINIT;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
return 0;
|
||||
int mutex_lock_retval = pthread_mutex_lock(&mutex);
|
||||
assert( mutex_lock_retval == 0 );
|
||||
return mutex_lock_retval;
|
||||
UNUSED(mutex_lock_retval);
|
||||
}
|
||||
|
||||
int JMutex::Unlock()
|
||||
{
|
||||
if (!initialized)
|
||||
return ERR_JMUTEX_NOTINIT;
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return 0;
|
||||
int mutex_unlock_retval = pthread_mutex_unlock(&mutex);
|
||||
assert( mutex_unlock_retval == 0 );
|
||||
return mutex_unlock_retval;
|
||||
UNUSED(mutex_unlock_retval);
|
||||
}
|
||||
|
||||
59
src/jthread/pthread/jsemaphore.cpp
Normal file
59
src/jthread/pthread/jsemaphore.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include "jthread/jsemaphore.h"
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
JSemaphore::JSemaphore() {
|
||||
int sem_init_retval = sem_init(&m_semaphore,0,0);
|
||||
assert(sem_init_retval == 0);
|
||||
UNUSED(sem_init_retval);
|
||||
}
|
||||
|
||||
JSemaphore::~JSemaphore() {
|
||||
int sem_destroy_retval = sem_destroy(&m_semaphore);
|
||||
assert(sem_destroy_retval == 0);
|
||||
UNUSED(sem_destroy_retval);
|
||||
}
|
||||
|
||||
JSemaphore::JSemaphore(int initval) {
|
||||
int sem_init_retval = sem_init(&m_semaphore,0,initval);
|
||||
assert(sem_init_retval == 0);
|
||||
UNUSED(sem_init_retval);
|
||||
}
|
||||
|
||||
void JSemaphore::Post() {
|
||||
int sem_post_retval = sem_post(&m_semaphore);
|
||||
assert(sem_post_retval == 0);
|
||||
UNUSED(sem_post_retval);
|
||||
}
|
||||
|
||||
void JSemaphore::Wait() {
|
||||
int sem_wait_retval = sem_wait(&m_semaphore);
|
||||
assert(sem_wait_retval == 0);
|
||||
UNUSED(sem_wait_retval);
|
||||
}
|
||||
|
||||
int JSemaphore::GetValue() {
|
||||
|
||||
int retval = 0;
|
||||
sem_getvalue(&m_semaphore,&retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -26,15 +26,19 @@
|
||||
*/
|
||||
|
||||
#include "jthread/jthread.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
|
||||
JThread::JThread()
|
||||
{
|
||||
retval = NULL;
|
||||
mutexinit = false;
|
||||
requeststop = false;
|
||||
running = false;
|
||||
started = false;
|
||||
}
|
||||
|
||||
JThread::~JThread()
|
||||
@@ -42,58 +46,59 @@ JThread::~JThread()
|
||||
Kill();
|
||||
}
|
||||
|
||||
void JThread::Stop() {
|
||||
runningmutex.Lock();
|
||||
requeststop = true;
|
||||
runningmutex.Unlock();
|
||||
}
|
||||
|
||||
void JThread::Wait() {
|
||||
void* status;
|
||||
runningmutex.Lock();
|
||||
if (started) {
|
||||
runningmutex.Unlock();
|
||||
int pthread_join_retval = pthread_join(threadid,&status);
|
||||
assert(pthread_join_retval == 0);
|
||||
UNUSED(pthread_join_retval);
|
||||
runningmutex.Lock();
|
||||
started = false;
|
||||
}
|
||||
runningmutex.Unlock();
|
||||
}
|
||||
|
||||
int JThread::Start()
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!mutexinit)
|
||||
{
|
||||
if (!runningmutex.IsInitialized())
|
||||
{
|
||||
if (runningmutex.Init() < 0)
|
||||
return ERR_JTHREAD_CANTINITMUTEX;
|
||||
}
|
||||
if (!continuemutex.IsInitialized())
|
||||
{
|
||||
if (continuemutex.Init() < 0)
|
||||
return ERR_JTHREAD_CANTINITMUTEX;
|
||||
}
|
||||
if (!continuemutex2.IsInitialized())
|
||||
{
|
||||
if (continuemutex2.Init() < 0)
|
||||
return ERR_JTHREAD_CANTINITMUTEX;
|
||||
}
|
||||
mutexinit = true;
|
||||
}
|
||||
|
||||
runningmutex.Lock();
|
||||
if (running)
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
return ERR_JTHREAD_ALREADYRUNNING;
|
||||
}
|
||||
requeststop = false;
|
||||
runningmutex.Unlock();
|
||||
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
|
||||
|
||||
//pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
|
||||
|
||||
continuemutex.Lock();
|
||||
status = pthread_create(&threadid,&attr,TheThread,this);
|
||||
status = pthread_create(&threadid,&attr,TheThread,this);
|
||||
pthread_attr_destroy(&attr);
|
||||
if (status != 0)
|
||||
{
|
||||
continuemutex.Unlock();
|
||||
return ERR_JTHREAD_CANTSTARTTHREAD;
|
||||
}
|
||||
|
||||
|
||||
/* Wait until 'running' is set */
|
||||
|
||||
runningmutex.Lock();
|
||||
|
||||
runningmutex.Lock();
|
||||
while (!running)
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
|
||||
|
||||
struct timespec req,rem;
|
||||
|
||||
req.tv_sec = 0;
|
||||
@@ -102,10 +107,11 @@ int JThread::Start()
|
||||
|
||||
runningmutex.Lock();
|
||||
}
|
||||
started = true;
|
||||
runningmutex.Unlock();
|
||||
|
||||
|
||||
continuemutex.Unlock();
|
||||
|
||||
|
||||
continuemutex2.Lock();
|
||||
continuemutex2.Unlock();
|
||||
return 0;
|
||||
@@ -113,13 +119,30 @@ int JThread::Start()
|
||||
|
||||
int JThread::Kill()
|
||||
{
|
||||
runningmutex.Lock();
|
||||
void* status;
|
||||
runningmutex.Lock();
|
||||
if (!running)
|
||||
{
|
||||
if (started) {
|
||||
runningmutex.Unlock();
|
||||
int pthread_join_retval = pthread_join(threadid,&status);
|
||||
assert(pthread_join_retval == 0);
|
||||
UNUSED(pthread_join_retval);
|
||||
runningmutex.Lock();
|
||||
started = false;
|
||||
}
|
||||
runningmutex.Unlock();
|
||||
return ERR_JTHREAD_NOTRUNNING;
|
||||
}
|
||||
pthread_cancel(threadid);
|
||||
if (started) {
|
||||
runningmutex.Unlock();
|
||||
int pthread_join_retval = pthread_join(threadid,&status);
|
||||
assert(pthread_join_retval == 0);
|
||||
UNUSED(pthread_join_retval);
|
||||
runningmutex.Lock();
|
||||
started = false;
|
||||
}
|
||||
running = false;
|
||||
runningmutex.Unlock();
|
||||
return 0;
|
||||
@@ -128,17 +151,26 @@ int JThread::Kill()
|
||||
bool JThread::IsRunning()
|
||||
{
|
||||
bool r;
|
||||
|
||||
runningmutex.Lock();
|
||||
|
||||
runningmutex.Lock();
|
||||
r = running;
|
||||
runningmutex.Unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
bool JThread::StopRequested() {
|
||||
bool r;
|
||||
|
||||
runningmutex.Lock();
|
||||
r = requeststop;
|
||||
runningmutex.Unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
void *JThread::GetReturnValue()
|
||||
{
|
||||
void *val;
|
||||
|
||||
|
||||
runningmutex.Lock();
|
||||
if (running)
|
||||
val = NULL;
|
||||
@@ -157,17 +189,17 @@ void *JThread::TheThread(void *param)
|
||||
{
|
||||
JThread *jthread;
|
||||
void *ret;
|
||||
|
||||
|
||||
jthread = (JThread *)param;
|
||||
|
||||
|
||||
jthread->continuemutex2.Lock();
|
||||
jthread->runningmutex.Lock();
|
||||
jthread->running = true;
|
||||
jthread->runningmutex.Unlock();
|
||||
|
||||
|
||||
jthread->continuemutex.Lock();
|
||||
jthread->continuemutex.Unlock();
|
||||
|
||||
|
||||
ret = jthread->Thread();
|
||||
|
||||
jthread->runningmutex.Lock();
|
||||
|
||||
43
src/jthread/win32/jevent.cpp
Normal file
43
src/jthread/win32/jevent.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
|
||||
This file is a part of the JThread package, which contains some object-
|
||||
oriented thread wrappers for different thread implementations.
|
||||
|
||||
Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#include "jthread/jevent.h"
|
||||
|
||||
Event::Event() {
|
||||
hEvent = CreateEvent(NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
CloseHandle(hEvent);
|
||||
}
|
||||
|
||||
void Event::wait() {
|
||||
WaitForSingleObject(hEvent, INFINITE);
|
||||
}
|
||||
|
||||
void Event::signal() {
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
@@ -24,43 +24,30 @@
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "jthread/jmutex.h"
|
||||
|
||||
JMutex::JMutex()
|
||||
{
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
JMutex::~JMutex()
|
||||
{
|
||||
if (initialized)
|
||||
#ifdef JMUTEX_CRITICALSECTION
|
||||
DeleteCriticalSection(&mutex);
|
||||
#else
|
||||
CloseHandle(mutex);
|
||||
#endif // JMUTEX_CRITICALSECTION
|
||||
}
|
||||
|
||||
int JMutex::Init()
|
||||
{
|
||||
if (initialized)
|
||||
return ERR_JMUTEX_ALREADYINIT;
|
||||
#ifdef JMUTEX_CRITICALSECTION
|
||||
InitializeCriticalSection(&mutex);
|
||||
#else
|
||||
mutex = CreateMutex(NULL,FALSE,NULL);
|
||||
if (mutex == NULL)
|
||||
return ERR_JMUTEX_CANTCREATEMUTEX;
|
||||
assert(mutex != NULL);
|
||||
#endif // JMUTEX_CRITICALSECTION
|
||||
}
|
||||
|
||||
JMutex::~JMutex()
|
||||
{
|
||||
#ifdef JMUTEX_CRITICALSECTION
|
||||
DeleteCriticalSection(&mutex);
|
||||
#else
|
||||
CloseHandle(mutex);
|
||||
#endif // JMUTEX_CRITICALSECTION
|
||||
initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JMutex::Lock()
|
||||
{
|
||||
if (!initialized)
|
||||
return ERR_JMUTEX_NOTINIT;
|
||||
#ifdef JMUTEX_CRITICALSECTION
|
||||
EnterCriticalSection(&mutex);
|
||||
#else
|
||||
@@ -71,8 +58,6 @@ int JMutex::Lock()
|
||||
|
||||
int JMutex::Unlock()
|
||||
{
|
||||
if (!initialized)
|
||||
return ERR_JMUTEX_NOTINIT;
|
||||
#ifdef JMUTEX_CRITICALSECTION
|
||||
LeaveCriticalSection(&mutex);
|
||||
#else
|
||||
|
||||
64
src/jthread/win32/jsemaphore.cpp
Executable file
64
src/jthread/win32/jsemaphore.cpp
Executable file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include "jthread/jsemaphore.h"
|
||||
|
||||
JSemaphore::JSemaphore() {
|
||||
m_hSemaphore = CreateSemaphore(
|
||||
0,
|
||||
0,
|
||||
MAX_SEMAPHORE_COUNT,
|
||||
0);
|
||||
}
|
||||
|
||||
JSemaphore::~JSemaphore() {
|
||||
CloseHandle(m_hSemaphore);
|
||||
}
|
||||
|
||||
JSemaphore::JSemaphore(int initval) {
|
||||
m_hSemaphore = CreateSemaphore(
|
||||
0,
|
||||
initval,
|
||||
MAX_SEMAPHORE_COUNT,
|
||||
0);
|
||||
}
|
||||
|
||||
void JSemaphore::Post() {
|
||||
ReleaseSemaphore(
|
||||
m_hSemaphore,
|
||||
1,
|
||||
0);
|
||||
}
|
||||
|
||||
void JSemaphore::Wait() {
|
||||
WaitForSingleObject(
|
||||
m_hSemaphore,
|
||||
INFINITE);
|
||||
}
|
||||
|
||||
int JSemaphore::GetValue() {
|
||||
|
||||
long int retval = 0;
|
||||
ReleaseSemaphore(
|
||||
m_hSemaphore,
|
||||
0,
|
||||
&retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
83
src/jthread/win32/jthread.cpp
Normal file → Executable file
83
src/jthread/win32/jthread.cpp
Normal file → Executable file
@@ -26,7 +26,8 @@
|
||||
*/
|
||||
|
||||
#include "jthread/jthread.h"
|
||||
|
||||
#include <assert.h>
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
#ifndef _WIN32_WCE
|
||||
#include <process.h>
|
||||
#endif // _WIN32_WCE
|
||||
@@ -34,7 +35,7 @@
|
||||
JThread::JThread()
|
||||
{
|
||||
retval = NULL;
|
||||
mutexinit = false;
|
||||
requeststop = false;
|
||||
running = false;
|
||||
}
|
||||
|
||||
@@ -43,35 +44,36 @@ JThread::~JThread()
|
||||
Kill();
|
||||
}
|
||||
|
||||
void JThread::Stop() {
|
||||
runningmutex.Lock();
|
||||
requeststop = true;
|
||||
runningmutex.Unlock();
|
||||
}
|
||||
|
||||
void JThread::Wait() {
|
||||
runningmutex.Lock();
|
||||
if (running)
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
WaitForSingleObject(threadhandle, INFINITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int JThread::Start()
|
||||
{
|
||||
if (!mutexinit)
|
||||
{
|
||||
if (!runningmutex.IsInitialized())
|
||||
{
|
||||
if (runningmutex.Init() < 0)
|
||||
return ERR_JTHREAD_CANTINITMUTEX;
|
||||
}
|
||||
if (!continuemutex.IsInitialized())
|
||||
{
|
||||
if (continuemutex.Init() < 0)
|
||||
return ERR_JTHREAD_CANTINITMUTEX;
|
||||
}
|
||||
if (!continuemutex2.IsInitialized())
|
||||
{
|
||||
if (continuemutex2.Init() < 0)
|
||||
return ERR_JTHREAD_CANTINITMUTEX;
|
||||
} mutexinit = true;
|
||||
}
|
||||
|
||||
runningmutex.Lock();
|
||||
if (running)
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
return ERR_JTHREAD_ALREADYRUNNING;
|
||||
}
|
||||
requeststop = false;
|
||||
runningmutex.Unlock();
|
||||
|
||||
|
||||
continuemutex.Lock();
|
||||
#ifndef _WIN32_WCE
|
||||
threadhandle = (HANDLE)_beginthreadex(NULL,0,TheThread,this,0,&threadid);
|
||||
@@ -83,10 +85,10 @@ int JThread::Start()
|
||||
continuemutex.Unlock();
|
||||
return ERR_JTHREAD_CANTSTARTTHREAD;
|
||||
}
|
||||
|
||||
|
||||
/* Wait until 'running' is set */
|
||||
|
||||
runningmutex.Lock();
|
||||
runningmutex.Lock();
|
||||
while (!running)
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
@@ -94,18 +96,18 @@ int JThread::Start()
|
||||
runningmutex.Lock();
|
||||
}
|
||||
runningmutex.Unlock();
|
||||
|
||||
|
||||
continuemutex.Unlock();
|
||||
|
||||
|
||||
continuemutex2.Lock();
|
||||
continuemutex2.Unlock();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JThread::Kill()
|
||||
{
|
||||
runningmutex.Lock();
|
||||
runningmutex.Lock();
|
||||
if (!running)
|
||||
{
|
||||
runningmutex.Unlock();
|
||||
@@ -121,17 +123,26 @@ int JThread::Kill()
|
||||
bool JThread::IsRunning()
|
||||
{
|
||||
bool r;
|
||||
|
||||
runningmutex.Lock();
|
||||
|
||||
runningmutex.Lock();
|
||||
r = running;
|
||||
runningmutex.Unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
bool JThread::StopRequested() {
|
||||
bool r;
|
||||
|
||||
runningmutex.Lock();
|
||||
r = requeststop;
|
||||
runningmutex.Unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
void *JThread::GetReturnValue()
|
||||
{
|
||||
void *val;
|
||||
|
||||
|
||||
runningmutex.Lock();
|
||||
if (running)
|
||||
val = NULL;
|
||||
@@ -156,23 +167,23 @@ DWORD WINAPI JThread::TheThread(void *param)
|
||||
void *ret;
|
||||
|
||||
jthread = (JThread *)param;
|
||||
|
||||
|
||||
jthread->continuemutex2.Lock();
|
||||
jthread->runningmutex.Lock();
|
||||
jthread->running = true;
|
||||
jthread->runningmutex.Unlock();
|
||||
|
||||
|
||||
jthread->continuemutex.Lock();
|
||||
jthread->continuemutex.Unlock();
|
||||
|
||||
|
||||
ret = jthread->Thread();
|
||||
|
||||
|
||||
jthread->runningmutex.Lock();
|
||||
jthread->running = false;
|
||||
jthread->retval = ret;
|
||||
CloseHandle(jthread->threadhandle);
|
||||
jthread->runningmutex.Unlock();
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void JThread::ThreadStarted()
|
||||
|
||||
@@ -165,7 +165,9 @@ void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
|
||||
If sneaking, keep in range from the last walked node and don't
|
||||
fall off from it
|
||||
*/
|
||||
if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
|
||||
if(control.sneak && m_sneak_node_exists &&
|
||||
!(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
|
||||
physics_override_sneak)
|
||||
{
|
||||
f32 maxd = 0.5*BS + sneak_max;
|
||||
v3f lwn_f = intToFloat(m_sneak_node, BS);
|
||||
@@ -225,7 +227,7 @@ void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
|
||||
// node.
|
||||
m_need_to_get_new_sneak_node = true;
|
||||
}
|
||||
if(m_need_to_get_new_sneak_node)
|
||||
if(m_need_to_get_new_sneak_node && physics_override_sneak)
|
||||
{
|
||||
v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
|
||||
v2f player_p2df(position.X, position.Z);
|
||||
@@ -264,6 +266,10 @@ void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
|
||||
// And the node above it has to be nonwalkable
|
||||
if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
|
||||
continue;
|
||||
if (!physics_override_sneak_glitch) {
|
||||
if (nodemgr->get(map->getNode(p+v3s16(0,2,0))).walkable)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
@@ -576,6 +582,6 @@ v3s16 LocalPlayer::getStandingNodePos()
|
||||
{
|
||||
if(m_sneak_node_exists)
|
||||
return m_sneak_node;
|
||||
return floatToInt(getPosition(), BS);
|
||||
return floatToInt(getPosition() - v3f(0, BS, 0), BS);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
std::list<ILogOutput*> log_outputs[LMT_NUM_VALUES];
|
||||
std::map<threadid_t, std::string> log_threadnames;
|
||||
JMutex log_threadnamemutex;
|
||||
|
||||
void log_add_output(ILogOutput *out, enum LogMessageLevel lev)
|
||||
{
|
||||
@@ -60,13 +61,17 @@ void log_remove_output(ILogOutput *out)
|
||||
void log_register_thread(const std::string &name)
|
||||
{
|
||||
threadid_t id = get_current_thread_id();
|
||||
log_threadnamemutex.Lock();
|
||||
log_threadnames[id] = name;
|
||||
log_threadnamemutex.Unlock();
|
||||
}
|
||||
|
||||
void log_deregister_thread()
|
||||
{
|
||||
threadid_t id = get_current_thread_id();
|
||||
log_threadnamemutex.Lock();
|
||||
log_threadnames.erase(id);
|
||||
log_threadnamemutex.Unlock();
|
||||
}
|
||||
|
||||
static std::string get_lev_string(enum LogMessageLevel lev)
|
||||
@@ -144,7 +149,7 @@ class Logbuf : public std::streambuf
|
||||
}
|
||||
m_buf += c;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
enum LogMessageLevel m_lev;
|
||||
std::string m_buf;
|
||||
|
||||
116
src/main.cpp
116
src/main.cpp
@@ -77,6 +77,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "subgame.h"
|
||||
#include "quicktune.h"
|
||||
#include "serverlist.h"
|
||||
#include "httpfetch.h"
|
||||
#include "guiEngine.h"
|
||||
#include "mapsector.h"
|
||||
|
||||
@@ -309,7 +310,7 @@ class MyEventReceiver : public IEventReceiver
|
||||
{
|
||||
return keyIsDown[keyCode];
|
||||
}
|
||||
|
||||
|
||||
// Checks whether a key was down and resets the state
|
||||
bool WasKeyDown(const KeyPress &keyCode)
|
||||
{
|
||||
@@ -361,7 +362,7 @@ class MyEventReceiver : public IEventReceiver
|
||||
|
||||
private:
|
||||
IrrlichtDevice *m_device;
|
||||
|
||||
|
||||
// The current state of keys
|
||||
KeyList keyIsDown;
|
||||
// Whether a key has been pressed or not
|
||||
@@ -405,7 +406,7 @@ class RealInputHandler : public InputHandler
|
||||
{
|
||||
return m_receiver->right_active;
|
||||
}
|
||||
|
||||
|
||||
virtual bool getLeftClicked()
|
||||
{
|
||||
return m_receiver->leftclicked;
|
||||
@@ -656,7 +657,7 @@ void SpeedTests()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
infostream<<"All of the following tests should take around 100ms each."
|
||||
<<std::endl;
|
||||
|
||||
@@ -668,7 +669,7 @@ void SpeedTests()
|
||||
tempf += 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TimeTaker timer("Testing floating-point vector speed");
|
||||
|
||||
@@ -682,7 +683,7 @@ void SpeedTests()
|
||||
|
||||
{
|
||||
TimeTaker timer("Testing std::map speed");
|
||||
|
||||
|
||||
std::map<v2s16, f32> map1;
|
||||
tempf = -324;
|
||||
const s16 ii=300;
|
||||
@@ -702,9 +703,8 @@ void SpeedTests()
|
||||
{
|
||||
infostream<<"Around 5000/ms should do well here."<<std::endl;
|
||||
TimeTaker timer("Testing mutex speed");
|
||||
|
||||
|
||||
JMutex m;
|
||||
m.Init();
|
||||
u32 n = 0;
|
||||
u32 i = 0;
|
||||
do{
|
||||
@@ -753,7 +753,7 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
Parse command line
|
||||
*/
|
||||
|
||||
|
||||
// List all allowed options
|
||||
std::map<std::string, ValueSpec> allowed_options;
|
||||
allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
|
||||
@@ -806,7 +806,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
Settings cmd_args;
|
||||
|
||||
|
||||
bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
|
||||
|
||||
if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
|
||||
@@ -843,11 +843,11 @@ int main(int argc, char *argv[])
|
||||
dstream<<"Build info: "<<minetest_build_info<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Low-level initialization
|
||||
*/
|
||||
|
||||
|
||||
// If trace is enabled, enable logging of certain things
|
||||
if(cmd_args.getFlag("trace")){
|
||||
dstream<<_("Enabling trace level debug output")<<std::endl;
|
||||
@@ -865,7 +865,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
porting::signal_handler_init();
|
||||
bool &kill = *porting::signal_handler_killstatus();
|
||||
|
||||
|
||||
porting::initializePaths();
|
||||
|
||||
// Create user data directory
|
||||
@@ -880,7 +880,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Debug handler
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
|
||||
// List gameids if requested
|
||||
if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list")
|
||||
{
|
||||
@@ -890,7 +890,7 @@ int main(int argc, char *argv[])
|
||||
dstream<<(*i)<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// List worlds if requested
|
||||
if(cmd_args.exists("world") && cmd_args.get("world") == "list"){
|
||||
dstream<<_("Available worlds:")<<std::endl;
|
||||
@@ -904,25 +904,25 @@ int main(int argc, char *argv[])
|
||||
" "<<_("with")<<" SER_FMT_VER_HIGHEST_READ="<<(int)SER_FMT_VER_HIGHEST_READ
|
||||
<<", "<<minetest_build_info
|
||||
<<std::endl;
|
||||
|
||||
|
||||
/*
|
||||
Basic initialization
|
||||
*/
|
||||
|
||||
// Initialize default settings
|
||||
set_default_settings(g_settings);
|
||||
|
||||
|
||||
// Initialize sockets
|
||||
sockets_init();
|
||||
atexit(sockets_cleanup);
|
||||
|
||||
|
||||
/*
|
||||
Read config file
|
||||
*/
|
||||
|
||||
|
||||
// Path of configuration file in use
|
||||
g_settings_path = "";
|
||||
|
||||
|
||||
if(cmd_args.exists("config"))
|
||||
{
|
||||
bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
|
||||
@@ -958,12 +958,12 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If no path found, use the first one (menu creates the file)
|
||||
if(g_settings_path == "")
|
||||
g_settings_path = filenames[0];
|
||||
}
|
||||
|
||||
|
||||
// Initialize debug streams
|
||||
#define DEBUGFILE "debug.txt"
|
||||
#if RUN_IN_PLACE
|
||||
@@ -973,7 +973,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
if(cmd_args.exists("logfile"))
|
||||
logfile = cmd_args.get("logfile");
|
||||
|
||||
|
||||
log_remove_output(&main_dstream_no_stderr_log_out);
|
||||
int loglevel = g_settings->getS32("debug_log_level");
|
||||
|
||||
@@ -986,13 +986,16 @@ int main(int argc, char *argv[])
|
||||
debugstreams_init(false, logfile.c_str());
|
||||
else
|
||||
debugstreams_init(false, NULL);
|
||||
|
||||
|
||||
infostream<<"logfile = "<<logfile<<std::endl;
|
||||
|
||||
// Initialize random seed
|
||||
srand(time(0));
|
||||
mysrand(time(0));
|
||||
|
||||
// Initialize HTTP fetcher
|
||||
httpfetch_init(g_settings->getS32("curl_parallel_limit"));
|
||||
|
||||
/*
|
||||
Run unit tests
|
||||
*/
|
||||
@@ -1020,7 +1023,7 @@ int main(int argc, char *argv[])
|
||||
port = g_settings->getU16("port");
|
||||
if(port == 0)
|
||||
port = 30000;
|
||||
|
||||
|
||||
// World directory
|
||||
std::string commanded_world = "";
|
||||
if(cmd_args.exists("world"))
|
||||
@@ -1031,12 +1034,12 @@ int main(int argc, char *argv[])
|
||||
commanded_world = cmd_args.get("nonopt0");
|
||||
else if(g_settings->exists("map-dir"))
|
||||
commanded_world = g_settings->get("map-dir");
|
||||
|
||||
|
||||
// World name
|
||||
std::string commanded_worldname = "";
|
||||
if(cmd_args.exists("worldname"))
|
||||
commanded_worldname = cmd_args.get("worldname");
|
||||
|
||||
|
||||
// Strip world.mt from commanded_world
|
||||
{
|
||||
std::string worldmt = "world.mt";
|
||||
@@ -1048,7 +1051,7 @@ int main(int argc, char *argv[])
|
||||
0, commanded_world.size()-worldmt.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If a world name was specified, convert it to a path
|
||||
if(commanded_worldname != ""){
|
||||
// Get information about available worlds
|
||||
@@ -1268,7 +1271,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
server.start(port);
|
||||
|
||||
|
||||
// Run server
|
||||
dedicated_server_loop(server, kill);
|
||||
|
||||
@@ -1280,17 +1283,17 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
More parameters
|
||||
*/
|
||||
|
||||
|
||||
std::string address = g_settings->get("address");
|
||||
if(commanded_world != "")
|
||||
address = "";
|
||||
else if(cmd_args.exists("address"))
|
||||
address = cmd_args.get("address");
|
||||
|
||||
|
||||
std::string playername = g_settings->get("name");
|
||||
if(cmd_args.exists("name"))
|
||||
playername = cmd_args.get("name");
|
||||
|
||||
|
||||
bool skip_main_menu = cmd_args.getFlag("go");
|
||||
|
||||
/*
|
||||
@@ -1298,7 +1301,7 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
|
||||
// Resolution selection
|
||||
|
||||
|
||||
bool fullscreen = g_settings->getBool("fullscreen");
|
||||
u16 screenW = g_settings->getU16("screenW");
|
||||
u16 screenH = g_settings->getU16("screenH");
|
||||
@@ -1312,7 +1315,7 @@ int main(int argc, char *argv[])
|
||||
// Determine driver
|
||||
|
||||
video::E_DRIVER_TYPE driverType;
|
||||
|
||||
|
||||
std::string driverstring = g_settings->get("video_driver");
|
||||
|
||||
if(driverstring == "null")
|
||||
@@ -1419,7 +1422,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (device == 0)
|
||||
return 1; // could not create selected driver.
|
||||
|
||||
|
||||
/*
|
||||
Continue initialization
|
||||
*/
|
||||
@@ -1434,10 +1437,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Create time getter
|
||||
g_timegetter = new IrrlichtTimeGetter(device);
|
||||
|
||||
|
||||
// Create game callback for menus
|
||||
g_gamecallback = new MainGameCallback(device);
|
||||
|
||||
|
||||
/*
|
||||
Speed tests (done after irrlicht is loaded to get timer)
|
||||
*/
|
||||
@@ -1448,7 +1451,7 @@ int main(int argc, char *argv[])
|
||||
device->drop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
device->setResizable(true);
|
||||
|
||||
bool random_input = g_settings->getBool("random_input")
|
||||
@@ -1458,7 +1461,7 @@ int main(int argc, char *argv[])
|
||||
input = new RandomInputHandler();
|
||||
else
|
||||
input = new RealInputHandler(device, &receiver);
|
||||
|
||||
|
||||
scene::ISceneManager* smgr = device->getSceneManager();
|
||||
|
||||
guienv = device->getGUIEnvironment();
|
||||
@@ -1473,7 +1476,9 @@ int main(int argc, char *argv[])
|
||||
fallback = "fallback_";
|
||||
u16 font_size = g_settings->getU16(fallback + "font_size");
|
||||
font_path = g_settings->get(fallback + "font_path");
|
||||
font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size);
|
||||
u32 font_shadow = g_settings->getU16(fallback + "font_shadow");
|
||||
u32 font_shadow_alpha = g_settings->getU16(fallback + "font_shadow_alpha");
|
||||
font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size, true, true, font_shadow, font_shadow_alpha);
|
||||
} else {
|
||||
font = guienv->getFont(font_path.c_str());
|
||||
}
|
||||
@@ -1488,7 +1493,7 @@ int main(int argc, char *argv[])
|
||||
// If font was not found, this will get us one
|
||||
font = skin->getFont();
|
||||
assert(font);
|
||||
|
||||
|
||||
u32 text_height = font->getDimension(L"Hello, world!").Height;
|
||||
infostream<<"text_height="<<text_height<<std::endl;
|
||||
|
||||
@@ -1556,7 +1561,7 @@ int main(int argc, char *argv[])
|
||||
Clear everything from the GUIEnvironment
|
||||
*/
|
||||
guienv->clear();
|
||||
|
||||
|
||||
/*
|
||||
We need some kind of a root node to be able to add
|
||||
custom gui elements directly on the screen.
|
||||
@@ -1564,7 +1569,7 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
guiroot = guienv->addStaticText(L"",
|
||||
core::rect<s32>(0, 0, 10000, 10000));
|
||||
|
||||
|
||||
SubgameSpec gamespec;
|
||||
WorldSpec worldspec;
|
||||
bool simple_singleplayer_mode = false;
|
||||
@@ -1588,13 +1593,13 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
first_loop = false;
|
||||
|
||||
|
||||
// Cursor can be non-visible when coming from the game
|
||||
device->getCursorControl()->setVisible(true);
|
||||
// Some stuff are left to scene manager when coming from the game
|
||||
// (map at least?)
|
||||
smgr->clear();
|
||||
|
||||
|
||||
// Initialize menu data
|
||||
MainMenuData menudata;
|
||||
menudata.address = address;
|
||||
@@ -1643,7 +1648,7 @@ int main(int argc, char *argv[])
|
||||
infostream<<"Waited for other menus"<<std::endl;
|
||||
|
||||
GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata,kill);
|
||||
|
||||
|
||||
delete temp;
|
||||
//once finished you'll never end up here
|
||||
smgr->clear();
|
||||
@@ -1674,8 +1679,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Save settings
|
||||
g_settings->set("name", playername);
|
||||
g_settings->set("address", address);
|
||||
g_settings->set("port", itos(port));
|
||||
|
||||
if((menudata.selected_world >= 0) &&
|
||||
(menudata.selected_world < (int)worldspecs.size()))
|
||||
@@ -1685,7 +1688,7 @@ int main(int argc, char *argv[])
|
||||
// Break out of menu-game loop to shut down cleanly
|
||||
if(device->run() == false || kill == true)
|
||||
break;
|
||||
|
||||
|
||||
current_playername = playername;
|
||||
current_password = password;
|
||||
current_address = address;
|
||||
@@ -1707,7 +1710,7 @@ int main(int argc, char *argv[])
|
||||
server["description"] = menudata.serverdescription;
|
||||
ServerList::insert(server);
|
||||
}
|
||||
|
||||
|
||||
// Set world path to selected one
|
||||
if ((menudata.selected_world >= 0) &&
|
||||
(menudata.selected_world < (int)worldspecs.size())) {
|
||||
@@ -1715,7 +1718,7 @@ int main(int argc, char *argv[])
|
||||
infostream<<"Selected world: "<<worldspec.name
|
||||
<<" ["<<worldspec.path<<"]"<<std::endl;
|
||||
}
|
||||
|
||||
|
||||
// If local game
|
||||
if(current_address == "")
|
||||
{
|
||||
@@ -1830,11 +1833,11 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
#endif // !SERVER
|
||||
|
||||
|
||||
// Update configuration file
|
||||
if(g_settings_path != "")
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
|
||||
|
||||
// Print modified quicktune values
|
||||
{
|
||||
bool header_printed = false;
|
||||
@@ -1851,10 +1854,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// Stop httpfetch thread (if started)
|
||||
httpfetch_cleanup();
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
||||
|
||||
|
||||
debugstreams_deinit();
|
||||
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
129
src/map.cpp
129
src/map.cpp
@@ -75,8 +75,6 @@ Map::Map(std::ostream &dout, IGameDef *gamedef):
|
||||
m_gamedef(gamedef),
|
||||
m_sector_cache(NULL)
|
||||
{
|
||||
/*m_sector_mutex.Init();
|
||||
assert(m_sector_mutex.IsInitialized());*/
|
||||
}
|
||||
|
||||
Map::~Map()
|
||||
@@ -931,7 +929,8 @@ void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
|
||||
/*
|
||||
*/
|
||||
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
std::map<v3s16, MapBlock*> &modified_blocks)
|
||||
std::map<v3s16, MapBlock*> &modified_blocks,
|
||||
bool remove_metadata)
|
||||
{
|
||||
INodeDefManager *ndef = m_gamedef->ndef();
|
||||
|
||||
@@ -1018,8 +1017,9 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
/*
|
||||
Remove node metadata
|
||||
*/
|
||||
|
||||
removeNodeMetadata(p);
|
||||
if (remove_metadata) {
|
||||
removeNodeMetadata(p);
|
||||
}
|
||||
|
||||
/*
|
||||
Set the node on the map
|
||||
@@ -1319,17 +1319,17 @@ void Map::removeNodeAndUpdate(v3s16 p,
|
||||
}
|
||||
}
|
||||
|
||||
bool Map::addNodeWithEvent(v3s16 p, MapNode n)
|
||||
bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
|
||||
{
|
||||
MapEditEvent event;
|
||||
event.type = MEET_ADDNODE;
|
||||
event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
|
||||
event.p = p;
|
||||
event.n = n;
|
||||
|
||||
bool succeeded = true;
|
||||
try{
|
||||
std::map<v3s16, MapBlock*> modified_blocks;
|
||||
addNodeAndUpdate(p, n, modified_blocks);
|
||||
addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
|
||||
|
||||
// Copy modified_blocks to event
|
||||
for(std::map<v3s16, MapBlock*>::iterator
|
||||
@@ -1679,7 +1679,7 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
v3s16 p0 = m_transforming_liquid.pop_front();
|
||||
u16 total_level = 0;
|
||||
// surrounding flowing liquid nodes
|
||||
NodeNeighbor neighbors[7];
|
||||
NodeNeighbor neighbors[7];
|
||||
// current level of every block
|
||||
s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
|
||||
// target levels
|
||||
@@ -1780,8 +1780,8 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
|
||||
total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
|
||||
(can_liquid_same_level - relax) &&
|
||||
can_liquid_same_level >= relax + 1) {
|
||||
total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
|
||||
can_liquid_same_level >= relax + 1) {
|
||||
total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
|
||||
}
|
||||
|
||||
// prevent lakes in air above unloaded blocks
|
||||
@@ -1790,9 +1790,9 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
}
|
||||
|
||||
// calculate self level 5 blocks
|
||||
u8 want_level =
|
||||
u8 want_level =
|
||||
total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
|
||||
? LIQUID_LEVEL_SOURCE
|
||||
? LIQUID_LEVEL_SOURCE
|
||||
: total_level / can_liquid_same_level;
|
||||
total_level -= want_level * can_liquid_same_level;
|
||||
|
||||
@@ -1850,7 +1850,7 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
|
||||
/*
|
||||
if (total_level > 0) //|| flowed != volume)
|
||||
infostream <<" AFTER level=" << (int)total_level
|
||||
infostream <<" AFTER level=" << (int)total_level
|
||||
//<< " flowed="<<flowed<< " volume=" << volume
|
||||
<< " wantsame="<<(int)want_level<< " top="
|
||||
<< (int)liquid_levels_want[D_TOP]<< " topwas="
|
||||
@@ -1860,7 +1860,7 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
|
||||
//u8 changed = 0;
|
||||
for (u16 i = 0; i < 7; i++) {
|
||||
if (liquid_levels_want[i] < 0 || !neighbors[i].l)
|
||||
if (liquid_levels_want[i] < 0 || !neighbors[i].l)
|
||||
continue;
|
||||
MapNode & n0 = neighbors[i].n;
|
||||
p0 = neighbors[i].p;
|
||||
@@ -1907,7 +1907,7 @@ void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
*/
|
||||
/*
|
||||
if (
|
||||
new_node_content == n0.getContent()
|
||||
new_node_content == n0.getContent()
|
||||
&& (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
|
||||
(n0.getLevel(nodemgr) == (u8)new_node_level
|
||||
//&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
|
||||
@@ -2279,7 +2279,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
|
||||
updateLighting(lighting_modified_blocks, modified_blocks);
|
||||
}
|
||||
|
||||
NodeMetadata* Map::getNodeMetadata(v3s16 p)
|
||||
NodeMetadata *Map::getNodeMetadata(v3s16 p)
|
||||
{
|
||||
v3s16 blockpos = getNodeBlockPos(p);
|
||||
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
||||
@@ -2289,8 +2289,7 @@ NodeMetadata* Map::getNodeMetadata(v3s16 p)
|
||||
<<PP(blockpos)<<std::endl;
|
||||
block = emergeBlock(blockpos, false);
|
||||
}
|
||||
if(!block)
|
||||
{
|
||||
if(!block){
|
||||
infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
|
||||
<<std::endl;
|
||||
return NULL;
|
||||
@@ -2299,7 +2298,7 @@ NodeMetadata* Map::getNodeMetadata(v3s16 p)
|
||||
return meta;
|
||||
}
|
||||
|
||||
void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
|
||||
bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
|
||||
{
|
||||
v3s16 blockpos = getNodeBlockPos(p);
|
||||
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
||||
@@ -2309,13 +2308,13 @@ void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
|
||||
<<PP(blockpos)<<std::endl;
|
||||
block = emergeBlock(blockpos, false);
|
||||
}
|
||||
if(!block)
|
||||
{
|
||||
if(!block){
|
||||
infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
|
||||
<<std::endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
block->m_node_metadata.set(p_rel, meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Map::removeNodeMetadata(v3s16 p)
|
||||
@@ -2342,8 +2341,7 @@ NodeTimer Map::getNodeTimer(v3s16 p)
|
||||
<<PP(blockpos)<<std::endl;
|
||||
block = emergeBlock(blockpos, false);
|
||||
}
|
||||
if(!block)
|
||||
{
|
||||
if(!block){
|
||||
infostream<<"WARNING: Map::getNodeTimer(): Block not found"
|
||||
<<std::endl;
|
||||
return NodeTimer();
|
||||
@@ -2362,8 +2360,7 @@ void Map::setNodeTimer(v3s16 p, NodeTimer t)
|
||||
<<PP(blockpos)<<std::endl;
|
||||
block = emergeBlock(blockpos, false);
|
||||
}
|
||||
if(!block)
|
||||
{
|
||||
if(!block){
|
||||
infostream<<"WARNING: Map::setNodeTimer(): Block not found"
|
||||
<<std::endl;
|
||||
return;
|
||||
@@ -3188,48 +3185,56 @@ void ServerMap::prepareBlock(MapBlock *block) {
|
||||
}
|
||||
}
|
||||
|
||||
s16 ServerMap::findGroundLevel(v2s16 p2d)
|
||||
/**
|
||||
* Get the ground level by searching for a non CONTENT_AIR node in a column from top to bottom
|
||||
*/
|
||||
s16 ServerMap::findGroundLevel(v2s16 p2d, bool cacheBlocks)
|
||||
{
|
||||
#if 0
|
||||
/*
|
||||
Uh, just do something random...
|
||||
*/
|
||||
// Find existing map from top to down
|
||||
s16 max=63;
|
||||
s16 min=-64;
|
||||
v3s16 p(p2d.X, max, p2d.Y);
|
||||
for(; p.Y>min; p.Y--)
|
||||
|
||||
s16 level;
|
||||
|
||||
// The reference height is the original mapgen height
|
||||
s16 referenceHeight = m_emerge->getGroundLevelAtPoint(p2d);
|
||||
s16 maxSearchHeight = 63 + referenceHeight;
|
||||
s16 minSearchHeight = -63 + referenceHeight;
|
||||
v3s16 probePosition(p2d.X, maxSearchHeight, p2d.Y);
|
||||
v3s16 blockPosition = getNodeBlockPos(probePosition);
|
||||
v3s16 prevBlockPosition = blockPosition;
|
||||
|
||||
// Cache the block to be inspected.
|
||||
if(cacheBlocks) {
|
||||
emergeBlock(blockPosition, true);
|
||||
}
|
||||
|
||||
// Probes the nodes in the given column
|
||||
for(; probePosition.Y > minSearchHeight; probePosition.Y--)
|
||||
{
|
||||
MapNode n = getNodeNoEx(p);
|
||||
if(n.getContent() != CONTENT_IGNORE)
|
||||
if(cacheBlocks) {
|
||||
// Calculate the block position of the given node
|
||||
blockPosition = getNodeBlockPos(probePosition);
|
||||
|
||||
// If the node is in an different block, cache it
|
||||
if(blockPosition != prevBlockPosition) {
|
||||
emergeBlock(blockPosition, true);
|
||||
prevBlockPosition = blockPosition;
|
||||
}
|
||||
}
|
||||
|
||||
MapNode node = getNodeNoEx(probePosition);
|
||||
if (node.getContent() != CONTENT_IGNORE &&
|
||||
node.getContent() != CONTENT_AIR) {
|
||||
break;
|
||||
}
|
||||
if(p.Y == min)
|
||||
goto plan_b;
|
||||
// If this node is not air, go to plan b
|
||||
if(getNodeNoEx(p).getContent() != CONTENT_AIR)
|
||||
goto plan_b;
|
||||
// Search existing walkable and return it
|
||||
for(; p.Y>min; p.Y--)
|
||||
{
|
||||
MapNode n = getNodeNoEx(p);
|
||||
if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
|
||||
return p.Y;
|
||||
}
|
||||
}
|
||||
|
||||
// Move to plan b
|
||||
plan_b:
|
||||
#endif
|
||||
// Could not determine the ground. Use map generator noise functions.
|
||||
if(probePosition.Y == minSearchHeight) {
|
||||
level = referenceHeight;
|
||||
} else {
|
||||
level = probePosition.Y;
|
||||
}
|
||||
|
||||
/*
|
||||
Determine from map generator noise functions
|
||||
*/
|
||||
|
||||
s16 level = m_emerge->getGroundLevelAtPoint(p2d);
|
||||
return level;
|
||||
|
||||
//double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
|
||||
//return (s16)level;
|
||||
}
|
||||
|
||||
bool ServerMap::loadFromFolders() {
|
||||
|
||||
29
src/map.h
29
src/map.h
@@ -61,6 +61,8 @@ enum MapEditEventType{
|
||||
MEET_ADDNODE,
|
||||
// Node removed (changed to air)
|
||||
MEET_REMOVENODE,
|
||||
// Node swapped (changed without metadata change)
|
||||
MEET_SWAPNODE,
|
||||
// Node metadata of block changed (not knowing which node exactly)
|
||||
// p stores block coordinate
|
||||
MEET_BLOCK_NODE_METADATA_CHANGED,
|
||||
@@ -99,6 +101,8 @@ struct MapEditEvent
|
||||
return VoxelArea(p);
|
||||
case MEET_REMOVENODE:
|
||||
return VoxelArea(p);
|
||||
case MEET_SWAPNODE:
|
||||
return VoxelArea(p);
|
||||
case MEET_BLOCK_NODE_METADATA_CHANGED:
|
||||
{
|
||||
v3s16 np1 = p*MAP_BLOCKSIZE;
|
||||
@@ -236,7 +240,8 @@ class Map /*: public NodeContainer*/
|
||||
These handle lighting but not faces.
|
||||
*/
|
||||
void addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
std::map<v3s16, MapBlock*> &modified_blocks);
|
||||
std::map<v3s16, MapBlock*> &modified_blocks,
|
||||
bool remove_metadata = true);
|
||||
void removeNodeAndUpdate(v3s16 p,
|
||||
std::map<v3s16, MapBlock*> &modified_blocks);
|
||||
|
||||
@@ -245,7 +250,7 @@ class Map /*: public NodeContainer*/
|
||||
These emit events.
|
||||
Return true if succeeded, false if not.
|
||||
*/
|
||||
bool addNodeWithEvent(v3s16 p, MapNode n);
|
||||
bool addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata = true);
|
||||
bool removeNodeWithEvent(v3s16 p);
|
||||
|
||||
/*
|
||||
@@ -307,7 +312,22 @@ class Map /*: public NodeContainer*/
|
||||
*/
|
||||
|
||||
NodeMetadata* getNodeMetadata(v3s16 p);
|
||||
void setNodeMetadata(v3s16 p, NodeMetadata *meta);
|
||||
|
||||
/**
|
||||
* Sets metadata for a node.
|
||||
* This method sets the metadata for a given node.
|
||||
* On success, it returns @c true and the object pointed to
|
||||
* by @p meta is then managed by the system and should
|
||||
* not be deleted by the caller.
|
||||
*
|
||||
* In case of failure, the method returns @c false and the
|
||||
* caller is still responsible for deleting the object!
|
||||
*
|
||||
* @param p node coordinates
|
||||
* @param meta pointer to @c NodeMetadata object
|
||||
* @return @c true on success, false on failure
|
||||
*/
|
||||
bool setNodeMetadata(v3s16 p, NodeMetadata *meta);
|
||||
void removeNodeMetadata(v3s16 p);
|
||||
|
||||
/*
|
||||
@@ -408,7 +428,7 @@ class ServerMap : public Map
|
||||
void prepareBlock(MapBlock *block);
|
||||
|
||||
// Helper for placing objects on ground level
|
||||
s16 findGroundLevel(v2s16 p2d);
|
||||
s16 findGroundLevel(v2s16 p2d, bool cacheBlocks);
|
||||
|
||||
/*
|
||||
Misc. helper functions for fiddling with directory and file
|
||||
@@ -476,6 +496,7 @@ class ServerMap : public Map
|
||||
u64 getSeed(){ return m_seed; }
|
||||
|
||||
MapgenParams *getMapgenParams(){ return m_mgparams; }
|
||||
void setMapgenParams(MapgenParams *mgparams){ m_mgparams = mgparams; }
|
||||
|
||||
// Parameters fed to the Mapgen
|
||||
MapgenParams *m_mgparams;
|
||||
|
||||
@@ -68,7 +68,6 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
|
||||
reallocate();
|
||||
|
||||
#ifndef SERVER
|
||||
//mesh_mutex.Init();
|
||||
mesh = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1111,19 +1111,20 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
|
||||
*/
|
||||
bool enable_shaders = g_settings->getBool("enable_shaders");
|
||||
bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
|
||||
bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
|
||||
|
||||
video::E_MATERIAL_TYPE shadermat1, shadermat2, shadermat3, bumpmaps1, bumpmaps2;
|
||||
shadermat1 = shadermat2 = shadermat3 = bumpmaps1 = bumpmaps2 = video::EMT_SOLID;
|
||||
video::E_MATERIAL_TYPE shadermat1, shadermat2, shadermat3,
|
||||
shadermat4, shadermat5;
|
||||
shadermat1 = shadermat2 = shadermat3 = shadermat4 = shadermat5 =
|
||||
video::EMT_SOLID;
|
||||
|
||||
if (enable_shaders) {
|
||||
IShaderSource *shdrsrc = m_gamedef->getShaderSource();
|
||||
shadermat1 = shdrsrc->getShader("test_shader_1").material;
|
||||
shadermat2 = shdrsrc->getShader("test_shader_2").material;
|
||||
shadermat3 = shdrsrc->getShader("test_shader_3").material;
|
||||
if (enable_bumpmapping) {
|
||||
bumpmaps1 = shdrsrc->getShader("bumpmaps_solids").material;
|
||||
bumpmaps2 = shdrsrc->getShader("bumpmaps_liquids").material;
|
||||
}
|
||||
shadermat1 = shdrsrc->getShader("solids_shader").material;
|
||||
shadermat2 = shdrsrc->getShader("liquids_shader").material;
|
||||
shadermat3 = shdrsrc->getShader("alpha_shader").material;
|
||||
shadermat4 = shdrsrc->getShader("leaves_shader").material;
|
||||
shadermat5 = shdrsrc->getShader("plants_shader").material;
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < collector.prebuffers.size(); i++)
|
||||
@@ -1204,22 +1205,18 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
|
||||
material.setFlag(video::EMF_FOG_ENABLE, true);
|
||||
//material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
|
||||
//material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
|
||||
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
||||
//material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
||||
material.setTexture(0, p.tile.texture);
|
||||
|
||||
if (enable_shaders) {
|
||||
video::E_MATERIAL_TYPE smat1 = shadermat1;
|
||||
video::E_MATERIAL_TYPE smat2 = shadermat2;
|
||||
video::E_MATERIAL_TYPE smat3 = shadermat3;
|
||||
|
||||
if (enable_bumpmapping) {
|
||||
ITextureSource *tsrc = data->m_gamedef->tsrc();
|
||||
std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
|
||||
|
||||
if (enable_shaders) {
|
||||
ITextureSource *tsrc = data->m_gamedef->tsrc();
|
||||
material.setTexture(2, tsrc->getTexture("disable_img.png"));
|
||||
if (enable_bumpmapping || enable_parallax_occlusion) {
|
||||
std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
|
||||
std::string normal_ext = "_normal.png";
|
||||
size_t pos = fname_base.find(".");
|
||||
std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
|
||||
|
||||
|
||||
if (tsrc->isKnownSourceImage(fname_normal)) {
|
||||
// look for image extension and replace it
|
||||
size_t i = 0;
|
||||
@@ -1227,19 +1224,15 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
|
||||
fname_base.replace(i, 4, normal_ext);
|
||||
i += normal_ext.length();
|
||||
}
|
||||
|
||||
material.setTexture(1, tsrc->getTexture(fname_base));
|
||||
|
||||
smat1 = bumpmaps1;
|
||||
smat2 = bumpmaps2;
|
||||
material.setTexture(2, tsrc->getTexture("enable_img.png"));
|
||||
}
|
||||
}
|
||||
|
||||
p.tile.applyMaterialOptionsWithShaders(material, smat1, smat2, smat3);
|
||||
p.tile.applyMaterialOptionsWithShaders(material,
|
||||
shadermat1, shadermat2, shadermat3, shadermat4, shadermat5);
|
||||
} else {
|
||||
p.tile.applyMaterialOptions(material);
|
||||
}
|
||||
|
||||
// Create meshbuffer
|
||||
|
||||
// This is a "Standard MeshBuffer",
|
||||
@@ -1300,7 +1293,8 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
|
||||
{
|
||||
bool enable_shaders = g_settings->getBool("enable_shaders");
|
||||
bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
|
||||
|
||||
bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
|
||||
|
||||
if(!m_has_animation)
|
||||
{
|
||||
m_animation_force_timer = 100000;
|
||||
@@ -1369,18 +1363,21 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
|
||||
os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
|
||||
// Set the texture
|
||||
buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
|
||||
if (enable_shaders && enable_bumpmapping)
|
||||
buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
|
||||
if (enable_shaders && (enable_bumpmapping || enable_parallax_occlusion))
|
||||
{
|
||||
std::string basename,normal;
|
||||
basename = tsrc->getTextureName(tile.texture_id);
|
||||
std::string fname_base,fname_normal;
|
||||
fname_base = tsrc->getTextureName(tile.texture_id);
|
||||
unsigned pos;
|
||||
pos = basename.find(".");
|
||||
normal = basename.substr (0, pos);
|
||||
normal += "_normal.png";
|
||||
os.str("");
|
||||
os<<normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
|
||||
if (tsrc->isKnownSourceImage(normal))
|
||||
pos = fname_base.find(".");
|
||||
fname_normal = fname_base.substr (0, pos);
|
||||
fname_normal += "_normal.png";
|
||||
if (tsrc->isKnownSourceImage(fname_normal)){
|
||||
os.str("");
|
||||
os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
|
||||
buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
|
||||
buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user