Compare commits
191 Commits
0.4.dev-20
...
0.4.dev-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bea1a944e | ||
|
|
3e419ffb38 | ||
|
|
a149c6ecde | ||
|
|
3f18e9a2da | ||
|
|
4b0494062e | ||
|
|
a09d86dd3c | ||
|
|
3ccb0f691b | ||
|
|
1709fd854a | ||
|
|
7bf446f671 | ||
|
|
e4bc76f55c | ||
|
|
81c5e5200b | ||
|
|
9c3830dd35 | ||
|
|
bf8cfce50e | ||
|
|
b235e4d290 | ||
|
|
b8cf6dee3e | ||
|
|
35df1ee253 | ||
|
|
353f9fd43c | ||
|
|
3899f83da8 | ||
|
|
5f3c70061d | ||
|
|
aba7134301 | ||
|
|
d7447cdf9e | ||
|
|
ff8d2bbc36 | ||
|
|
70c98c9972 | ||
|
|
45f32838b9 | ||
|
|
7d6e80a423 | ||
|
|
f3ec1452cd | ||
|
|
29f03756bd | ||
|
|
fe8c5546f0 | ||
|
|
c4315a7afa | ||
|
|
704782c95b | ||
|
|
67059e1932 | ||
|
|
e2ea711136 | ||
|
|
74aa598769 | ||
|
|
747ff7daf8 | ||
|
|
1f670fc688 | ||
|
|
388906ac70 | ||
|
|
49d9745dc4 | ||
|
|
7c15d793ac | ||
|
|
d90cb3511c | ||
|
|
5100272dc3 | ||
|
|
afda9810f1 | ||
|
|
22df8b81b3 | ||
|
|
7a427c9070 | ||
|
|
a35e4bdbda | ||
|
|
0aeb5adb8f | ||
|
|
9c5cc217bf | ||
|
|
24603d7ad3 | ||
|
|
1eac6ff8ff | ||
|
|
8742b3dab2 | ||
|
|
93cdc9b9dc | ||
|
|
8ed74a3429 | ||
|
|
07a5e59ae7 | ||
|
|
f8c2f1cf54 | ||
|
|
b1f043d010 | ||
|
|
251c0c8508 | ||
|
|
3214daca4c | ||
|
|
e4485f5412 | ||
|
|
4eaaa0ec53 | ||
|
|
42323014ea | ||
|
|
c59d139eeb | ||
|
|
2b4d21160f | ||
|
|
e29a5b11e4 | ||
|
|
b58ee690a3 | ||
|
|
d8edd3d0c8 | ||
|
|
1b078efd5f | ||
|
|
e8e73d37fb | ||
|
|
2eec788544 | ||
|
|
dca28b9e12 | ||
|
|
e879c92596 | ||
|
|
7281c68b27 | ||
|
|
d228f1b658 | ||
|
|
2e60593c5b | ||
|
|
531fe10f51 | ||
|
|
454b07455a | ||
|
|
a4dc6f2c76 | ||
|
|
5608f12f9b | ||
|
|
18d8e3acec | ||
|
|
578fc5bb44 | ||
|
|
e8660cfd8e | ||
|
|
6a57eabb14 | ||
|
|
a67540807a | ||
|
|
20c377ca65 | ||
|
|
5436f1c7d5 | ||
|
|
4107390337 | ||
|
|
e74da72b82 | ||
|
|
24953ba974 | ||
|
|
22a5a1fa43 | ||
|
|
7bac62ee7f | ||
|
|
fcda11c6b0 | ||
|
|
66e1cb324c | ||
|
|
a869d23451 | ||
|
|
41f4941b74 | ||
|
|
b22168d3da | ||
|
|
b9b56bac6b | ||
|
|
fa670fa4b5 | ||
|
|
b131a0cf0c | ||
|
|
d7cef8d762 | ||
|
|
4483fc7df3 | ||
|
|
2cd5addc0c | ||
|
|
5bf06efb2a | ||
|
|
5196e309b8 | ||
|
|
bee1adc651 | ||
|
|
f0e7da8a63 | ||
|
|
5bd32eca0f | ||
|
|
491287c0af | ||
|
|
8ecfd88d92 | ||
|
|
b54178bbbd | ||
|
|
67547f74fc | ||
|
|
56ba193c77 | ||
|
|
d5594e431c | ||
|
|
7bfa2d4da6 | ||
|
|
4cf00f5698 | ||
|
|
a18326e97a | ||
|
|
36c9463467 | ||
|
|
13159c1a48 | ||
|
|
0fbef74f31 | ||
|
|
52122c342d | ||
|
|
96ee73f790 | ||
|
|
dc70f50b59 | ||
|
|
e297c73913 | ||
|
|
280e1a2512 | ||
|
|
1518b8f753 | ||
|
|
5403b1cf6d | ||
|
|
29110d5428 | ||
|
|
462003363f | ||
|
|
b494324ec3 | ||
|
|
4cee9a86ac | ||
|
|
7cad0a2dcd | ||
|
|
ed1ff06867 | ||
|
|
80a64341f0 | ||
|
|
bbf3eebb1a | ||
|
|
6b20ea0752 | ||
|
|
3241ad3ae8 | ||
|
|
9e7ccedba4 | ||
|
|
b9ffb5f30d | ||
|
|
9d456ca87a | ||
|
|
18350ad37f | ||
|
|
0983f65da7 | ||
|
|
42bbd5c9ae | ||
|
|
92ae11bd3b | ||
|
|
443f45eca1 | ||
|
|
a9ddbb4beb | ||
|
|
f8c3743991 | ||
|
|
072c265c30 | ||
|
|
440e9cdbef | ||
|
|
ace005bf7c | ||
|
|
75821c6889 | ||
|
|
d376148ca1 | ||
|
|
582ca33507 | ||
|
|
a6ca7eb29d | ||
|
|
02c035c548 | ||
|
|
418041d906 | ||
|
|
a3a400992e | ||
|
|
68625b047f | ||
|
|
9a1df7bf38 | ||
|
|
81b9cfdfa4 | ||
|
|
ae7aa65069 | ||
|
|
9e0f969b8b | ||
|
|
14a8fa4d68 | ||
|
|
07e5c9bd5f | ||
|
|
49c2ca0216 | ||
|
|
04172e3dd0 | ||
|
|
2c56a9f588 | ||
|
|
20e025b3d8 | ||
|
|
10591d2080 | ||
|
|
af29c0ade5 | ||
|
|
4c876ac62e | ||
|
|
66bf7b0b01 | ||
|
|
17d51a1609 | ||
|
|
0a59605e05 | ||
|
|
202658678d | ||
|
|
4f01db256f | ||
|
|
c04f4a7d73 | ||
|
|
f5e46ba7fc | ||
|
|
8609af54fc | ||
|
|
9a4ef87859 | ||
|
|
a57f4c23d8 | ||
|
|
d629f4d2fb | ||
|
|
6f20a73782 | ||
|
|
8cb7badd63 | ||
|
|
0f3c2f6541 | ||
|
|
56496ad5d8 | ||
|
|
e15dca2a9f | ||
|
|
a32706bc26 | ||
|
|
bdcca4767c | ||
|
|
38f4dbfb51 | ||
|
|
a374b1a6a3 | ||
|
|
0ff94978f5 | ||
|
|
fa24e6b995 | ||
|
|
37b64ff616 | ||
|
|
a2fd8d1db1 |
@@ -7,10 +7,15 @@ endif(${CMAKE_VERSION} STREQUAL "2.8.2")
|
||||
# This can be read from ${PROJECT_NAME} after project() is called
|
||||
project(minetest)
|
||||
|
||||
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 dev-20120326)
|
||||
set(VERSION_PATCH dev-20120603)
|
||||
if(VERSION_EXTRA)
|
||||
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
|
||||
endif()
|
||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||
|
||||
MESSAGE(STATUS "*** Will build version ${VERSION_STRING} ***")
|
||||
@@ -85,13 +90,20 @@ elseif(UNIX) # Linux, BSD etc
|
||||
endif()
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest" DESTINATION "${SHAREDIR}/games")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minimal" DESTINATION "${SHAREDIR}/games")
|
||||
set(MINETEST_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game")
|
||||
if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE})
|
||||
install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
|
||||
install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/")
|
||||
install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game")
|
||||
endif()
|
||||
if(BUILD_CLIENT)
|
||||
#install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/sounds/base/pack" DESTINATION "${SHAREDIR}/sounds/base")
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/textures/base/pack" DESTINATION "${SHAREDIR}/textures/base")
|
||||
endif()
|
||||
if(RUN_IN_PLACE)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/mods/minetest/mods_here.txt" DESTINATION "${SHAREDIR}/mods/minetest")
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/all/textures_here.txt" DESTINATION "${SHAREDIR}/textures/all")
|
||||
endif()
|
||||
|
||||
install(FILES "README.txt" DESTINATION "${DOCDIR}")
|
||||
|
||||
40
README.txt
40
README.txt
@@ -79,13 +79,20 @@ Compiling on GNU/Linux:
|
||||
-----------------------
|
||||
|
||||
Install dependencies. Here's an example for Debian/Ubuntu:
|
||||
$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev
|
||||
$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev
|
||||
|
||||
Download source, extract (this is the URL to the latest of source repository, which might not work at all times):
|
||||
$ wget https://github.com/celeron55/minetest/tarball/master -O master.tar.gz
|
||||
$ tar xf master.tar.gz
|
||||
$ cd celeron55-minetest-286edd4 (or similar)
|
||||
|
||||
Download minetest_game (otherwise only the "Minimal development test" game is available)
|
||||
$ cd games/
|
||||
$ wget https://github.com/celeron55/minetest_game/tarball/master -O master.tar.gz
|
||||
$ tar xf master.tar.gz
|
||||
$ mv celeron55-minetest_game-* minetest_game
|
||||
$ cd ..
|
||||
|
||||
Build a version that runs directly from the source directory:
|
||||
$ cmake . -DRUN_IN_PLACE=1
|
||||
$ make -j2
|
||||
@@ -102,6 +109,9 @@ $ ./minetest
|
||||
|
||||
Compiling on Windows:
|
||||
---------------------
|
||||
- This section is outdated. In addition to what is described here:
|
||||
- In addition to minetest, you need to download minetest_game.
|
||||
- If you wish to have sound support, you need libogg, libvorbis and libopenal
|
||||
|
||||
- You need:
|
||||
* CMake:
|
||||
@@ -315,6 +325,34 @@ 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.
|
||||
|
||||
Lua
|
||||
---------------
|
||||
|
||||
Lua is licensed under the terms of the MIT license reproduced below.
|
||||
This means that Lua is free software and can be used for both academic
|
||||
and commercial purposes at absolutely no cost.
|
||||
|
||||
For details and rationale, see http://www.lua.org/license.html .
|
||||
|
||||
Copyright (C) 1994-2008 Lua.org, PUC-Rio.
|
||||
|
||||
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.
|
||||
|
||||
Fonts
|
||||
---------------
|
||||
|
||||
188
builtin/auth.lua
Normal file
188
builtin/auth.lua
Normal file
@@ -0,0 +1,188 @@
|
||||
-- Minetest: builtin/auth.lua
|
||||
|
||||
--
|
||||
-- Authentication handler
|
||||
--
|
||||
|
||||
function minetest.string_to_privs(str, delim)
|
||||
assert(type(str) == "string")
|
||||
delim = delim or ','
|
||||
privs = {}
|
||||
for _, priv in pairs(string.split(str, delim)) do
|
||||
privs[priv:trim()] = true
|
||||
end
|
||||
return privs
|
||||
end
|
||||
|
||||
function minetest.privs_to_string(privs, delim)
|
||||
assert(type(privs) == "table")
|
||||
delim = delim or ','
|
||||
list = {}
|
||||
for priv, bool in pairs(privs) do
|
||||
if bool then
|
||||
table.insert(list, priv)
|
||||
end
|
||||
end
|
||||
return table.concat(list, delim)
|
||||
end
|
||||
|
||||
assert(minetest.string_to_privs("a,b").b == true)
|
||||
assert(minetest.privs_to_string({a=true,b=true}) == "a,b")
|
||||
|
||||
minetest.auth_file_path = minetest.get_worldpath().."/auth.txt"
|
||||
minetest.auth_table = {}
|
||||
|
||||
local function read_auth_file()
|
||||
local newtable = {}
|
||||
local file, errmsg = io.open(minetest.auth_file_path, 'rb')
|
||||
if not file then
|
||||
minetest.log("info", minetest.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
|
||||
return
|
||||
end
|
||||
for line in file:lines() do
|
||||
if line ~= "" then
|
||||
local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)")
|
||||
if not name or not password or not privilegestring then
|
||||
error("Invalid line in auth.txt: "..dump(line))
|
||||
end
|
||||
local privileges = minetest.string_to_privs(privilegestring)
|
||||
newtable[name] = {password=password, privileges=privileges}
|
||||
end
|
||||
end
|
||||
io.close(file)
|
||||
minetest.auth_table = newtable
|
||||
minetest.notify_authentication_modified()
|
||||
end
|
||||
|
||||
local function save_auth_file()
|
||||
local newtable = {}
|
||||
-- Check table for validness before attempting to save
|
||||
for name, stuff in pairs(minetest.auth_table) do
|
||||
assert(type(name) == "string")
|
||||
assert(name ~= "")
|
||||
assert(type(stuff) == "table")
|
||||
assert(type(stuff.password) == "string")
|
||||
assert(type(stuff.privileges) == "table")
|
||||
end
|
||||
local file, errmsg = io.open(minetest.auth_file_path, 'w+b')
|
||||
if not file then
|
||||
error(minetest.auth_file_path.." could not be opened for writing: "..errmsg)
|
||||
end
|
||||
for name, stuff in pairs(minetest.auth_table) do
|
||||
local privstring = minetest.privs_to_string(stuff.privileges)
|
||||
file:write(name..":"..stuff.password..":"..privstring..'\n')
|
||||
end
|
||||
io.close(file)
|
||||
end
|
||||
|
||||
read_auth_file()
|
||||
|
||||
minetest.builtin_auth_handler = {
|
||||
get_auth = function(name)
|
||||
assert(type(name) == "string")
|
||||
-- Figure out what password to use for a new player (singleplayer
|
||||
-- always has an empty password, otherwise use default, which is
|
||||
-- usually empty too)
|
||||
local new_password_hash = ""
|
||||
-- If not in authentication table, return nil
|
||||
if not minetest.auth_table[name] then
|
||||
return nil
|
||||
end
|
||||
-- Figure out what privileges the player should have.
|
||||
-- Take a copy of the privilege table
|
||||
local privileges = {}
|
||||
for priv, _ in pairs(minetest.auth_table[name].privileges) do
|
||||
privileges[priv] = true
|
||||
end
|
||||
-- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
|
||||
if minetest.is_singleplayer() then
|
||||
for priv, def in pairs(minetest.registered_privileges) do
|
||||
if def.give_to_singleplayer then
|
||||
privileges[priv] = true
|
||||
end
|
||||
end
|
||||
-- For the admin, give everything
|
||||
elseif name == minetest.setting_get("name") then
|
||||
for priv, def in pairs(minetest.registered_privileges) do
|
||||
privileges[priv] = true
|
||||
end
|
||||
end
|
||||
-- All done
|
||||
return {
|
||||
password = minetest.auth_table[name].password,
|
||||
privileges = privileges,
|
||||
}
|
||||
end,
|
||||
create_auth = function(name, password)
|
||||
assert(type(name) == "string")
|
||||
assert(type(password) == "string")
|
||||
minetest.log('info', "Built-in authentication handler adding player '"..name.."'")
|
||||
minetest.auth_table[name] = {
|
||||
password = password,
|
||||
privileges = minetest.string_to_privs(minetest.setting_get("default_privs")),
|
||||
}
|
||||
save_auth_file()
|
||||
end,
|
||||
set_password = function(name, password)
|
||||
assert(type(name) == "string")
|
||||
assert(type(password) == "string")
|
||||
if not minetest.auth_table[name] then
|
||||
minetest.builtin_auth_handler.create_auth(name, password)
|
||||
else
|
||||
minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'")
|
||||
minetest.auth_table[name].password = password
|
||||
save_auth_file()
|
||||
end
|
||||
return true
|
||||
end,
|
||||
set_privileges = function(name, privileges)
|
||||
assert(type(name) == "string")
|
||||
assert(type(privileges) == "table")
|
||||
if not minetest.auth_table[name] then
|
||||
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
|
||||
end
|
||||
minetest.auth_table[name].privileges = privileges
|
||||
minetest.notify_authentication_modified(name)
|
||||
save_auth_file()
|
||||
end,
|
||||
reload = function()
|
||||
read_auth_file()
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
function minetest.register_authentication_handler(handler)
|
||||
if minetest.registered_auth_handler then
|
||||
error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname)
|
||||
end
|
||||
minetest.registered_auth_handler = handler
|
||||
minetest.registered_auth_handler_modname = minetest.get_current_modname()
|
||||
end
|
||||
|
||||
function minetest.get_auth_handler()
|
||||
if minetest.registered_auth_handler then
|
||||
return minetest.registered_auth_handler
|
||||
end
|
||||
return minetest.builtin_auth_handler
|
||||
end
|
||||
|
||||
function minetest.set_player_password(name, password)
|
||||
if minetest.get_auth_handler().set_password then
|
||||
minetest.get_auth_handler().set_password(name, password)
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.set_player_privs(name, privs)
|
||||
if minetest.get_auth_handler().set_privileges then
|
||||
minetest.get_auth_handler().set_privileges(name, privs)
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.auth_reload()
|
||||
if minetest.get_auth_handler().reload then
|
||||
return minetest.get_auth_handler().reload()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@@ -5,800 +5,19 @@
|
||||
-- before loading and running any mods.
|
||||
--
|
||||
|
||||
--
|
||||
-- Override some Lua library functions
|
||||
--
|
||||
|
||||
-- Initialize some very basic things
|
||||
print = minetest.debug
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
function basic_dump2(o)
|
||||
if type(o) == "number" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "string" then
|
||||
return string.format("%q", o)
|
||||
elseif type(o) == "boolean" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "function" then
|
||||
return "<function>"
|
||||
elseif type(o) == "userdata" then
|
||||
return "<userdata>"
|
||||
elseif type(o) == "nil" then
|
||||
return "nil"
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function dump2(o, name, dumped)
|
||||
name = name or "_"
|
||||
dumped = dumped or {}
|
||||
io.write(name, " = ")
|
||||
if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
|
||||
or type(o) == "function" or type(o) == "nil"
|
||||
or type(o) == "userdata" then
|
||||
io.write(basic_dump2(o), "\n")
|
||||
elseif type(o) == "table" then
|
||||
if dumped[o] then
|
||||
io.write(dumped[o], "\n")
|
||||
else
|
||||
dumped[o] = name
|
||||
io.write("{}\n") -- new table
|
||||
for k,v in pairs(o) do
|
||||
local fieldname = string.format("%s[%s]", name, basic_dump2(k))
|
||||
dump2(v, fieldname, dumped)
|
||||
end
|
||||
end
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function dump(o, dumped)
|
||||
dumped = dumped or {}
|
||||
if type(o) == "number" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "string" then
|
||||
return string.format("%q", o)
|
||||
elseif type(o) == "table" then
|
||||
if dumped[o] then
|
||||
return "<circular reference>"
|
||||
end
|
||||
dumped[o] = true
|
||||
local t = {}
|
||||
for k,v in pairs(o) do
|
||||
t[#t+1] = "" .. k .. " = " .. dump(v, dumped)
|
||||
end
|
||||
return "{" .. table.concat(t, ", ") .. "}"
|
||||
elseif type(o) == "boolean" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "function" then
|
||||
return "<function>"
|
||||
elseif type(o) == "userdata" then
|
||||
return "<userdata>"
|
||||
elseif type(o) == "nil" then
|
||||
return "nil"
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Item definition helpers
|
||||
--
|
||||
|
||||
function minetest.inventorycube(img1, img2, img3)
|
||||
img2 = img2 or img1
|
||||
img3 = img3 or img1
|
||||
return "[inventorycube"
|
||||
.. "{" .. img1:gsub("%^", "&")
|
||||
.. "{" .. img2:gsub("%^", "&")
|
||||
.. "{" .. img3:gsub("%^", "&")
|
||||
end
|
||||
|
||||
function minetest.pos_to_string(pos)
|
||||
return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
|
||||
end
|
||||
|
||||
function minetest.get_pointed_thing_position(pointed_thing, above)
|
||||
if pointed_thing.type == "node" then
|
||||
if above then
|
||||
-- The position where a node would be placed
|
||||
return pointed_thing.above
|
||||
else
|
||||
-- The position where a node would be dug
|
||||
return pointed_thing.under
|
||||
end
|
||||
elseif pointed_thing.type == "object" then
|
||||
obj = pointed_thing.ref
|
||||
if obj ~= nil then
|
||||
return obj:getpos()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.dir_to_facedir(dir)
|
||||
if math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 3
|
||||
else
|
||||
return 1
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 2
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.dir_to_wallmounted(dir)
|
||||
if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
|
||||
if dir.y < 0 then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 3
|
||||
else
|
||||
return 2
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 5
|
||||
else
|
||||
return 4
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.get_node_drops(nodename, toolname)
|
||||
local drop = ItemStack({name=nodename}):get_definition().drop
|
||||
if drop == nil then
|
||||
-- default drop
|
||||
return {ItemStack({name=nodename})}
|
||||
elseif type(drop) == "string" then
|
||||
-- itemstring drop
|
||||
return {ItemStack(drop)}
|
||||
elseif drop.items == nil then
|
||||
-- drop = {} to disable default drop
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Extended drop table
|
||||
local got_items = {}
|
||||
local got_count = 0
|
||||
local _, item, tool
|
||||
for _, item in ipairs(drop.items) do
|
||||
local good_rarity = true
|
||||
local good_tool = true
|
||||
if item.rarity ~= nil then
|
||||
good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
|
||||
end
|
||||
if item.tools ~= nil then
|
||||
good_tool = false
|
||||
for _, tool in ipairs(item.tools) do
|
||||
if tool:sub(1, 1) == '~' then
|
||||
good_tool = toolname:find(tool:sub(2)) ~= nil
|
||||
else
|
||||
good_tool = toolname == tool
|
||||
end
|
||||
if good_tool then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if good_rarity and good_tool then
|
||||
got_count = got_count + 1
|
||||
for _, add_item in ipairs(item.items) do
|
||||
got_items[#got_items+1] = add_item
|
||||
end
|
||||
if drop.max_items ~= nil and got_count == drop.max_items then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return got_items
|
||||
end
|
||||
|
||||
function minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
local item = itemstack:peek_item()
|
||||
local def = itemstack:get_definition()
|
||||
if def.type == "node" and pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.above
|
||||
local oldnode = minetest.env:get_node(pos)
|
||||
local olddef = ItemStack({name=oldnode.name}):get_definition()
|
||||
|
||||
if not olddef.buildable_to then
|
||||
minetest.log("info", placer:get_player_name() .. " tried to place"
|
||||
.. " node in invalid position " .. minetest.pos_to_string(pos)
|
||||
.. ", replacing " .. oldnode.name)
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log("action", placer:get_player_name() .. " places node "
|
||||
.. def.name .. " at " .. minetest.pos_to_string(pos))
|
||||
|
||||
local newnode = {name = def.name, param1 = 0, param2 = 0}
|
||||
|
||||
-- Calculate direction for wall mounted stuff like torches and signs
|
||||
if def.paramtype2 == 'wallmounted' then
|
||||
local under = pointed_thing.under
|
||||
local above = pointed_thing.above
|
||||
local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
|
||||
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
||||
-- Calculate the direction for furnaces and chests and stuff
|
||||
elseif def.paramtype2 == 'facedir' then
|
||||
local playerpos = placer:getpos()
|
||||
local dir = {x = pos.x - playerpos.x, y = pos.y - playerpos.y, z = pos.z - playerpos.z}
|
||||
newnode.param2 = minetest.dir_to_facedir(dir)
|
||||
minetest.log("action", "facedir: " .. newnode.param2)
|
||||
end
|
||||
|
||||
-- Add node and update
|
||||
minetest.env:add_node(pos, newnode)
|
||||
|
||||
-- Set metadata owner
|
||||
if def.metadata_name ~= "" then
|
||||
minetest.env:get_meta(pos):set_owner(placer:get_player_name())
|
||||
end
|
||||
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||
callback(pos, newnode, placer)
|
||||
end
|
||||
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function minetest.item_place_object(itemstack, placer, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
|
||||
if pos ~= nil then
|
||||
local item = itemstack:take_item()
|
||||
minetest.env:add_item(pos, item)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function minetest.item_place(itemstack, placer, pointed_thing)
|
||||
if itemstack:get_definition().type == "node" then
|
||||
return minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
else
|
||||
return minetest.item_place_object(itemstack, placer, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.item_drop(itemstack, dropper, pos)
|
||||
minetest.env:add_item(pos, itemstack)
|
||||
return ""
|
||||
end
|
||||
|
||||
function minetest.item_eat(hp_change, replace_with_item)
|
||||
return function(itemstack, user, pointed_thing) -- closure
|
||||
if itemstack:take_item() ~= nil then
|
||||
user:set_hp(user:get_hp() + hp_change)
|
||||
itemstack:add_item(replace_with_item) -- note: replace_with_item is optional
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.node_punch(pos, node, puncher)
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_punchnodes) do
|
||||
callback(pos, node, puncher)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function minetest.node_dig(pos, node, digger)
|
||||
minetest.debug("node_dig")
|
||||
|
||||
local def = ItemStack({name=node.name}):get_definition()
|
||||
if not def.diggable then
|
||||
minetest.debug("not diggable")
|
||||
minetest.log("info", digger:get_player_name() .. " tried to dig "
|
||||
.. node.name .. " which is not diggable "
|
||||
.. minetest.pos_to_string(pos))
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if meta ~= nil and not meta:get_allow_removal() then
|
||||
minetest.debug("dig prevented by metadata")
|
||||
minetest.log("info", digger:get_player_name() .. " tried to dig "
|
||||
.. node.name .. ", but removal is disabled by metadata "
|
||||
.. minetest.pos_to_string(pos))
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log('action', digger:get_player_name() .. " digs "
|
||||
.. node.name .. " at " .. minetest.pos_to_string(pos))
|
||||
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
local wielded = digger:get_wielded_item()
|
||||
local drops = minetest.get_node_drops(node.name, wielded:get_name())
|
||||
|
||||
-- Wear out tool
|
||||
tp = wielded:get_tool_capabilities()
|
||||
dp = minetest.get_dig_params(def.groups, tp)
|
||||
wielded:add_wear(dp.wear)
|
||||
digger:set_wielded_item(wielded)
|
||||
|
||||
-- Add dropped items
|
||||
local _, dropped_item
|
||||
for _, dropped_item in ipairs(drops) do
|
||||
digger:get_inventory():add_item("main", dropped_item)
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove node and update
|
||||
minetest.env:remove_node(pos)
|
||||
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_dignodes) do
|
||||
callback(pos, node, digger)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Item definition defaults
|
||||
--
|
||||
|
||||
minetest.nodedef_default = {
|
||||
-- Item properties
|
||||
type="node",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 99,
|
||||
usable = false,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = minetest.item_place,
|
||||
on_drop = minetest.item_drop,
|
||||
on_use = nil,
|
||||
|
||||
on_punch = minetest.node_punch,
|
||||
on_dig = minetest.node_dig,
|
||||
|
||||
-- Node properties
|
||||
drawtype = "normal",
|
||||
visual_scale = 1.0,
|
||||
tile_images = {""},
|
||||
special_materials = {
|
||||
{image="", backface_culling=true},
|
||||
{image="", backface_culling=true},
|
||||
},
|
||||
alpha = 255,
|
||||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
paramtype2 = "none",
|
||||
is_ground_content = false,
|
||||
sunlight_propagates = false,
|
||||
walkable = true,
|
||||
pointable = true,
|
||||
diggable = true,
|
||||
climbable = false,
|
||||
buildable_to = false,
|
||||
metadata_name = "",
|
||||
liquidtype = "none",
|
||||
liquid_alternative_flowing = "",
|
||||
liquid_alternative_source = "",
|
||||
liquid_viscosity = 0,
|
||||
light_source = 0,
|
||||
damage_per_second = 0,
|
||||
selection_box = {type="regular"},
|
||||
legacy_facedir_simple = false,
|
||||
legacy_wallmounted = false,
|
||||
}
|
||||
|
||||
minetest.craftitemdef_default = {
|
||||
type="craft",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 99,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = minetest.item_place,
|
||||
on_drop = minetest.item_drop,
|
||||
on_use = nil,
|
||||
}
|
||||
|
||||
minetest.tooldef_default = {
|
||||
type="tool",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 1,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = minetest.item_place,
|
||||
on_drop = minetest.item_drop,
|
||||
on_use = nil,
|
||||
}
|
||||
|
||||
minetest.noneitemdef_default = { -- This is used for the hand and unknown items
|
||||
type="none",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 99,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = nil,
|
||||
on_drop = nil,
|
||||
on_use = nil,
|
||||
}
|
||||
|
||||
--
|
||||
-- Make raw registration functions inaccessible to anyone except builtin.lua
|
||||
--
|
||||
|
||||
local register_item_raw = minetest.register_item_raw
|
||||
minetest.register_item_raw = nil
|
||||
|
||||
local register_alias_raw = minetest.register_alias_raw
|
||||
minetest.register_item_raw = nil
|
||||
|
||||
--
|
||||
-- Item / entity / ABM registration functions
|
||||
--
|
||||
|
||||
minetest.registered_abms = {}
|
||||
minetest.registered_entities = {}
|
||||
minetest.registered_items = {}
|
||||
minetest.registered_nodes = {}
|
||||
minetest.registered_craftitems = {}
|
||||
minetest.registered_tools = {}
|
||||
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)
|
||||
|
||||
-- These item names may not be used because they would interfere
|
||||
-- with legacy itemstrings
|
||||
local forbidden_item_names = {
|
||||
MaterialItem = true,
|
||||
MaterialItem2 = true,
|
||||
MaterialItem3 = true,
|
||||
NodeItem = true,
|
||||
node = true,
|
||||
CraftItem = true,
|
||||
craft = true,
|
||||
MBOItem = true,
|
||||
ToolItem = true,
|
||||
tool = true,
|
||||
}
|
||||
|
||||
local function check_modname_prefix(name)
|
||||
if name:sub(1,1) == ":" then
|
||||
-- Escape the modname prefix enforcement mechanism
|
||||
return name:sub(2)
|
||||
else
|
||||
-- Modname prefix enforcement
|
||||
local expected_prefix = minetest.get_current_modname() .. ":"
|
||||
if name:sub(1, #expected_prefix) ~= expected_prefix then
|
||||
error("Name " .. name .. " does not follow naming conventions: " ..
|
||||
"\"modname:\" or \":\" prefix required")
|
||||
end
|
||||
local subname = name:sub(#expected_prefix+1)
|
||||
if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
|
||||
error("Name " .. name .. " does not follow naming conventions: " ..
|
||||
"contains unallowed characters")
|
||||
end
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.register_abm(spec)
|
||||
-- Add to minetest.registered_abms
|
||||
minetest.registered_abms[#minetest.registered_abms+1] = spec
|
||||
end
|
||||
|
||||
function minetest.register_entity(name, prototype)
|
||||
-- Check name
|
||||
if name == nil then
|
||||
error("Unable to register entity: Name is nil")
|
||||
end
|
||||
name = check_modname_prefix(tostring(name))
|
||||
|
||||
prototype.name = name
|
||||
prototype.__index = prototype -- so that it can be used as a metatable
|
||||
|
||||
-- Add to minetest.registered_entities
|
||||
minetest.registered_entities[name] = prototype
|
||||
end
|
||||
|
||||
function minetest.register_item(name, itemdef)
|
||||
-- Check name
|
||||
if name == nil then
|
||||
error("Unable to register item: Name is nil")
|
||||
end
|
||||
name = check_modname_prefix(tostring(name))
|
||||
if forbidden_item_names[name] then
|
||||
error("Unable to register item: Name is forbidden: " .. name)
|
||||
end
|
||||
itemdef.name = name
|
||||
|
||||
-- Apply defaults and add to registered_* table
|
||||
if itemdef.type == "node" then
|
||||
setmetatable(itemdef, {__index = minetest.nodedef_default})
|
||||
minetest.registered_nodes[itemdef.name] = itemdef
|
||||
elseif itemdef.type == "craft" then
|
||||
setmetatable(itemdef, {__index = minetest.craftitemdef_default})
|
||||
minetest.registered_craftitems[itemdef.name] = itemdef
|
||||
elseif itemdef.type == "tool" then
|
||||
setmetatable(itemdef, {__index = minetest.tooldef_default})
|
||||
minetest.registered_tools[itemdef.name] = itemdef
|
||||
elseif itemdef.type == "none" then
|
||||
setmetatable(itemdef, {__index = minetest.noneitemdef_default})
|
||||
else
|
||||
error("Unable to register item: Type is invalid: " .. dump(itemdef))
|
||||
end
|
||||
|
||||
-- Flowing liquid uses param2
|
||||
if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
|
||||
itemdef.paramtype2 = "flowingliquid"
|
||||
end
|
||||
|
||||
-- BEGIN Legacy stuff
|
||||
if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
|
||||
minetest.register_craft({
|
||||
type="cooking",
|
||||
output=itemdef.cookresult_itemstring,
|
||||
recipe=itemdef.name,
|
||||
cooktime=itemdef.furnace_cooktime
|
||||
})
|
||||
end
|
||||
if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
|
||||
minetest.register_craft({
|
||||
type="fuel",
|
||||
recipe=itemdef.name,
|
||||
burntime=itemdef.furnace_burntime
|
||||
})
|
||||
end
|
||||
-- END Legacy stuff
|
||||
|
||||
-- Disable all further modifications
|
||||
getmetatable(itemdef).__newindex = {}
|
||||
|
||||
--minetest.log("Registering item: " .. itemdef.name)
|
||||
minetest.registered_items[itemdef.name] = itemdef
|
||||
minetest.registered_aliases[itemdef.name] = nil
|
||||
register_item_raw(itemdef)
|
||||
end
|
||||
|
||||
function minetest.register_node(name, nodedef)
|
||||
nodedef.type = "node"
|
||||
minetest.register_item(name, nodedef)
|
||||
end
|
||||
|
||||
function minetest.register_craftitem(name, craftitemdef)
|
||||
craftitemdef.type = "craft"
|
||||
|
||||
-- BEGIN Legacy stuff
|
||||
if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
|
||||
craftitemdef.inventory_image = craftitemdef.image
|
||||
end
|
||||
-- END Legacy stuff
|
||||
|
||||
minetest.register_item(name, craftitemdef)
|
||||
end
|
||||
|
||||
function minetest.register_tool(name, tooldef)
|
||||
tooldef.type = "tool"
|
||||
tooldef.stack_max = 1
|
||||
|
||||
-- BEGIN Legacy stuff
|
||||
if tooldef.inventory_image == nil and tooldef.image ~= nil then
|
||||
tooldef.inventory_image = tooldef.image
|
||||
end
|
||||
if tooldef.tool_capabilities == nil and
|
||||
(tooldef.full_punch_interval ~= nil or
|
||||
tooldef.basetime ~= nil or
|
||||
tooldef.dt_weight ~= nil or
|
||||
tooldef.dt_crackiness ~= nil or
|
||||
tooldef.dt_crumbliness ~= nil or
|
||||
tooldef.dt_cuttability ~= nil or
|
||||
tooldef.basedurability ~= nil or
|
||||
tooldef.dd_weight ~= nil or
|
||||
tooldef.dd_crackiness ~= nil or
|
||||
tooldef.dd_crumbliness ~= nil or
|
||||
tooldef.dd_cuttability ~= nil) then
|
||||
tooldef.tool_capabilities = {
|
||||
full_punch_interval = tooldef.full_punch_interval,
|
||||
basetime = tooldef.basetime,
|
||||
dt_weight = tooldef.dt_weight,
|
||||
dt_crackiness = tooldef.dt_crackiness,
|
||||
dt_crumbliness = tooldef.dt_crumbliness,
|
||||
dt_cuttability = tooldef.dt_cuttability,
|
||||
basedurability = tooldef.basedurability,
|
||||
dd_weight = tooldef.dd_weight,
|
||||
dd_crackiness = tooldef.dd_crackiness,
|
||||
dd_crumbliness = tooldef.dd_crumbliness,
|
||||
dd_cuttability = tooldef.dd_cuttability,
|
||||
}
|
||||
end
|
||||
-- END Legacy stuff
|
||||
|
||||
minetest.register_item(name, tooldef)
|
||||
end
|
||||
|
||||
function minetest.register_alias(name, convert_to)
|
||||
if forbidden_item_names[name] then
|
||||
error("Unable to register alias: Name is forbidden: " .. name)
|
||||
end
|
||||
if minetest.registered_items[name] ~= nil then
|
||||
minetest.log("WARNING: Not registering alias, item with same name" ..
|
||||
" is already defined: " .. name .. " -> " .. convert_to)
|
||||
else
|
||||
--minetest.log("Registering alias: " .. name .. " -> " .. convert_to)
|
||||
minetest.registered_aliases[name] = convert_to
|
||||
register_alias_raw(name, convert_to)
|
||||
end
|
||||
end
|
||||
|
||||
-- Alias the forbidden item names to "" so they can't be
|
||||
-- created via itemstrings (e.g. /give)
|
||||
local name
|
||||
for name in pairs(forbidden_item_names) do
|
||||
minetest.registered_aliases[name] = ""
|
||||
register_alias_raw(name, "")
|
||||
end
|
||||
|
||||
|
||||
-- Deprecated:
|
||||
-- Aliases for minetest.register_alias (how ironic...)
|
||||
--minetest.alias_node = minetest.register_alias
|
||||
--minetest.alias_tool = minetest.register_alias
|
||||
--minetest.alias_craftitem = minetest.register_alias
|
||||
|
||||
--
|
||||
-- Built-in node definitions. Also defined in C.
|
||||
--
|
||||
|
||||
minetest.register_item(":unknown", {
|
||||
type = "none",
|
||||
description = "Unknown Item",
|
||||
inventory_image = "unknown_item.png",
|
||||
on_place = minetest.item_place,
|
||||
on_drop = minetest.item_drop,
|
||||
})
|
||||
|
||||
minetest.register_node(":air", {
|
||||
description = "Air (you hacker you!)",
|
||||
inventory_image = "unknown_block.png",
|
||||
wield_image = "unknown_block.png",
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
air_equivalent = true,
|
||||
})
|
||||
|
||||
minetest.register_node(":ignore", {
|
||||
description = "Ignore (you hacker you!)",
|
||||
inventory_image = "unknown_block.png",
|
||||
wield_image = "unknown_block.png",
|
||||
drawtype = "airlike",
|
||||
paramtype = "none",
|
||||
sunlight_propagates = false,
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true, -- A way to remove accidentally placed ignores
|
||||
air_equivalent = true,
|
||||
})
|
||||
|
||||
-- The hand (bare definition)
|
||||
minetest.register_item(":", {
|
||||
type = "none",
|
||||
})
|
||||
|
||||
--
|
||||
-- Default material types
|
||||
--
|
||||
function digprop_err()
|
||||
minetest.log("info", debug.traceback())
|
||||
minetest.log("info", "WARNING: The minetest.digprop_* functions are obsolete and need to be replaced by item groups.")
|
||||
end
|
||||
|
||||
minetest.digprop_constanttime = digprop_err
|
||||
minetest.digprop_stonelike = digprop_err
|
||||
minetest.digprop_dirtlike = digprop_err
|
||||
minetest.digprop_gravellike = digprop_err
|
||||
minetest.digprop_woodlike = digprop_err
|
||||
minetest.digprop_leaveslike = digprop_err
|
||||
minetest.digprop_glasslike = digprop_err
|
||||
|
||||
--
|
||||
-- Creative inventory
|
||||
--
|
||||
|
||||
minetest.creative_inventory = {}
|
||||
|
||||
minetest.add_to_creative_inventory = function(itemstring)
|
||||
table.insert(minetest.creative_inventory, itemstring)
|
||||
end
|
||||
|
||||
--
|
||||
-- Callback registration
|
||||
--
|
||||
|
||||
local function make_registration()
|
||||
local t = {}
|
||||
local registerfunc = function(func) table.insert(t, func) end
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
|
||||
minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
|
||||
minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
|
||||
minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()
|
||||
minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
|
||||
minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
|
||||
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()
|
||||
|
||||
--
|
||||
-- Set random seed
|
||||
--
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
-- END
|
||||
-- Load other files
|
||||
dofile(minetest.get_modpath("__builtin").."/misc_helpers.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/item.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/misc_register.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/item_entity.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/deprecated.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/misc.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/privileges.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/auth.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/chatcommands.lua")
|
||||
dofile(minetest.get_modpath("__builtin").."/static_spawn.lua")
|
||||
|
||||
|
||||
353
builtin/chatcommands.lua
Normal file
353
builtin/chatcommands.lua
Normal file
@@ -0,0 +1,353 @@
|
||||
-- Minetest: builtin/chatcommands.lua
|
||||
|
||||
--
|
||||
-- Chat command handler
|
||||
--
|
||||
|
||||
minetest.chatcommands = {}
|
||||
function minetest.register_chatcommand(cmd, def)
|
||||
def = def or {}
|
||||
def.params = def.params or ""
|
||||
def.description = def.description or ""
|
||||
def.privs = def.privs or {}
|
||||
minetest.chatcommands[cmd] = def
|
||||
end
|
||||
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
|
||||
if not param then
|
||||
param = ""
|
||||
end
|
||||
local cmd_def = minetest.chatcommands[cmd]
|
||||
if cmd_def then
|
||||
if not cmd_def.func then
|
||||
-- This is a C++ command
|
||||
return false
|
||||
else
|
||||
local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
|
||||
if has_privs then
|
||||
cmd_def.func(name, param)
|
||||
else
|
||||
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
|
||||
end
|
||||
return true -- handled chat message
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
--
|
||||
-- Chat commands
|
||||
--
|
||||
|
||||
-- Register C++ commands without functions
|
||||
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)"})
|
||||
minetest.register_chatcommand("status", {description = "print server status line"})
|
||||
minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
|
||||
minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
|
||||
minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}})
|
||||
minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}})
|
||||
minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}})
|
||||
|
||||
-- Register other commands
|
||||
minetest.register_chatcommand("help", {
|
||||
privs = {},
|
||||
params = "(nothing)/all/privs/<cmd>",
|
||||
description = "Get help for commands or list privileges",
|
||||
func = function(name, param)
|
||||
local format_help_line = function(cmd, def)
|
||||
local msg = "/"..cmd
|
||||
if def.params and def.params ~= "" then msg = msg .. " " .. def.params end
|
||||
if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
|
||||
return msg
|
||||
end
|
||||
if param == "" then
|
||||
local msg = ""
|
||||
cmds = {}
|
||||
for cmd, def in pairs(minetest.chatcommands) do
|
||||
if minetest.check_player_privs(name, def.privs) then
|
||||
table.insert(cmds, cmd)
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(name, "Available commands: "..table.concat(cmds, " "))
|
||||
minetest.chat_send_player(name, "Use '/help <cmd>' to get more information, or '/help all' to list everything.")
|
||||
elseif param == "all" then
|
||||
minetest.chat_send_player(name, "Available commands:")
|
||||
for cmd, def in pairs(minetest.chatcommands) do
|
||||
if minetest.check_player_privs(name, def.privs) then
|
||||
minetest.chat_send_player(name, format_help_line(cmd, def))
|
||||
end
|
||||
end
|
||||
elseif param == "privs" then
|
||||
minetest.chat_send_player(name, "Available privileges:")
|
||||
for priv, def in pairs(minetest.registered_privileges) do
|
||||
minetest.chat_send_player(name, priv..": "..def.description)
|
||||
end
|
||||
else
|
||||
local cmd = param
|
||||
def = minetest.chatcommands[cmd]
|
||||
if not def then
|
||||
minetest.chat_send_player(name, "Command not available: "..cmd)
|
||||
else
|
||||
minetest.chat_send_player(name, format_help_line(cmd, def))
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("privs", {
|
||||
params = "<name>",
|
||||
description = "print out privileges of player",
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
param = name
|
||||
else
|
||||
--[[if not minetest.check_player_privs(name, {privs=true}) then
|
||||
minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
|
||||
return
|
||||
end]]
|
||||
end
|
||||
minetest.chat_send_player(name, "Privileges of "..param..": "..minetest.privs_to_string(minetest.get_player_privs(param), ' '))
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("grant", {
|
||||
params = "<name> <privilege>|all",
|
||||
description = "Give privilege to player",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
if not minetest.check_player_privs(name, {privs=true}) and
|
||||
not minetest.check_player_privs(name, {basic_privs=true}) then
|
||||
minetest.chat_send_player(name, "Your privileges are insufficient.")
|
||||
return
|
||||
end
|
||||
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
|
||||
if not grantname or not grantprivstr then
|
||||
minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
|
||||
return
|
||||
end
|
||||
local grantprivs = minetest.string_to_privs(grantprivstr)
|
||||
if grantprivstr == "all" then
|
||||
grantprivs = minetest.registered_privileges
|
||||
end
|
||||
local privs = minetest.get_player_privs(grantname)
|
||||
local privs_known = true
|
||||
for priv, _ in pairs(grantprivs) do
|
||||
if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
|
||||
minetest.chat_send_player(name, "Your privileges are insufficient.")
|
||||
return
|
||||
end
|
||||
if not minetest.registered_privileges[priv] then
|
||||
minetest.chat_send_player(name, "Unknown privilege: "..priv)
|
||||
privs_known = false
|
||||
end
|
||||
privs[priv] = true
|
||||
end
|
||||
if not privs_known then
|
||||
return
|
||||
end
|
||||
minetest.set_player_privs(grantname, privs)
|
||||
minetest.chat_send_player(name, "Privileges of "..grantname..": "..minetest.privs_to_string(minetest.get_player_privs(grantname), ' '))
|
||||
if grantname ~= name then
|
||||
minetest.chat_send_player(grantname, name.." granted you privileges: "..minetest.privs_to_string(grantprivs, ' '))
|
||||
end
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("revoke", {
|
||||
params = "<name> <privilege>|all",
|
||||
description = "Remove privilege from player",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
if not minetest.check_player_privs(name, {privs=true}) and
|
||||
not minetest.check_player_privs(name, {basic_privs=true}) then
|
||||
minetest.chat_send_player(name, "Your privileges are insufficient.")
|
||||
return
|
||||
end
|
||||
local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
|
||||
if not revokename or not revokeprivstr then
|
||||
minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
|
||||
return
|
||||
end
|
||||
local revokeprivs = minetest.string_to_privs(revokeprivstr)
|
||||
local privs = minetest.get_player_privs(revokename)
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
|
||||
minetest.chat_send_player(name, "Your privileges are insufficient.")
|
||||
return
|
||||
end
|
||||
end
|
||||
if revokeprivstr == "all" then
|
||||
privs = {}
|
||||
else
|
||||
for priv, _ in pairs(revokeprivs) do
|
||||
privs[priv] = nil
|
||||
end
|
||||
end
|
||||
minetest.set_player_privs(revokename, privs)
|
||||
minetest.chat_send_player(name, "Privileges of "..revokename..": "..minetest.privs_to_string(minetest.get_player_privs(revokename), ' '))
|
||||
if revokename ~= name then
|
||||
minetest.chat_send_player(revokename, name.." revoked privileges from you: "..minetest.privs_to_string(revokeprivs, ' '))
|
||||
end
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("setpassword", {
|
||||
params = "<name> <password>",
|
||||
description = "set given password",
|
||||
privs = {password=true},
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
minetest.chat_send_player(name, "Password field required")
|
||||
return
|
||||
end
|
||||
minetest.set_player_password(name, param)
|
||||
minetest.chat_send_player(name, "Password set")
|
||||
end,
|
||||
})
|
||||
minetest.register_chatcommand("clearpassword", {
|
||||
params = "<name>",
|
||||
description = "set empty password",
|
||||
privs = {password=true},
|
||||
func = function(name, param)
|
||||
minetest.set_player_password(name, '')
|
||||
minetest.chat_send_player(name, "Password cleared")
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("auth_reload", {
|
||||
params = "",
|
||||
description = "reload authentication data",
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local done = minetest.auth_reload()
|
||||
if done then
|
||||
minetest.chat_send_player(name, "Done.")
|
||||
else
|
||||
minetest.chat_send_player(name, "Failed.")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("teleport", {
|
||||
params = "<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>",
|
||||
description = "teleport to given position",
|
||||
privs = {teleport=true},
|
||||
func = function(name, param)
|
||||
-- Returns (pos, true) if found, otherwise (pos, false)
|
||||
local function find_free_position_near(pos)
|
||||
local tries = {
|
||||
{x=1,y=0,z=0},
|
||||
{x=-1,y=0,z=0},
|
||||
{x=0,y=0,z=1},
|
||||
{x=0,y=0,z=-1},
|
||||
}
|
||||
for _, d in ipairs(tries) do
|
||||
local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z}
|
||||
local n = minetest.env:get_node(p)
|
||||
if not minetest.registered_nodes[n.name].walkable then
|
||||
return p, true
|
||||
end
|
||||
end
|
||||
return pos, false
|
||||
end
|
||||
|
||||
local teleportee = nil
|
||||
local p = {}
|
||||
p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
teleportee = minetest.env:get_player_by_name(name)
|
||||
if teleportee and p.x and p.y and p.z then
|
||||
minetest.chat_send_player(name, "Teleporting to ("..p.x..", "..p.y..", "..p.z..")")
|
||||
teleportee:setpos(p)
|
||||
return
|
||||
end
|
||||
|
||||
local teleportee = nil
|
||||
local p = nil
|
||||
local target_name = nil
|
||||
target_name = string.match(param, "^([^ ]+)$")
|
||||
teleportee = minetest.env:get_player_by_name(name)
|
||||
if target_name then
|
||||
local target = minetest.env:get_player_by_name(target_name)
|
||||
if target then
|
||||
p = target:getpos()
|
||||
end
|
||||
end
|
||||
if teleportee and p then
|
||||
p = find_free_position_near(p)
|
||||
minetest.chat_send_player(name, "Teleporting to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
|
||||
teleportee:setpos(p)
|
||||
return
|
||||
end
|
||||
|
||||
if minetest.check_player_privs(name, {bring=true}) then
|
||||
local teleportee = nil
|
||||
local p = {}
|
||||
local teleportee_name = nil
|
||||
teleportee_name, p.x, p.y, p.z = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
if teleportee_name then
|
||||
teleportee = minetest.env:get_player_by_name(teleportee_name)
|
||||
end
|
||||
if teleportee and p.x and p.y and p.z then
|
||||
minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to ("..p.x..", "..p.y..", "..p.z..")")
|
||||
teleportee:setpos(p)
|
||||
return
|
||||
end
|
||||
|
||||
local teleportee = nil
|
||||
local p = nil
|
||||
local teleportee_name = nil
|
||||
local target_name = nil
|
||||
teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$")
|
||||
if teleportee_name then
|
||||
teleportee = minetest.env:get_player_by_name(teleportee_name)
|
||||
end
|
||||
if target_name then
|
||||
local target = minetest.env:get_player_by_name(target_name)
|
||||
if target then
|
||||
p = target:getpos()
|
||||
end
|
||||
end
|
||||
if teleportee and p then
|
||||
p = find_free_position_near(p)
|
||||
minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
|
||||
teleportee:setpos(p)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
minetest.chat_send_player(name, "Invalid parameters (\""..param.."\") or player not found (see /help teleport)")
|
||||
return
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("set", {
|
||||
params = "[-n] <name> <value> | <name>",
|
||||
description = "set or read server configuration setting",
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
|
||||
if arg and arg == "-n" and setname and setvalue then
|
||||
minetest.setting_set(setname, setvalue)
|
||||
minetest.chat_send_player(name, setname.." = "..setvalue)
|
||||
return
|
||||
end
|
||||
local setname, setvalue = string.match(param, "([^ ]+) (.+)")
|
||||
if setname and setvalue then
|
||||
if not minetest.setting_get(setname) then
|
||||
minetest.chat_send_player(name, "Failed. Use '/set -n <name> <value>' to create a new setting.")
|
||||
return
|
||||
end
|
||||
minetest.setting_set(setname, setvalue)
|
||||
minetest.chat_send_player(name, setname.." = "..setvalue)
|
||||
return
|
||||
end
|
||||
local setname = string.match(param, "([^ ]+)")
|
||||
if setname then
|
||||
local setvalue = minetest.setting_get(setname)
|
||||
if not setvalue then
|
||||
setvalue = "<not set>"
|
||||
end
|
||||
minetest.chat_send_player(name, setname.." = "..setvalue)
|
||||
return
|
||||
end
|
||||
minetest.chat_send_player(name, "Invalid parameters (see /help set)")
|
||||
end,
|
||||
})
|
||||
|
||||
19
builtin/deprecated.lua
Normal file
19
builtin/deprecated.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
-- Minetest: builtin/deprecated.lua
|
||||
|
||||
--
|
||||
-- Default material types
|
||||
--
|
||||
function digprop_err()
|
||||
minetest.log("info", debug.traceback())
|
||||
minetest.log("info", "WARNING: The minetest.digprop_* functions are obsolete and need to be replaced by item groups.")
|
||||
end
|
||||
|
||||
minetest.digprop_constanttime = digprop_err
|
||||
minetest.digprop_stonelike = digprop_err
|
||||
minetest.digprop_dirtlike = digprop_err
|
||||
minetest.digprop_gravellike = digprop_err
|
||||
minetest.digprop_woodlike = digprop_err
|
||||
minetest.digprop_leaveslike = digprop_err
|
||||
minetest.digprop_glasslike = digprop_err
|
||||
|
||||
|
||||
442
builtin/item.lua
Normal file
442
builtin/item.lua
Normal file
@@ -0,0 +1,442 @@
|
||||
-- Minetest: builtin/item.lua
|
||||
|
||||
--
|
||||
-- Item definition helpers
|
||||
--
|
||||
|
||||
function minetest.inventorycube(img1, img2, img3)
|
||||
img2 = img2 or img1
|
||||
img3 = img3 or img1
|
||||
return "[inventorycube"
|
||||
.. "{" .. img1:gsub("%^", "&")
|
||||
.. "{" .. img2:gsub("%^", "&")
|
||||
.. "{" .. img3:gsub("%^", "&")
|
||||
end
|
||||
|
||||
function minetest.get_pointed_thing_position(pointed_thing, above)
|
||||
if pointed_thing.type == "node" then
|
||||
if above then
|
||||
-- The position where a node would be placed
|
||||
return pointed_thing.above
|
||||
else
|
||||
-- The position where a node would be dug
|
||||
return pointed_thing.under
|
||||
end
|
||||
elseif pointed_thing.type == "object" then
|
||||
obj = pointed_thing.ref
|
||||
if obj ~= nil then
|
||||
return obj:getpos()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.dir_to_facedir(dir)
|
||||
if math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 3
|
||||
else
|
||||
return 1
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 2
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.dir_to_wallmounted(dir)
|
||||
if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
|
||||
if dir.y < 0 then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif math.abs(dir.x) > math.abs(dir.z) then
|
||||
if dir.x < 0 then
|
||||
return 3
|
||||
else
|
||||
return 2
|
||||
end
|
||||
else
|
||||
if dir.z < 0 then
|
||||
return 5
|
||||
else
|
||||
return 4
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.get_node_drops(nodename, toolname)
|
||||
local drop = ItemStack({name=nodename}):get_definition().drop
|
||||
if drop == nil then
|
||||
-- default drop
|
||||
return {ItemStack({name=nodename})}
|
||||
elseif type(drop) == "string" then
|
||||
-- itemstring drop
|
||||
return {ItemStack(drop)}
|
||||
elseif drop.items == nil then
|
||||
-- drop = {} to disable default drop
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Extended drop table
|
||||
local got_items = {}
|
||||
local got_count = 0
|
||||
local _, item, tool
|
||||
for _, item in ipairs(drop.items) do
|
||||
local good_rarity = true
|
||||
local good_tool = true
|
||||
if item.rarity ~= nil then
|
||||
good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
|
||||
end
|
||||
if item.tools ~= nil then
|
||||
good_tool = false
|
||||
for _, tool in ipairs(item.tools) do
|
||||
if tool:sub(1, 1) == '~' then
|
||||
good_tool = toolname:find(tool:sub(2)) ~= nil
|
||||
else
|
||||
good_tool = toolname == tool
|
||||
end
|
||||
if good_tool then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if good_rarity and good_tool then
|
||||
got_count = got_count + 1
|
||||
for _, add_item in ipairs(item.items) do
|
||||
got_items[#got_items+1] = add_item
|
||||
end
|
||||
if drop.max_items ~= nil and got_count == drop.max_items then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return got_items
|
||||
end
|
||||
|
||||
function minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
local item = itemstack:peek_item()
|
||||
local def = itemstack:get_definition()
|
||||
if def.type == "node" and pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.above
|
||||
local oldnode = minetest.env:get_node(pos)
|
||||
local olddef = ItemStack({name=oldnode.name}):get_definition()
|
||||
|
||||
if not olddef.buildable_to then
|
||||
minetest.log("info", placer:get_player_name() .. " tried to place"
|
||||
.. " node in invalid position " .. minetest.pos_to_string(pos)
|
||||
.. ", replacing " .. oldnode.name)
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log("action", placer:get_player_name() .. " places node "
|
||||
.. def.name .. " at " .. minetest.pos_to_string(pos))
|
||||
|
||||
local newnode = {name = def.name, param1 = 0, param2 = 0}
|
||||
|
||||
-- Calculate direction for wall mounted stuff like torches and signs
|
||||
if def.paramtype2 == 'wallmounted' then
|
||||
local under = pointed_thing.under
|
||||
local above = pointed_thing.above
|
||||
local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
|
||||
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
||||
-- Calculate the direction for furnaces and chests and stuff
|
||||
elseif def.paramtype2 == 'facedir' then
|
||||
local playerpos = placer:getpos()
|
||||
local dir = {x = pos.x - playerpos.x, y = pos.y - playerpos.y, z = pos.z - playerpos.z}
|
||||
newnode.param2 = minetest.dir_to_facedir(dir)
|
||||
minetest.log("action", "facedir: " .. newnode.param2)
|
||||
end
|
||||
|
||||
-- Add node and update
|
||||
minetest.env:add_node(pos, newnode)
|
||||
|
||||
-- Run callback
|
||||
if def.after_place_node then
|
||||
def.after_place_node(pos, placer)
|
||||
end
|
||||
|
||||
-- Run script hook (deprecated)
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||
callback(pos, newnode, placer)
|
||||
end
|
||||
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function minetest.item_place_object(itemstack, placer, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
|
||||
if pos ~= nil then
|
||||
local item = itemstack:take_item()
|
||||
minetest.env:add_item(pos, item)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function minetest.item_place(itemstack, placer, pointed_thing)
|
||||
if itemstack:get_definition().type == "node" then
|
||||
return minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
else
|
||||
return minetest.item_place_object(itemstack, placer, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.item_drop(itemstack, dropper, pos)
|
||||
if dropper.get_player_name then
|
||||
local v = dropper:get_look_dir()
|
||||
local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z}
|
||||
local obj = minetest.env:add_item(p, itemstack)
|
||||
v.x = v.x*2
|
||||
v.y = v.y*2 + 1
|
||||
v.z = v.z*2
|
||||
obj:setvelocity(v)
|
||||
else
|
||||
minetest.env:add_item(pos, itemstack)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function minetest.item_eat(hp_change, replace_with_item)
|
||||
return function(itemstack, user, pointed_thing) -- closure
|
||||
if itemstack:take_item() ~= nil then
|
||||
user:set_hp(user:get_hp() + hp_change)
|
||||
itemstack:add_item(replace_with_item) -- note: replace_with_item is optional
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.node_punch(pos, node, puncher)
|
||||
-- Run script hook
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_punchnodes) do
|
||||
callback(pos, node, puncher)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function minetest.node_dig(pos, node, digger)
|
||||
minetest.debug("node_dig")
|
||||
|
||||
local def = ItemStack({name=node.name}):get_definition()
|
||||
if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
|
||||
minetest.debug("not diggable")
|
||||
minetest.log("info", digger:get_player_name() .. " tried to dig "
|
||||
.. node.name .. " which is not diggable "
|
||||
.. minetest.pos_to_string(pos))
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log('action', digger:get_player_name() .. " digs "
|
||||
.. node.name .. " at " .. minetest.pos_to_string(pos))
|
||||
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
local wielded = digger:get_wielded_item()
|
||||
local drops = minetest.get_node_drops(node.name, wielded:get_name())
|
||||
|
||||
-- Wear out tool
|
||||
tp = wielded:get_tool_capabilities()
|
||||
dp = minetest.get_dig_params(def.groups, tp)
|
||||
wielded:add_wear(dp.wear)
|
||||
digger:set_wielded_item(wielded)
|
||||
|
||||
-- Add dropped items
|
||||
local _, dropped_item
|
||||
for _, dropped_item in ipairs(drops) do
|
||||
digger:get_inventory():add_item("main", dropped_item)
|
||||
end
|
||||
end
|
||||
|
||||
local oldnode = nil
|
||||
local oldmetadata = nil
|
||||
if def.after_dig_node then
|
||||
oldnode = node;
|
||||
oldmetadata = minetest.env:get_meta(pos):to_table()
|
||||
end
|
||||
|
||||
-- Remove node and update
|
||||
minetest.env:remove_node(pos)
|
||||
|
||||
-- Run callback
|
||||
if def.after_dig_node then
|
||||
def.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
end
|
||||
|
||||
-- Run script hook (deprecated)
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_dignodes) do
|
||||
callback(pos, node, digger)
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.node_metadata_inventory_move_allow_all(pos, from_list,
|
||||
from_index, to_list, to_index, count, player)
|
||||
minetest.log("verbose", "node_metadata_inventory_move_allow_all")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
local from_stack = inv:get_stack(from_list, from_index)
|
||||
local taken_items = from_stack:take_item(count)
|
||||
inv:set_stack(from_list, from_index, from_stack)
|
||||
|
||||
local to_stack = inv:get_stack(to_list, to_index)
|
||||
to_stack:add_item(taken_items)
|
||||
inv:set_stack(to_list, to_index, to_stack)
|
||||
end
|
||||
|
||||
function minetest.node_metadata_inventory_offer_allow_all(pos, listname, index, stack, player)
|
||||
minetest.log("verbose", "node_metadata_inventory_offer_allow_all")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local the_stack = inv:get_stack(listname, index)
|
||||
the_stack:add_item(stack)
|
||||
inv:set_stack(listname, index, the_stack)
|
||||
return ItemStack("")
|
||||
end
|
||||
|
||||
function minetest.node_metadata_inventory_take_allow_all(pos, listname, index, count, player)
|
||||
minetest.log("verbose", "node_metadata_inventory_take_allow_all")
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local the_stack = inv:get_stack(listname, index)
|
||||
local taken_items = the_stack:take_item(count)
|
||||
inv:set_stack(listname, index, the_stack)
|
||||
return taken_items
|
||||
end
|
||||
|
||||
-- This is used to allow mods to redefine minetest.item_place and so on
|
||||
-- NOTE: This is not the preferred way. Preferred way is to provide enough
|
||||
-- callbacks to not require redefining global functions. -celeron55
|
||||
local function redef_wrapper(table, name)
|
||||
return function(...)
|
||||
return table[name](...)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Item definition defaults
|
||||
--
|
||||
|
||||
minetest.nodedef_default = {
|
||||
-- Item properties
|
||||
type="node",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 99,
|
||||
usable = false,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
|
||||
on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
|
||||
on_use = nil,
|
||||
can_dig = nil,
|
||||
|
||||
on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch
|
||||
on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig
|
||||
|
||||
on_receive_fields = nil,
|
||||
|
||||
on_metadata_inventory_move = minetest.node_metadata_inventory_move_allow_all,
|
||||
on_metadata_inventory_offer = minetest.node_metadata_inventory_offer_allow_all,
|
||||
on_metadata_inventory_take = minetest.node_metadata_inventory_take_allow_all,
|
||||
|
||||
-- Node properties
|
||||
drawtype = "normal",
|
||||
visual_scale = 1.0,
|
||||
tile_images = {""},
|
||||
special_materials = {
|
||||
{image="", backface_culling=true},
|
||||
{image="", backface_culling=true},
|
||||
},
|
||||
alpha = 255,
|
||||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
paramtype2 = "none",
|
||||
is_ground_content = false,
|
||||
sunlight_propagates = false,
|
||||
walkable = true,
|
||||
pointable = true,
|
||||
diggable = true,
|
||||
climbable = false,
|
||||
buildable_to = false,
|
||||
liquidtype = "none",
|
||||
liquid_alternative_flowing = "",
|
||||
liquid_alternative_source = "",
|
||||
liquid_viscosity = 0,
|
||||
light_source = 0,
|
||||
damage_per_second = 0,
|
||||
selection_box = {type="regular"},
|
||||
legacy_facedir_simple = false,
|
||||
legacy_wallmounted = false,
|
||||
}
|
||||
|
||||
minetest.craftitemdef_default = {
|
||||
type="craft",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 99,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
|
||||
on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
|
||||
on_use = nil,
|
||||
}
|
||||
|
||||
minetest.tooldef_default = {
|
||||
type="tool",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 1,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
|
||||
on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
|
||||
on_use = nil,
|
||||
}
|
||||
|
||||
minetest.noneitemdef_default = { -- This is used for the hand and unknown items
|
||||
type="none",
|
||||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = {x=1,y=1,z=1},
|
||||
stack_max = 99,
|
||||
liquids_pointable = false,
|
||||
tool_capabilities = nil,
|
||||
|
||||
-- Interaction callbacks
|
||||
on_place = nil,
|
||||
on_drop = nil,
|
||||
on_use = nil,
|
||||
}
|
||||
|
||||
103
builtin/item_entity.lua
Normal file
103
builtin/item_entity.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
-- Minetest: builtin/item_entity.lua
|
||||
|
||||
function minetest.spawn_item(pos, item)
|
||||
-- Take item in any format
|
||||
local stack = ItemStack(item)
|
||||
local obj = minetest.env:add_entity(pos, "__builtin:item")
|
||||
obj:get_luaentity():set_item(stack:to_string())
|
||||
return obj
|
||||
end
|
||||
|
||||
minetest.register_entity("__builtin:item", {
|
||||
initial_properties = {
|
||||
hp_max = 1,
|
||||
physical = true,
|
||||
collisionbox = {-0.17,-0.17,-0.17, 0.17,0.17,0.17},
|
||||
visual = "sprite",
|
||||
visual_size = {x=0.5, y=0.5},
|
||||
textures = {""},
|
||||
spritediv = {x=1, y=1},
|
||||
initial_sprite_basepos = {x=0, y=0},
|
||||
is_visible = false,
|
||||
},
|
||||
|
||||
itemstring = '',
|
||||
physical_state = true,
|
||||
|
||||
set_item = function(self, itemstring)
|
||||
self.itemstring = itemstring
|
||||
local stack = ItemStack(itemstring)
|
||||
local itemtable = stack:to_table()
|
||||
local itemname = nil
|
||||
if itemtable then
|
||||
itemname = stack:to_table().name
|
||||
end
|
||||
local item_texture = nil
|
||||
local item_type = ""
|
||||
if minetest.registered_items[itemname] then
|
||||
item_texture = minetest.registered_items[itemname].inventory_image
|
||||
item_type = minetest.registered_items[itemname].type
|
||||
end
|
||||
prop = {
|
||||
is_visible = true,
|
||||
visual = "sprite",
|
||||
textures = {"unknown_item.png"}
|
||||
}
|
||||
if item_texture and item_texture ~= "" then
|
||||
prop.visual = "sprite"
|
||||
prop.textures = {item_texture}
|
||||
prop.visual_size = {x=0.50, y=0.50}
|
||||
else
|
||||
prop.visual = "wielditem"
|
||||
prop.textures = {itemname}
|
||||
prop.visual_size = {x=0.20, y=0.20}
|
||||
prop.automatic_rotate = math.pi * 0.25
|
||||
end
|
||||
self.object:set_properties(prop)
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
return self.itemstring
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
self.itemstring = staticdata
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
self.object:setvelocity({x=0, y=2, z=0})
|
||||
self.object:setacceleration({x=0, y=-10, z=0})
|
||||
self:set_item(self.itemstring)
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
local p = self.object:getpos()
|
||||
p.y = p.y - 0.3
|
||||
local nn = minetest.env:get_node(p).name
|
||||
if minetest.registered_nodes[nn].walkable then
|
||||
if self.physical_state then
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
self.object:setacceleration({x=0, y=0, z=0})
|
||||
self.physical_state = false
|
||||
self.object:set_properties({
|
||||
physical = false
|
||||
})
|
||||
end
|
||||
else
|
||||
if not self.physical_state then
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
self.object:setacceleration({x=0, y=-10, z=0})
|
||||
self.physical_state = true
|
||||
self.object:set_properties({
|
||||
physical = true
|
||||
})
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(self, hitter)
|
||||
if self.itemstring ~= '' then
|
||||
hitter:get_inventory():add_item("main", self.itemstring)
|
||||
end
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
||||
|
||||
101
builtin/misc.lua
Normal file
101
builtin/misc.lua
Normal file
@@ -0,0 +1,101 @@
|
||||
-- Minetest: builtin/misc.lua
|
||||
|
||||
--
|
||||
-- Misc. API functions
|
||||
--
|
||||
|
||||
minetest.timers_to_add = {}
|
||||
minetest.timers = {}
|
||||
minetest.register_globalstep(function(dtime)
|
||||
for _, timer in ipairs(minetest.timers_to_add) do
|
||||
table.insert(minetest.timers, timer)
|
||||
end
|
||||
minetest.timers_to_add = {}
|
||||
for index, timer in ipairs(minetest.timers) do
|
||||
timer.time = timer.time - dtime
|
||||
if timer.time <= 0 then
|
||||
timer.func(timer.param)
|
||||
table.remove(minetest.timers,index)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function minetest.after(time, func, param)
|
||||
table.insert(minetest.timers_to_add, {time=time, func=func, param=param})
|
||||
end
|
||||
|
||||
function minetest.check_player_privs(name, privs)
|
||||
local player_privs = minetest.get_player_privs(name)
|
||||
local missing_privileges = {}
|
||||
for priv, val in pairs(privs) do
|
||||
if val then
|
||||
if not player_privs[priv] then
|
||||
table.insert(missing_privileges, priv)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #missing_privileges > 0 then
|
||||
return false, missing_privileges
|
||||
end
|
||||
return true, ""
|
||||
end
|
||||
|
||||
function minetest.get_connected_players()
|
||||
-- This could be optimized a bit, but leave that for later
|
||||
local list = {}
|
||||
for _, obj in pairs(minetest.env:get_objects_inside_radius({x=0,y=0,z=0}, 1000000)) do
|
||||
if obj:is_player() then
|
||||
table.insert(list, obj)
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function minetest.hash_node_position(pos)
|
||||
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
|
||||
end
|
||||
|
||||
function minetest.get_item_group(name, group)
|
||||
if not minetest.registered_items[name] or not
|
||||
minetest.registered_items[name].groups[group] then
|
||||
return 0
|
||||
end
|
||||
return minetest.registered_items[name].groups[group]
|
||||
end
|
||||
|
||||
function minetest.get_node_group(name, group)
|
||||
return minetest.get_item_group(name, group)
|
||||
end
|
||||
|
||||
function minetest.string_to_pos(value)
|
||||
local p = {}
|
||||
p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
||||
if p.x and p.y and p.z then
|
||||
p.x = tonumber(p.x)
|
||||
p.y = tonumber(p.y)
|
||||
p.z = tonumber(p.z)
|
||||
return p
|
||||
end
|
||||
local p = {}
|
||||
p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
|
||||
if p.x and p.y and p.z then
|
||||
p.x = tonumber(p.x)
|
||||
p.y = tonumber(p.y)
|
||||
p.z = tonumber(p.z)
|
||||
return p
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
assert(minetest.string_to_pos("10.0, 5, -2").x == 10)
|
||||
assert(minetest.string_to_pos("( 10.0, 5, -2)").z == -2)
|
||||
assert(minetest.string_to_pos("asd, 5, -2)") == nil)
|
||||
|
||||
function minetest.setting_get_pos(name)
|
||||
local value = minetest.setting_get(name)
|
||||
if not value then
|
||||
return nil
|
||||
end
|
||||
return minetest.string_to_pos(value)
|
||||
end
|
||||
|
||||
94
builtin/misc_helpers.lua
Normal file
94
builtin/misc_helpers.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
-- Minetest: builtin/misc_helpers.lua
|
||||
|
||||
function basic_dump2(o)
|
||||
if type(o) == "number" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "string" then
|
||||
return string.format("%q", o)
|
||||
elseif type(o) == "boolean" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "function" then
|
||||
return "<function>"
|
||||
elseif type(o) == "userdata" then
|
||||
return "<userdata>"
|
||||
elseif type(o) == "nil" then
|
||||
return "nil"
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function dump2(o, name, dumped)
|
||||
name = name or "_"
|
||||
dumped = dumped or {}
|
||||
io.write(name, " = ")
|
||||
if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
|
||||
or type(o) == "function" or type(o) == "nil"
|
||||
or type(o) == "userdata" then
|
||||
io.write(basic_dump2(o), "\n")
|
||||
elseif type(o) == "table" then
|
||||
if dumped[o] then
|
||||
io.write(dumped[o], "\n")
|
||||
else
|
||||
dumped[o] = name
|
||||
io.write("{}\n") -- new table
|
||||
for k,v in pairs(o) do
|
||||
local fieldname = string.format("%s[%s]", name, basic_dump2(k))
|
||||
dump2(v, fieldname, dumped)
|
||||
end
|
||||
end
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function dump(o, dumped)
|
||||
dumped = dumped or {}
|
||||
if type(o) == "number" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "string" then
|
||||
return string.format("%q", o)
|
||||
elseif type(o) == "table" then
|
||||
if dumped[o] then
|
||||
return "<circular reference>"
|
||||
end
|
||||
dumped[o] = true
|
||||
local t = {}
|
||||
for k,v in pairs(o) do
|
||||
t[#t+1] = "" .. k .. " = " .. dump(v, dumped)
|
||||
end
|
||||
return "{" .. table.concat(t, ", ") .. "}"
|
||||
elseif type(o) == "boolean" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "function" then
|
||||
return "<function>"
|
||||
elseif type(o) == "userdata" then
|
||||
return "<userdata>"
|
||||
elseif type(o) == "nil" then
|
||||
return "nil"
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function string:split(sep)
|
||||
local sep, fields = sep or ",", {}
|
||||
local pattern = string.format("([^%s]+)", sep)
|
||||
self:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||||
return fields
|
||||
end
|
||||
|
||||
function string:trim()
|
||||
return (self:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
assert(string.trim("\n \t\tfoo bar\t ") == "foo bar")
|
||||
|
||||
function minetest.pos_to_string(pos)
|
||||
return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
|
||||
end
|
||||
|
||||
|
||||
324
builtin/misc_register.lua
Normal file
324
builtin/misc_register.lua
Normal file
@@ -0,0 +1,324 @@
|
||||
-- Minetest: builtin/misc_register.lua
|
||||
|
||||
--
|
||||
-- Make raw registration functions inaccessible to anyone except this file
|
||||
--
|
||||
|
||||
local register_item_raw = minetest.register_item_raw
|
||||
minetest.register_item_raw = nil
|
||||
|
||||
local register_alias_raw = minetest.register_alias_raw
|
||||
minetest.register_item_raw = nil
|
||||
|
||||
--
|
||||
-- Item / entity / ABM registration functions
|
||||
--
|
||||
|
||||
minetest.registered_abms = {}
|
||||
minetest.registered_entities = {}
|
||||
minetest.registered_items = {}
|
||||
minetest.registered_nodes = {}
|
||||
minetest.registered_craftitems = {}
|
||||
minetest.registered_tools = {}
|
||||
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)
|
||||
|
||||
-- These item names may not be used because they would interfere
|
||||
-- with legacy itemstrings
|
||||
local forbidden_item_names = {
|
||||
MaterialItem = true,
|
||||
MaterialItem2 = true,
|
||||
MaterialItem3 = true,
|
||||
NodeItem = true,
|
||||
node = true,
|
||||
CraftItem = true,
|
||||
craft = true,
|
||||
MBOItem = true,
|
||||
ToolItem = true,
|
||||
tool = true,
|
||||
}
|
||||
|
||||
local function check_modname_prefix(name)
|
||||
if name:sub(1,1) == ":" then
|
||||
-- Escape the modname prefix enforcement mechanism
|
||||
return name:sub(2)
|
||||
else
|
||||
-- Modname prefix enforcement
|
||||
local expected_prefix = minetest.get_current_modname() .. ":"
|
||||
if name:sub(1, #expected_prefix) ~= expected_prefix then
|
||||
error("Name " .. name .. " does not follow naming conventions: " ..
|
||||
"\"modname:\" or \":\" prefix required")
|
||||
end
|
||||
local subname = name:sub(#expected_prefix+1)
|
||||
if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
|
||||
error("Name " .. name .. " does not follow naming conventions: " ..
|
||||
"contains unallowed characters")
|
||||
end
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
function minetest.register_abm(spec)
|
||||
-- Add to minetest.registered_abms
|
||||
minetest.registered_abms[#minetest.registered_abms+1] = spec
|
||||
end
|
||||
|
||||
function minetest.register_entity(name, prototype)
|
||||
-- Check name
|
||||
if name == nil then
|
||||
error("Unable to register entity: Name is nil")
|
||||
end
|
||||
name = check_modname_prefix(tostring(name))
|
||||
|
||||
prototype.name = name
|
||||
prototype.__index = prototype -- so that it can be used as a metatable
|
||||
|
||||
-- Add to minetest.registered_entities
|
||||
minetest.registered_entities[name] = prototype
|
||||
end
|
||||
|
||||
function minetest.register_item(name, itemdef)
|
||||
-- Check name
|
||||
if name == nil then
|
||||
error("Unable to register item: Name is nil")
|
||||
end
|
||||
name = check_modname_prefix(tostring(name))
|
||||
if forbidden_item_names[name] then
|
||||
error("Unable to register item: Name is forbidden: " .. name)
|
||||
end
|
||||
itemdef.name = name
|
||||
|
||||
-- Apply defaults and add to registered_* table
|
||||
if itemdef.type == "node" then
|
||||
setmetatable(itemdef, {__index = minetest.nodedef_default})
|
||||
minetest.registered_nodes[itemdef.name] = itemdef
|
||||
elseif itemdef.type == "craft" then
|
||||
setmetatable(itemdef, {__index = minetest.craftitemdef_default})
|
||||
minetest.registered_craftitems[itemdef.name] = itemdef
|
||||
elseif itemdef.type == "tool" then
|
||||
setmetatable(itemdef, {__index = minetest.tooldef_default})
|
||||
minetest.registered_tools[itemdef.name] = itemdef
|
||||
elseif itemdef.type == "none" then
|
||||
setmetatable(itemdef, {__index = minetest.noneitemdef_default})
|
||||
else
|
||||
error("Unable to register item: Type is invalid: " .. dump(itemdef))
|
||||
end
|
||||
|
||||
-- Flowing liquid uses param2
|
||||
if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
|
||||
itemdef.paramtype2 = "flowingliquid"
|
||||
end
|
||||
|
||||
-- BEGIN Legacy stuff
|
||||
if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
|
||||
minetest.register_craft({
|
||||
type="cooking",
|
||||
output=itemdef.cookresult_itemstring,
|
||||
recipe=itemdef.name,
|
||||
cooktime=itemdef.furnace_cooktime
|
||||
})
|
||||
end
|
||||
if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
|
||||
minetest.register_craft({
|
||||
type="fuel",
|
||||
recipe=itemdef.name,
|
||||
burntime=itemdef.furnace_burntime
|
||||
})
|
||||
end
|
||||
-- END Legacy stuff
|
||||
|
||||
-- Disable all further modifications
|
||||
getmetatable(itemdef).__newindex = {}
|
||||
|
||||
--minetest.log("Registering item: " .. itemdef.name)
|
||||
minetest.registered_items[itemdef.name] = itemdef
|
||||
minetest.registered_aliases[itemdef.name] = nil
|
||||
register_item_raw(itemdef)
|
||||
end
|
||||
|
||||
function minetest.register_node(name, nodedef)
|
||||
nodedef.type = "node"
|
||||
minetest.register_item(name, nodedef)
|
||||
end
|
||||
|
||||
function minetest.register_craftitem(name, craftitemdef)
|
||||
craftitemdef.type = "craft"
|
||||
|
||||
-- BEGIN Legacy stuff
|
||||
if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
|
||||
craftitemdef.inventory_image = craftitemdef.image
|
||||
end
|
||||
-- END Legacy stuff
|
||||
|
||||
minetest.register_item(name, craftitemdef)
|
||||
end
|
||||
|
||||
function minetest.register_tool(name, tooldef)
|
||||
tooldef.type = "tool"
|
||||
tooldef.stack_max = 1
|
||||
|
||||
-- BEGIN Legacy stuff
|
||||
if tooldef.inventory_image == nil and tooldef.image ~= nil then
|
||||
tooldef.inventory_image = tooldef.image
|
||||
end
|
||||
if tooldef.tool_capabilities == nil and
|
||||
(tooldef.full_punch_interval ~= nil or
|
||||
tooldef.basetime ~= nil or
|
||||
tooldef.dt_weight ~= nil or
|
||||
tooldef.dt_crackiness ~= nil or
|
||||
tooldef.dt_crumbliness ~= nil or
|
||||
tooldef.dt_cuttability ~= nil or
|
||||
tooldef.basedurability ~= nil or
|
||||
tooldef.dd_weight ~= nil or
|
||||
tooldef.dd_crackiness ~= nil or
|
||||
tooldef.dd_crumbliness ~= nil or
|
||||
tooldef.dd_cuttability ~= nil) then
|
||||
tooldef.tool_capabilities = {
|
||||
full_punch_interval = tooldef.full_punch_interval,
|
||||
basetime = tooldef.basetime,
|
||||
dt_weight = tooldef.dt_weight,
|
||||
dt_crackiness = tooldef.dt_crackiness,
|
||||
dt_crumbliness = tooldef.dt_crumbliness,
|
||||
dt_cuttability = tooldef.dt_cuttability,
|
||||
basedurability = tooldef.basedurability,
|
||||
dd_weight = tooldef.dd_weight,
|
||||
dd_crackiness = tooldef.dd_crackiness,
|
||||
dd_crumbliness = tooldef.dd_crumbliness,
|
||||
dd_cuttability = tooldef.dd_cuttability,
|
||||
}
|
||||
end
|
||||
-- END Legacy stuff
|
||||
|
||||
minetest.register_item(name, tooldef)
|
||||
end
|
||||
|
||||
function minetest.register_alias(name, convert_to)
|
||||
if forbidden_item_names[name] then
|
||||
error("Unable to register alias: Name is forbidden: " .. name)
|
||||
end
|
||||
if minetest.registered_items[name] ~= nil then
|
||||
minetest.log("WARNING: Not registering alias, item with same name" ..
|
||||
" is already defined: " .. name .. " -> " .. convert_to)
|
||||
else
|
||||
--minetest.log("Registering alias: " .. name .. " -> " .. convert_to)
|
||||
minetest.registered_aliases[name] = convert_to
|
||||
register_alias_raw(name, convert_to)
|
||||
end
|
||||
end
|
||||
|
||||
-- Alias the forbidden item names to "" so they can't be
|
||||
-- created via itemstrings (e.g. /give)
|
||||
local name
|
||||
for name in pairs(forbidden_item_names) do
|
||||
minetest.registered_aliases[name] = ""
|
||||
register_alias_raw(name, "")
|
||||
end
|
||||
|
||||
|
||||
-- Deprecated:
|
||||
-- Aliases for minetest.register_alias (how ironic...)
|
||||
--minetest.alias_node = minetest.register_alias
|
||||
--minetest.alias_tool = minetest.register_alias
|
||||
--minetest.alias_craftitem = minetest.register_alias
|
||||
|
||||
--
|
||||
-- Built-in node definitions. Also defined in C.
|
||||
--
|
||||
|
||||
minetest.register_item(":unknown", {
|
||||
type = "none",
|
||||
description = "Unknown Item",
|
||||
inventory_image = "unknown_item.png",
|
||||
on_place = minetest.item_place,
|
||||
on_drop = minetest.item_drop,
|
||||
})
|
||||
|
||||
minetest.register_node(":air", {
|
||||
description = "Air (you hacker you!)",
|
||||
inventory_image = "unknown_block.png",
|
||||
wield_image = "unknown_block.png",
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true,
|
||||
air_equivalent = true,
|
||||
})
|
||||
|
||||
minetest.register_node(":ignore", {
|
||||
description = "Ignore (you hacker you!)",
|
||||
inventory_image = "unknown_block.png",
|
||||
wield_image = "unknown_block.png",
|
||||
drawtype = "airlike",
|
||||
paramtype = "none",
|
||||
sunlight_propagates = false,
|
||||
walkable = false,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = true, -- A way to remove accidentally placed ignores
|
||||
air_equivalent = true,
|
||||
})
|
||||
|
||||
-- The hand (bare definition)
|
||||
minetest.register_item(":", {
|
||||
type = "none",
|
||||
})
|
||||
|
||||
--
|
||||
-- Creative inventory
|
||||
--
|
||||
|
||||
minetest.creative_inventory = {}
|
||||
|
||||
minetest.add_to_creative_inventory = function(itemstring)
|
||||
table.insert(minetest.creative_inventory, itemstring)
|
||||
end
|
||||
|
||||
--
|
||||
-- Callback registration
|
||||
--
|
||||
|
||||
local function make_registration()
|
||||
local t = {}
|
||||
local registerfunc = function(func) table.insert(t, func) end
|
||||
return t, registerfunc
|
||||
end
|
||||
|
||||
minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
|
||||
minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
|
||||
minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
|
||||
minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
|
||||
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_joinplayers, minetest.register_on_joinplayer = make_registration()
|
||||
minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
|
||||
|
||||
minetest.registered_on_placenodes = {}
|
||||
minetest.register_on_placenode = function(callback)
|
||||
minetest.log("info", debug.traceback())
|
||||
minetest.log("info", "WARNING: minetest.register_on_placenode is deprecated. Use on_construct or after_place_node in node definition instead.")
|
||||
table.insert(minetest.registered_on_placenodes, callback)
|
||||
end
|
||||
minetest.registered_on_dignodes = {}
|
||||
minetest.register_on_dignode = function(callback)
|
||||
minetest.log("info", debug.traceback())
|
||||
minetest.log("info", "WARNING: minetest.register_on_dignode is deprecated. Use on_destruct or after_dig_node in node definition instead.")
|
||||
table.insert(minetest.registered_on_dignodes, callback)
|
||||
end
|
||||
|
||||
48
builtin/privileges.lua
Normal file
48
builtin/privileges.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
-- Minetest: builtin/privileges.lua
|
||||
|
||||
--
|
||||
-- Privileges
|
||||
--
|
||||
|
||||
minetest.registered_privileges = {}
|
||||
|
||||
function minetest.register_privilege(name, param)
|
||||
local function fill_defaults(def)
|
||||
if def.give_to_singleplayer == nil then
|
||||
def.give_to_singleplayer = true
|
||||
end
|
||||
if def.description == nil then
|
||||
def.description = "(no description)"
|
||||
end
|
||||
end
|
||||
local def = {}
|
||||
if type(param) == "table" then
|
||||
def = param
|
||||
else
|
||||
def = {description = param}
|
||||
end
|
||||
fill_defaults(def)
|
||||
minetest.registered_privileges[name] = def
|
||||
end
|
||||
|
||||
minetest.register_privilege("interact", "Can interact with things and modify the world")
|
||||
minetest.register_privilege("teleport", "Can use /teleport command")
|
||||
minetest.register_privilege("bring", "Can teleport other players")
|
||||
minetest.register_privilege("settime", "Can use /time")
|
||||
minetest.register_privilege("privs", "Can modify privileges")
|
||||
minetest.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
|
||||
minetest.register_privilege("server", "Can do server maintenance stuff")
|
||||
minetest.register_privilege("shout", "Can speak in chat")
|
||||
minetest.register_privilege("ban", "Can ban and unban players")
|
||||
minetest.register_privilege("give", "Can use /give and /giveme")
|
||||
minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
|
||||
minetest.register_privilege("fly", {
|
||||
description = "Can fly using the free_move mode",
|
||||
give_to_singleplayer = false,
|
||||
})
|
||||
minetest.register_privilege("fast", {
|
||||
description = "Can walk fast using the fast_move mode",
|
||||
give_to_singleplayer = false,
|
||||
})
|
||||
|
||||
|
||||
33
builtin/static_spawn.lua
Normal file
33
builtin/static_spawn.lua
Normal file
@@ -0,0 +1,33 @@
|
||||
-- Minetest: builtin/static_spawn.lua
|
||||
|
||||
local function warn_invalid_static_spawnpoint()
|
||||
if minetest.setting_get("static_spawnpoint") and
|
||||
not minetest.setting_get_pos("static_spawnpoint") then
|
||||
minetest.log('error', "The static_spawnpoint setting is invalid: \""..
|
||||
minetest.setting_get("static_spawnpoint").."\"")
|
||||
end
|
||||
end
|
||||
|
||||
warn_invalid_static_spawnpoint()
|
||||
|
||||
local function put_player_in_spawn(obj)
|
||||
warn_invalid_static_spawnpoint()
|
||||
local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint")
|
||||
if not static_spawnpoint then
|
||||
return false
|
||||
end
|
||||
minetest.log('action', "Moving "..obj:get_player_name()..
|
||||
" to static spawnpoint at "..
|
||||
minetest.pos_to_string(static_spawnpoint))
|
||||
obj:setpos(static_spawnpoint)
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_on_newplayer(function(obj)
|
||||
put_player_in_spawn(obj)
|
||||
end)
|
||||
|
||||
minetest.register_on_respawnplayer(function(obj)
|
||||
return put_player_in_spawn(obj)
|
||||
end)
|
||||
|
||||
711
doc/lua_api.txt
711
doc/lua_api.txt
@@ -25,7 +25,7 @@ If you have any difficulty in understanding this, please read:
|
||||
Startup
|
||||
--------
|
||||
Mods are loaded during server startup from the mod load paths by running
|
||||
the init.lua scripts.
|
||||
the init.lua scripts in a shared environment.
|
||||
|
||||
Mod load path
|
||||
-------------
|
||||
@@ -46,6 +46,25 @@ On an installed version on linux:
|
||||
~/.minetest/mods/gameid/ <-- User-installed mods
|
||||
~/.minetest/worlds/worldname/worldmods
|
||||
|
||||
Mod load path for world-specific games
|
||||
--------------------------------------
|
||||
It is possible to include a game in a world; in this case, no mods or
|
||||
games are loaded or checked from anywhere else.
|
||||
|
||||
This is useful for eg. adventure worlds.
|
||||
|
||||
This happens if the following directory exists:
|
||||
$world/game/
|
||||
|
||||
Mods should be then be placed in:
|
||||
$world/game/mods/
|
||||
|
||||
Modpack support
|
||||
----------------
|
||||
Mods can be put in a subdirectory, if the parent directory, which otherwise
|
||||
should be a mod, contains a file named modpack.txt. This file shall be
|
||||
empty, except for lines starting with #, which are comments.
|
||||
|
||||
Mod directory structure
|
||||
------------------------
|
||||
mods
|
||||
@@ -167,53 +186,11 @@ Examples of sound parameter tables:
|
||||
-- Play connected to an object, looped
|
||||
{
|
||||
object = <an ObjectRef>,
|
||||
gain = 1.0, -- default
|
||||
max_hear_distance = 32, -- default
|
||||
gain = 1.0, -- default
|
||||
max_hear_distance = 32, -- default
|
||||
loop = true, -- only sounds connected to objects can be looped
|
||||
}
|
||||
|
||||
Representations of simple things
|
||||
--------------------------------
|
||||
MapNode representation:
|
||||
{name="name", param1=num, param2=num}
|
||||
|
||||
MapNodes do not directly have any other data associated with them.
|
||||
If you want to access the definition of the node, you access
|
||||
minetest.registered_nodes[node.name]
|
||||
|
||||
param1 and param2 are 8 bit and 4 bit integers, respectively. They
|
||||
are reserved for certain automated functions. If you don't use these
|
||||
functions, you can use them to store arbitrary values.
|
||||
|
||||
param1 is reserved for the engine when:
|
||||
paramtype != "none"
|
||||
param2 is reserved for the engine when any of these are used:
|
||||
liquidtype == "flowing"
|
||||
drawtype == "flowingliquid"
|
||||
drawtype == "torchlike"
|
||||
drawtype == "signlike"
|
||||
|
||||
Position/vector:
|
||||
{x=num, y=num, z=num}
|
||||
Currently the API does not provide any helper functions for addition,
|
||||
subtraction and whatever; you can define those that you need yourself.
|
||||
|
||||
stackstring/itemstring: A stack of items in serialized format.
|
||||
eg. 'default:dirt 5'
|
||||
eg. 'default:pick_wood 21323'
|
||||
eg. 'default:apple'
|
||||
|
||||
item: A stack of items in Lua table format.
|
||||
eg. {name="default:dirt", count=5, wear=0, metadata=""}
|
||||
^ 5 dirt nodes
|
||||
eg. {name="default:pick_wood", count=1, wear=21323, metadata=""}
|
||||
^ a wooden pick about 1/3 weared out
|
||||
eg. {name="default:apple", count=1, wear=0, metadata=""}
|
||||
^ an apple.
|
||||
|
||||
Any time an item must be passed to a function, it can be an
|
||||
ItemStack (see below), an itemstring or a table in the above format.
|
||||
|
||||
SimpleSoundSpec:
|
||||
eg. ""
|
||||
eg. "default_place_node"
|
||||
@@ -221,6 +198,95 @@ eg. {}
|
||||
eg. {name="default_place_node"}
|
||||
eg. {name="default_place_node", gain=1.0}
|
||||
|
||||
Registered definitions of stuff
|
||||
--------------------------------
|
||||
Anything added using certain minetest.register_* functions get added to
|
||||
the global minetest.registered_* tables.
|
||||
|
||||
minetest.register_entity(name, prototype table)
|
||||
-> minetest.registered_entities[name]
|
||||
|
||||
minetest.register_node(name, node definition)
|
||||
-> minetest.registered_items[name]
|
||||
-> minetest.registered_nodes[name]
|
||||
|
||||
minetest.register_tool(name, item definition)
|
||||
-> minetest.registered_items[name]
|
||||
|
||||
minetest.register_craftitem(name, item definition)
|
||||
-> minetest.registered_items[name]
|
||||
|
||||
Note that in some cases you will stumble upon things that are not contained
|
||||
in these tables (eg. when a mod has been removed). Always check for
|
||||
existence before trying to access the fields.
|
||||
|
||||
Example: If you want to check the drawtype of a node, you could do:
|
||||
|
||||
local function get_nodedef_field(nodename, fieldname)
|
||||
if not minetest.registered_nodes[nodename] then
|
||||
return nil
|
||||
end
|
||||
return minetest.registered_nodes[nodename][fieldname]
|
||||
end
|
||||
local drawtype = get_nodedef_field(nodename, "drawtype")
|
||||
|
||||
Example: minetest.get_item_group(name, group) has been implemented as:
|
||||
|
||||
function minetest.get_item_group(name, group)
|
||||
if not minetest.registered_items[name] or not
|
||||
minetest.registered_items[name].groups[group] then
|
||||
return 0
|
||||
end
|
||||
return minetest.registered_items[name].groups[group]
|
||||
end
|
||||
|
||||
Nodes
|
||||
------
|
||||
Nodes are the bulk data of the world: cubes and other things that take the
|
||||
space of a cube. Huge amounts of them are handled efficiently, but they
|
||||
are quite static.
|
||||
|
||||
The definition of a node is stored and can be accessed by name in
|
||||
minetest.registered_nodes[node.name]
|
||||
See "Registered definitions of stuff".
|
||||
|
||||
Nodes are passed by value between Lua and the engine.
|
||||
They are represented by a table:
|
||||
{name="name", param1=num, param2=num}
|
||||
|
||||
param1 and param2 are 8 bit and 4 bit integers, respectively. The engine
|
||||
uses them for certain automated functions. If you don't use these
|
||||
functions, you can use them to store arbitrary values.
|
||||
|
||||
The functions of param1 and param2 are determined by certain fields in the
|
||||
node definition:
|
||||
param1 is reserved for the engine when paramtype != "none":
|
||||
paramtype = "light"
|
||||
^ The value stores light with and without sun in it's
|
||||
upper and lower 4 bits.
|
||||
param2 is reserved for the engine when any of these are used:
|
||||
liquidtype == "flowing"
|
||||
^ The level and some flags of the liquid is stored in param2
|
||||
drawtype == "flowingliquid"
|
||||
^ The drawn liquid level is read from param2
|
||||
drawtype == "torchlike"
|
||||
drawtype == "signlike"
|
||||
paramtype2 == "wallmounted"
|
||||
^ The rotation of the node is stored in param2. You can make this value
|
||||
by using minetest.dir_to_wallmounted().
|
||||
paramtype2 == "facedir"
|
||||
^ The rotation of the node is stored in param2. Furnaces and chests are
|
||||
rotated this way. Can be made by using minetest.dir_to_facedir().
|
||||
|
||||
Nodes can also contain extra data. See "Node Metadata".
|
||||
|
||||
Representations of simple things
|
||||
--------------------------------
|
||||
Position/vector:
|
||||
{x=num, y=num, z=num}
|
||||
Currently the API does not provide any helper functions for addition,
|
||||
subtraction and whatever; you can define those that you need yourself.
|
||||
|
||||
Items
|
||||
------
|
||||
Node (register_node):
|
||||
@@ -230,6 +296,28 @@ Tool (register_tool):
|
||||
Craftitem (register_craftitem):
|
||||
A miscellaneous item
|
||||
|
||||
Items and item stacks can exist in three formats:
|
||||
|
||||
Serialized; This is called stackstring or itemstring:
|
||||
eg. 'default:dirt 5'
|
||||
eg. 'default:pick_wood 21323'
|
||||
eg. 'default:apple'
|
||||
|
||||
Table format:
|
||||
eg. {name="default:dirt", count=5, wear=0, metadata=""}
|
||||
^ 5 dirt nodes
|
||||
eg. {name="default:pick_wood", count=1, wear=21323, metadata=""}
|
||||
^ a wooden pick about 1/3 weared out
|
||||
eg. {name="default:apple", count=1, wear=0, metadata=""}
|
||||
^ an apple.
|
||||
|
||||
ItemStack:
|
||||
C++ native format with many helper methods. Useful for converting between
|
||||
formats. See the Class reference section for details.
|
||||
|
||||
When an item must be passed to a function, it can usually be in any of
|
||||
these formats.
|
||||
|
||||
Groups
|
||||
-------
|
||||
In a number of places, there is a group table. Groups define the
|
||||
@@ -249,14 +337,17 @@ Usage:
|
||||
- When not defined, the rating of a group defaults to 0. Thus when you
|
||||
read groups, you must interpret nil and 0 as the same value, 0.
|
||||
|
||||
You can read the rating of a group for an item or a node by using
|
||||
minetest.get_item_group(itemname, groupname)
|
||||
|
||||
Groups of items
|
||||
----------------
|
||||
Groups of items can define what kind of an item it is (eg. wool).
|
||||
|
||||
Groups of nodes
|
||||
----------------
|
||||
In addition to the general item things, whether a node is diggable and how
|
||||
long it takes is defined by using groups.
|
||||
In addition to the general item things, groups are used to define whether
|
||||
a node is destroyable and how long it takes to destroy by a tool.
|
||||
|
||||
Groups of entities
|
||||
-------------------
|
||||
@@ -281,7 +372,7 @@ Groups in crafting recipes
|
||||
{'group:water'},
|
||||
{'group:bowl'},
|
||||
},
|
||||
preserve = {'group:bowl'},
|
||||
preserve = {'group:bowl'},
|
||||
}
|
||||
|
||||
Special groups
|
||||
@@ -292,6 +383,7 @@ Special groups
|
||||
damage, and get weared out much faster, or not be able to get drops
|
||||
from destroyed nodes.
|
||||
- 0 is something that is directly accessible at the start of gameplay
|
||||
- There is no upper limit
|
||||
- dig_immediate: (player can always pick up node without tool wear)
|
||||
- 2: node is removed without tool wear after 0.5 seconds or so
|
||||
(rail, sign)
|
||||
@@ -299,6 +391,7 @@ Special groups
|
||||
|
||||
Known damage and digging time defining groups
|
||||
----------------------------------------------
|
||||
Valid ratings for these are 0, 1, 2 and 3, unless otherwise stated.
|
||||
- crumbly: dirt, sand
|
||||
- cracky: tough but crackable stuff like stone.
|
||||
- snappy: something that can be cut using fine tools; eg. leaves, small
|
||||
@@ -311,7 +404,7 @@ Known damage and digging time defining groups
|
||||
Can be added to nodes that shouldn't logically be breakable by the
|
||||
hand but are. Somewhat similar to dig_immediate, but times are more
|
||||
like {[1]=3.50,[2]=2.00,[3]=0.70} and this does not override the
|
||||
speed of a tool if the tool can dig at a larger speed than this
|
||||
speed of a tool if the tool can dig at a faster speed than this
|
||||
suggests for the hand.
|
||||
|
||||
Examples of custom groups
|
||||
@@ -334,97 +427,249 @@ Groups such as **crumbly**, **cracky** and **snappy** are used for this
|
||||
purpose. Rating is 1, 2 or 3. A higher rating for such a group implies
|
||||
faster digging time.
|
||||
|
||||
Also, the **level** group is used.
|
||||
The **level** group is used to limit the toughness of nodes a tool can dig
|
||||
and to scale the digging times / damage to a greater extent.
|
||||
|
||||
^ PLEASE DO UNDERSTAND THIS, otherwise you cannot use the system to it's
|
||||
full potential.
|
||||
|
||||
Tools define their properties by a list of parameters for groups. They
|
||||
cannot dig other groups; thus it is important to use a standard bunch of
|
||||
groups to enable interaction with tools.
|
||||
|
||||
**Example definition of the digging capabilities of a tool:**
|
||||
**Tools define:**
|
||||
* Full punch interval
|
||||
* Maximum drop level
|
||||
* For an arbitrary list of groups:
|
||||
* Uses (until the tool breaks)
|
||||
* Maximum level (usually 0, 1, 2 or 3)
|
||||
* Digging times
|
||||
|
||||
**Full punch interval**:
|
||||
When used as a weapon, the tool will do full damage if this time is spent
|
||||
between punches. If eg. half the time is spent, the tool will do half
|
||||
damage.
|
||||
|
||||
**Maximum drop level**
|
||||
Suggests the maximum level of node, when dug with the tool, that will drop
|
||||
it's useful item. (eg. iron ore to drop a lump of iron).
|
||||
- This is not automated; it is the responsibility of the node definition
|
||||
to implement this
|
||||
|
||||
**Uses**
|
||||
Determines how many uses the tool has when it is used for digging a node,
|
||||
of this group, of the maximum level. For lower leveled nodes, the use count
|
||||
is multiplied by 3^leveldiff.
|
||||
- uses=10, leveldiff=0 -> actual uses: 10
|
||||
- uses=10, leveldiff=1 -> actual uses: 30
|
||||
- uses=10, leveldiff=2 -> actual uses: 90
|
||||
|
||||
**Maximum level**
|
||||
Tells what is the maximum level of a node of this group that the tool will
|
||||
be able to dig.
|
||||
|
||||
**Digging times**
|
||||
List of digging times for different ratings of the group, for nodes of the
|
||||
maximum level.
|
||||
* For example, as a lua table, ''times={2=2.00, 3=0.70}''. This would
|
||||
result in the tool to be able to dig nodes that have a rating of 2 or 3
|
||||
for this group, and unable to dig the rating 1, which is the toughest.
|
||||
Unless there is a matching group that enables digging otherwise.
|
||||
* For entities, damage equals the amount of nodes dug in the time spent
|
||||
between hits, with a maximum time of ''full_punch_interval''.
|
||||
|
||||
Example definition of the capabilities of a tool
|
||||
-------------------------------------------------
|
||||
tool_capabilities = {
|
||||
full_punch_interval=1.5,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
crumbly={maxwear=0.01, maxlevel=2, times={[1]=0.80, [2]=0.60, [3]=0.40}}
|
||||
crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
|
||||
}
|
||||
}
|
||||
|
||||
**Tools define:**
|
||||
* Full punch interval Maximum drop level For an arbitrary list of groups:
|
||||
* Maximum level (usually 0, 1, 2 or 3) Maximum wear (0...1) Digging times
|
||||
This makes the tool be able to dig nodes that fullfill both of these:
|
||||
- Have the **crumbly** group
|
||||
- Have a **level** group less or equal to 2
|
||||
|
||||
**Full punch interval**: When used as a weapon, the tool will do full
|
||||
damage if this time is spent between punches. If eg. half the time is
|
||||
spent, the tool will do half damage.
|
||||
Table of resulting digging times:
|
||||
crumbly 0 1 2 3 4 <- level
|
||||
-> 0 - - - - -
|
||||
1 0.80 1.60 1.60 - -
|
||||
2 0.60 1.20 1.20 - -
|
||||
3 0.40 0.80 0.80 - -
|
||||
|
||||
**Maximum drop level** suggests the maximum level of node, when dug with
|
||||
the tool, that will drop it's useful item. (eg. iron ore to drop a lump of
|
||||
iron).
|
||||
level diff: 2 1 0 -1 -2
|
||||
|
||||
**Maximum level** tells what is the maximum level of a node of this group
|
||||
that the tool will be able to dig.
|
||||
Table of resulting tool uses:
|
||||
-> 0 - - - - -
|
||||
1 180 60 20 - -
|
||||
2 180 60 20 - -
|
||||
3 180 60 20 - -
|
||||
|
||||
**Maximum wear** determines how much the tool wears out when it is used for
|
||||
digging a node, of this group, of the maximum level. For lower leveled
|
||||
tools, the wear is divided by //4// to the exponent //level difference//.
|
||||
This means for a maximum wear of 0.1, a level difference 1 will result in
|
||||
wear=0.1/4=0.025, and a level difference of 2 will result in
|
||||
wear=0.1/(4*4)=0.00625.
|
||||
|
||||
**Digging times** is basically a list of digging times for different
|
||||
ratings of the group. It also determines the damage done to entities, based
|
||||
on their "armor groups".
|
||||
* For example, as a lua table, ''times={2=2.00, 3=0.70}''. This would
|
||||
* result the tool to be able to dig nodes that have a rating of 2 or 3
|
||||
* for this group, and unable to dig the rating 1, which is the toughest.
|
||||
* Unless there is a matching group that enables digging otherwise. For
|
||||
* entities, damage equals the amount of nodes dug in the time spent
|
||||
* between hits, with a maximum of ''full_punch_interval''.
|
||||
Notes:
|
||||
- At crumbly=0, the node is not diggable.
|
||||
- At crumbly=3, the level difference digging time divider kicks in and makes
|
||||
easy nodes to be quickly breakable.
|
||||
- At level > 2, the node is not diggable, because it's level > maxlevel
|
||||
|
||||
Entity damage mechanism
|
||||
------------------------
|
||||
Damage calculation:
|
||||
- Take the time spent after the last hit
|
||||
- Limit time to full_punch_interval
|
||||
- Take the damage groups and imagine a bunch of nodes that have them
|
||||
- Damage in HP is the amount of nodes destroyed in this time.
|
||||
|
||||
Client predicts damage based on damage groups. Because of this, it is able to
|
||||
give an immediate response when an entity is damaged or dies; the response is
|
||||
pre-defined somehow (eg. by defining a sprite animation) (not implemented;
|
||||
TODO).
|
||||
- Currently a smoke puff will appear when an entity dies.
|
||||
|
||||
The group **immortal** will completely disable normal damage.
|
||||
The group **immortal** completely disables normal damage.
|
||||
|
||||
Entities can define a special armor group, which is **punch_operable**. This
|
||||
group will disable the regular damage mechanism for players punching it by hand
|
||||
or a non-tool item.
|
||||
group disables the regular damage mechanism for players punching it by hand or
|
||||
a non-tool item, so that it can do something else than take damage.
|
||||
|
||||
On the Lua side, every punch calls ''entity:on_punch(puncher,
|
||||
time_from_last_punch, tool_capabilities, direction)''. This should never be
|
||||
called directly, because damage is usually not handled by the entity itself.
|
||||
* ''puncher'' is the object performing the punch. Can be nil. Should never be
|
||||
accessed unless absolutely required.
|
||||
accessed unless absolutely required, to encourage interoperability.
|
||||
* ''time_from_last_punch'' is time from last punch (by puncher) or nil.
|
||||
* ''tool_capabilities'' can be nil.
|
||||
* ''direction'' is a unit vector, pointing from the source of the punch to
|
||||
the punched object.
|
||||
|
||||
To punch an entity/object in Lua, call ''object:punch(puncher, time_from_last_punch, tool_capabilities, direction)''.
|
||||
To punch an entity/object in Lua, call ''object:punch(puncher,
|
||||
time_from_last_punch, tool_capabilities, direction)''.
|
||||
* Return value is tool wear.
|
||||
* Parameters are equal to the above callback.
|
||||
* If ''direction'' is nil and ''puncher'' is not nil, ''direction'' will be
|
||||
automatically filled in based on the location of ''puncher''.
|
||||
|
||||
Node Metadata
|
||||
-------------
|
||||
The instance of a node in the world normally only contains the three values
|
||||
mentioned in "Nodes". However, it is possible to insert extra data into a
|
||||
node. It is called "node metadata"; See "NodeMetaRef".
|
||||
|
||||
Metadata contains two things:
|
||||
- A key-value store
|
||||
- An inventory
|
||||
|
||||
Some of the values in the key-value store are handled specially:
|
||||
- formspec: Defines a right-click inventory menu. See "Formspec".
|
||||
- infotext: Text shown on the screen when the node is pointed at
|
||||
|
||||
Example stuff:
|
||||
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"list[current_name;main;0,0;8,4;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
meta:set_string("infotext", "Chest");
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 8*4)
|
||||
print(dump(meta:to_table()))
|
||||
meta:from_table({
|
||||
inventory = {
|
||||
main = {[1] = "default:dirt", [2] = "", [3] = "", [4] = "", [5] = "", [6] = "", [7] = "", [8] = "", [9] = "", [10] = "", [11] = "", [12] = "", [13] = "", [14] = "default:cobble", [15] = "", [16] = "", [17] = "", [18] = "", [19] = "", [20] = "default:cobble", [21] = "", [22] = "", [23] = "", [24] = "", [25] = "", [26] = "", [27] = "", [28] = "", [29] = "", [30] = "", [31] = "", [32] = ""}
|
||||
},
|
||||
fields = {
|
||||
formspec = "invsize[8,9;]list[current_name;main;0,0;8,4;]list[current_player;main;0,5;8,4;]",
|
||||
infotext = "Chest"
|
||||
}
|
||||
})
|
||||
|
||||
Formspec
|
||||
--------
|
||||
Formspec defines a menu. Currently not much else than inventories are
|
||||
supported. It is a string, with a somewhat strange format.
|
||||
|
||||
Spaces and newlines can be inserted between the blocks, as is used in the
|
||||
examples.
|
||||
|
||||
Examples:
|
||||
- Chest:
|
||||
invsize[8,9;]
|
||||
list[current_name;main;0,0;8,4;]
|
||||
list[current_player;main;0,5;8,4;]
|
||||
- Furnace:
|
||||
invsize[8,9;]
|
||||
list[current_name;fuel;2,3;1,1;]
|
||||
list[current_name;src;2,1;1,1;]
|
||||
list[current_name;dst;5,1;2,2;]
|
||||
list[current_player;main;0,5;8,4;]
|
||||
- Minecraft-like player inventory
|
||||
invsize[8,7.5;]
|
||||
image[1,0.6;1,2;player.png]
|
||||
list[current_player;main;0,3.5;8,4;]
|
||||
list[current_player;craft;3,0;3,3;]
|
||||
list[current_player;craftpreview;7,1;1,1;]
|
||||
|
||||
Elements:
|
||||
|
||||
invsize[<W>,<H>;]
|
||||
^ Define the size of the menu in inventory slots
|
||||
|
||||
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;]
|
||||
^ Show an inventory list
|
||||
|
||||
image[<X>,<Y>;<W>,<H>;<texture name>]
|
||||
^ Show an image
|
||||
^ Position and size units are inventory slots
|
||||
|
||||
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
|
||||
^ Textual field; will be sent to server when a button is clicked
|
||||
^ Position and size units are inventory slots
|
||||
^ Not implemented
|
||||
|
||||
button[<X>,<Y>;<W>,<H>;<name>;<label>]
|
||||
^ Clickable button. When clicked, fields will be sent.
|
||||
^ Button will be visible as a field, with the value "active".
|
||||
^ Position and size units are inventory slots
|
||||
^ Not implemented
|
||||
|
||||
Inventory location:
|
||||
- "context": Selected node metadata (deprecated: "current_name")
|
||||
- "current_player": Player to whom the menu is shown
|
||||
- "player:<name>": Any player
|
||||
- "nodemeta:<X>,<Y>,<Z>": Any node metadata
|
||||
|
||||
Helper functions
|
||||
-----------------
|
||||
dump2(obj, name="_", dumped={})
|
||||
^ Return object serialized as a string, handles reference loops
|
||||
dump(obj, dumped={})
|
||||
^ Return object serialized as a string
|
||||
string:split(separator)
|
||||
^ eg. string:split("a,b", ",") == {"a","b"}
|
||||
string:trim()
|
||||
^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
|
||||
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
|
||||
|
||||
minetest namespace reference
|
||||
-----------------------------
|
||||
minetest.get_current_modname() -> string
|
||||
minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
|
||||
^ Useful for loading additional .lua modules or static data from mod
|
||||
minetest.get_worldpath(modname) -> eg. "/home/user/.minetest/world"
|
||||
minetest.get_worldpath() -> eg. "/home/user/.minetest/world"
|
||||
^ Useful for storing custom data
|
||||
minetest.is_singleplayer()
|
||||
|
||||
minetest.debug(line)
|
||||
^ Always printed to stderr and logfile (print() is redirected here)
|
||||
minetest.log(line)
|
||||
minetest.log(loglevel, line)
|
||||
^ loglevel one of "error", "action", "info", "verbose"
|
||||
|
||||
Registration functions: (Call these only at load time)
|
||||
minetest.register_entity(name, prototype table)
|
||||
minetest.register_abm(abm definition)
|
||||
minetest.register_node(name, node definition)
|
||||
@@ -433,42 +678,138 @@ minetest.register_craftitem(name, item definition)
|
||||
minetest.register_alias(name, convert_to)
|
||||
minetest.register_craft(recipe)
|
||||
|
||||
Global callback registration functions: (Call these only at load time)
|
||||
minetest.register_globalstep(func(dtime))
|
||||
^ Called every server step, usually interval of 0.05s
|
||||
minetest.register_on_placenode(func(pos, newnode, placer))
|
||||
^ Called when a node has been placed
|
||||
^ Deprecated: Use on_construct or after_place_node in node definition instead
|
||||
minetest.register_on_dignode(func(pos, oldnode, digger))
|
||||
^ Called when a node has been dug. digger can be nil.
|
||||
^ Deprecated: Use on_destruct or after_dig_node in node definition instead
|
||||
minetest.register_on_punchnode(func(pos, node, puncher))
|
||||
minetest.register_on_generated(func(minp, maxp))
|
||||
^ Called when a node is punched
|
||||
minetest.register_on_generated(func(minp, maxp, blockseed))
|
||||
^ Called after generating a piece of world. Modifying nodes inside the area
|
||||
is a bit faster than usually.
|
||||
minetest.register_on_newplayer(func(ObjectRef))
|
||||
^ Called after a new player has been created
|
||||
minetest.register_on_dieplayer(func(ObjectRef))
|
||||
^ Called when a player dies
|
||||
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
|
||||
^ currently called _before_ repositioning of player occurs
|
||||
minetest.register_on_chat_message(func(name, message))
|
||||
|
||||
minetest.add_to_creative_inventory(itemstring)
|
||||
Other registration functions:
|
||||
minetest.register_chatcommand(cmd, chatcommand definition)
|
||||
minetest.register_privilege(name, definition)
|
||||
^ definition: "description text"
|
||||
^ definition: {
|
||||
description = "description text",
|
||||
give_to_singleplayer = boolean, -- default: true
|
||||
}
|
||||
minetest.register_authentication_handler(handler)
|
||||
^ See minetest.builtin_auth_handler in builtin.lua for reference
|
||||
|
||||
Setting-related:
|
||||
minetest.setting_set(name, value)
|
||||
minetest.setting_get(name) -> string or nil
|
||||
minetest.setting_getbool(name) -> boolean value or nil
|
||||
minetest.setting_get_pos(name) -> position or nil
|
||||
minetest.add_to_creative_inventory(itemstring)
|
||||
|
||||
Authentication:
|
||||
minetest.notify_authentication_modified(name)
|
||||
^ Should be called by the authentication handler if privileges change.
|
||||
^ To report everybody, set name=nil.
|
||||
minetest.get_password_hash(name, raw_password)
|
||||
^ Convert a name-password pair to a password hash that minetest can use
|
||||
minetest.string_to_privs(str) -> {priv1=true,...}
|
||||
minetest.privs_to_string(privs) -> "priv1,priv2,..."
|
||||
^ Convert between two privilege representations
|
||||
minetest.set_player_password(name, password_hash)
|
||||
minetest.set_player_privs(name, {priv1=true,...})
|
||||
minetest.get_player_privs(name) -> {priv1=true,...}
|
||||
minetest.auth_reload()
|
||||
^ These call the authentication handler
|
||||
minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs
|
||||
^ A quickhand for checking privileges
|
||||
|
||||
Chat:
|
||||
minetest.chat_send_all(text)
|
||||
minetest.chat_send_player(name, text)
|
||||
minetest.get_player_privs(name) -> set of privs
|
||||
|
||||
Inventory:
|
||||
minetest.get_inventory(location) -> InvRef
|
||||
^ location = eg. {type="player", name="celeron55"}
|
||||
{type="node", pos={x=, y=, z=}}
|
||||
|
||||
Item handling:
|
||||
minetest.inventorycube(img1, img2, img3)
|
||||
^ Returns a string for making an image of a cube (useful as an item image)
|
||||
minetest.get_pointed_thing_position(pointed_thing, above)
|
||||
^ Get position of a pointed_thing (that you can get from somewhere)
|
||||
minetest.dir_to_facedir(dir)
|
||||
^ Convert a vector to a facedir value, used in param2 for paramtype2="facedir"
|
||||
minetest.dir_to_wallmounted(dir)
|
||||
^ Convert a vector to a wallmounted value, used for paramtype2="wallmounted"
|
||||
minetest.get_node_drops(nodename, toolname)
|
||||
^ Returns list of item names.
|
||||
^ Note: This will be removed or modified in a future version.
|
||||
minetest.get_craft_result(input) -> output, decremented_input
|
||||
^ input.method = 'normal' or 'cooking' or 'fuel'
|
||||
^ input.width = for example 3
|
||||
^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
|
||||
stack 5, stack 6, stack 7, stack 8, stack 9 }
|
||||
^ output.item = ItemStack, if unsuccessful: empty ItemStack
|
||||
^ output.time = number, if unsuccessful: 0
|
||||
^ decremented_input = like input
|
||||
|
||||
Defaults for the on_* item definition functions:
|
||||
(These return the leftover itemstack)
|
||||
minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
^ Place item as a node
|
||||
minetest.item_place_object(itemstack, placer, pointed_thing)
|
||||
^ Place item as-is
|
||||
minetest.item_place(itemstack, placer, pointed_thing)
|
||||
^ Use one of the above based on what the item is.
|
||||
minetest.item_drop(itemstack, dropper, pos)
|
||||
^ Drop the item
|
||||
minetest.item_eat(hp_change, replace_with_item)
|
||||
^ Eat the item. replace_with_item can be nil.
|
||||
|
||||
Defaults for the on_punch and on_dig node definition callbacks:
|
||||
minetest.node_punch(pos, node, puncher)
|
||||
^ Calls functions registered by minetest.register_on_punchnode()
|
||||
minetest.node_dig(pos, node, digger)
|
||||
^ Checks if node can be dug, puts item into inventory, removes node
|
||||
^ Calls functions registered by minetest.registered_on_dignodes()
|
||||
|
||||
Sounds:
|
||||
minetest.sound_play(spec, parameters) -> handle
|
||||
^ spec = SimpleSoundSpec
|
||||
^ parameters = sound parameter table
|
||||
minetest.sound_stop(handle)
|
||||
|
||||
minetest.debug(line)
|
||||
^ Goes to dstream
|
||||
minetest.log(line)
|
||||
minetest.log(loglevel, line)
|
||||
^ loglevel one of "error", "action", "info", "verbose"
|
||||
Timing:
|
||||
minetest.after(time, func, param)
|
||||
^ Call function after time seconds
|
||||
^ param is optional; to pass multiple parameters, pass a table.
|
||||
|
||||
Random:
|
||||
minetest.get_connected_players() -> list of ObjectRefs
|
||||
minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer
|
||||
^ Gives a unique hash number for a node position (16+16+16=48bit)
|
||||
minetest.get_item_group(name, group) -> rating
|
||||
^ Get rating of a group of an item. (0 = not in group)
|
||||
minetest.get_node_group(name, group) -> rating
|
||||
^ Deprecated: An alias for the former.
|
||||
|
||||
Global objects:
|
||||
minetest.env - environment reference
|
||||
minetest.env - EnvRef of the server environment and world.
|
||||
^ Using this you can access nodes and entities
|
||||
|
||||
Global tables:
|
||||
minetest.registered_items
|
||||
@@ -499,45 +840,48 @@ Class reference
|
||||
----------------
|
||||
EnvRef: basically ServerEnvironment and ServerMap combined.
|
||||
methods:
|
||||
- add_node(pos, node)
|
||||
- remove_node(pos)
|
||||
- set_node(pos, node)
|
||||
- add_node(pos, node): alias set_node(pos, node)
|
||||
- remove_node(pos): equivalent to set_node(pos, "air")
|
||||
- get_node(pos)
|
||||
^ Returns {name="ignore", ...} for unloaded area
|
||||
- get_node_or_nil(pos)
|
||||
^ Returns nil for unloaded area
|
||||
- get_node_light(pos, timeofday) -> 0...15 or nil
|
||||
^ timeofday: nil = current time, 0 = night, 0.5 = day
|
||||
- add_entity(pos, name): Returns ObjectRef or nil if failed
|
||||
- add_item(pos, itemstring)
|
||||
- add_rat(pos)
|
||||
- add_firefly(pos)
|
||||
- add_entity(pos, name): Spawn Lua-defined entity at position
|
||||
^ Returns ObjectRef, or nil if failed
|
||||
- add_item(pos, itemstring): Spawn item
|
||||
^ Returns ObjectRef, or nil if failed
|
||||
- get_meta(pos) -- Get a NodeMetaRef at that position
|
||||
- get_player_by_name(name) -- Get an ObjectRef to a player
|
||||
- get_objects_inside_radius(pos, radius)
|
||||
- set_timeofday(val): val: 0...1; 0 = midnight, 0.5 = midday
|
||||
- get_timeofday()
|
||||
- find_node_near(pos, radius, nodenames) -> pos or nil
|
||||
^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
|
||||
- find_nodes_in_area(minp, maxp, nodenames) -> list of positions
|
||||
^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
|
||||
- get_perlin(seeddiff, octaves, persistence, scale)
|
||||
^ Return world-specific perlin noise (int(worldseed)+seeddiff)
|
||||
Deprecated:
|
||||
- add_rat(pos): Add C++ rat object (no-op)
|
||||
- add_firefly(pos): Add C++ firefly object (no-op)
|
||||
|
||||
NodeMetaRef (this stuff is subject to change in a future version)
|
||||
NodeMetaRef: Node metadata - reference extra data and functionality stored
|
||||
in a node
|
||||
- Can be gotten via minetest.env:get_nodemeta(pos)
|
||||
methods:
|
||||
- get_type()
|
||||
- allows_text_input()
|
||||
- set_text(text) -- eg. set the text of a sign
|
||||
- get_text()
|
||||
- get_owner()
|
||||
- set_owner(string)
|
||||
Generic node metadata specific:
|
||||
- set_infotext(infotext)
|
||||
- get_inventory() -> InvRef
|
||||
- set_inventory_draw_spec(string)
|
||||
- set_allow_text_input(bool)
|
||||
- set_allow_removal(bool)
|
||||
- set_enforce_owner(bool)
|
||||
- is_inventory_modified()
|
||||
- reset_inventory_modified()
|
||||
- is_text_modified()
|
||||
- reset_text_modified()
|
||||
- set_string(name, value)
|
||||
- get_string(name)
|
||||
- set_int(name, value)
|
||||
- get_int(name)
|
||||
- set_float(name, value)
|
||||
- get_float(name)
|
||||
- get_inventory() -> InvRef
|
||||
- to_table() -> nil or {fields = {...}, inventory = {list1 = {}, ...}}
|
||||
- from_table(nil or {})
|
||||
^ See "Node Metadata"
|
||||
|
||||
ObjectRef: Moving things in the game are generally these
|
||||
(basically reference to a C++ ServerActiveObject)
|
||||
@@ -557,6 +901,8 @@ methods:
|
||||
- get_wield_index(): returns the index of the wielded item
|
||||
- get_wielded_item() -> ItemStack
|
||||
- set_wielded_item(item): replaces the wielded item, returns true if successful
|
||||
- set_armor_groups({group1=rating, group2=rating, ...})
|
||||
- set_properties(object property table)
|
||||
LuaEntitySAO-only: (no-op for other objects)
|
||||
- setvelocity({x=num, y=num, z=num})
|
||||
- getvelocity() -> {x=num, y=num, z=num}
|
||||
@@ -569,17 +915,18 @@ LuaEntitySAO-only: (no-op for other objects)
|
||||
- select_horiz_by_yawpitch=false)
|
||||
- ^ Select sprite from spritesheet with optional animation and DM-style
|
||||
- texture selection based on yaw relative to camera
|
||||
- set_armor_groups({group1=rating, group2=rating, ...})
|
||||
- get_entity_name() (DEPRECATED: Will be removed in a future version)
|
||||
- get_luaentity()
|
||||
Player-only: (no-op for other objects)
|
||||
- get_player_name(): will return nil if is not a player
|
||||
- is_player(): true for players, false for others
|
||||
- get_player_name(): returns "" if is not a player
|
||||
- get_look_dir(): get camera direction as a unit vector
|
||||
- get_look_pitch(): pitch in radians
|
||||
- get_look_yaw(): yaw in radians (wraps around pretty randomly as of now)
|
||||
|
||||
InvRef: Reference to an inventory
|
||||
methods:
|
||||
- is_empty(listname): return true if list is empty
|
||||
- get_size(listname): get size of a list
|
||||
- set_size(listname, size): set size of a list
|
||||
- get_stack(listname, i): get a copy of stack index i in list
|
||||
@@ -595,6 +942,7 @@ methods:
|
||||
returns the items that were actually removed (as an ItemStack)
|
||||
|
||||
ItemStack: A stack of items.
|
||||
- Can be created via ItemStack(itemstack or itemstring or table or nil)
|
||||
methods:
|
||||
- is_empty(): return true if stack is empty
|
||||
- get_name(): returns item name (e.g. "default:stone")
|
||||
@@ -623,6 +971,21 @@ methods:
|
||||
^ returns copied ItemStack
|
||||
^ if n is omitted, n=1 is used
|
||||
|
||||
PseudoRandom: A pseudorandom number generator
|
||||
- Can be created via PseudoRandom(seed)
|
||||
methods:
|
||||
- next(): return next integer random number [0...32767]
|
||||
- next(min, max): return next integer random number [min...max]
|
||||
(max - min) must be 32767 or <= 6553 due to the simple
|
||||
implementation making bad distribution otherwise.
|
||||
|
||||
PerlinNoise: A perlin noise generator
|
||||
- Can be created via PerlinNoise(seed, octaves, persistence, scale)
|
||||
- Also minetest.env:get_perlin(seeddiff, octaves, persistence, scale)
|
||||
methods:
|
||||
- get2d(pos) -> 2d noise value at pos={x=,y=}
|
||||
- get3d(pos) -> 3d noise value at pos={x=,y=,z=}
|
||||
|
||||
Registered entities
|
||||
--------------------
|
||||
- Functions receive a "luaentity" as self:
|
||||
@@ -651,26 +1014,40 @@ Registered entities
|
||||
Definition tables
|
||||
------------------
|
||||
|
||||
Entity definition (register_entity)
|
||||
Object Properties
|
||||
{
|
||||
physical = true,
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
visual = "cube"/"sprite",
|
||||
visual = "cube"/"sprite"/"upright_sprite",
|
||||
visual_size = {x=1, y=1},
|
||||
textures = {texture,texture,texture,texture,texture,texture},
|
||||
textures = {}, -- number of required textures depends on visual
|
||||
spritediv = {x=1, y=1},
|
||||
initial_sprite_basepos = {x=0, y=0},
|
||||
is_visible = true,
|
||||
makes_footstep_sound = false,
|
||||
}
|
||||
|
||||
Entity definition (register_entity)
|
||||
{
|
||||
(Deprecated: Everything in object properties is read directly from here)
|
||||
|
||||
initial_properties = <initial object properties>,
|
||||
|
||||
on_activate = function(self, staticdata),
|
||||
on_step = function(self, dtime),
|
||||
on_punch = function(self, hitter),
|
||||
on_rightclick = function(self, clicker),
|
||||
get_staticdata = function(self),
|
||||
^ Called sometimes; the string returned is passed to on_activate when
|
||||
the entity is re-activated from static state
|
||||
|
||||
# Also you can define arbitrary member variables here
|
||||
myvariable = whatever,
|
||||
}
|
||||
|
||||
ABM (ActiveBlockModifier) definition (register_abm)
|
||||
{
|
||||
-- In the following two fields, also group:groupname will work.
|
||||
nodenames = {"default:lava_source"},
|
||||
neighbors = {"default:water_source", "default:water_flowing"}, -- (any of these)
|
||||
^ If left out or empty, any neighbor will do
|
||||
@@ -703,9 +1080,18 @@ Item definition (register_node, register_craftitem, register_tool)
|
||||
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
|
||||
}
|
||||
}
|
||||
on_drop = func(item, dropper, pos),
|
||||
on_place = func(item, placer, pointed_thing),
|
||||
on_use = func(item, user, pointed_thing),
|
||||
|
||||
on_place = func(itemstack, placer, pointed_thing),
|
||||
^ default: minetest.item_place
|
||||
on_drop = func(itemstack, dropper, pos),
|
||||
^ default: minetest.item_drop
|
||||
on_use = func(itemstack, user, pointed_thing),
|
||||
^ default: nil
|
||||
^ Function must return either nil if no item shall be removed from
|
||||
inventory, or an itemstack to replace the original itemstack.
|
||||
eg. itemstack:take_item(); return itemstack
|
||||
^ Otherwise, the function is free to do what it wants.
|
||||
^ The default functions handle regular use cases.
|
||||
}
|
||||
|
||||
Node definition (register_node)
|
||||
@@ -732,7 +1118,6 @@ Node definition (register_node)
|
||||
buildable_to = false,
|
||||
drop = "",
|
||||
-- alternatively drop = { max_items = ..., items = { ... } }
|
||||
metadata_name = "",
|
||||
liquidtype = "none",
|
||||
liquid_alternative_flowing = "",
|
||||
liquid_alternative_source = "",
|
||||
@@ -747,6 +1132,69 @@ Node definition (register_node)
|
||||
dig = <SimpleSoundSpec>, -- "__group" = group-based sound (default)
|
||||
dug = <SimpleSoundSpec>,
|
||||
},
|
||||
|
||||
on_construct = func(pos),
|
||||
^ Node constructor; always called after adding node
|
||||
^ Can set up metadata and stuff like that
|
||||
^ default: nil
|
||||
on_destruct = func(pos),
|
||||
^ Node destructor; always called before removing node
|
||||
^ default: nil
|
||||
|
||||
after_place_node = func(pos, placer),
|
||||
^ Called after constructing node when node was placed using
|
||||
minetest.item_place_node
|
||||
^ default: nil
|
||||
after_dig_node = func(pos, oldnode, oldmetadata, digger),
|
||||
^ oldmetadata is in table format
|
||||
^ Called after destructing node when node was dug using
|
||||
minetest.node_dig
|
||||
^ default: nil
|
||||
can_dig = function(pos,player)
|
||||
^ returns true if node can be dug, or false if not
|
||||
^ default: nil
|
||||
|
||||
on_punch = func(pos, node, puncher),
|
||||
^ default: minetest.node_punch
|
||||
^ By default: does nothing
|
||||
on_dig = func(pos, node, digger),
|
||||
^ default: minetest.node_dig
|
||||
^ By default: checks privileges, wears out tool and removes node
|
||||
|
||||
on_receive_fields = func(pos, formname, fields, sender),
|
||||
^ fields = {name1 = value1, name2 = value2, ...}
|
||||
^ Called when an UI form (eg. sign text input) returns data
|
||||
^ default: nil
|
||||
|
||||
on_metadata_inventory_move = func(pos, from_list, from_index,
|
||||
to_list, to_index, count, player),
|
||||
^ Called when a player wants to move items inside the metadata
|
||||
^ Should move items, or some items, if permitted. If not, should do
|
||||
nothing.
|
||||
^ The engine ensures the action is valid, i.e. the stack fits at the
|
||||
given position
|
||||
^ default: minetest.node_metadata_inventory_move_allow_all
|
||||
|
||||
on_metadata_inventory_offer = func(pos, listname, index, stack, player),
|
||||
^ Called when a player wants to put something into the metadata
|
||||
inventory
|
||||
^ Should check if the action is permitted (the engine ensures the
|
||||
action is valid, i.e. the stack fits at the given position)
|
||||
^ If permitted, modify the metadata inventory and return the
|
||||
"leftover" stack (normally nil).
|
||||
^ If not permitted, return itemstack.
|
||||
^ default: minetest.node_metadata_inventory_offer_allow_all
|
||||
|
||||
on_metadata_inventory_take = func(pos, listname, index, count, player),
|
||||
^ Called when a player wants to take something out of the metadata
|
||||
inventory
|
||||
^ Should check if the action is permitted (the engine ensures the
|
||||
action is valid, i.e. there's a stack of at least “count” items at
|
||||
that position)
|
||||
^ If permitted, modify the metadata inventory and return the
|
||||
stack of items
|
||||
^ If not permitted, return nil.
|
||||
^ default: minetest.node_metadata_inventory_take_allow_all
|
||||
}
|
||||
|
||||
Recipe: (register_craft)
|
||||
@@ -795,4 +1243,11 @@ Recipe (furnace fuel):
|
||||
burntime = 1,
|
||||
}
|
||||
|
||||
Chatcommand definition (register_chatcommand)
|
||||
{
|
||||
params = "<name> <privilege>", -- short parameter description
|
||||
description = "Remove privilege from player", -- full description
|
||||
privs = {privs=true}, -- require the "privs" privilege to run
|
||||
func = function(name, param), -- called when command is run
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ LIGHT_MAX = 14
|
||||
-- Definitions made by this mod that other mods can use too
|
||||
default = {}
|
||||
|
||||
-- Load other files
|
||||
dofile(minetest.get_modpath("default").."/mapgen.lua")
|
||||
|
||||
--
|
||||
-- Tool definition
|
||||
--
|
||||
@@ -24,10 +27,10 @@ minetest.register_item(":", {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level = 0,
|
||||
groupcaps = {
|
||||
fleshy = {times={[2]=2.00, [3]=1.00}, maxwear=0, maxlevel=1},
|
||||
crumbly = {times={[3]=0.70}, maxwear=0, maxlevel=1},
|
||||
snappy = {times={[3]=0.40}, maxwear=0, maxlevel=1},
|
||||
oddly_breakable_by_hand = {times={[1]=3.50,[2]=2.00,[3]=0.70}, maxwear=0, maxlevel=3},
|
||||
fleshy = {times={[2]=2.00, [3]=1.00}, uses=0, maxlevel=1},
|
||||
crumbly = {times={[2]=3.00, [3]=0.70}, uses=0, maxlevel=1},
|
||||
snappy = {times={[3]=0.40}, uses=0, maxlevel=1},
|
||||
oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=1.40}, uses=0, maxlevel=3},
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -38,7 +41,7 @@ minetest.register_tool("default:pick_wood", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
cracky={times={[2]=1.50, [3]=0.80}, maxwear=0.1, maxlevel=1}
|
||||
cracky={times={[2]=2.00, [3]=1.20}, uses=10, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -48,7 +51,7 @@ minetest.register_tool("default:pick_stone", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
cracky={times={[1]=1.50, [2]=0.80, [3]=0.60}, maxwear=0.05, maxlevel=1}
|
||||
cracky={times={[1]=2.00, [2]=1.20, [3]=0.80}, uses=20, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -58,7 +61,7 @@ minetest.register_tool("default:pick_steel", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
cracky={times={[1]=1.00, [2]=0.60, [3]=0.40}, maxwear=0.1, maxlevel=2}
|
||||
cracky={times={[1]=4.00, [2]=1.60, [3]=1.00}, uses=10, maxlevel=2}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -69,9 +72,9 @@ minetest.register_tool("default:pick_mese", {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
cracky={times={[1]=0.2, [2]=0.2, [3]=0.2}, maxwear=0.05, maxlevel=3},
|
||||
crumbly={times={[1]=0.2, [2]=0.2, [3]=0.2}, maxwear=0.05, maxlevel=3},
|
||||
snappy={times={[1]=0.2, [2]=0.2, [3]=0.2}, maxwear=0.05, maxlevel=3}
|
||||
cracky={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3},
|
||||
crumbly={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3},
|
||||
snappy={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -81,7 +84,7 @@ minetest.register_tool("default:shovel_wood", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=1.50, [2]=0.80, [3]=0.50}, maxwear=0.1, maxlevel=1}
|
||||
crumbly={times={[1]=2.00, [2]=0.80, [3]=0.50}, uses=10, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -91,7 +94,7 @@ minetest.register_tool("default:shovel_stone", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=0.80, [2]=0.50, [3]=0.30}, maxwear=0.05, maxlevel=1}
|
||||
crumbly={times={[1]=1.20, [2]=0.50, [3]=0.30}, uses=20, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -101,7 +104,7 @@ minetest.register_tool("default:shovel_steel", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=0.50, [2]=0.35, [3]=0.30}, maxwear=0.1, maxlevel=2}
|
||||
crumbly={times={[1]=1.00, [2]=0.70, [3]=0.60}, uses=10, maxlevel=2}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -111,8 +114,8 @@ minetest.register_tool("default:axe_wood", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
choppy={times={[2]=1.40, [3]=0.80}, maxwear=0.1, maxlevel=1},
|
||||
fleshy={times={[2]=1.50, [3]=0.80}, maxwear=0.1, maxlevel=1}
|
||||
choppy={times={[2]=1.40, [3]=0.80}, uses=10, maxlevel=1},
|
||||
fleshy={times={[2]=1.50, [3]=0.80}, uses=10, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -122,8 +125,8 @@ minetest.register_tool("default:axe_stone", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
choppy={times={[1]=1.50, [2]=1.00, [3]=0.60}, maxwear=0.05, maxlevel=1},
|
||||
fleshy={times={[2]=1.30, [3]=0.70}, maxwear=0.05, maxlevel=1}
|
||||
choppy={times={[1]=1.50, [2]=1.00, [3]=0.60}, uses=20, maxlevel=1},
|
||||
fleshy={times={[2]=1.30, [3]=0.70}, uses=20, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -133,8 +136,8 @@ minetest.register_tool("default:axe_steel", {
|
||||
tool_capabilities = {
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
choppy={times={[1]=1.00, [2]=0.80, [3]=0.50}, maxwear=0.1, maxlevel=2},
|
||||
fleshy={times={[2]=1.10, [3]=0.60}, maxwear=0.03, maxlevel=1}
|
||||
choppy={times={[1]=2.00, [2]=1.60, [3]=1.00}, uses=10, maxlevel=2},
|
||||
fleshy={times={[2]=1.10, [3]=0.60}, uses=40, maxlevel=1}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -145,9 +148,9 @@ minetest.register_tool("default:sword_wood", {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
fleshy={times={[2]=1.10, [3]=0.60}, maxwear=0.1, maxlevel=1},
|
||||
snappy={times={[2]=1.00, [3]=0.50}, maxwear=0.1, maxlevel=1},
|
||||
choppy={times={[3]=1.00}, maxwear=0.05, maxlevel=0}
|
||||
fleshy={times={[2]=1.10, [3]=0.60}, uses=10, maxlevel=1},
|
||||
snappy={times={[2]=1.00, [3]=0.50}, uses=10, maxlevel=1},
|
||||
choppy={times={[3]=1.00}, uses=20, maxlevel=0}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -158,9 +161,9 @@ minetest.register_tool("default:sword_stone", {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=0,
|
||||
groupcaps={
|
||||
fleshy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
|
||||
snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
|
||||
choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
|
||||
fleshy={times={[2]=0.80, [3]=0.40}, uses=20, maxlevel=1},
|
||||
snappy={times={[2]=0.80, [3]=0.40}, uses=20, maxlevel=1},
|
||||
choppy={times={[3]=0.90}, uses=20, maxlevel=0}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -171,9 +174,9 @@ minetest.register_tool("default:sword_steel", {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=1.00, [2]=0.40, [3]=0.20}, maxwear=0.1, maxlevel=2},
|
||||
snappy={times={[2]=0.70, [3]=0.30}, maxwear=0.03, maxlevel=1},
|
||||
choppy={times={[3]=0.70}, maxwear=0.03, maxlevel=0}
|
||||
fleshy={times={[1]=2.00, [2]=0.80, [3]=0.40}, uses=10, maxlevel=2},
|
||||
snappy={times={[2]=0.70, [3]=0.30}, uses=40, maxlevel=1},
|
||||
choppy={times={[3]=0.70}, uses=40, maxlevel=0}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -962,7 +965,7 @@ minetest.register_node("default:mese", {
|
||||
description = "Mese",
|
||||
tile_images = {"default_mese.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=1},
|
||||
groups = {cracky=1,level=2},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
@@ -1109,6 +1112,21 @@ minetest.register_node("default:sign_wall", {
|
||||
groups = {choppy=2,dig_immediate=2},
|
||||
legacy_wallmounted = true,
|
||||
sounds = default.node_sound_defaults(),
|
||||
on_construct = function(pos)
|
||||
--local n = minetest.env:get_node(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec", "hack:sign_text_input")
|
||||
meta:set_string("infotext", "\"\"")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
--print("Sign at "..minetest.pos_to_string(pos).." got "..dump(fields))
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
fields.text = fields.text or ""
|
||||
print((sender:get_player_name() or "").." wrote \""..fields.text..
|
||||
"\" to sign at "..minetest.pos_to_string(pos))
|
||||
meta:set_string("text", fields.text)
|
||||
meta:set_string("infotext", '"'..fields.text..'"')
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("default:chest", {
|
||||
@@ -1116,32 +1134,312 @@ minetest.register_node("default:chest", {
|
||||
tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
|
||||
paramtype2 = "facedir",
|
||||
metadata_name = "chest",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
legacy_facedir_simple = true,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"list[current_name;main;0,0;8,4;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
meta:set_string("infotext", "Chest")
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 8*4)
|
||||
end,
|
||||
can_dig = function(pos,player)
|
||||
local meta = minetest.env:get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index,
|
||||
to_list, to_index, count, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff in chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_move_allow_all(
|
||||
pos, from_list, from_index, to_list, to_index, count, player)
|
||||
end,
|
||||
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff to chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_offer_allow_all(
|
||||
pos, listname, index, stack, player)
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, count, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" takes stuff from chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_take_allow_all(
|
||||
pos, listname, index, count, player)
|
||||
end,
|
||||
})
|
||||
|
||||
local function has_locked_chest_privilege(meta, player)
|
||||
if player:get_player_name() ~= meta:get_string("owner") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_node("default:chest_locked", {
|
||||
description = "Locked Chest",
|
||||
tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
"default_chest_side.png", "default_chest_side.png", "default_chest_lock.png"},
|
||||
paramtype2 = "facedir",
|
||||
metadata_name = "locked_chest",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
legacy_facedir_simple = true,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name() or "")
|
||||
meta:set_string("infotext", "Locked Chest (owned by "..
|
||||
meta:get_string("owner")..")")
|
||||
end,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"list[current_name;main;0,0;8,4;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
meta:set_string("infotext", "Locked Chest")
|
||||
meta:set_string("owner", "")
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 8*4)
|
||||
end,
|
||||
can_dig = function(pos,player)
|
||||
local meta = minetest.env:get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index,
|
||||
to_list, to_index, count, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return
|
||||
end
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff in locked chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_move_allow_all(
|
||||
pos, from_list, from_index, to_list, to_index, count, player)
|
||||
end,
|
||||
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return stack
|
||||
end
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff to locked chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_offer_allow_all(
|
||||
pos, listname, index, stack, player)
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, count, player)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
if not has_locked_chest_privilege(meta, player) then
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" tried to access a locked chest belonging to "..
|
||||
meta:get_string("owner").." at "..
|
||||
minetest.pos_to_string(pos))
|
||||
return
|
||||
end
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" takes stuff from locked chest at "..minetest.pos_to_string(pos))
|
||||
return minetest.node_metadata_inventory_take_allow_all(
|
||||
pos, listname, index, count, player)
|
||||
end,
|
||||
})
|
||||
|
||||
default.furnace_inactive_formspec =
|
||||
"invsize[8,9;]"..
|
||||
"image[2,2;1,1;default_furnace_fire_bg.png]"..
|
||||
"list[current_name;fuel;2,3;1,1;]"..
|
||||
"list[current_name;src;2,1;1,1;]"..
|
||||
"list[current_name;dst;5,1;2,2;]"..
|
||||
"list[current_player;main;0,5;8,4;]"
|
||||
|
||||
minetest.register_node("default:furnace", {
|
||||
description = "Furnace",
|
||||
tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
|
||||
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
|
||||
paramtype2 = "facedir",
|
||||
metadata_name = "furnace",
|
||||
groups = {cracky=2},
|
||||
legacy_facedir_simple = true,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec", default.furnace_inactive_formspec)
|
||||
meta:set_string("infotext", "Furnace")
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("fuel", 1)
|
||||
inv:set_size("src", 1)
|
||||
inv:set_size("dst", 4)
|
||||
end,
|
||||
can_dig = function(pos,player)
|
||||
local meta = minetest.env:get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
if not inv:is_empty("fuel") then
|
||||
return false
|
||||
elseif not inv:is_empty("dst") then
|
||||
return false
|
||||
elseif not inv:is_empty("src") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("default:furnace_active", {
|
||||
description = "Furnace",
|
||||
tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
|
||||
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front_active.png"},
|
||||
paramtype2 = "facedir",
|
||||
light_source = 8,
|
||||
drop = "default:furnace",
|
||||
groups = {cracky=2},
|
||||
legacy_facedir_simple = true,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("formspec", default.furnace_inactive_formspec)
|
||||
meta:set_string("infotext", "Furnace");
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("fuel", 1)
|
||||
inv:set_size("src", 1)
|
||||
inv:set_size("dst", 4)
|
||||
end,
|
||||
can_dig = function(pos,player)
|
||||
local meta = minetest.env:get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
if not inv:is_empty("fuel") then
|
||||
return false
|
||||
elseif not inv:is_empty("dst") then
|
||||
return false
|
||||
elseif not inv:is_empty("src") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
function hacky_swap_node(pos,name)
|
||||
local node = minetest.env:get_node(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
local meta0 = meta:to_table()
|
||||
if node.name == name then
|
||||
return
|
||||
end
|
||||
node.name = name
|
||||
local meta0 = meta:to_table()
|
||||
minetest.env:set_node(pos,node)
|
||||
meta = minetest.env:get_meta(pos)
|
||||
meta:from_table(meta0)
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"default:furnace","default:furnace_active"},
|
||||
interval = 1.0,
|
||||
chance = 1,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
for i, name in ipairs({
|
||||
"fuel_totaltime",
|
||||
"fuel_time",
|
||||
"src_totaltime",
|
||||
"src_time"
|
||||
}) do
|
||||
if meta:get_string(name) == "" then
|
||||
meta:set_float(name, 0.0)
|
||||
end
|
||||
end
|
||||
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
local srclist = inv:get_list("src")
|
||||
local cooked = nil
|
||||
|
||||
if srclist then
|
||||
cooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
|
||||
end
|
||||
|
||||
local was_active = false
|
||||
|
||||
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
|
||||
was_active = true
|
||||
meta:set_float("fuel_time", meta:get_float("fuel_time") + 1)
|
||||
meta:set_float("src_time", meta:get_float("src_time") + 1)
|
||||
if cooked and cooked.item and meta:get_float("src_time") >= cooked.time then
|
||||
-- check if there's room for output in "dst" list
|
||||
if inv:room_for_item("dst",cooked.item) then
|
||||
-- Put result in "dst" list
|
||||
inv:add_item("dst", cooked.item)
|
||||
-- take stuff from "src" list
|
||||
srcstack = inv:get_stack("src", 1)
|
||||
srcstack:take_item()
|
||||
inv:set_stack("src", 1, srcstack)
|
||||
else
|
||||
print("Could not insert '"..cooked.item.."'")
|
||||
end
|
||||
meta:set_string("src_time", 0)
|
||||
end
|
||||
end
|
||||
|
||||
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
|
||||
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")
|
||||
meta:set_string("formspec",
|
||||
"invsize[8,9;]"..
|
||||
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
|
||||
(100-percent)..":default_furnace_fire_fg.png]"..
|
||||
"list[current_name;fuel;2,3;1,1;]"..
|
||||
"list[current_name;src;2,1;1,1;]"..
|
||||
"list[current_name;dst;5,1;2,2;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
return
|
||||
end
|
||||
|
||||
local fuel = nil
|
||||
local cooked = nil
|
||||
local fuellist = inv:get_list("fuel")
|
||||
local srclist = inv:get_list("src")
|
||||
|
||||
if srclist then
|
||||
cooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
|
||||
end
|
||||
if fuellist then
|
||||
fuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
|
||||
end
|
||||
|
||||
if fuel.time <= 0 then
|
||||
meta:set_string("infotext","Furnace out of fuel")
|
||||
hacky_swap_node(pos,"default:furnace")
|
||||
meta:set_string("formspec", default.furnace_inactive_formspec)
|
||||
return
|
||||
end
|
||||
|
||||
if cooked.item:is_empty() then
|
||||
if was_active then
|
||||
meta:set_string("infotext","Furnace is empty")
|
||||
hacky_swap_node(pos,"default:furnace")
|
||||
meta:set_string("formspec", default.furnace_inactive_formspec)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
meta:set_string("fuel_totaltime", fuel.time)
|
||||
meta:set_string("fuel_time", 0)
|
||||
|
||||
local stack = inv:get_stack("fuel", 1)
|
||||
stack:take_item()
|
||||
inv:set_stack("fuel", 1, stack)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("default:cobble", {
|
||||
@@ -1298,6 +1596,29 @@ minetest.add_to_creative_inventory('default:water_source')
|
||||
minetest.add_to_creative_inventory('default:lava_source')
|
||||
minetest.add_to_creative_inventory('default:ladder')
|
||||
|
||||
--
|
||||
-- Aliases for the current map generator outputs
|
||||
--
|
||||
|
||||
minetest.register_alias("mapgen_air", "air")
|
||||
minetest.register_alias("mapgen_stone", "default:stone")
|
||||
minetest.register_alias("mapgen_tree", "default:tree")
|
||||
minetest.register_alias("mapgen_leaves", "default:leaves")
|
||||
minetest.register_alias("mapgen_apple", "default:apple")
|
||||
minetest.register_alias("mapgen_water_source", "default:water_source")
|
||||
minetest.register_alias("mapgen_dirt", "default:dirt")
|
||||
minetest.register_alias("mapgen_sand", "default:sand")
|
||||
minetest.register_alias("mapgen_gravel", "default:gravel")
|
||||
minetest.register_alias("mapgen_clay", "default:clay")
|
||||
minetest.register_alias("mapgen_lava_source", "default:lava_source")
|
||||
minetest.register_alias("mapgen_cobble", "default:cobble")
|
||||
minetest.register_alias("mapgen_mossycobble", "default:mossycobble")
|
||||
minetest.register_alias("mapgen_dirt_with_grass", "default:dirt_with_grass")
|
||||
minetest.register_alias("mapgen_junglegrass", "default:junglegrass")
|
||||
minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal")
|
||||
minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron")
|
||||
minetest.register_alias("mapgen_mese", "default:mese")
|
||||
|
||||
--
|
||||
-- Some common functions
|
||||
--
|
||||
@@ -1500,6 +1821,57 @@ minetest.register_on_chat_message(function(name, message)
|
||||
end
|
||||
end)
|
||||
|
||||
--
|
||||
-- Test some things
|
||||
--
|
||||
|
||||
local function test_get_craft_result()
|
||||
print("test_get_craft_result()")
|
||||
-- normal
|
||||
local input = {
|
||||
method = "normal",
|
||||
width = 2,
|
||||
items = {"", "default:coal_lump", "", "default:stick"}
|
||||
}
|
||||
print("torch crafting input: "..dump(input))
|
||||
local output, decremented_input = minetest.get_craft_result(input)
|
||||
print("torch crafting output: "..dump(output))
|
||||
print("torch crafting decremented input: "..dump(decremented_input))
|
||||
assert(output.item)
|
||||
print("torch crafting output.item:to_table(): "..dump(output.item:to_table()))
|
||||
assert(output.item:get_name() == "default:torch")
|
||||
assert(output.item:get_count() == 4)
|
||||
-- fuel
|
||||
local input = {
|
||||
method = "fuel",
|
||||
width = 1,
|
||||
items = {"default:coal_lump"}
|
||||
}
|
||||
print("coal fuel input: "..dump(input))
|
||||
local output, decremented_input = minetest.get_craft_result(input)
|
||||
print("coal fuel output: "..dump(output))
|
||||
print("coal fuel decremented input: "..dump(decremented_input))
|
||||
assert(output.time)
|
||||
assert(output.time > 0)
|
||||
-- cook
|
||||
local input = {
|
||||
method = "cooking",
|
||||
width = 1,
|
||||
items = {"default:cobble"}
|
||||
}
|
||||
print("cobble cooking input: "..dump(output))
|
||||
local output, decremented_input = minetest.get_craft_result(input)
|
||||
print("cobble cooking output: "..dump(output))
|
||||
print("cobble cooking decremented input: "..dump(decremented_input))
|
||||
assert(output.time)
|
||||
assert(output.time > 0)
|
||||
assert(output.item)
|
||||
print("cobble cooking output.item:to_table(): "..dump(output.item:to_table()))
|
||||
assert(output.item:get_name() == "default:stone")
|
||||
assert(output.item:get_count() == 1)
|
||||
end
|
||||
test_get_craft_result()
|
||||
|
||||
--
|
||||
-- Done, print some random stuff
|
||||
--
|
||||
|
||||
114
games/minimal/mods/default/mapgen.lua
Normal file
114
games/minimal/mods/default/mapgen.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
-- minetest/default/mapgen.lua
|
||||
|
||||
--
|
||||
-- Aliases for map generator outputs
|
||||
--
|
||||
|
||||
minetest.register_alias("mapgen_air", "air")
|
||||
minetest.register_alias("mapgen_stone", "default:stone")
|
||||
minetest.register_alias("mapgen_tree", "default:tree")
|
||||
minetest.register_alias("mapgen_leaves", "default:leaves")
|
||||
minetest.register_alias("mapgen_apple", "default:apple")
|
||||
minetest.register_alias("mapgen_water_source", "default:water_source")
|
||||
minetest.register_alias("mapgen_dirt", "default:dirt")
|
||||
minetest.register_alias("mapgen_sand", "default:sand")
|
||||
minetest.register_alias("mapgen_gravel", "default:gravel")
|
||||
minetest.register_alias("mapgen_clay", "default:clay")
|
||||
minetest.register_alias("mapgen_lava_source", "default:lava_source")
|
||||
minetest.register_alias("mapgen_cobble", "default:cobble")
|
||||
minetest.register_alias("mapgen_mossycobble", "default:mossycobble")
|
||||
minetest.register_alias("mapgen_dirt_with_grass", "default:dirt_with_grass")
|
||||
minetest.register_alias("mapgen_junglegrass", "default:junglegrass")
|
||||
minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal")
|
||||
minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron")
|
||||
minetest.register_alias("mapgen_mese", "default:mese")
|
||||
|
||||
--
|
||||
-- Ore generation
|
||||
--
|
||||
|
||||
local function generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, ore_per_chunk, height_min, height_max)
|
||||
if maxp.y < height_min or minp.y > height_max then
|
||||
return
|
||||
end
|
||||
local y_min = math.max(minp.y, height_min)
|
||||
local y_max = math.min(maxp.y, height_max)
|
||||
local volume = (maxp.x-minp.x+1)*(y_max-y_min+1)*(maxp.z-minp.z+1)
|
||||
local pr = PseudoRandom(seed)
|
||||
local num_chunks = math.floor(chunks_per_volume * volume)
|
||||
local chunk_size = 3
|
||||
if ore_per_chunk <= 4 then
|
||||
chunk_size = 2
|
||||
end
|
||||
local inverse_chance = math.floor(chunk_size*chunk_size*chunk_size / ore_per_chunk)
|
||||
--print("generate_ore num_chunks: "..dump(num_chunks))
|
||||
for i=1,num_chunks do
|
||||
local y0 = pr:next(y_min, y_max-chunk_size+1)
|
||||
if y0 >= height_min and y0 <= height_max then
|
||||
local x0 = pr:next(minp.x, maxp.x-chunk_size+1)
|
||||
local z0 = pr:next(minp.z, maxp.z-chunk_size+1)
|
||||
local p0 = {x=x0, y=y0, z=z0}
|
||||
for x1=0,chunk_size-1 do
|
||||
for y1=0,chunk_size-1 do
|
||||
for z1=0,chunk_size-1 do
|
||||
if pr:next(1,inverse_chance) == 1 then
|
||||
local x2 = x0+x1
|
||||
local y2 = y0+y1
|
||||
local z2 = z0+z1
|
||||
local p2 = {x=x2, y=y2, z=z2}
|
||||
if minetest.env:get_node(p2).name == wherein then
|
||||
minetest.env:set_node(p2, {name=name})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--print("generate_ore done")
|
||||
end
|
||||
|
||||
minetest.register_on_generated(function(minp, maxp, seed)
|
||||
generate_ore("default:stone_with_coal", "default:stone", minp, maxp, seed, 1/8/8/8, 5, -31000, 64)
|
||||
generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+1, 1/16/16/16, 5, -5, 7)
|
||||
generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+2, 1/12/12/12, 5, -16, -5)
|
||||
generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+3, 1/9/9/9, 5, -31000, -17)
|
||||
-- Generate clay
|
||||
if maxp.y >= 2 and minp.y <= 0 then
|
||||
-- Assume X and Z lengths are equal
|
||||
local divlen = 4
|
||||
local divs = (maxp.x-minp.x)/divlen+1;
|
||||
for divx=0+1,divs-1-1 do
|
||||
for divz=0+1,divs-1-1 do
|
||||
local cx = minp.x + math.floor((divx+0.5)*divlen)
|
||||
local cz = minp.z + math.floor((divz+0.5)*divlen)
|
||||
if minetest.env:get_node({x=cx,y=1,z=cz}).name == "default:water_source" and
|
||||
minetest.env:get_node({x=cx,y=0,z=cz}).name == "default:sand" then
|
||||
local is_shallow = true
|
||||
local num_water_around = 0
|
||||
if minetest.env:get_node({x=cx-divlen*2,y=1,z=cz+0}).name == "default:water_source" then
|
||||
num_water_around = num_water_around + 1 end
|
||||
if minetest.env:get_node({x=cx+divlen*2,y=1,z=cz+0}).name == "default:water_source" then
|
||||
num_water_around = num_water_around + 1 end
|
||||
if minetest.env:get_node({x=cx+0,y=1,z=cz-divlen*2}).name == "default:water_source" then
|
||||
num_water_around = num_water_around + 1 end
|
||||
if minetest.env:get_node({x=cx+0,y=1,z=cz+divlen*2}).name == "default:water_source" then
|
||||
num_water_around = num_water_around + 1 end
|
||||
if num_water_around >= 2 then
|
||||
is_shallow = false
|
||||
end
|
||||
if is_shallow then
|
||||
for x1=-divlen,divlen do
|
||||
for z1=-divlen,divlen do
|
||||
if minetest.env:get_node({x=cx+x1,y=0,z=cz+z1}).name == "default:sand" then
|
||||
minetest.env:set_node({x=cx+x1,y=0,z=cz+z1}, {name="default:clay"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
BIN
games/minimal/mods/default/sounds/default_grass_footstep.1.ogg
Normal file
BIN
games/minimal/mods/default/sounds/default_grass_footstep.1.ogg
Normal file
Binary file not shown.
BIN
games/minimal/mods/default/textures/default_furnace_fire_bg.png
Normal file
BIN
games/minimal/mods/default/textures/default_furnace_fire_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 313 B |
BIN
games/minimal/mods/default/textures/default_furnace_fire_fg.png
Normal file
BIN
games/minimal/mods/default/textures/default_furnace_fire_fg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 865 B |
Binary file not shown.
|
After Width: | Height: | Size: 376 B |
@@ -6,38 +6,54 @@
|
||||
|
||||
experimental = {}
|
||||
|
||||
timers_to_add = {}
|
||||
timers = {}
|
||||
minetest.register_globalstep(function(dtime)
|
||||
for indes, timer in ipairs(timers_to_add) do
|
||||
table.insert(timers, timer)
|
||||
end
|
||||
timers_to_add = {}
|
||||
for index, timer in ipairs(timers) do
|
||||
timer.time = timer.time - dtime
|
||||
if timer.time <= 0 then
|
||||
timer.func()
|
||||
timers[index] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
after = function(time, func)
|
||||
table.insert(timers_to_add, {time=time, func=func})
|
||||
--[[
|
||||
experimental.player_visual_index = 0
|
||||
function switch_player_visual()
|
||||
for _, obj in pairs(minetest.get_connected_players()) do
|
||||
if experimental.player_visual_index == 0 then
|
||||
obj:set_properties({visual="upright_sprite"})
|
||||
else
|
||||
obj:set_properties({visual="cube"})
|
||||
end
|
||||
end
|
||||
experimental.player_visual_index = (experimental.player_visual_index + 1) % 2
|
||||
minetest.after(1.0, switch_player_visual)
|
||||
end
|
||||
minetest.after(1.0, switch_player_visual)
|
||||
]]
|
||||
|
||||
minetest.register_node("experimental:soundblock", {
|
||||
tile_images = {"unknown_block.png", "default_tnt_bottom.png",
|
||||
"default_tnt_side.png", "default_tnt_side.png",
|
||||
"default_tnt_side.png", "default_tnt_side.png"},
|
||||
inventory_image = minetest.inventorycube("unknown_block.png",
|
||||
"default_tnt_side.png", "default_tnt_side.png"),
|
||||
groups = {dig_immediate=3},
|
||||
})
|
||||
|
||||
minetest.register_alias("sb", "experimental:soundblock")
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"experimental:soundblock"},
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
action = function(p0, node, _, _)
|
||||
minetest.sound_play("default_grass_footstep", {pos=p0, gain=0.5})
|
||||
end,
|
||||
})
|
||||
|
||||
--[[
|
||||
stepsound = -1
|
||||
function test_sound()
|
||||
print("test_sound")
|
||||
stepsound = minetest.sound_play("default_grass_footstep", {gain=1.0})
|
||||
after(2.0, test_sound)
|
||||
--after(0.1, test_sound_stop)
|
||||
minetest.after(2.0, test_sound)
|
||||
--minetest.after(0.1, test_sound_stop)
|
||||
end
|
||||
function test_sound_stop()
|
||||
print("test_sound_stop")
|
||||
minetest.sound_stop(stepsound)
|
||||
after(2.0, test_sound)
|
||||
minetest.after(2.0, test_sound)
|
||||
end
|
||||
test_sound()
|
||||
--]]
|
||||
@@ -415,16 +431,17 @@ minetest.register_alias("TNT", "experimental:tnt")
|
||||
--
|
||||
|
||||
minetest.register_entity("experimental:dummyball", {
|
||||
-- Static definition
|
||||
hp_max = 20,
|
||||
physical = false,
|
||||
collisionbox = {-0.4,-0.4,-0.4, 0.4,0.4,0.4},
|
||||
visual = "sprite",
|
||||
visual_size = {x=1, y=1},
|
||||
textures = {"experimental_dummyball.png"},
|
||||
spritediv = {x=1, y=3},
|
||||
initial_sprite_basepos = {x=0, y=0},
|
||||
-- Dynamic variables
|
||||
initial_properties = {
|
||||
hp_max = 20,
|
||||
physical = false,
|
||||
collisionbox = {-0.4,-0.4,-0.4, 0.4,0.4,0.4},
|
||||
visual = "sprite",
|
||||
visual_size = {x=1, y=1},
|
||||
textures = {"experimental_dummyball.png"},
|
||||
spritediv = {x=1, y=3},
|
||||
initial_sprite_basepos = {x=0, y=0},
|
||||
},
|
||||
|
||||
phase = 0,
|
||||
phasetimer = 0,
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
minetest.register_on_newplayer(function(player)
|
||||
print("on_newplayer")
|
||||
if minetest.setting_getbool("give_initial_stuff") then
|
||||
print("giving give_initial_stuff to player")
|
||||
player:get_inventory():add_item('main', 'default:pick_steel')
|
||||
player:get_inventory():add_item('main', 'default:torch 99')
|
||||
player:get_inventory():add_item('main', 'default:axe_steel')
|
||||
player:get_inventory():add_item('main', 'default:shovel_steel')
|
||||
player:get_inventory():add_item('main', 'default:cobble 99')
|
||||
end
|
||||
print("[minimal] giving initial stuff to player")
|
||||
player:get_inventory():add_item('main', 'default:pick_stone')
|
||||
player:get_inventory():add_item('main', 'default:torch 99')
|
||||
player:get_inventory():add_item('main', 'default:cobble 99')
|
||||
player:get_inventory():add_item('main', 'default:wood 99')
|
||||
player:get_inventory():add_item('main', 'default:axe_steel')
|
||||
player:get_inventory():add_item('main', 'default:shovel_steel')
|
||||
player:get_inventory():add_item('main', 'default:pick_wood')
|
||||
player:get_inventory():add_item('main', 'default:pick_steel')
|
||||
player:get_inventory():add_item('main', 'default:pick_mese')
|
||||
player:get_inventory():add_item('main', 'default:mese 99')
|
||||
player:get_inventory():add_item('main', 'default:water_source 99')
|
||||
end)
|
||||
|
||||
|
||||
11
games/minimal/mods/test/init.lua
Normal file
11
games/minimal/mods/test/init.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
--
|
||||
-- Minimal Development Test
|
||||
-- Mod: test
|
||||
--
|
||||
|
||||
-- Try out PseudoRandom
|
||||
pseudo = PseudoRandom(13)
|
||||
assert(pseudo:next() == 22290)
|
||||
assert(pseudo:next() == 13854)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# Uncomment settings by removing the preceding #.
|
||||
#
|
||||
# Further documentation:
|
||||
# http://celeron.55.lt/~celeron55/minetest/wiki/doku.php
|
||||
# http://c55.me/minetest/wiki/doku.php
|
||||
#
|
||||
# NOTE: This file might not be up-to-date, refer to the
|
||||
# defaultsettings.cpp file for an up-to-date list:
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# A vim command to convert most of defaultsettings.cpp to conf file format:
|
||||
# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
|
||||
# Note: Some of the settings are implemented in Lua
|
||||
|
||||
#
|
||||
# Client and server
|
||||
@@ -47,7 +48,6 @@
|
||||
#keymap_rangeselect = KEY_KEY_R
|
||||
#keymap_freemove = KEY_KEY_K
|
||||
#keymap_fastmove = KEY_KEY_J
|
||||
#keymap_frametime_graph = KEY_F1
|
||||
#keymap_screenshot = KEY_F12
|
||||
# Some (temporary) keys for debugging
|
||||
#keymap_print_debug_stacks = KEY_KEY_P
|
||||
@@ -58,11 +58,17 @@
|
||||
# (to not waste CPU power for no benefit)
|
||||
#fps_max = 60
|
||||
# The allowed adjustment range for the automatic rendering range adjustment
|
||||
#viewing_range_nodes_max = 300
|
||||
#viewing_range_nodes_min = 25
|
||||
#viewing_range_nodes_max = 160
|
||||
#viewing_range_nodes_min = 35
|
||||
# Initial window size
|
||||
#screenW = 800
|
||||
#screenH = 600
|
||||
#fullscreen = false
|
||||
#fullscreen_bpp = 24
|
||||
# Experimental option, might cause visible spaces between blocks
|
||||
# when set to higher number than 0
|
||||
#fsaa = 0
|
||||
#vsync = false
|
||||
# Address to connect to (#blank = start local server)
|
||||
#address =
|
||||
# Enable random user input, for testing
|
||||
@@ -78,8 +84,6 @@
|
||||
# Enable smooth lighting with simple ambient occlusion;
|
||||
# disable for speed or for different looks.
|
||||
#smooth_lighting = true
|
||||
# Whether to draw a frametime graph (for debugging frametime)
|
||||
#frametime_graph = false
|
||||
# Enable combining mainly used textures to a bigger one for improved speed
|
||||
# disable if it causes graphics glitches.
|
||||
#enable_texture_atlas = true
|
||||
@@ -110,19 +114,22 @@
|
||||
#console_color = (0,0,0)
|
||||
# In-game chat console background alpha (opaqueness, between 0 and 255)
|
||||
#console_alpha = 200
|
||||
# Sound settings
|
||||
#enable_sound = true
|
||||
#sound_volume = 0.7
|
||||
|
||||
#
|
||||
# Server stuff
|
||||
#
|
||||
|
||||
# Default game (default when creating a new world)
|
||||
#default_game = mesetint
|
||||
# Map directory (everything in the world is stored here)
|
||||
#map-dir = /custom/map
|
||||
#default_game = minetest
|
||||
# World directory (everything in the world is stored here)
|
||||
#map-dir = /custom/world
|
||||
# Message of the Day
|
||||
#motd = Welcome to this awesome Minetest server!
|
||||
# Maximum number of players connected simultaneously
|
||||
#max_users = 20
|
||||
#max_users = 100
|
||||
# Set to false to allow old clients to connect
|
||||
#strict_protocol_version_checking = true
|
||||
# Set to true to enable creative mode (unlimited inventory)
|
||||
@@ -139,10 +146,12 @@
|
||||
#default_password =
|
||||
# Available privileges: build, teleport, settime, privs, shout
|
||||
#default_privs = build, shout
|
||||
# Whether players are transferred to client without any range limit
|
||||
# Whether players are shown to clients without any range limit
|
||||
#unlimited_player_transfer_distance = true
|
||||
# Whether to enable players killing each other
|
||||
#enable_pvp = true
|
||||
# If this is set, players will always (re)spawn at the given position
|
||||
#static_spawnpoint = 0, 10, 0
|
||||
|
||||
# Profiler data print interval. #0 = disable.
|
||||
#profiler_print_interval = 0
|
||||
@@ -155,17 +164,18 @@
|
||||
#max_simultaneous_block_sends_per_client = 2
|
||||
# how many blocks are flying in the wire simultaneously per server
|
||||
#max_simultaneous_block_sends_server_total = 8
|
||||
#max_block_send_distance = 7
|
||||
#max_block_generate_distance = 5
|
||||
#time_send_interval = 20
|
||||
# From how far blocks are sent to clients (value * 16 nodes)
|
||||
#max_block_send_distance = 10
|
||||
# From how far blocks are generated for clients (value * 16 nodes)
|
||||
#max_block_generate_distance = 6
|
||||
# Interval of sending time of day to clients
|
||||
#time_send_interval = 5
|
||||
# Length of day/night cycle. 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged
|
||||
#time_speed = 72
|
||||
#time_speed = 96
|
||||
#server_unload_unused_data_timeout = 29
|
||||
# Interval of saving important changes in the world
|
||||
#server_map_save_interval = 5.3
|
||||
# To reduce lag, block transfers are slowed down when a player is building something.
|
||||
# This determines how long they are slowed down after placing or removing a node.
|
||||
#full_block_send_enable_min_time_from_building = 2.0
|
||||
# Set to true to enable experimental features or stuff that is tested
|
||||
# (varies from version to version, usually not useful at all)
|
||||
#enable_experimental = false
|
||||
|
||||
|
||||
5
misc/winresource.rc
Normal file
5
misc/winresource.rc
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <richedit.h>
|
||||
LANGUAGE 0, SUBLANG_NEUTRAL
|
||||
130 ICON "minetest-icon.ico"
|
||||
@@ -67,7 +67,7 @@ endif(ENABLE_SOUND AND BUILD_CLIENT)
|
||||
|
||||
if(SOUND_PROBLEM)
|
||||
message(FATAL_ERROR "Sound enabled, but cannot be used.\n"
|
||||
"To continue, either fill in the required paths or disable sound.")
|
||||
"To continue, either fill in the required paths or disable sound. (-DENABLE_SOUND=0)")
|
||||
endif()
|
||||
if(USE_SOUND)
|
||||
set(sound_SRCS sound_openal.cpp)
|
||||
@@ -106,6 +106,8 @@ if(WIN32)
|
||||
CACHE FILEPATH "Path to zlibwapi.dll (for installation)")
|
||||
set(IRRLICHT_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../irrlicht-1.7.2"
|
||||
CACHE PATH "irrlicht dir")
|
||||
set(MINGWM10_DLL ""
|
||||
CACHE FILEPATH "Path to mingwm10.dll (for installation)")
|
||||
if(ENABLE_SOUND)
|
||||
set(OPENAL_DLL "" CACHE FILEPATH "Path to OpenAL32.dll for installation (optional)")
|
||||
set(OGG_DLL "" CACHE FILEPATH "Path to libogg.dll for installation (optional)")
|
||||
@@ -153,18 +155,19 @@ configure_file(
|
||||
)
|
||||
|
||||
set(common_SRCS
|
||||
genericobject.cpp
|
||||
voxelalgorithms.cpp
|
||||
sound.cpp
|
||||
quicktune.cpp
|
||||
subgame.cpp
|
||||
inventorymanager.cpp
|
||||
mods.cpp
|
||||
serverremoteplayer.cpp
|
||||
content_abm.cpp
|
||||
craftdef.cpp
|
||||
nameidmapping.cpp
|
||||
itemdef.cpp
|
||||
nodedef.cpp
|
||||
luaentity_common.cpp
|
||||
object_properties.cpp
|
||||
scriptapi.cpp
|
||||
script.cpp
|
||||
log.cpp
|
||||
@@ -172,9 +175,9 @@ set(common_SRCS
|
||||
mapgen.cpp
|
||||
content_nodemeta.cpp
|
||||
content_mapnode.cpp
|
||||
auth.cpp
|
||||
collision.cpp
|
||||
nodemetadata.cpp
|
||||
nodetimer.cpp
|
||||
serverobject.cpp
|
||||
noise.cpp
|
||||
porting.cpp
|
||||
@@ -205,14 +208,19 @@ set(common_SRCS
|
||||
|
||||
# This gives us the icon
|
||||
if(WIN32)
|
||||
set(WINRESOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../misc/winresource.rc)
|
||||
if(MINGW)
|
||||
if(NOT CMAKE_RC_COMPILER)
|
||||
set(CMAKE_RC_COMPILER "windres.exe")
|
||||
endif()
|
||||
ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/winresource_rc.o
|
||||
COMMAND windres.exe -I${CMAKE_CURRENT_SOURCE_DIR}
|
||||
-i${CMAKE_CURRENT_SOURCE_DIR}/winresource.rc
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/winresource_rc.o)
|
||||
COMMAND ${CMAKE_RC_COMPILER} -I${CMAKE_CURRENT_SOURCE_DIR}
|
||||
-i${WINRESOURCE_FILE}
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/winresource_rc.o
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
SET(common_SRCS ${common_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/winresource_rc.o)
|
||||
else(MINGW) # Probably MSVC
|
||||
set(common_SRCS ${common_SRCS} winresource.rc)
|
||||
set(common_SRCS ${common_SRCS} ${WINRESOURCE_FILE})
|
||||
endif(MINGW)
|
||||
endif()
|
||||
|
||||
@@ -220,6 +228,7 @@ endif()
|
||||
set(minetest_SRCS
|
||||
${common_SRCS}
|
||||
${sound_SRCS}
|
||||
localplayer.cpp
|
||||
sky.cpp
|
||||
clientmap.cpp
|
||||
content_cso.cpp
|
||||
@@ -347,14 +356,18 @@ else()
|
||||
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-unused-but-set-variable")
|
||||
endif(HAS_UNUSED_BUT_SET_VARIABLE_WARNING)
|
||||
endif()
|
||||
|
||||
|
||||
if(MINGW)
|
||||
set(OTHER_FLAGS "-mthreads -fexceptions")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_OSX_ARCHITECTURES i386 CACHE STRING "do not build for 64-bit" FORCE)
|
||||
set(ARCH i386)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} ${OTHER_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
|
||||
|
||||
if(USE_GPROF)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
|
||||
@@ -374,6 +387,9 @@ endif()
|
||||
# Installation
|
||||
#
|
||||
if(WIN32)
|
||||
if(MINGWM10_DLL)
|
||||
install(FILES ${MINGWM10_DLL} DESTINATION ${BINDIR})
|
||||
endif()
|
||||
if(DEFINED ZLIB_DLL)
|
||||
install(FILES ${ZLIB_DLL} DESTINATION ${BINDIR})
|
||||
endif()
|
||||
|
||||
300
src/auth.cpp
300
src/auth.cpp
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2011 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 "auth.h"
|
||||
#include <fstream>
|
||||
#include <jmutexautolock.h>
|
||||
//#include "main.h" // for g_settings
|
||||
#include <sstream>
|
||||
#include "strfnd.h"
|
||||
#include "log.h"
|
||||
|
||||
std::set<std::string> privsToSet(u64 privs)
|
||||
{
|
||||
std::set<std::string> s;
|
||||
if(privs & PRIV_INTERACT)
|
||||
s.insert("interact");
|
||||
if(privs & PRIV_TELEPORT)
|
||||
s.insert("teleport");
|
||||
if(privs & PRIV_SETTIME)
|
||||
s.insert("settime");
|
||||
if(privs & PRIV_PRIVS)
|
||||
s.insert("privs");
|
||||
if(privs & PRIV_SHOUT)
|
||||
s.insert("shout");
|
||||
if(privs & PRIV_BAN)
|
||||
s.insert("ban");
|
||||
if(privs & PRIV_GIVE)
|
||||
s.insert("give");
|
||||
if(privs & PRIV_PASSWORD)
|
||||
s.insert("password");
|
||||
return s;
|
||||
}
|
||||
|
||||
// Convert a privileges value into a human-readable string,
|
||||
// with each component separated by a comma.
|
||||
std::string privsToString(u64 privs)
|
||||
{
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
if(privs & PRIV_INTERACT)
|
||||
os<<"interact,";
|
||||
if(privs & PRIV_TELEPORT)
|
||||
os<<"teleport,";
|
||||
if(privs & PRIV_SETTIME)
|
||||
os<<"settime,";
|
||||
if(privs & PRIV_PRIVS)
|
||||
os<<"privs,";
|
||||
if(privs & PRIV_SHOUT)
|
||||
os<<"shout,";
|
||||
if(privs & PRIV_BAN)
|
||||
os<<"ban,";
|
||||
if(privs & PRIV_GIVE)
|
||||
os<<"give,";
|
||||
if(privs & PRIV_PASSWORD)
|
||||
os<<"password,";
|
||||
if(os.tellp())
|
||||
{
|
||||
// Drop the trailing comma. (Why on earth can't
|
||||
// you truncate a C++ stream anyway???)
|
||||
std::string tmp = os.str();
|
||||
return tmp.substr(0, tmp.length() -1);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// Converts a comma-seperated list of privilege values into a
|
||||
// privileges value. The reverse of privsToString(). Returns
|
||||
// PRIV_INVALID if there is anything wrong with the input.
|
||||
u64 stringToPrivs(std::string str)
|
||||
{
|
||||
u64 privs=0;
|
||||
Strfnd f(str);
|
||||
while(f.atend() == false)
|
||||
{
|
||||
std::string s = trim(f.next(","));
|
||||
if(s == "build")
|
||||
privs |= PRIV_INTERACT;
|
||||
else if(s == "interact")
|
||||
privs |= PRIV_INTERACT;
|
||||
else if(s == "teleport")
|
||||
privs |= PRIV_TELEPORT;
|
||||
else if(s == "settime")
|
||||
privs |= PRIV_SETTIME;
|
||||
else if(s == "privs")
|
||||
privs |= PRIV_PRIVS;
|
||||
else if(s == "shout")
|
||||
privs |= PRIV_SHOUT;
|
||||
else if(s == "ban")
|
||||
privs |= PRIV_BAN;
|
||||
else if(s == "give")
|
||||
privs |= PRIV_GIVE;
|
||||
else if(s == "password")
|
||||
privs |= PRIV_PASSWORD;
|
||||
else
|
||||
return PRIV_INVALID;
|
||||
}
|
||||
return privs;
|
||||
}
|
||||
|
||||
AuthManager::AuthManager(const std::string &authfilepath):
|
||||
m_authfilepath(authfilepath),
|
||||
m_modified(false)
|
||||
{
|
||||
m_mutex.Init();
|
||||
|
||||
try{
|
||||
load();
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
infostream<<"WARNING: AuthManager: creating "
|
||||
<<m_authfilepath<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
AuthManager::~AuthManager()
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
void AuthManager::load()
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
infostream<<"AuthManager: loading from "<<m_authfilepath<<std::endl;
|
||||
std::ifstream is(m_authfilepath.c_str(), std::ios::binary);
|
||||
if(is.good() == false)
|
||||
{
|
||||
infostream<<"AuthManager: failed loading from "<<m_authfilepath<<std::endl;
|
||||
throw SerializationError("AuthManager::load(): Couldn't open file");
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(is.eof() || is.good() == false)
|
||||
break;
|
||||
|
||||
// Read a line
|
||||
std::string line;
|
||||
std::getline(is, line, '\n');
|
||||
|
||||
std::istringstream iss(line);
|
||||
|
||||
// Read name
|
||||
std::string name;
|
||||
std::getline(iss, name, ':');
|
||||
|
||||
// Read password
|
||||
std::string pwd;
|
||||
std::getline(iss, pwd, ':');
|
||||
|
||||
// Read privileges
|
||||
std::string stringprivs;
|
||||
std::getline(iss, stringprivs, ':');
|
||||
u64 privs = stringToPrivs(stringprivs);
|
||||
|
||||
// Store it
|
||||
AuthData ad;
|
||||
ad.pwd = pwd;
|
||||
ad.privs = privs;
|
||||
m_authdata[name] = ad;
|
||||
}
|
||||
|
||||
m_modified = false;
|
||||
}
|
||||
|
||||
void AuthManager::save()
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
infostream<<"AuthManager: saving to "<<m_authfilepath<<std::endl;
|
||||
std::ofstream os(m_authfilepath.c_str(), std::ios::binary);
|
||||
if(os.good() == false)
|
||||
{
|
||||
infostream<<"AuthManager: failed saving to "<<m_authfilepath<<std::endl;
|
||||
throw SerializationError("AuthManager::save(): Couldn't open file");
|
||||
}
|
||||
|
||||
for(core::map<std::string, AuthData>::Iterator
|
||||
i = m_authdata.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
std::string name = i.getNode()->getKey();
|
||||
if(name == "")
|
||||
continue;
|
||||
AuthData ad = i.getNode()->getValue();
|
||||
os<<name<<":"<<ad.pwd<<":"<<privsToString(ad.privs)<<"\n";
|
||||
}
|
||||
|
||||
m_modified = false;
|
||||
}
|
||||
|
||||
bool AuthManager::exists(const std::string &username)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
core::map<std::string, AuthData>::Node *n;
|
||||
n = m_authdata.find(username);
|
||||
if(n == NULL)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuthManager::set(const std::string &username, AuthData ad)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
m_authdata[username] = ad;
|
||||
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
void AuthManager::add(const std::string &username)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
m_authdata[username] = AuthData();
|
||||
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
std::string AuthManager::getPassword(const std::string &username)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
core::map<std::string, AuthData>::Node *n;
|
||||
n = m_authdata.find(username);
|
||||
if(n == NULL)
|
||||
throw AuthNotFoundException("");
|
||||
|
||||
return n->getValue().pwd;
|
||||
}
|
||||
|
||||
void AuthManager::setPassword(const std::string &username,
|
||||
const std::string &password)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
core::map<std::string, AuthData>::Node *n;
|
||||
n = m_authdata.find(username);
|
||||
if(n == NULL)
|
||||
throw AuthNotFoundException("");
|
||||
|
||||
AuthData ad = n->getValue();
|
||||
ad.pwd = password;
|
||||
n->setValue(ad);
|
||||
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
u64 AuthManager::getPrivs(const std::string &username)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
core::map<std::string, AuthData>::Node *n;
|
||||
n = m_authdata.find(username);
|
||||
if(n == NULL)
|
||||
throw AuthNotFoundException("");
|
||||
|
||||
return n->getValue().privs;
|
||||
}
|
||||
|
||||
void AuthManager::setPrivs(const std::string &username, u64 privs)
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
|
||||
core::map<std::string, AuthData>::Node *n;
|
||||
n = m_authdata.find(username);
|
||||
if(n == NULL)
|
||||
throw AuthNotFoundException("");
|
||||
|
||||
AuthData ad = n->getValue();
|
||||
ad.privs = privs;
|
||||
n->setValue(ad);
|
||||
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
bool AuthManager::isModified()
|
||||
{
|
||||
JMutexAutoLock lock(m_mutex);
|
||||
return m_modified;
|
||||
}
|
||||
|
||||
|
||||
107
src/auth.h
107
src/auth.h
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2011 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 AUTH_HEADER
|
||||
#define AUTH_HEADER
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <jthread.h>
|
||||
#include <jmutex.h>
|
||||
#include "irrlichttypes.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
// Player privileges. These form a bitmask stored in the privs field
|
||||
// of the player, and define things they're allowed to do. See also
|
||||
// the static methods Player::privsToString and stringToPrivs that
|
||||
// convert these to human-readable form.
|
||||
const u64 PRIV_INTERACT = 1; // Can interact
|
||||
const u64 PRIV_TELEPORT = 2; // Can teleport
|
||||
const u64 PRIV_SETTIME = 4; // Can set the time
|
||||
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
|
||||
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
|
||||
// ,settings)
|
||||
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
|
||||
// players
|
||||
const u64 PRIV_BAN = 64; // Can ban players
|
||||
const u64 PRIV_GIVE = 128; // Can give stuff
|
||||
const u64 PRIV_PASSWORD = 256; // Can set other players' passwords
|
||||
|
||||
// Default privileges - these can be overriden for new players using the
|
||||
// config option "default_privs" - however, this value still applies for
|
||||
// players that existed before the privileges system was added.
|
||||
const u64 PRIV_DEFAULT = PRIV_INTERACT|PRIV_SHOUT;
|
||||
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
|
||||
const u64 PRIV_INVALID = 0x8000000000000000ULL;
|
||||
|
||||
std::set<std::string> privsToSet(u64 privs);
|
||||
|
||||
// Convert a privileges value into a human-readable string,
|
||||
// with each component separated by a comma.
|
||||
std::string privsToString(u64 privs);
|
||||
|
||||
// Converts a comma-seperated list of privilege values into a
|
||||
// privileges value. The reverse of privsToString(). Returns
|
||||
// PRIV_INVALID if there is anything wrong with the input.
|
||||
u64 stringToPrivs(std::string str);
|
||||
|
||||
struct AuthData
|
||||
{
|
||||
std::string pwd;
|
||||
u64 privs;
|
||||
|
||||
AuthData():
|
||||
privs(PRIV_DEFAULT)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class AuthNotFoundException : public BaseException
|
||||
{
|
||||
public:
|
||||
AuthNotFoundException(const char *s):
|
||||
BaseException(s)
|
||||
{}
|
||||
};
|
||||
|
||||
class AuthManager
|
||||
{
|
||||
public:
|
||||
AuthManager(const std::string &authfilepath);
|
||||
~AuthManager();
|
||||
void load();
|
||||
void save();
|
||||
bool exists(const std::string &username);
|
||||
void set(const std::string &username, AuthData ad);
|
||||
void add(const std::string &username);
|
||||
std::string getPassword(const std::string &username);
|
||||
void setPassword(const std::string &username,
|
||||
const std::string &password);
|
||||
u64 getPrivs(const std::string &username);
|
||||
void setPrivs(const std::string &username, u64 privs);
|
||||
bool isModified();
|
||||
private:
|
||||
JMutex m_mutex;
|
||||
std::string m_authfilepath;
|
||||
core::map<std::string, AuthData> m_authdata;
|
||||
bool m_modified;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,13 @@ static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
bool base64_is_valid(std::string const& s)
|
||||
{
|
||||
for(int i=0; i<s.size(); i++)
|
||||
if(!is_base64(s[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <string>
|
||||
|
||||
bool base64_is_valid(std::string const& s);
|
||||
std::string base64_encode(unsigned char const* , unsigned int len);
|
||||
std::string base64_decode(std::string const& s);
|
||||
|
||||
@@ -290,6 +290,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
// FOV and aspect ratio
|
||||
m_aspect = (f32)screensize.X / (f32) screensize.Y;
|
||||
m_fov_y = fov_degrees * PI / 180.0;
|
||||
// Increase vertical FOV on lower aspect ratios (<16:10)
|
||||
m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect)));
|
||||
// WTF is this? It can't be right
|
||||
m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y));
|
||||
m_cameranode->setAspectRatio(m_aspect);
|
||||
m_cameranode->setFOV(m_fov_y);
|
||||
@@ -348,7 +351,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
||||
if ((hypot(speed.X, speed.Z) > BS) &&
|
||||
(player->touching_ground) &&
|
||||
(g_settings->getBool("view_bobbing") == true) &&
|
||||
(g_settings->getBool("free_move") == false))
|
||||
(g_settings->getBool("free_move") == false ||
|
||||
!m_gamedef->checkLocalPrivilege("fly")))
|
||||
{
|
||||
// Start animation
|
||||
m_view_bobbing_state = 1;
|
||||
|
||||
106
src/client.cpp
106
src/client.cpp
@@ -1397,45 +1397,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
}
|
||||
else if(command == TOCLIENT_PLAYERITEM)
|
||||
{
|
||||
std::string datastring((char*)&data[2], datasize-2);
|
||||
std::istringstream is(datastring, std::ios_base::binary);
|
||||
|
||||
u16 count = readU16(is);
|
||||
|
||||
for (u16 i = 0; i < count; ++i) {
|
||||
u16 peer_id = readU16(is);
|
||||
Player *player = m_env.getPlayer(peer_id);
|
||||
|
||||
if (player == NULL)
|
||||
{
|
||||
infostream<<"Client: ignoring player item "
|
||||
<< deSerializeString(is)
|
||||
<< " for non-existing peer id " << peer_id
|
||||
<< std::endl;
|
||||
continue;
|
||||
} else if (player->isLocal()) {
|
||||
infostream<<"Client: ignoring player item "
|
||||
<< deSerializeString(is)
|
||||
<< " for local player" << std::endl;
|
||||
continue;
|
||||
} else {
|
||||
InventoryList *inv = player->inventory.getList("main");
|
||||
std::string itemstring(deSerializeString(is));
|
||||
ItemStack item;
|
||||
item.deSerialize(itemstring, m_itemdef);
|
||||
inv->changeItem(0, item);
|
||||
if(itemstring.empty())
|
||||
{
|
||||
infostream<<"Client: empty player item for peer "
|
||||
<<peer_id<<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
infostream<<"Client: player item for peer "
|
||||
<<peer_id<<": "<<itemstring<<std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
|
||||
}
|
||||
else if(command == TOCLIENT_DEATHSCREEN)
|
||||
{
|
||||
@@ -1677,10 +1639,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
int client_id = -1;
|
||||
switch(type){
|
||||
case 0: // local
|
||||
client_id = m_sound->playSound(name, false, gain);
|
||||
client_id = m_sound->playSound(name, loop, gain);
|
||||
break;
|
||||
case 1: // positional
|
||||
client_id = m_sound->playSoundAt(name, false, gain, pos);
|
||||
client_id = m_sound->playSoundAt(name, loop, gain, pos);
|
||||
break;
|
||||
case 2: { // object
|
||||
ClientActiveObject *cao = m_env.getActiveObject(object_id);
|
||||
@@ -1712,6 +1674,21 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
||||
m_sound->stopSound(client_id);
|
||||
}
|
||||
}
|
||||
else if(command == TOCLIENT_PRIVILEGES)
|
||||
{
|
||||
std::string datastring((char*)&data[2], datasize-2);
|
||||
std::istringstream is(datastring, std::ios_base::binary);
|
||||
|
||||
m_privileges.clear();
|
||||
infostream<<"Client: Privileges updated: ";
|
||||
u16 num_privileges = readU16(is);
|
||||
for(u16 i=0; i<num_privileges; i++){
|
||||
std::string priv = deSerializeString(is);
|
||||
m_privileges.insert(priv);
|
||||
infostream<<priv<<" ";
|
||||
}
|
||||
infostream<<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
infostream<<"Client: Ignoring unknown command "
|
||||
@@ -1763,33 +1740,23 @@ void Client::interact(u8 action, const PointedThing& pointed)
|
||||
Send(0, data, true);
|
||||
}
|
||||
|
||||
void Client::sendSignNodeText(v3s16 p, std::string text)
|
||||
void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields)
|
||||
{
|
||||
/*
|
||||
u16 command
|
||||
v3s16 p
|
||||
u16 textlen
|
||||
textdata
|
||||
*/
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
u8 buf[12];
|
||||
|
||||
// Write command
|
||||
writeU16(buf, TOSERVER_SIGNNODETEXT);
|
||||
os.write((char*)buf, 2);
|
||||
|
||||
// Write p
|
||||
writeV3S16(buf, p);
|
||||
os.write((char*)buf, 6);
|
||||
|
||||
u16 textlen = text.size();
|
||||
// Write text length
|
||||
writeS16(buf, textlen);
|
||||
os.write((char*)buf, 2);
|
||||
writeU16(os, TOSERVER_NODEMETA_FIELDS);
|
||||
writeV3S16(os, p);
|
||||
os<<serializeString(formname);
|
||||
writeU16(os, fields.size());
|
||||
for(std::map<std::string, std::string>::const_iterator
|
||||
i = fields.begin(); i != fields.end(); i++){
|
||||
const std::string &name = i->first;
|
||||
const std::string &value = i->second;
|
||||
os<<serializeString(name);
|
||||
os<<serializeLongString(value);
|
||||
}
|
||||
|
||||
// Write text
|
||||
os.write((char*)text.c_str(), textlen);
|
||||
|
||||
// Make data buffer
|
||||
std::string s = os.str();
|
||||
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
||||
@@ -2371,32 +2338,41 @@ ClientEvent Client::getClientEvent()
|
||||
|
||||
void Client::afterContentReceived()
|
||||
{
|
||||
verbosestream<<"Client::afterContentReceived() started"<<std::endl;
|
||||
assert(m_itemdef_received);
|
||||
assert(m_nodedef_received);
|
||||
assert(m_media_received);
|
||||
|
||||
|
||||
// remove the information about which checksum each texture
|
||||
// ought to have
|
||||
m_media_name_sha1_map.clear();
|
||||
|
||||
// Rebuild inherited images and recreate textures
|
||||
verbosestream<<"Rebuilding images and textures"<<std::endl;
|
||||
m_tsrc->rebuildImagesAndTextures();
|
||||
|
||||
// Update texture atlas
|
||||
verbosestream<<"Updating texture atlas"<<std::endl;
|
||||
if(g_settings->getBool("enable_texture_atlas"))
|
||||
m_tsrc->buildMainAtlas(this);
|
||||
|
||||
// Update node aliases
|
||||
verbosestream<<"Updating node aliases"<<std::endl;
|
||||
m_nodedef->updateAliases(m_itemdef);
|
||||
|
||||
// Update node textures
|
||||
verbosestream<<"Updating node textures"<<std::endl;
|
||||
m_nodedef->updateTextures(m_tsrc);
|
||||
|
||||
// Update item textures and meshes
|
||||
verbosestream<<"Updating item textures and meshes"<<std::endl;
|
||||
m_itemdef->updateTexturesAndMeshes(this);
|
||||
|
||||
// Start mesh update thread after setting up content definitions
|
||||
verbosestream<<"Starting mesh update thread"<<std::endl;
|
||||
m_mesh_update_thread.Start();
|
||||
|
||||
verbosestream<<"Client::afterContentReceived() done"<<std::endl;
|
||||
}
|
||||
|
||||
float Client::getRTT(void)
|
||||
|
||||
25
src/client.h
25
src/client.h
@@ -20,8 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef CLIENT_HEADER
|
||||
#define CLIENT_HEADER
|
||||
|
||||
#ifndef SERVER
|
||||
|
||||
#include "connection.h"
|
||||
#include "environment.h"
|
||||
#include "common_irrlicht.h"
|
||||
@@ -35,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "inventorymanager.h"
|
||||
#include "filesys.h"
|
||||
#include "filecache.h"
|
||||
#include "localplayer.h"
|
||||
|
||||
struct MeshMakeData;
|
||||
class MapBlockMesh;
|
||||
@@ -211,11 +210,12 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
|
||||
void interact(u8 action, const PointedThing& pointed);
|
||||
|
||||
void sendSignNodeText(v3s16 p, std::string text);
|
||||
void sendNodemetaFields(v3s16 p, const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields);
|
||||
void sendInventoryAction(InventoryAction *a);
|
||||
void sendChatMessage(const std::wstring &message);
|
||||
void sendChangePassword(const std::wstring oldpassword,
|
||||
const std::wstring newpassword);
|
||||
const std::wstring newpassword);
|
||||
void sendDamage(u8 damage);
|
||||
void sendRespawn();
|
||||
|
||||
@@ -262,14 +262,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
|
||||
u16 getHP();
|
||||
|
||||
float getAvgRtt()
|
||||
{
|
||||
try{
|
||||
return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
|
||||
} catch(con::PeerNotFoundException){
|
||||
return 1337;
|
||||
}
|
||||
}
|
||||
bool checkPrivilege(const std::string &priv)
|
||||
{ return (m_privileges.count(priv) != 0); }
|
||||
|
||||
bool getChatMessage(std::wstring &message);
|
||||
void typeChatMessage(const std::wstring& message);
|
||||
@@ -312,6 +306,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
virtual u16 allocateUnknownNodeId(const std::string &name);
|
||||
virtual ISoundManager* getSoundManager();
|
||||
virtual MtEventManager* getEventManager();
|
||||
virtual bool checkLocalPrivilege(const std::string &priv)
|
||||
{ return checkPrivilege(priv); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -392,9 +388,10 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
|
||||
std::map<int, s32> m_sounds_client_to_server;
|
||||
// And relations to objects
|
||||
std::map<int, u16> m_sounds_to_objects;
|
||||
};
|
||||
|
||||
#endif // !SERVER
|
||||
// Privileges
|
||||
std::set<std::string> m_privileges;
|
||||
};
|
||||
|
||||
#endif // !CLIENT_HEADER
|
||||
|
||||
|
||||
@@ -324,6 +324,16 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||
Occlusion culling
|
||||
*/
|
||||
|
||||
// No occlusion culling when free_move is on and camera is
|
||||
// inside ground
|
||||
bool occlusion_culling_enabled = true;
|
||||
if(g_settings->getBool("free_move")){
|
||||
MapNode n = getNodeNoEx(cam_pos_nodes);
|
||||
if(n.getContent() == CONTENT_IGNORE ||
|
||||
nodemgr->get(n).solidness == 2)
|
||||
occlusion_culling_enabled = false;
|
||||
}
|
||||
|
||||
v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
|
||||
cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
|
||||
float step = BS*1;
|
||||
@@ -334,6 +344,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||
s16 bs2 = MAP_BLOCKSIZE/2 + 1;
|
||||
u32 needed_count = 1;
|
||||
if(
|
||||
occlusion_culling_enabled &&
|
||||
isOccluded(this, spn, cpn + v3s16(0,0,0),
|
||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||
isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
|
||||
@@ -598,9 +609,9 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
|
||||
if(z_directions[0].X < -99){
|
||||
for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){
|
||||
z_directions[i] = v3f(
|
||||
0.01 * myrand_range(-80, 80),
|
||||
0.01 * myrand_range(-100, 100),
|
||||
1.0,
|
||||
0.01 * myrand_range(-80, 80)
|
||||
0.01 * myrand_range(-100, 100)
|
||||
);
|
||||
z_offsets[i] = 0.01 * myrand_range(0,100);
|
||||
}
|
||||
|
||||
@@ -50,9 +50,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
PROTOCOL_VERSION 9:
|
||||
ContentFeatures and NodeDefManager use a different serialization
|
||||
format; better for future version cross-compatibility
|
||||
Many things
|
||||
PROTOCOL_VERSION 10:
|
||||
TOCLIENT_PRIVILEGES
|
||||
Version raised to force 'fly' and 'fast' privileges into effect.
|
||||
*/
|
||||
|
||||
#define PROTOCOL_VERSION 9
|
||||
#define PROTOCOL_VERSION 10
|
||||
|
||||
#define PROTOCOL_ID 0x4f457403
|
||||
|
||||
@@ -200,7 +204,7 @@ enum ToClientCommand
|
||||
wstring reason
|
||||
*/
|
||||
|
||||
TOCLIENT_PLAYERITEM = 0x36,
|
||||
TOCLIENT_PLAYERITEM = 0x36, // Obsolete
|
||||
/*
|
||||
u16 command
|
||||
u16 count of player items
|
||||
@@ -291,6 +295,15 @@ enum ToClientCommand
|
||||
u16 command
|
||||
s32 sound_id
|
||||
*/
|
||||
|
||||
TOCLIENT_PRIVILEGES = 0x41,
|
||||
/*
|
||||
u16 command
|
||||
u16 number of privileges
|
||||
for each privilege
|
||||
u16 len
|
||||
u8[len] privilege
|
||||
*/
|
||||
};
|
||||
|
||||
enum ToServerCommand
|
||||
@@ -402,7 +415,7 @@ enum ToServerCommand
|
||||
wstring message
|
||||
*/
|
||||
|
||||
TOSERVER_SIGNNODETEXT = 0x33,
|
||||
TOSERVER_SIGNNODETEXT = 0x33, // obsolete
|
||||
/*
|
||||
u16 command
|
||||
v3s16 p
|
||||
@@ -471,6 +484,20 @@ enum ToServerCommand
|
||||
s32[len] sound_id
|
||||
*/
|
||||
|
||||
TOSERVER_NODEMETA_FIELDS = 0x3b,
|
||||
/*
|
||||
u16 command
|
||||
v3s16 p
|
||||
u16 len
|
||||
u8[len] form name (reserved for future use)
|
||||
u16 number of fields
|
||||
for each field:
|
||||
u16 len
|
||||
u8[len] field name
|
||||
u32 len
|
||||
u8[len] field value
|
||||
*/
|
||||
|
||||
TOSERVER_REQUEST_MEDIA = 0x40,
|
||||
/*
|
||||
u16 command
|
||||
|
||||
@@ -29,6 +29,10 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
|
||||
{
|
||||
collisionMoveResult result;
|
||||
|
||||
// If there is no speed, there are no collisions
|
||||
if(speed_f.getLength() == 0)
|
||||
return result;
|
||||
|
||||
v3f oldpos_f = pos_f;
|
||||
v3s16 oldpos_i = floatToInt(oldpos_f, BS);
|
||||
|
||||
@@ -196,6 +200,10 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
|
||||
f32 dtime, v3f &pos_f, v3f &speed_f)
|
||||
{
|
||||
collisionMoveResult final_result;
|
||||
|
||||
// If there is no speed, there are no collisions
|
||||
if(speed_f.getLength() == 0)
|
||||
return final_result;
|
||||
|
||||
// Maximum time increment (for collision detection etc)
|
||||
// time = distance / speed
|
||||
|
||||
@@ -99,9 +99,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// Length of cracking animation in count of images
|
||||
#define CRACK_ANIMATION_LENGTH 5
|
||||
|
||||
// Some stuff needed by old code moved to here from heightmap.h
|
||||
#define GROUNDHEIGHT_NOTFOUND_SETVALUE (-10e6)
|
||||
#define GROUNDHEIGHT_VALID_MINVALUE ( -9e6)
|
||||
// Maximum hit points of a player
|
||||
#define PLAYER_MAX_HP 20
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -36,9 +36,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "content_cso.h"
|
||||
#include "sound.h"
|
||||
#include "nodedef.h"
|
||||
#include "localplayer.h"
|
||||
class Settings;
|
||||
struct ToolCapabilities;
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
|
||||
|
||||
/*
|
||||
@@ -533,28 +536,37 @@ void ItemCAO::initialize(const std::string &data)
|
||||
}
|
||||
|
||||
/*
|
||||
LuaEntityCAO
|
||||
GenericCAO
|
||||
*/
|
||||
|
||||
#include "luaentity_common.h"
|
||||
#include "genericobject.h"
|
||||
|
||||
class LuaEntityCAO : public ClientActiveObject
|
||||
class GenericCAO : public ClientActiveObject
|
||||
{
|
||||
private:
|
||||
// Only set at initialization
|
||||
std::string m_name;
|
||||
bool m_is_player;
|
||||
bool m_is_local_player; // determined locally
|
||||
// Property-ish things
|
||||
ObjectProperties m_prop;
|
||||
//
|
||||
scene::ISceneManager *m_smgr;
|
||||
IrrlichtDevice *m_irr;
|
||||
core::aabbox3d<f32> m_selection_box;
|
||||
scene::IMeshSceneNode *m_meshnode;
|
||||
scene::IBillboardSceneNode *m_spritenode;
|
||||
scene::ITextSceneNode* m_textnode;
|
||||
v3f m_position;
|
||||
v3f m_velocity;
|
||||
v3f m_acceleration;
|
||||
float m_yaw;
|
||||
s16 m_hp;
|
||||
struct LuaEntityProperties *m_prop;
|
||||
SmoothTranslator pos_translator;
|
||||
// Spritesheet/animation stuff
|
||||
v2f m_tx_size;
|
||||
v2s16 m_tx_basepos;
|
||||
bool m_initial_tx_basepos_set;
|
||||
bool m_tx_select_horiz_by_yawpitch;
|
||||
int m_anim_frame;
|
||||
int m_anim_num_frames;
|
||||
@@ -562,28 +574,40 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
float m_anim_timer;
|
||||
ItemGroupList m_armor_groups;
|
||||
float m_reset_textures_timer;
|
||||
bool m_visuals_expired;
|
||||
float m_step_distance_counter;
|
||||
u8 m_last_light;
|
||||
|
||||
public:
|
||||
LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
|
||||
GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
|
||||
ClientActiveObject(0, gamedef, env),
|
||||
//
|
||||
m_is_player(false),
|
||||
m_is_local_player(false),
|
||||
//
|
||||
m_smgr(NULL),
|
||||
m_irr(NULL),
|
||||
m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
|
||||
m_meshnode(NULL),
|
||||
m_spritenode(NULL),
|
||||
m_textnode(NULL),
|
||||
m_position(v3f(0,10*BS,0)),
|
||||
m_velocity(v3f(0,0,0)),
|
||||
m_acceleration(v3f(0,0,0)),
|
||||
m_yaw(0),
|
||||
m_hp(1),
|
||||
m_prop(new LuaEntityProperties),
|
||||
m_tx_size(1,1),
|
||||
m_tx_basepos(0,0),
|
||||
m_initial_tx_basepos_set(false),
|
||||
m_tx_select_horiz_by_yawpitch(false),
|
||||
m_anim_frame(0),
|
||||
m_anim_num_frames(1),
|
||||
m_anim_framelength(0.2),
|
||||
m_anim_timer(0),
|
||||
m_reset_textures_timer(-1)
|
||||
m_reset_textures_timer(-1),
|
||||
m_visuals_expired(false),
|
||||
m_step_distance_counter(0),
|
||||
m_last_light(255)
|
||||
{
|
||||
if(gamedef == NULL)
|
||||
ClientActiveObject::registerType(getType(), create);
|
||||
@@ -591,108 +615,62 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
|
||||
void initialize(const std::string &data)
|
||||
{
|
||||
infostream<<"LuaEntityCAO: Got init data"<<std::endl;
|
||||
|
||||
infostream<<"GenericCAO: Got init data"<<std::endl;
|
||||
std::istringstream is(data, std::ios::binary);
|
||||
// version
|
||||
u8 version = readU8(is);
|
||||
// check version
|
||||
if(version != 1)
|
||||
if(version != 0){
|
||||
errorstream<<"GenericCAO: Unsupported init data version"
|
||||
<<std::endl;
|
||||
return;
|
||||
// pos
|
||||
}
|
||||
m_name = deSerializeString(is);
|
||||
m_is_player = readU8(is);
|
||||
m_position = readV3F1000(is);
|
||||
// yaw
|
||||
m_yaw = readF1000(is);
|
||||
// hp
|
||||
m_hp = readS16(is);
|
||||
// properties
|
||||
std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
|
||||
m_prop->deSerialize(prop_is);
|
||||
|
||||
infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
|
||||
|
||||
m_selection_box = m_prop->collisionbox;
|
||||
m_selection_box.MinEdge *= BS;
|
||||
m_selection_box.MaxEdge *= BS;
|
||||
|
||||
pos_translator.init(m_position);
|
||||
|
||||
m_tx_size.X = 1.0 / m_prop->spritediv.X;
|
||||
m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
|
||||
m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
|
||||
m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
|
||||
|
||||
int num_messages = readU8(is);
|
||||
for(int i=0; i<num_messages; i++){
|
||||
std::string message = deSerializeLongString(is);
|
||||
processMessage(message);
|
||||
}
|
||||
|
||||
pos_translator.init(m_position);
|
||||
updateNodePos();
|
||||
|
||||
if(m_is_player){
|
||||
Player *player = m_env->getPlayer(m_name.c_str());
|
||||
if(player && player->isLocal()){
|
||||
m_is_local_player = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~LuaEntityCAO()
|
||||
~GenericCAO()
|
||||
{
|
||||
delete m_prop;
|
||||
}
|
||||
|
||||
static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
|
||||
{
|
||||
return new LuaEntityCAO(gamedef, env);
|
||||
return new GenericCAO(gamedef, env);
|
||||
}
|
||||
|
||||
u8 getType() const
|
||||
{
|
||||
return ACTIVEOBJECT_TYPE_LUAENTITY;
|
||||
return ACTIVEOBJECT_TYPE_GENERIC;
|
||||
}
|
||||
core::aabbox3d<f32>* getSelectionBox()
|
||||
{
|
||||
if(!m_prop.is_visible || m_is_local_player)
|
||||
return NULL;
|
||||
return &m_selection_box;
|
||||
}
|
||||
v3f getPosition()
|
||||
{
|
||||
return pos_translator.vect_show;
|
||||
}
|
||||
|
||||
void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
|
||||
IrrlichtDevice *irr)
|
||||
{
|
||||
m_smgr = smgr;
|
||||
|
||||
if(m_meshnode != NULL || m_spritenode != NULL)
|
||||
return;
|
||||
|
||||
//video::IVideoDriver* driver = smgr->getVideoDriver();
|
||||
|
||||
if(m_prop->visual == "sprite"){
|
||||
infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
|
||||
m_spritenode = smgr->addBillboardSceneNode(
|
||||
NULL, v2f(1, 1), v3f(0,0,0), -1);
|
||||
m_spritenode->setMaterialTexture(0,
|
||||
tsrc->getTextureRaw("unknown_block.png"));
|
||||
m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
|
||||
m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
|
||||
m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
|
||||
m_spritenode->setColor(video::SColor(255,0,0,0));
|
||||
m_spritenode->setVisible(false); /* Set visible when brightness is known */
|
||||
m_spritenode->setSize(m_prop->visual_size*BS);
|
||||
{
|
||||
const float txs = 1.0 / 1;
|
||||
const float tys = 1.0 / 1;
|
||||
setBillboardTextureMatrix(m_spritenode,
|
||||
txs, tys, 0, 0);
|
||||
}
|
||||
} else if(m_prop->visual == "cube"){
|
||||
infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
|
||||
scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
|
||||
m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
|
||||
mesh->drop();
|
||||
|
||||
m_meshnode->setScale(v3f(1));
|
||||
// Will be shown when we know the brightness
|
||||
m_meshnode->setVisible(false);
|
||||
} else {
|
||||
infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
|
||||
<<"\" not supported"<<std::endl;
|
||||
}
|
||||
updateTextures("");
|
||||
updateNodePos();
|
||||
}
|
||||
|
||||
void removeFromScene()
|
||||
{
|
||||
@@ -706,10 +684,157 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
}
|
||||
}
|
||||
|
||||
void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
|
||||
IrrlichtDevice *irr)
|
||||
{
|
||||
m_smgr = smgr;
|
||||
m_irr = irr;
|
||||
|
||||
if(m_meshnode != NULL || m_spritenode != NULL)
|
||||
return;
|
||||
|
||||
m_visuals_expired = false;
|
||||
|
||||
if(!m_prop.is_visible || m_is_local_player)
|
||||
return;
|
||||
|
||||
//video::IVideoDriver* driver = smgr->getVideoDriver();
|
||||
|
||||
if(m_prop.visual == "sprite"){
|
||||
infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl;
|
||||
m_spritenode = smgr->addBillboardSceneNode(
|
||||
NULL, v2f(1, 1), v3f(0,0,0), -1);
|
||||
m_spritenode->setMaterialTexture(0,
|
||||
tsrc->getTextureRaw("unknown_block.png"));
|
||||
m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
|
||||
m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
|
||||
m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
|
||||
u8 li = m_last_light;
|
||||
m_spritenode->setColor(video::SColor(255,li,li,li));
|
||||
m_spritenode->setSize(m_prop.visual_size*BS);
|
||||
{
|
||||
const float txs = 1.0 / 1;
|
||||
const float tys = 1.0 / 1;
|
||||
setBillboardTextureMatrix(m_spritenode,
|
||||
txs, tys, 0, 0);
|
||||
}
|
||||
}
|
||||
else if(m_prop.visual == "upright_sprite")
|
||||
{
|
||||
scene::SMesh *mesh = new scene::SMesh();
|
||||
double dx = BS*m_prop.visual_size.X/2;
|
||||
double dy = BS*m_prop.visual_size.Y/2;
|
||||
{ // Front
|
||||
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
|
||||
u8 li = m_last_light;
|
||||
video::SColor c(255,li,li,li);
|
||||
video::S3DVertex vertices[4] =
|
||||
{
|
||||
video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
|
||||
video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
|
||||
video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
|
||||
video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
|
||||
};
|
||||
u16 indices[] = {0,1,2,2,3,0};
|
||||
buf->append(vertices, 4, indices, 6);
|
||||
// Set material
|
||||
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
|
||||
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
|
||||
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
// Add to mesh
|
||||
mesh->addMeshBuffer(buf);
|
||||
buf->drop();
|
||||
}
|
||||
{ // Back
|
||||
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
|
||||
u8 li = m_last_light;
|
||||
video::SColor c(255,li,li,li);
|
||||
video::S3DVertex vertices[4] =
|
||||
{
|
||||
video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
|
||||
video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
|
||||
video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
|
||||
video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
|
||||
};
|
||||
u16 indices[] = {0,1,2,2,3,0};
|
||||
buf->append(vertices, 4, indices, 6);
|
||||
// Set material
|
||||
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
|
||||
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
|
||||
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
||||
// Add to mesh
|
||||
mesh->addMeshBuffer(buf);
|
||||
buf->drop();
|
||||
}
|
||||
m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
|
||||
mesh->drop();
|
||||
// Set it to use the materials of the meshbuffers directly.
|
||||
// This is needed for changing the texture in the future
|
||||
m_meshnode->setReadOnlyMaterials(true);
|
||||
}
|
||||
else if(m_prop.visual == "cube"){
|
||||
infostream<<"GenericCAO::addToScene(): cube"<<std::endl;
|
||||
scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
|
||||
m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
|
||||
mesh->drop();
|
||||
|
||||
m_meshnode->setScale(v3f(m_prop.visual_size.X,
|
||||
m_prop.visual_size.Y,
|
||||
m_prop.visual_size.X));
|
||||
u8 li = m_last_light;
|
||||
setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
|
||||
} else if(m_prop.visual == "wielditem"){
|
||||
infostream<<"GenericCAO::addToScene(): node"<<std::endl;
|
||||
infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
|
||||
if(m_prop.textures.size() >= 1){
|
||||
infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
|
||||
IItemDefManager *idef = m_gamedef->idef();
|
||||
ItemStack item(m_prop.textures[0], 1, 0, "", idef);
|
||||
scene::IMesh *mesh = item.getDefinition(idef).wield_mesh;
|
||||
m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
|
||||
|
||||
m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
|
||||
m_prop.visual_size.Y/2,
|
||||
m_prop.visual_size.X/2));
|
||||
u8 li = m_last_light;
|
||||
setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
|
||||
}
|
||||
} else {
|
||||
infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
|
||||
<<"\" not supported"<<std::endl;
|
||||
}
|
||||
updateTextures("");
|
||||
|
||||
scene::ISceneNode *node = NULL;
|
||||
if(m_spritenode)
|
||||
node = m_spritenode;
|
||||
else if(m_meshnode)
|
||||
node = m_meshnode;
|
||||
if(node && m_is_player && !m_is_local_player){
|
||||
// Add a text node for showing the name
|
||||
gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
|
||||
std::wstring wname = narrow_to_wide(m_name);
|
||||
m_textnode = smgr->addTextSceneNode(gui->getBuiltInFont(),
|
||||
wname.c_str(), video::SColor(255,255,255,255), node);
|
||||
m_textnode->setPosition(v3f(0, BS*1.1, 0));
|
||||
}
|
||||
|
||||
updateNodePos();
|
||||
}
|
||||
|
||||
void expireVisuals()
|
||||
{
|
||||
m_visuals_expired = true;
|
||||
}
|
||||
|
||||
void updateLight(u8 light_at_pos)
|
||||
{
|
||||
bool is_visible = (m_hp != 0);
|
||||
u8 li = decode_light(light_at_pos);
|
||||
m_last_light = li;
|
||||
video::SColor color(255,li,li,li);
|
||||
if(m_meshnode){
|
||||
setMeshColor(m_meshnode->getMesh(), color);
|
||||
@@ -730,6 +855,9 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
{
|
||||
if(m_meshnode){
|
||||
m_meshnode->setPosition(pos_translator.vect_show);
|
||||
v3f rot = m_meshnode->getRotation();
|
||||
rot.Y = -m_yaw;
|
||||
m_meshnode->setRotation(rot);
|
||||
}
|
||||
if(m_spritenode){
|
||||
m_spritenode->setPosition(pos_translator.vect_show);
|
||||
@@ -738,8 +866,16 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
|
||||
void step(float dtime, ClientEnvironment *env)
|
||||
{
|
||||
if(m_prop->physical){
|
||||
core::aabbox3d<f32> box = m_prop->collisionbox;
|
||||
v3f lastpos = pos_translator.vect_show;
|
||||
|
||||
if(m_visuals_expired && m_smgr && m_irr){
|
||||
m_visuals_expired = false;
|
||||
removeFromScene();
|
||||
addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
|
||||
}
|
||||
|
||||
if(m_prop.physical){
|
||||
core::aabbox3d<f32> box = m_prop.collisionbox;
|
||||
box.MinEdge *= BS;
|
||||
box.MaxEdge *= BS;
|
||||
collisionMoveResult moveresult;
|
||||
@@ -767,6 +903,20 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
updateNodePos();
|
||||
}
|
||||
|
||||
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
|
||||
m_step_distance_counter += moved;
|
||||
if(m_step_distance_counter > 1.5*BS){
|
||||
m_step_distance_counter = 0;
|
||||
if(!m_is_local_player && m_prop.makes_footstep_sound){
|
||||
INodeDefManager *ndef = m_gamedef->ndef();
|
||||
v3s16 p = floatToInt(getPosition() + v3f(0,
|
||||
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
|
||||
MapNode n = m_env->getMap().getNodeNoEx(p);
|
||||
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
|
||||
m_gamedef->sound()->playSoundAt(spec, false, getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
m_anim_timer += dtime;
|
||||
if(m_anim_timer >= m_anim_framelength){
|
||||
m_anim_timer -= m_anim_framelength;
|
||||
@@ -784,6 +934,10 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
updateTextures("");
|
||||
}
|
||||
}
|
||||
if(fabs(m_prop.automatic_rotate) > 0.001){
|
||||
m_yaw += dtime * m_prop.automatic_rotate * 180 / PI;
|
||||
updateNodePos();
|
||||
}
|
||||
}
|
||||
|
||||
void updateTexturePos()
|
||||
@@ -838,76 +992,125 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
{
|
||||
ITextureSource *tsrc = m_gamedef->tsrc();
|
||||
|
||||
if(m_spritenode){
|
||||
std::string texturestring = "unknown_block.png";
|
||||
if(m_prop->textures.size() >= 1)
|
||||
texturestring = m_prop->textures[0];
|
||||
texturestring += mod;
|
||||
m_spritenode->setMaterialTexture(0,
|
||||
tsrc->getTextureRaw(texturestring));
|
||||
}
|
||||
if(m_meshnode){
|
||||
for (u32 i = 0; i < 6; ++i)
|
||||
if(m_spritenode)
|
||||
{
|
||||
if(m_prop.visual == "sprite")
|
||||
{
|
||||
std::string texturestring = "unknown_block.png";
|
||||
if(m_prop->textures.size() > i)
|
||||
texturestring = m_prop->textures[i];
|
||||
if(m_prop.textures.size() >= 1)
|
||||
texturestring = m_prop.textures[0];
|
||||
texturestring += mod;
|
||||
AtlasPointer ap = tsrc->getTexture(texturestring);
|
||||
m_spritenode->setMaterialTexture(0,
|
||||
tsrc->getTextureRaw(texturestring));
|
||||
}
|
||||
}
|
||||
if(m_meshnode)
|
||||
{
|
||||
if(m_prop.visual == "cube")
|
||||
{
|
||||
for (u32 i = 0; i < 6; ++i)
|
||||
{
|
||||
std::string texturestring = "unknown_block.png";
|
||||
if(m_prop.textures.size() > i)
|
||||
texturestring = m_prop.textures[i];
|
||||
texturestring += mod;
|
||||
AtlasPointer ap = tsrc->getTexture(texturestring);
|
||||
|
||||
// Get the tile texture and atlas transformation
|
||||
video::ITexture* atlas = ap.atlas;
|
||||
v2f pos = ap.pos;
|
||||
v2f size = ap.size;
|
||||
// Get the tile texture and atlas transformation
|
||||
video::ITexture* atlas = ap.atlas;
|
||||
v2f pos = ap.pos;
|
||||
v2f size = ap.size;
|
||||
|
||||
// Set material flags and texture
|
||||
video::SMaterial& material = m_meshnode->getMaterial(i);
|
||||
material.setFlag(video::EMF_LIGHTING, false);
|
||||
material.setFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
material.setTexture(0, atlas);
|
||||
material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
|
||||
material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
|
||||
// Set material flags and texture
|
||||
video::SMaterial& material = m_meshnode->getMaterial(i);
|
||||
material.setFlag(video::EMF_LIGHTING, false);
|
||||
material.setFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
material.setTexture(0, atlas);
|
||||
material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
|
||||
material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
|
||||
}
|
||||
}
|
||||
else if(m_prop.visual == "upright_sprite")
|
||||
{
|
||||
scene::IMesh *mesh = m_meshnode->getMesh();
|
||||
{
|
||||
std::string tname = "unknown_object.png";
|
||||
if(m_prop.textures.size() >= 1)
|
||||
tname = m_prop.textures[0];
|
||||
tname += mod;
|
||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
|
||||
buf->getMaterial().setTexture(0,
|
||||
tsrc->getTextureRaw(tname));
|
||||
}
|
||||
{
|
||||
std::string tname = "unknown_object.png";
|
||||
if(m_prop.textures.size() >= 2)
|
||||
tname = m_prop.textures[1];
|
||||
else if(m_prop.textures.size() >= 1)
|
||||
tname = m_prop.textures[0];
|
||||
tname += mod;
|
||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
|
||||
buf->getMaterial().setTexture(0,
|
||||
tsrc->getTextureRaw(tname));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processMessage(const std::string &data)
|
||||
{
|
||||
//infostream<<"LuaEntityCAO: Got message"<<std::endl;
|
||||
//infostream<<"GenericCAO: Got message"<<std::endl;
|
||||
std::istringstream is(data, std::ios::binary);
|
||||
// command
|
||||
u8 cmd = readU8(is);
|
||||
if(cmd == LUAENTITY_CMD_UPDATE_POSITION) // update position
|
||||
if(cmd == GENERIC_CMD_SET_PROPERTIES)
|
||||
{
|
||||
m_prop = gob_read_set_properties(is);
|
||||
|
||||
m_selection_box = m_prop.collisionbox;
|
||||
m_selection_box.MinEdge *= BS;
|
||||
m_selection_box.MaxEdge *= BS;
|
||||
|
||||
m_tx_size.X = 1.0 / m_prop.spritediv.X;
|
||||
m_tx_size.Y = 1.0 / m_prop.spritediv.Y;
|
||||
|
||||
if(!m_initial_tx_basepos_set){
|
||||
m_initial_tx_basepos_set = true;
|
||||
m_tx_basepos = m_prop.initial_sprite_basepos;
|
||||
}
|
||||
|
||||
expireVisuals();
|
||||
}
|
||||
else if(cmd == GENERIC_CMD_UPDATE_POSITION)
|
||||
{
|
||||
// do_interpolate
|
||||
bool do_interpolate = readU8(is);
|
||||
// pos
|
||||
m_position = readV3F1000(is);
|
||||
// velocity
|
||||
m_velocity = readV3F1000(is);
|
||||
// acceleration
|
||||
m_acceleration = readV3F1000(is);
|
||||
// yaw
|
||||
m_yaw = readF1000(is);
|
||||
// is_end_position (for interpolation)
|
||||
if(fabs(m_prop.automatic_rotate) < 0.001)
|
||||
m_yaw = readF1000(is);
|
||||
bool do_interpolate = readU8(is);
|
||||
bool is_end_position = readU8(is);
|
||||
// update_interval
|
||||
float update_interval = readF1000(is);
|
||||
|
||||
// Place us a bit higher if we're physical, to not sink into
|
||||
// the ground due to sucky collision detection...
|
||||
if(m_prop.physical)
|
||||
m_position += v3f(0,0.002,0);
|
||||
|
||||
if(do_interpolate){
|
||||
if(!m_prop->physical)
|
||||
if(!m_prop.physical)
|
||||
pos_translator.update(m_position, is_end_position, update_interval);
|
||||
} else {
|
||||
pos_translator.init(m_position);
|
||||
}
|
||||
updateNodePos();
|
||||
}
|
||||
else if(cmd == LUAENTITY_CMD_SET_TEXTURE_MOD) // set texture modification
|
||||
else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD)
|
||||
{
|
||||
std::string mod = deSerializeString(is);
|
||||
updateTextures(mod);
|
||||
}
|
||||
else if(cmd == LUAENTITY_CMD_SET_SPRITE) // set sprite
|
||||
else if(cmd == GENERIC_CMD_SET_SPRITE)
|
||||
{
|
||||
v2s16 p = readV2S16(is);
|
||||
int num_frames = readU16(is);
|
||||
@@ -921,15 +1124,14 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
|
||||
updateTexturePos();
|
||||
}
|
||||
else if(cmd == LUAENTITY_CMD_PUNCHED)
|
||||
else if(cmd == GENERIC_CMD_PUNCHED)
|
||||
{
|
||||
/*s16 damage =*/ readS16(is);
|
||||
s16 result_hp = readS16(is);
|
||||
|
||||
m_hp = result_hp;
|
||||
// TODO: Execute defined fast response
|
||||
}
|
||||
else if(cmd == LUAENTITY_CMD_UPDATE_ARMOR_GROUPS)
|
||||
else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS)
|
||||
{
|
||||
m_armor_groups.clear();
|
||||
int armor_groups_size = readU16(is);
|
||||
@@ -952,7 +1154,7 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
toolcap,
|
||||
punchitem,
|
||||
time_from_last_punch);
|
||||
|
||||
|
||||
if(result.did_punch && result.damage != 0)
|
||||
{
|
||||
if(result.damage < m_hp){
|
||||
@@ -963,13 +1165,15 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
// As there is no definition, make a smoke puff
|
||||
ClientSimpleObject *simple = createSmokePuff(
|
||||
m_smgr, m_env, m_position,
|
||||
m_prop->visual_size * BS);
|
||||
m_prop.visual_size * BS);
|
||||
m_env->addSimpleObject(simple);
|
||||
}
|
||||
// TODO: Execute defined fast response
|
||||
// Flashing shall suffice as there is no definition
|
||||
m_reset_textures_timer = 0.05;
|
||||
if(result.damage >= 2)
|
||||
m_reset_textures_timer += 0.05 * result.damage;
|
||||
updateTextures("^[brighten");
|
||||
m_reset_textures_timer = 0.1;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -978,7 +1182,7 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
std::string debugInfoText()
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
os<<"LuaEntityCAO hp="<<m_hp<<"\n";
|
||||
os<<"GenericCAO hp="<<m_hp<<"\n";
|
||||
os<<"armor={";
|
||||
for(ItemGroupList::const_iterator i = m_armor_groups.begin();
|
||||
i != m_armor_groups.end(); i++){
|
||||
@@ -990,307 +1194,6 @@ class LuaEntityCAO : public ClientActiveObject
|
||||
};
|
||||
|
||||
// Prototype
|
||||
LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
|
||||
|
||||
/*
|
||||
PlayerCAO
|
||||
*/
|
||||
|
||||
class PlayerCAO : public ClientActiveObject
|
||||
{
|
||||
private:
|
||||
core::aabbox3d<f32> m_selection_box;
|
||||
scene::IMeshSceneNode *m_node;
|
||||
scene::ITextSceneNode* m_text;
|
||||
std::string m_name;
|
||||
v3f m_position;
|
||||
float m_yaw;
|
||||
SmoothTranslator pos_translator;
|
||||
bool m_is_local_player;
|
||||
LocalPlayer *m_local_player;
|
||||
float m_damage_visual_timer;
|
||||
bool m_dead;
|
||||
float m_step_distance_counter;
|
||||
|
||||
public:
|
||||
PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
|
||||
ClientActiveObject(0, gamedef, env),
|
||||
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
|
||||
m_node(NULL),
|
||||
m_text(NULL),
|
||||
m_position(v3f(0,10*BS,0)),
|
||||
m_yaw(0),
|
||||
m_is_local_player(false),
|
||||
m_local_player(NULL),
|
||||
m_damage_visual_timer(0),
|
||||
m_dead(false),
|
||||
m_step_distance_counter(0)
|
||||
{
|
||||
if(gamedef == NULL)
|
||||
ClientActiveObject::registerType(getType(), create);
|
||||
}
|
||||
|
||||
void initialize(const std::string &data)
|
||||
{
|
||||
infostream<<"PlayerCAO: Got init data"<<std::endl;
|
||||
|
||||
std::istringstream is(data, std::ios::binary);
|
||||
// version
|
||||
u8 version = readU8(is);
|
||||
// check version
|
||||
if(version != 0)
|
||||
return;
|
||||
// name
|
||||
m_name = deSerializeString(is);
|
||||
// pos
|
||||
m_position = readV3F1000(is);
|
||||
// yaw
|
||||
m_yaw = readF1000(is);
|
||||
// dead
|
||||
m_dead = readU8(is);
|
||||
|
||||
pos_translator.init(m_position);
|
||||
|
||||
Player *player = m_env->getPlayer(m_name.c_str());
|
||||
if(player && player->isLocal()){
|
||||
m_is_local_player = true;
|
||||
m_local_player = (LocalPlayer*)player;
|
||||
}
|
||||
}
|
||||
|
||||
~PlayerCAO()
|
||||
{
|
||||
if(m_node)
|
||||
m_node->remove();
|
||||
}
|
||||
|
||||
static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
|
||||
{
|
||||
return new PlayerCAO(gamedef, env);
|
||||
}
|
||||
|
||||
u8 getType() const
|
||||
{
|
||||
return ACTIVEOBJECT_TYPE_PLAYER;
|
||||
}
|
||||
core::aabbox3d<f32>* getSelectionBox()
|
||||
{
|
||||
if(m_is_local_player)
|
||||
return NULL;
|
||||
if(m_dead)
|
||||
return NULL;
|
||||
return &m_selection_box;
|
||||
}
|
||||
v3f getPosition()
|
||||
{
|
||||
return pos_translator.vect_show;
|
||||
}
|
||||
|
||||
void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
|
||||
IrrlichtDevice *irr)
|
||||
{
|
||||
if(m_node != NULL)
|
||||
return;
|
||||
if(m_is_local_player)
|
||||
return;
|
||||
|
||||
//video::IVideoDriver* driver = smgr->getVideoDriver();
|
||||
gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
|
||||
|
||||
scene::SMesh *mesh = new scene::SMesh();
|
||||
{ // Front
|
||||
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
|
||||
video::SColor c(255,255,255,255);
|
||||
video::S3DVertex vertices[4] =
|
||||
{
|
||||
video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
|
||||
video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
|
||||
video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
|
||||
video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
|
||||
};
|
||||
u16 indices[] = {0,1,2,2,3,0};
|
||||
buf->append(vertices, 4, indices, 6);
|
||||
// Set material
|
||||
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
|
||||
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
|
||||
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
// Add to mesh
|
||||
mesh->addMeshBuffer(buf);
|
||||
buf->drop();
|
||||
}
|
||||
{ // Back
|
||||
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
|
||||
video::SColor c(255,255,255,255);
|
||||
video::S3DVertex vertices[4] =
|
||||
{
|
||||
video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
|
||||
video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
|
||||
video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
|
||||
video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
|
||||
};
|
||||
u16 indices[] = {0,1,2,2,3,0};
|
||||
buf->append(vertices, 4, indices, 6);
|
||||
// Set material
|
||||
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
|
||||
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
|
||||
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
|
||||
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
||||
// Add to mesh
|
||||
mesh->addMeshBuffer(buf);
|
||||
buf->drop();
|
||||
}
|
||||
m_node = smgr->addMeshSceneNode(mesh, NULL);
|
||||
mesh->drop();
|
||||
// Set it to use the materials of the meshbuffers directly.
|
||||
// This is needed for changing the texture in the future
|
||||
m_node->setReadOnlyMaterials(true);
|
||||
updateNodePos();
|
||||
|
||||
// Add a text node for showing the name
|
||||
std::wstring wname = narrow_to_wide(m_name);
|
||||
m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
|
||||
wname.c_str(), video::SColor(255,255,255,255), m_node);
|
||||
m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
|
||||
|
||||
updateTextures("");
|
||||
updateVisibility();
|
||||
updateNodePos();
|
||||
}
|
||||
|
||||
void removeFromScene()
|
||||
{
|
||||
if(m_node == NULL)
|
||||
return;
|
||||
|
||||
m_node->remove();
|
||||
m_node = NULL;
|
||||
}
|
||||
|
||||
void updateLight(u8 light_at_pos)
|
||||
{
|
||||
if(m_node == NULL)
|
||||
return;
|
||||
|
||||
u8 li = decode_light(light_at_pos);
|
||||
video::SColor color(255,li,li,li);
|
||||
setMeshColor(m_node->getMesh(), color);
|
||||
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
v3s16 getLightPosition()
|
||||
{
|
||||
return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
|
||||
}
|
||||
|
||||
void updateVisibility()
|
||||
{
|
||||
if(m_node == NULL)
|
||||
return;
|
||||
|
||||
m_node->setVisible(!m_dead);
|
||||
}
|
||||
|
||||
void updateNodePos()
|
||||
{
|
||||
if(m_node == NULL)
|
||||
return;
|
||||
|
||||
m_node->setPosition(pos_translator.vect_show);
|
||||
|
||||
v3f rot = m_node->getRotation();
|
||||
rot.Y = -m_yaw;
|
||||
m_node->setRotation(rot);
|
||||
}
|
||||
|
||||
void step(float dtime, ClientEnvironment *env)
|
||||
{
|
||||
v3f lastpos = pos_translator.vect_show;
|
||||
pos_translator.translate(dtime);
|
||||
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
|
||||
updateVisibility();
|
||||
updateNodePos();
|
||||
|
||||
if(m_damage_visual_timer > 0){
|
||||
m_damage_visual_timer -= dtime;
|
||||
if(m_damage_visual_timer <= 0){
|
||||
updateTextures("");
|
||||
}
|
||||
}
|
||||
|
||||
m_step_distance_counter += moved;
|
||||
if(m_step_distance_counter > 1.5*BS){
|
||||
m_step_distance_counter = 0;
|
||||
if(!m_is_local_player){
|
||||
INodeDefManager *ndef = m_gamedef->ndef();
|
||||
v3s16 p = floatToInt(getPosition()+v3f(0,-0.5*BS, 0), BS);
|
||||
MapNode n = m_env->getMap().getNodeNoEx(p);
|
||||
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
|
||||
m_gamedef->sound()->playSoundAt(spec, false, getPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processMessage(const std::string &data)
|
||||
{
|
||||
//infostream<<"PlayerCAO: Got message"<<std::endl;
|
||||
std::istringstream is(data, std::ios::binary);
|
||||
// command
|
||||
u8 cmd = readU8(is);
|
||||
if(cmd == 0) // update position
|
||||
{
|
||||
// pos
|
||||
m_position = readV3F1000(is);
|
||||
// yaw
|
||||
m_yaw = readF1000(is);
|
||||
|
||||
pos_translator.update(m_position, false);
|
||||
|
||||
updateNodePos();
|
||||
}
|
||||
else if(cmd == 1) // punched
|
||||
{
|
||||
// damage
|
||||
s16 damage = readS16(is);
|
||||
m_damage_visual_timer = 0.05;
|
||||
if(damage >= 2)
|
||||
m_damage_visual_timer += 0.05 * damage;
|
||||
updateTextures("^[brighten");
|
||||
}
|
||||
else if(cmd == 2) // died or respawned
|
||||
{
|
||||
m_dead = readU8(is);
|
||||
updateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void updateTextures(const std::string &mod)
|
||||
{
|
||||
if(!m_node)
|
||||
return;
|
||||
ITextureSource *tsrc = m_gamedef->tsrc();
|
||||
scene::IMesh *mesh = m_node->getMesh();
|
||||
if(mesh){
|
||||
{
|
||||
std::string tname = "player.png";
|
||||
tname += mod;
|
||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
|
||||
buf->getMaterial().setTexture(0,
|
||||
tsrc->getTextureRaw(tname));
|
||||
}
|
||||
{
|
||||
std::string tname = "player_back.png";
|
||||
tname += mod;
|
||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
|
||||
buf->getMaterial().setTexture(0,
|
||||
tsrc->getTextureRaw(tname));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Prototype
|
||||
PlayerCAO proto_PlayerCAO(NULL, NULL);
|
||||
GenericCAO proto_GenericCAO(NULL, NULL);
|
||||
|
||||
|
||||
|
||||
@@ -210,8 +210,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
top_is_same_liquid = true;
|
||||
|
||||
u16 l = 0;
|
||||
// If this liquid emits light and doesn't contain light, draw
|
||||
// it at what it emits, for an increased effect
|
||||
u8 light_source = nodedef->get(n).light_source;
|
||||
if(light_source != 0){
|
||||
//l = decode_light(undiminish_light(light_source));
|
||||
l = decode_light(light_source);
|
||||
l = l | (l<<8);
|
||||
}
|
||||
// Use the light of the node on top if possible
|
||||
if(nodedef->get(ntop).param_type == CPT_LIGHT)
|
||||
else if(nodedef->get(ntop).param_type == CPT_LIGHT)
|
||||
l = getInteriorLight(ntop, 0, data);
|
||||
// Otherwise use the light of this node (the liquid)
|
||||
else
|
||||
@@ -727,12 +735,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
|
||||
TileSpec tile_nocrack = tile;
|
||||
tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
|
||||
|
||||
|
||||
// A hack to put wood the right way around in the posts
|
||||
ITextureSource *tsrc = data->m_gamedef->tsrc();
|
||||
TileSpec tile_rot = tile;
|
||||
tile_rot.texture = tsrc->getTexture(tsrc->getTextureName(
|
||||
tile.texture.id) + "^[transformR90");
|
||||
|
||||
u16 l = getInteriorLight(n, 1, data);
|
||||
video::SColor c = MapBlock_LightColor(255, l);
|
||||
|
||||
const f32 post_rad=(f32)BS/10;
|
||||
const f32 bar_rad=(f32)BS/20;
|
||||
const f32 post_rad=(f32)BS/8;
|
||||
const f32 bar_rad=(f32)BS/16;
|
||||
const f32 bar_len=(f32)(BS/2)-post_rad;
|
||||
|
||||
v3f pos = intToFloat(p, BS);
|
||||
@@ -742,13 +756,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
post.MinEdge += pos;
|
||||
post.MaxEdge += pos;
|
||||
f32 postuv[24]={
|
||||
0.4,0.4,0.6,0.6,
|
||||
0.4,0.4,0.6,0.6,
|
||||
0.35,0,0.65,1,
|
||||
0.35,0,0.65,1,
|
||||
0.35,0,0.65,1,
|
||||
0.35,0,0.65,1};
|
||||
makeCuboid(&collector, post, &tile, 1, c, postuv);
|
||||
6/16.,6/16.,10/16.,10/16.,
|
||||
6/16.,6/16.,10/16.,10/16.,
|
||||
0/16.,0,4/16.,1,
|
||||
4/16.,0,8/16.,1,
|
||||
8/16.,0,12/16.,1,
|
||||
12/16.,0,16/16.,1};
|
||||
makeCuboid(&collector, post, &tile_rot, 1, c, postuv);
|
||||
|
||||
// Now a section of fence, +X, if there's a post there
|
||||
v3s16 p2 = p;
|
||||
@@ -762,17 +776,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
bar.MinEdge += pos;
|
||||
bar.MaxEdge += pos;
|
||||
f32 xrailuv[24]={
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6};
|
||||
0/16.,2/16.,16/16.,4/16.,
|
||||
0/16.,4/16.,16/16.,6/16.,
|
||||
6/16.,6/16.,8/16.,8/16.,
|
||||
10/16.,10/16.,12/16.,12/16.,
|
||||
0/16.,8/16.,16/16.,10/16.,
|
||||
0/16.,14/16.,16/16.,16/16.};
|
||||
makeCuboid(&collector, bar, &tile_nocrack, 1,
|
||||
c, xrailuv);
|
||||
bar.MinEdge.Y -= BS/2;
|
||||
bar.MaxEdge.Y -= BS/2;
|
||||
// TODO: no crack
|
||||
makeCuboid(&collector, bar, &tile_nocrack, 1,
|
||||
c, xrailuv);
|
||||
}
|
||||
@@ -789,13 +802,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
bar.MinEdge += pos;
|
||||
bar.MaxEdge += pos;
|
||||
f32 zrailuv[24]={
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6,
|
||||
0,0.4,1,0.6};
|
||||
|
||||
3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
|
||||
4/16.,1/16.,6/16.,5/16., // for wood texture instead
|
||||
0/16.,9/16.,16/16.,11/16.,
|
||||
0/16.,6/16.,16/16.,8/16.,
|
||||
6/16.,6/16.,8/16.,8/16.,
|
||||
10/16.,10/16.,12/16.,12/16.};
|
||||
makeCuboid(&collector, bar, &tile_nocrack, 1,
|
||||
c, zrailuv);
|
||||
bar.MinEdge.Y -= BS/2;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2010-2012 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 General Public License as published by
|
||||
@@ -18,15 +18,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "content_nodemeta.h"
|
||||
|
||||
#include <map>
|
||||
#include "inventory.h"
|
||||
#include "log.h"
|
||||
#include "utility.h"
|
||||
#include "craftdef.h"
|
||||
#include "gamedef.h"
|
||||
|
||||
class Inventory;
|
||||
#include <sstream>
|
||||
|
||||
#define NODEMETA_GENERIC 1
|
||||
#define NODEMETA_SIGN 14
|
||||
@@ -34,809 +29,167 @@ class Inventory;
|
||||
#define NODEMETA_FURNACE 16
|
||||
#define NODEMETA_LOCKABLE_CHEST 17
|
||||
|
||||
core::map<u16, NodeMetadata::Factory> NodeMetadata::m_types;
|
||||
core::map<std::string, NodeMetadata::Factory2> NodeMetadata::m_names;
|
||||
// Returns true if node timer must be set
|
||||
static bool content_nodemeta_deserialize_legacy_body(
|
||||
std::istream &is, s16 id, NodeMetadata *meta)
|
||||
{
|
||||
meta->clear();
|
||||
|
||||
class SignNodeMetadata : public NodeMetadata
|
||||
{
|
||||
public:
|
||||
SignNodeMetadata(IGameDef *gamedef, std::string text);
|
||||
//~SignNodeMetadata();
|
||||
|
||||
virtual u16 typeId() const;
|
||||
virtual const char* typeName() const
|
||||
{ return "sign"; }
|
||||
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
|
||||
static NodeMetadata* create(IGameDef *gamedef);
|
||||
virtual NodeMetadata* clone(IGameDef *gamedef);
|
||||
virtual void serializeBody(std::ostream &os);
|
||||
virtual std::string infoText();
|
||||
|
||||
virtual bool allowsTextInput(){ return true; }
|
||||
virtual std::string getText(){ return m_text; }
|
||||
virtual void setText(const std::string &t){ m_text = t; }
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
};
|
||||
|
||||
class ChestNodeMetadata : public NodeMetadata
|
||||
{
|
||||
public:
|
||||
ChestNodeMetadata(IGameDef *gamedef);
|
||||
~ChestNodeMetadata();
|
||||
|
||||
virtual u16 typeId() const;
|
||||
virtual const char* typeName() const
|
||||
{ return "chest"; }
|
||||
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
|
||||
static NodeMetadata* create(IGameDef *gamedef);
|
||||
virtual NodeMetadata* clone(IGameDef *gamedef);
|
||||
virtual void serializeBody(std::ostream &os);
|
||||
virtual std::string infoText();
|
||||
virtual Inventory* getInventory() {return m_inventory;}
|
||||
virtual bool nodeRemovalDisabled();
|
||||
virtual std::string getInventoryDrawSpecString();
|
||||
|
||||
private:
|
||||
Inventory *m_inventory;
|
||||
};
|
||||
|
||||
class LockingChestNodeMetadata : public NodeMetadata
|
||||
{
|
||||
public:
|
||||
LockingChestNodeMetadata(IGameDef *gamedef);
|
||||
~LockingChestNodeMetadata();
|
||||
|
||||
virtual u16 typeId() const;
|
||||
virtual const char* typeName() const
|
||||
{ return "locked_chest"; }
|
||||
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
|
||||
static NodeMetadata* create(IGameDef *gamedef);
|
||||
virtual NodeMetadata* clone(IGameDef *gamedef);
|
||||
virtual void serializeBody(std::ostream &os);
|
||||
virtual std::string infoText();
|
||||
virtual Inventory* getInventory() {return m_inventory;}
|
||||
virtual bool nodeRemovalDisabled();
|
||||
virtual std::string getInventoryDrawSpecString();
|
||||
|
||||
virtual std::string getOwner(){ return m_text; }
|
||||
virtual void setOwner(std::string t){ m_text = t; }
|
||||
|
||||
private:
|
||||
Inventory *m_inventory;
|
||||
std::string m_text;
|
||||
};
|
||||
|
||||
class FurnaceNodeMetadata : public NodeMetadata
|
||||
{
|
||||
public:
|
||||
FurnaceNodeMetadata(IGameDef *gamedef);
|
||||
~FurnaceNodeMetadata();
|
||||
|
||||
virtual u16 typeId() const;
|
||||
virtual const char* typeName() const
|
||||
{ return "furnace"; }
|
||||
virtual NodeMetadata* clone(IGameDef *gamedef);
|
||||
static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
|
||||
static NodeMetadata* create(IGameDef *gamedef);
|
||||
virtual void serializeBody(std::ostream &os);
|
||||
virtual std::string infoText();
|
||||
virtual Inventory* getInventory() {return m_inventory;}
|
||||
virtual void inventoryModified();
|
||||
virtual bool step(float dtime);
|
||||
virtual bool nodeRemovalDisabled();
|
||||
virtual std::string getInventoryDrawSpecString();
|
||||
|
||||
protected:
|
||||
bool getCookResult(bool remove, std::string &cookresult, float &cooktime);
|
||||
bool getBurnResult(bool remove, float &burntime);
|
||||
|
||||
private:
|
||||
Inventory *m_inventory;
|
||||
std::string m_infotext;
|
||||
float m_step_accumulator;
|
||||
float m_fuel_totaltime;
|
||||
float m_fuel_time;
|
||||
float m_src_totaltime;
|
||||
float m_src_time;
|
||||
};
|
||||
|
||||
/*
|
||||
SignNodeMetadata
|
||||
*/
|
||||
|
||||
// Prototype
|
||||
SignNodeMetadata proto_SignNodeMetadata(NULL, "");
|
||||
|
||||
SignNodeMetadata::SignNodeMetadata(IGameDef *gamedef, std::string text):
|
||||
NodeMetadata(gamedef),
|
||||
m_text(text)
|
||||
{
|
||||
NodeMetadata::registerType(typeId(), typeName(), create, create);
|
||||
}
|
||||
u16 SignNodeMetadata::typeId() const
|
||||
{
|
||||
return NODEMETA_SIGN;
|
||||
}
|
||||
NodeMetadata* SignNodeMetadata::create(std::istream &is, IGameDef *gamedef)
|
||||
{
|
||||
std::string text = deSerializeString(is);
|
||||
return new SignNodeMetadata(gamedef, text);
|
||||
}
|
||||
NodeMetadata* SignNodeMetadata::create(IGameDef *gamedef)
|
||||
{
|
||||
return new SignNodeMetadata(gamedef, "");
|
||||
}
|
||||
NodeMetadata* SignNodeMetadata::clone(IGameDef *gamedef)
|
||||
{
|
||||
return new SignNodeMetadata(gamedef, m_text);
|
||||
}
|
||||
void SignNodeMetadata::serializeBody(std::ostream &os)
|
||||
{
|
||||
os<<serializeString(m_text);
|
||||
}
|
||||
std::string SignNodeMetadata::infoText()
|
||||
{
|
||||
return std::string("\"")+m_text+"\"";
|
||||
}
|
||||
|
||||
/*
|
||||
ChestNodeMetadata
|
||||
*/
|
||||
|
||||
// Prototype
|
||||
ChestNodeMetadata proto_ChestNodeMetadata(NULL);
|
||||
|
||||
ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef):
|
||||
NodeMetadata(gamedef)
|
||||
{
|
||||
NodeMetadata::registerType(typeId(), typeName(), create, create);
|
||||
m_inventory = NULL;
|
||||
}
|
||||
ChestNodeMetadata::~ChestNodeMetadata()
|
||||
{
|
||||
delete m_inventory;
|
||||
}
|
||||
u16 ChestNodeMetadata::typeId() const
|
||||
{
|
||||
return NODEMETA_CHEST;
|
||||
}
|
||||
NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
|
||||
{
|
||||
ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->deSerialize(is);
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef)
|
||||
{
|
||||
ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->addList("0", 8*4);
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef)
|
||||
{
|
||||
ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(*m_inventory);
|
||||
return d;
|
||||
}
|
||||
void ChestNodeMetadata::serializeBody(std::ostream &os)
|
||||
{
|
||||
m_inventory->serialize(os);
|
||||
}
|
||||
std::string ChestNodeMetadata::infoText()
|
||||
{
|
||||
return "Chest";
|
||||
}
|
||||
bool ChestNodeMetadata::nodeRemovalDisabled()
|
||||
{
|
||||
/*
|
||||
Disable removal if chest contains something
|
||||
*/
|
||||
InventoryList *list = m_inventory->getList("0");
|
||||
if(list == NULL)
|
||||
return false;
|
||||
if(list->getUsedSlots() == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
std::string ChestNodeMetadata::getInventoryDrawSpecString()
|
||||
{
|
||||
return
|
||||
"invsize[8,9;]"
|
||||
"list[current_name;0;0,0;8,4;]"
|
||||
"list[current_player;main;0,5;8,4;]";
|
||||
}
|
||||
|
||||
/*
|
||||
LockingChestNodeMetadata
|
||||
*/
|
||||
|
||||
// Prototype
|
||||
LockingChestNodeMetadata proto_LockingChestNodeMetadata(NULL);
|
||||
|
||||
LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef):
|
||||
NodeMetadata(gamedef)
|
||||
{
|
||||
NodeMetadata::registerType(typeId(), typeName(), create, create);
|
||||
m_inventory = NULL;
|
||||
}
|
||||
LockingChestNodeMetadata::~LockingChestNodeMetadata()
|
||||
{
|
||||
delete m_inventory;
|
||||
}
|
||||
u16 LockingChestNodeMetadata::typeId() const
|
||||
{
|
||||
return NODEMETA_LOCKABLE_CHEST;
|
||||
}
|
||||
NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
|
||||
{
|
||||
LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
|
||||
d->setOwner(deSerializeString(is));
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->deSerialize(is);
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef)
|
||||
{
|
||||
LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->addList("0", 8*4);
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef)
|
||||
{
|
||||
LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(*m_inventory);
|
||||
return d;
|
||||
}
|
||||
void LockingChestNodeMetadata::serializeBody(std::ostream &os)
|
||||
{
|
||||
os<<serializeString(m_text);
|
||||
m_inventory->serialize(os);
|
||||
}
|
||||
std::string LockingChestNodeMetadata::infoText()
|
||||
{
|
||||
return "Locking Chest";
|
||||
}
|
||||
bool LockingChestNodeMetadata::nodeRemovalDisabled()
|
||||
{
|
||||
/*
|
||||
Disable removal if chest contains something
|
||||
*/
|
||||
InventoryList *list = m_inventory->getList("0");
|
||||
if(list == NULL)
|
||||
return false;
|
||||
if(list->getUsedSlots() == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
std::string LockingChestNodeMetadata::getInventoryDrawSpecString()
|
||||
{
|
||||
return
|
||||
"invsize[8,9;]"
|
||||
"list[current_name;0;0,0;8,4;]"
|
||||
"list[current_player;main;0,5;8,4;]";
|
||||
}
|
||||
|
||||
/*
|
||||
FurnaceNodeMetadata
|
||||
*/
|
||||
|
||||
// Prototype
|
||||
FurnaceNodeMetadata proto_FurnaceNodeMetadata(NULL);
|
||||
|
||||
FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef):
|
||||
NodeMetadata(gamedef)
|
||||
{
|
||||
NodeMetadata::registerType(typeId(), typeName(), create, create);
|
||||
|
||||
m_inventory = NULL;
|
||||
|
||||
m_infotext = "Furnace is inactive";
|
||||
|
||||
m_step_accumulator = 0;
|
||||
m_fuel_totaltime = 0;
|
||||
m_fuel_time = 0;
|
||||
m_src_totaltime = 0;
|
||||
m_src_time = 0;
|
||||
}
|
||||
FurnaceNodeMetadata::~FurnaceNodeMetadata()
|
||||
{
|
||||
delete m_inventory;
|
||||
}
|
||||
u16 FurnaceNodeMetadata::typeId() const
|
||||
{
|
||||
return NODEMETA_FURNACE;
|
||||
}
|
||||
NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef)
|
||||
{
|
||||
FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef);
|
||||
d->m_inventory = new Inventory(*m_inventory);
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef)
|
||||
{
|
||||
FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
|
||||
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->deSerialize(is);
|
||||
|
||||
int temp = 0;
|
||||
is>>temp;
|
||||
d->m_fuel_totaltime = (float)temp/10;
|
||||
temp = 0;
|
||||
is>>temp;
|
||||
d->m_fuel_time = (float)temp/10;
|
||||
temp = 0;
|
||||
is>>temp;
|
||||
d->m_src_totaltime = (float)temp/10;
|
||||
temp = 0;
|
||||
is>>temp;
|
||||
d->m_src_time = (float)temp/10;
|
||||
|
||||
if(is.eof())
|
||||
if(id == NODEMETA_GENERIC) // GenericNodeMetadata (0.4-dev)
|
||||
{
|
||||
// Old furnaces didn't serialize src_totaltime and src_time
|
||||
d->m_src_totaltime = 0;
|
||||
d->m_src_time = 0;
|
||||
d->m_infotext = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
// New furnaces also serialize the infotext (so that the
|
||||
// client doesn't need to have the list of cooking recipes).
|
||||
d->m_infotext = deSerializeJsonString(is);
|
||||
}
|
||||
meta->getInventory()->deSerialize(is);
|
||||
deSerializeLongString(is); // m_text
|
||||
deSerializeString(is); // m_owner
|
||||
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef)
|
||||
{
|
||||
FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->addList("fuel", 1);
|
||||
d->m_inventory->addList("src", 1);
|
||||
d->m_inventory->addList("dst", 4);
|
||||
return d;
|
||||
}
|
||||
void FurnaceNodeMetadata::serializeBody(std::ostream &os)
|
||||
{
|
||||
m_inventory->serialize(os);
|
||||
os<<itos(m_fuel_totaltime*10)<<" ";
|
||||
os<<itos(m_fuel_time*10)<<" ";
|
||||
os<<itos(m_src_totaltime*10)<<" ";
|
||||
os<<itos(m_src_time*10)<<" ";
|
||||
os<<serializeJsonString(m_infotext);
|
||||
}
|
||||
std::string FurnaceNodeMetadata::infoText()
|
||||
{
|
||||
return m_infotext;
|
||||
}
|
||||
bool FurnaceNodeMetadata::nodeRemovalDisabled()
|
||||
{
|
||||
/*
|
||||
Disable removal if furnace is not empty
|
||||
*/
|
||||
InventoryList *list[3] = {m_inventory->getList("src"),
|
||||
m_inventory->getList("dst"), m_inventory->getList("fuel")};
|
||||
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(list[i] == NULL)
|
||||
continue;
|
||||
if(list[i]->getUsedSlots() == 0)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
void FurnaceNodeMetadata::inventoryModified()
|
||||
{
|
||||
infostream<<"Furnace inventory modification callback"<<std::endl;
|
||||
}
|
||||
bool FurnaceNodeMetadata::step(float dtime)
|
||||
{
|
||||
if(dtime > 60.0)
|
||||
infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
|
||||
|
||||
InventoryList *dst_list = m_inventory->getList("dst");
|
||||
assert(dst_list);
|
||||
|
||||
// Update at a fixed frequency
|
||||
const float interval = 2.0;
|
||||
m_step_accumulator += dtime;
|
||||
bool changed = false;
|
||||
while(m_step_accumulator > interval)
|
||||
{
|
||||
m_step_accumulator -= interval;
|
||||
dtime = interval;
|
||||
|
||||
//infostream<<"Furnace step dtime="<<dtime<<std::endl;
|
||||
|
||||
bool changed_this_loop = false;
|
||||
|
||||
// Check
|
||||
// 1. if the source item is cookable
|
||||
// 2. if there is room for the cooked item
|
||||
std::string cookresult;
|
||||
float cooktime;
|
||||
bool cookable = getCookResult(false, cookresult, cooktime);
|
||||
ItemStack cookresult_item;
|
||||
bool room_available = false;
|
||||
if(cookable)
|
||||
{
|
||||
cookresult_item.deSerialize(cookresult, m_gamedef->idef());
|
||||
room_available = dst_list->roomForItem(cookresult_item);
|
||||
}
|
||||
|
||||
// Step fuel time
|
||||
bool burning = (m_fuel_time < m_fuel_totaltime);
|
||||
if(burning)
|
||||
{
|
||||
changed_this_loop = true;
|
||||
m_fuel_time += dtime;
|
||||
}
|
||||
|
||||
std::string infotext;
|
||||
if(room_available)
|
||||
{
|
||||
float burntime;
|
||||
if(burning)
|
||||
{
|
||||
changed_this_loop = true;
|
||||
m_src_time += dtime;
|
||||
m_src_totaltime = cooktime;
|
||||
infotext = "Furnace is cooking";
|
||||
}
|
||||
else if(getBurnResult(true, burntime))
|
||||
{
|
||||
// Fuel inserted
|
||||
changed_this_loop = true;
|
||||
m_fuel_time = 0;
|
||||
m_fuel_totaltime = burntime;
|
||||
//m_src_time += dtime;
|
||||
//m_src_totaltime = cooktime;
|
||||
infotext = "Furnace is cooking";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_src_time = 0;
|
||||
m_src_totaltime = 0;
|
||||
infotext = "Furnace is out of fuel";
|
||||
}
|
||||
if(m_src_totaltime > 0.001 && m_src_time >= m_src_totaltime)
|
||||
{
|
||||
// One item fully cooked
|
||||
changed_this_loop = true;
|
||||
dst_list->addItem(cookresult_item);
|
||||
getCookResult(true, cookresult, cooktime); // decrement source
|
||||
m_src_totaltime = 0;
|
||||
m_src_time = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not cookable or no room available
|
||||
m_src_totaltime = 0;
|
||||
m_src_time = 0;
|
||||
if(cookable)
|
||||
infotext = "Furnace is overloaded";
|
||||
else if(burning)
|
||||
infotext = "Furnace is active";
|
||||
else
|
||||
{
|
||||
infotext = "Furnace is inactive";
|
||||
m_fuel_totaltime = 0;
|
||||
m_fuel_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Do this so it doesn't always show (0%) for weak fuel
|
||||
if(m_fuel_totaltime > 3) {
|
||||
infotext += " (";
|
||||
infotext += itos(m_fuel_time/m_fuel_totaltime*100);
|
||||
infotext += "%)";
|
||||
}
|
||||
|
||||
if(infotext != m_infotext)
|
||||
{
|
||||
m_infotext = infotext;
|
||||
changed_this_loop = true;
|
||||
}
|
||||
|
||||
if(burning && m_fuel_time >= m_fuel_totaltime)
|
||||
{
|
||||
m_fuel_time = 0;
|
||||
m_fuel_totaltime = 0;
|
||||
}
|
||||
|
||||
if(changed_this_loop)
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_step_accumulator = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
|
||||
{
|
||||
return
|
||||
"invsize[8,9;]"
|
||||
"list[current_name;fuel;2,3;1,1;]"
|
||||
"list[current_name;src;2,1;1,1;]"
|
||||
"list[current_name;dst;5,1;2,2;]"
|
||||
"list[current_player;main;0,5;8,4;]";
|
||||
}
|
||||
bool FurnaceNodeMetadata::getCookResult(bool remove,
|
||||
std::string &cookresult, float &cooktime)
|
||||
{
|
||||
std::vector<ItemStack> items;
|
||||
InventoryList *src_list = m_inventory->getList("src");
|
||||
assert(src_list);
|
||||
items.push_back(src_list->getItem(0));
|
||||
|
||||
CraftInput ci(CRAFT_METHOD_COOKING, 1, items);
|
||||
CraftOutput co;
|
||||
bool found = m_gamedef->getCraftDefManager()->getCraftResult(
|
||||
ci, co, remove, m_gamedef);
|
||||
if(remove)
|
||||
src_list->changeItem(0, ci.items[0]);
|
||||
|
||||
cookresult = co.item;
|
||||
cooktime = co.time;
|
||||
return found;
|
||||
}
|
||||
bool FurnaceNodeMetadata::getBurnResult(bool remove, float &burntime)
|
||||
{
|
||||
std::vector<ItemStack> items;
|
||||
InventoryList *fuel_list = m_inventory->getList("fuel");
|
||||
assert(fuel_list);
|
||||
items.push_back(fuel_list->getItem(0));
|
||||
|
||||
CraftInput ci(CRAFT_METHOD_FUEL, 1, items);
|
||||
CraftOutput co;
|
||||
bool found = m_gamedef->getCraftDefManager()->getCraftResult(
|
||||
ci, co, remove, m_gamedef);
|
||||
if(remove)
|
||||
fuel_list->changeItem(0, ci.items[0]);
|
||||
|
||||
burntime = co.time;
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
GenericNodeMetadata
|
||||
*/
|
||||
|
||||
class GenericNodeMetadata : public NodeMetadata
|
||||
{
|
||||
private:
|
||||
Inventory *m_inventory;
|
||||
std::string m_text;
|
||||
std::string m_owner;
|
||||
|
||||
std::string m_infotext;
|
||||
std::string m_inventorydrawspec;
|
||||
bool m_allow_text_input;
|
||||
bool m_removal_disabled;
|
||||
bool m_enforce_owner;
|
||||
|
||||
bool m_inventory_modified;
|
||||
bool m_text_modified;
|
||||
|
||||
std::map<std::string, std::string> m_stringvars;
|
||||
|
||||
public:
|
||||
u16 typeId() const
|
||||
{
|
||||
return NODEMETA_GENERIC;
|
||||
}
|
||||
const char* typeName() const
|
||||
{
|
||||
return "generic";
|
||||
}
|
||||
|
||||
GenericNodeMetadata(IGameDef *gamedef):
|
||||
NodeMetadata(gamedef),
|
||||
|
||||
m_inventory(NULL),
|
||||
m_text(""),
|
||||
m_owner(""),
|
||||
|
||||
m_infotext("GenericNodeMetadata"),
|
||||
m_inventorydrawspec(""),
|
||||
m_allow_text_input(false),
|
||||
m_removal_disabled(false),
|
||||
m_enforce_owner(false),
|
||||
|
||||
m_inventory_modified(false),
|
||||
m_text_modified(false)
|
||||
{
|
||||
NodeMetadata::registerType(typeId(), typeName(), create, create);
|
||||
}
|
||||
virtual ~GenericNodeMetadata()
|
||||
{
|
||||
delete m_inventory;
|
||||
}
|
||||
NodeMetadata* clone(IGameDef *gamedef)
|
||||
{
|
||||
GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef);
|
||||
|
||||
d->m_inventory = new Inventory(*m_inventory);
|
||||
d->m_text = m_text;
|
||||
d->m_owner = m_owner;
|
||||
|
||||
d->m_infotext = m_infotext;
|
||||
d->m_inventorydrawspec = m_inventorydrawspec;
|
||||
d->m_allow_text_input = m_allow_text_input;
|
||||
d->m_removal_disabled = m_removal_disabled;
|
||||
d->m_enforce_owner = m_enforce_owner;
|
||||
d->m_inventory_modified = m_inventory_modified;
|
||||
d->m_text_modified = m_text_modified;
|
||||
return d;
|
||||
}
|
||||
static NodeMetadata* create(IGameDef *gamedef)
|
||||
{
|
||||
GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
return d;
|
||||
}
|
||||
static NodeMetadata* create(std::istream &is, IGameDef *gamedef)
|
||||
{
|
||||
GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
|
||||
|
||||
d->m_inventory = new Inventory(gamedef->idef());
|
||||
d->m_inventory->deSerialize(is);
|
||||
d->m_text = deSerializeLongString(is);
|
||||
d->m_owner = deSerializeString(is);
|
||||
|
||||
d->m_infotext = deSerializeString(is);
|
||||
d->m_inventorydrawspec = deSerializeString(is);
|
||||
d->m_allow_text_input = readU8(is);
|
||||
d->m_removal_disabled = readU8(is);
|
||||
d->m_enforce_owner = readU8(is);
|
||||
meta->setString("infotext",deSerializeString(is));
|
||||
meta->setString("formspec",deSerializeString(is));
|
||||
readU8(is); // m_allow_text_input
|
||||
readU8(is); // m_allow_removal
|
||||
readU8(is); // m_enforce_owner
|
||||
|
||||
int num_vars = readU32(is);
|
||||
for(int i=0; i<num_vars; i++){
|
||||
std::string name = deSerializeString(is);
|
||||
std::string var = deSerializeLongString(is);
|
||||
d->m_stringvars[name] = var;
|
||||
meta->setString(name, var);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
void serializeBody(std::ostream &os)
|
||||
{
|
||||
m_inventory->serialize(os);
|
||||
os<<serializeLongString(m_text);
|
||||
os<<serializeString(m_owner);
|
||||
|
||||
os<<serializeString(m_infotext);
|
||||
os<<serializeString(m_inventorydrawspec);
|
||||
writeU8(os, m_allow_text_input);
|
||||
writeU8(os, m_removal_disabled);
|
||||
writeU8(os, m_enforce_owner);
|
||||
|
||||
int num_vars = m_stringvars.size();
|
||||
writeU32(os, num_vars);
|
||||
for(std::map<std::string, std::string>::iterator
|
||||
i = m_stringvars.begin(); i != m_stringvars.end(); i++){
|
||||
os<<serializeString(i->first);
|
||||
os<<serializeLongString(i->second);
|
||||
}
|
||||
}
|
||||
|
||||
std::string infoText()
|
||||
{
|
||||
return m_infotext;
|
||||
}
|
||||
Inventory* getInventory()
|
||||
{
|
||||
return m_inventory;
|
||||
}
|
||||
void inventoryModified()
|
||||
{
|
||||
m_inventory_modified = true;
|
||||
}
|
||||
bool step(float dtime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool nodeRemovalDisabled()
|
||||
else if(id == NODEMETA_SIGN) // SignNodeMetadata
|
||||
{
|
||||
return m_removal_disabled;
|
||||
meta->setString("text", deSerializeLongString(is));
|
||||
//meta->setString("infotext","\"${text}\"");
|
||||
meta->setString("infotext",
|
||||
std::string("\"") + meta->getString("text") + "\"");
|
||||
meta->setString("formspec","field[text;;${text}]");
|
||||
return false;
|
||||
}
|
||||
std::string getInventoryDrawSpecString()
|
||||
else if(id == NODEMETA_CHEST) // ChestNodeMetadata
|
||||
{
|
||||
return m_inventorydrawspec;
|
||||
}
|
||||
bool allowsTextInput()
|
||||
{
|
||||
return m_allow_text_input;
|
||||
}
|
||||
std::string getText()
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
void setText(const std::string &t)
|
||||
{
|
||||
m_text = t;
|
||||
m_text_modified = true;
|
||||
}
|
||||
std::string getOwner()
|
||||
{
|
||||
if(m_enforce_owner)
|
||||
return m_owner;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
void setOwner(std::string t)
|
||||
{
|
||||
m_owner = t;
|
||||
}
|
||||
|
||||
/* Interface for GenericNodeMetadata */
|
||||
meta->getInventory()->deSerialize(is);
|
||||
|
||||
void setInfoText(const std::string &text)
|
||||
{
|
||||
infostream<<"GenericNodeMetadata::setInfoText(\""
|
||||
<<text<<"\")"<<std::endl;
|
||||
m_infotext = text;
|
||||
// Rename inventory list "0" to "main"
|
||||
Inventory *inv = meta->getInventory();
|
||||
if(!inv->getList("main") && inv->getList("0")){
|
||||
inv->getList("0")->setName("main");
|
||||
}
|
||||
assert(inv->getList("main") && !inv->getList("0"));
|
||||
|
||||
meta->setString("formspec","invsize[8,9;]"
|
||||
"list[current_name;main;0,0;8,4;]"
|
||||
"list[current_player;main;0,5;8,4;]");
|
||||
return false;
|
||||
}
|
||||
void setInventoryDrawSpec(const std::string &text)
|
||||
else if(id == NODEMETA_LOCKABLE_CHEST) // LockingChestNodeMetadata
|
||||
{
|
||||
m_inventorydrawspec = text;
|
||||
}
|
||||
void setAllowTextInput(bool b)
|
||||
{
|
||||
m_allow_text_input = b;
|
||||
}
|
||||
void setRemovalDisabled(bool b)
|
||||
{
|
||||
m_removal_disabled = b;
|
||||
}
|
||||
void setEnforceOwner(bool b)
|
||||
{
|
||||
m_enforce_owner = b;
|
||||
}
|
||||
bool isInventoryModified()
|
||||
{
|
||||
return m_inventory_modified;
|
||||
}
|
||||
void resetInventoryModified()
|
||||
{
|
||||
m_inventory_modified = false;
|
||||
}
|
||||
bool isTextModified()
|
||||
{
|
||||
return m_text_modified;
|
||||
}
|
||||
void resetTextModified()
|
||||
{
|
||||
m_text_modified = false;
|
||||
}
|
||||
void setString(const std::string &name, const std::string &var)
|
||||
{
|
||||
m_stringvars[name] = var;
|
||||
}
|
||||
std::string getString(const std::string &name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator i;
|
||||
i = m_stringvars.find(name);
|
||||
if(i == m_stringvars.end())
|
||||
return "";
|
||||
return i->second;
|
||||
}
|
||||
};
|
||||
meta->setString("owner", deSerializeString(is));
|
||||
meta->getInventory()->deSerialize(is);
|
||||
|
||||
// Prototype
|
||||
GenericNodeMetadata proto_GenericNodeMetadata(NULL);
|
||||
// Rename inventory list "0" to "main"
|
||||
Inventory *inv = meta->getInventory();
|
||||
if(!inv->getList("main") && inv->getList("0")){
|
||||
inv->getList("0")->setName("main");
|
||||
}
|
||||
assert(inv->getList("main") && !inv->getList("0"));
|
||||
|
||||
meta->setString("formspec","invsize[8,9;]"
|
||||
"list[current_name;main;0,0;8,4;]"
|
||||
"list[current_player;main;0,5;8,4;]");
|
||||
return false;
|
||||
}
|
||||
else if(id == NODEMETA_FURNACE) // FurnaceNodeMetadata
|
||||
{
|
||||
meta->getInventory()->deSerialize(is);
|
||||
int temp = 0;
|
||||
is>>temp;
|
||||
meta->setString("fuel_totaltime", ftos((float)temp/10));
|
||||
temp = 0;
|
||||
is>>temp;
|
||||
meta->setString("fuel_time", ftos((float)temp/10));
|
||||
temp = 0;
|
||||
is>>temp;
|
||||
//meta->setString("src_totaltime", ftos((float)temp/10));
|
||||
temp = 0;
|
||||
is>>temp;
|
||||
meta->setString("src_time", ftos((float)temp/10));
|
||||
|
||||
meta->setString("formspec","invsize[8,9;]"
|
||||
"list[current_name;fuel;2,3;1,1;]"
|
||||
"list[current_name;src;2,1;1,1;]"
|
||||
"list[current_name;dst;5,1;2,2;]"
|
||||
"list[current_player;main;0,5;8,4;]");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw SerializationError("Unknown legacy node metadata");
|
||||
}
|
||||
}
|
||||
|
||||
static bool content_nodemeta_deserialize_legacy_meta(
|
||||
std::istream &is, NodeMetadata *meta)
|
||||
{
|
||||
// Read id
|
||||
s16 id = readS16(is);
|
||||
|
||||
// Read data
|
||||
std::string data = deSerializeString(is);
|
||||
std::istringstream tmp_is(data, std::ios::binary);
|
||||
return content_nodemeta_deserialize_legacy_body(tmp_is, id, meta);
|
||||
}
|
||||
|
||||
void content_nodemeta_deserialize_legacy(std::istream &is,
|
||||
NodeMetadataList *meta, NodeTimerList *timers,
|
||||
IGameDef *gamedef)
|
||||
{
|
||||
meta->clear();
|
||||
timers->clear();
|
||||
|
||||
u16 version = readU16(is);
|
||||
|
||||
if(version > 1)
|
||||
{
|
||||
infostream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
|
||||
<<std::endl;
|
||||
throw SerializationError(__FUNCTION_NAME);
|
||||
}
|
||||
|
||||
u16 count = readU16(is);
|
||||
|
||||
for(u16 i=0; i<count; i++)
|
||||
{
|
||||
u16 p16 = readU16(is);
|
||||
|
||||
v3s16 p(0,0,0);
|
||||
p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
|
||||
p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
|
||||
p.Y += p16 / MAP_BLOCKSIZE;
|
||||
p16 -= p.Y * MAP_BLOCKSIZE;
|
||||
p.X += p16;
|
||||
|
||||
if(meta->get(p) != NULL)
|
||||
{
|
||||
infostream<<"WARNING: "<<__FUNCTION_NAME<<": "
|
||||
<<"already set data at position"
|
||||
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
|
||||
<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
NodeMetadata *data = new NodeMetadata(gamedef);
|
||||
bool need_timer = content_nodemeta_deserialize_legacy_meta(is, data);
|
||||
meta->set(p, data);
|
||||
|
||||
if(need_timer)
|
||||
timers->set(p, NodeTimer(1., 0.));
|
||||
}
|
||||
}
|
||||
|
||||
void content_nodemeta_serialize_legacy(std::ostream &os, NodeMetadataList *meta)
|
||||
{
|
||||
// Sorry, I was too lazy to implement this. --kahrl
|
||||
writeU16(os, 1); // version
|
||||
writeU16(os, 0); // count
|
||||
}
|
||||
|
||||
// END
|
||||
|
||||
@@ -21,6 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#define CONTENT_NODEMETA_HEADER
|
||||
|
||||
#include "nodemetadata.h"
|
||||
#include "nodetimer.h"
|
||||
|
||||
/*
|
||||
Legacy nodemeta definitions
|
||||
*/
|
||||
|
||||
void content_nodemeta_deserialize_legacy(std::istream &is,
|
||||
NodeMetadataList *meta, NodeTimerList *timers,
|
||||
IGameDef *gamedef);
|
||||
|
||||
void content_nodemeta_serialize_legacy(std::ostream &os, NodeMetadataList *meta);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -32,5 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// Special type, not stored as a static object
|
||||
#define ACTIVEOBJECT_TYPE_PLAYER 100
|
||||
|
||||
// Special type, only exists as CAO
|
||||
#define ACTIVEOBJECT_TYPE_GENERIC 101
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "serialization.h" // For compressZlib
|
||||
#include "tool.h" // For ToolCapabilities
|
||||
#include "gamedef.h"
|
||||
#include "player.h"
|
||||
#include "scriptapi.h"
|
||||
#include "genericobject.h"
|
||||
|
||||
core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
|
||||
|
||||
@@ -333,9 +336,6 @@ ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
|
||||
LuaEntitySAO
|
||||
*/
|
||||
|
||||
#include "scriptapi.h"
|
||||
#include "luaentity_common.h"
|
||||
|
||||
// Prototype (registers item for deserialization)
|
||||
LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
|
||||
|
||||
@@ -345,11 +345,11 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
|
||||
m_init_name(name),
|
||||
m_init_state(state),
|
||||
m_registered(false),
|
||||
m_prop(new LuaEntityProperties),
|
||||
m_hp(-1),
|
||||
m_velocity(0,0,0),
|
||||
m_acceleration(0,0,0),
|
||||
m_yaw(0),
|
||||
m_properties_sent(true),
|
||||
m_last_sent_yaw(0),
|
||||
m_last_sent_position(0,0,0),
|
||||
m_last_sent_velocity(0,0,0),
|
||||
@@ -374,7 +374,6 @@ LuaEntitySAO::~LuaEntitySAO()
|
||||
lua_State *L = m_env->getLua();
|
||||
scriptapi_luaentity_rm(L, m_id);
|
||||
}
|
||||
delete m_prop;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::addedToEnvironment()
|
||||
@@ -387,13 +386,12 @@ void LuaEntitySAO::addedToEnvironment()
|
||||
|
||||
if(m_registered){
|
||||
// Get properties
|
||||
scriptapi_luaentity_get_properties(L, m_id, m_prop);
|
||||
scriptapi_luaentity_get_properties(L, m_id, &m_prop);
|
||||
// Initialize HP from properties
|
||||
m_hp = m_prop->hp_max;
|
||||
m_hp = m_prop.hp_max;
|
||||
// Activate entity, supplying serialized state
|
||||
scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
|
||||
}
|
||||
|
||||
// Activate entity, supplying serialized state
|
||||
scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
|
||||
}
|
||||
|
||||
ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
|
||||
@@ -433,10 +431,19 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
|
||||
|
||||
void LuaEntitySAO::step(float dtime, bool send_recommended)
|
||||
{
|
||||
if(!m_properties_sent)
|
||||
{
|
||||
m_properties_sent = true;
|
||||
std::string str = getPropertyPacket();
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
m_last_sent_position_timer += dtime;
|
||||
|
||||
if(m_prop->physical){
|
||||
core::aabbox3d<f32> box = m_prop->collisionbox;
|
||||
if(m_prop.physical){
|
||||
core::aabbox3d<f32> box = m_prop.collisionbox;
|
||||
box.MinEdge *= BS;
|
||||
box.MaxEdge *= BS;
|
||||
collisionMoveResult moveresult;
|
||||
@@ -482,16 +489,10 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
|
||||
|
||||
if(m_armor_groups_sent == false){
|
||||
m_armor_groups_sent = true;
|
||||
std::ostringstream os(std::ios::binary);
|
||||
writeU8(os, LUAENTITY_CMD_UPDATE_ARMOR_GROUPS);
|
||||
writeU16(os, m_armor_groups.size());
|
||||
for(ItemGroupList::const_iterator i = m_armor_groups.begin();
|
||||
i != m_armor_groups.end(); i++){
|
||||
os<<serializeString(i->first);
|
||||
writeS16(os, i->second);
|
||||
}
|
||||
std::string str = gob_cmd_update_armor_groups(
|
||||
m_armor_groups);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, os.str());
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
}
|
||||
@@ -499,18 +500,15 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
|
||||
std::string LuaEntitySAO::getClientInitializationData()
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// version
|
||||
writeU8(os, 1);
|
||||
// pos
|
||||
writeU8(os, 0); // version
|
||||
os<<serializeString(""); // name
|
||||
writeU8(os, 0); // is_player
|
||||
writeV3F1000(os, m_base_position);
|
||||
// yaw
|
||||
writeF1000(os, m_yaw);
|
||||
// hp
|
||||
writeS16(os, m_hp);
|
||||
// properties
|
||||
std::ostringstream prop_os(std::ios::binary);
|
||||
m_prop->serialize(prop_os);
|
||||
os<<serializeLongString(prop_os.str());
|
||||
writeU8(os, 2); // number of messages stuffed in here
|
||||
os<<serializeLongString(getPropertyPacket()); // message 1
|
||||
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
|
||||
// return result
|
||||
return os.str();
|
||||
}
|
||||
@@ -573,15 +571,9 @@ int LuaEntitySAO::punch(v3f dir,
|
||||
<<" hp, health now "<<getHP()<<" hp"<<std::endl;
|
||||
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, LUAENTITY_CMD_PUNCHED);
|
||||
// damage
|
||||
writeS16(os, result.damage);
|
||||
// result_hp
|
||||
writeS16(os, getHP());
|
||||
std::string str = gob_cmd_punched(result.damage, getHP());
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, os.str());
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
@@ -604,17 +596,6 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
|
||||
scriptapi_luaentity_rightclick(L, m_id, clicker);
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setHP(s16 hp)
|
||||
{
|
||||
if(hp < 0) hp = 0;
|
||||
m_hp = hp;
|
||||
}
|
||||
|
||||
s16 LuaEntitySAO::getHP()
|
||||
{
|
||||
return m_hp;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setPos(v3f pos)
|
||||
{
|
||||
m_base_position = pos;
|
||||
@@ -644,6 +625,33 @@ std::string LuaEntitySAO::getDescription()
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setHP(s16 hp)
|
||||
{
|
||||
if(hp < 0) hp = 0;
|
||||
m_hp = hp;
|
||||
}
|
||||
|
||||
s16 LuaEntitySAO::getHP() const
|
||||
{
|
||||
return m_hp;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
|
||||
{
|
||||
m_armor_groups = armor_groups;
|
||||
m_armor_groups_sent = false;
|
||||
}
|
||||
|
||||
ObjectProperties* LuaEntitySAO::accessObjectProperties()
|
||||
{
|
||||
return &m_prop;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::notifyObjectPropertiesModified()
|
||||
{
|
||||
m_properties_sent = false;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setVelocity(v3f velocity)
|
||||
{
|
||||
m_velocity = velocity;
|
||||
@@ -676,29 +684,23 @@ float LuaEntitySAO::getYaw()
|
||||
|
||||
void LuaEntitySAO::setTextureMod(const std::string &mod)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, LUAENTITY_CMD_SET_TEXTURE_MOD);
|
||||
// parameters
|
||||
os<<serializeString(mod);
|
||||
std::string str = gob_cmd_set_texture_mod(mod);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, os.str());
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
|
||||
bool select_horiz_by_yawpitch)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, LUAENTITY_CMD_SET_SPRITE);
|
||||
// parameters
|
||||
writeV2S16(os, p);
|
||||
writeU16(os, num_frames);
|
||||
writeF1000(os, framelength);
|
||||
writeU8(os, select_horiz_by_yawpitch);
|
||||
std::string str = gob_cmd_set_sprite(
|
||||
p,
|
||||
num_frames,
|
||||
framelength,
|
||||
select_horiz_by_yawpitch
|
||||
);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, os.str());
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
@@ -707,10 +709,9 @@ std::string LuaEntitySAO::getName()
|
||||
return m_init_name;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
|
||||
std::string LuaEntitySAO::getPropertyPacket()
|
||||
{
|
||||
m_armor_groups = armor_groups;
|
||||
m_armor_groups_sent = false;
|
||||
return gob_cmd_set_properties(m_prop);
|
||||
}
|
||||
|
||||
void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
|
||||
@@ -725,27 +726,423 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
|
||||
|
||||
float update_interval = m_env->getSendRecommendedInterval();
|
||||
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, LUAENTITY_CMD_UPDATE_POSITION);
|
||||
|
||||
// do_interpolate
|
||||
writeU8(os, do_interpolate);
|
||||
// pos
|
||||
writeV3F1000(os, m_base_position);
|
||||
// velocity
|
||||
writeV3F1000(os, m_velocity);
|
||||
// acceleration
|
||||
writeV3F1000(os, m_acceleration);
|
||||
// yaw
|
||||
writeF1000(os, m_yaw);
|
||||
// is_end_position (for interpolation)
|
||||
writeU8(os, is_movement_end);
|
||||
// update_interval (for interpolation)
|
||||
writeF1000(os, update_interval);
|
||||
|
||||
std::string str = gob_cmd_update_position(
|
||||
m_base_position,
|
||||
m_velocity,
|
||||
m_acceleration,
|
||||
m_yaw,
|
||||
do_interpolate,
|
||||
is_movement_end,
|
||||
update_interval
|
||||
);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, os.str());
|
||||
ActiveObjectMessage aom(getId(), false, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
/*
|
||||
PlayerSAO
|
||||
*/
|
||||
|
||||
// No prototype, PlayerSAO does not need to be deserialized
|
||||
|
||||
PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
|
||||
const std::set<std::string> &privs, bool is_singleplayer):
|
||||
ServerActiveObject(env_, v3f(0,0,0)),
|
||||
m_player(player_),
|
||||
m_peer_id(peer_id_),
|
||||
m_inventory(NULL),
|
||||
m_last_good_position(0,0,0),
|
||||
m_last_good_position_age(0),
|
||||
m_time_from_last_punch(0),
|
||||
m_wield_index(0),
|
||||
m_position_not_sent(false),
|
||||
m_armor_groups_sent(false),
|
||||
m_properties_sent(true),
|
||||
m_privs(privs),
|
||||
m_is_singleplayer(is_singleplayer),
|
||||
// public
|
||||
m_teleported(false),
|
||||
m_inventory_not_sent(false),
|
||||
m_hp_not_sent(false),
|
||||
m_wielded_item_not_sent(false)
|
||||
{
|
||||
assert(m_player);
|
||||
assert(m_peer_id != 0);
|
||||
setBasePosition(m_player->getPosition());
|
||||
m_inventory = &m_player->inventory;
|
||||
m_armor_groups["choppy"] = 2;
|
||||
m_armor_groups["fleshy"] = 3;
|
||||
|
||||
m_prop.hp_max = PLAYER_MAX_HP;
|
||||
m_prop.physical = false;
|
||||
m_prop.weight = 75;
|
||||
m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
|
||||
m_prop.visual = "upright_sprite";
|
||||
m_prop.visual_size = v2f(1, 2);
|
||||
m_prop.textures.clear();
|
||||
m_prop.textures.push_back("player.png");
|
||||
m_prop.textures.push_back("player_back.png");
|
||||
m_prop.spritediv = v2s16(1,1);
|
||||
m_prop.is_visible = (getHP() != 0);
|
||||
m_prop.makes_footstep_sound = true;
|
||||
}
|
||||
|
||||
PlayerSAO::~PlayerSAO()
|
||||
{
|
||||
if(m_inventory != &m_player->inventory)
|
||||
delete m_inventory;
|
||||
|
||||
}
|
||||
|
||||
std::string PlayerSAO::getDescription()
|
||||
{
|
||||
return std::string("player ") + m_player->getName();
|
||||
}
|
||||
|
||||
// Called after id has been set and has been inserted in environment
|
||||
void PlayerSAO::addedToEnvironment()
|
||||
{
|
||||
ServerActiveObject::addedToEnvironment();
|
||||
ServerActiveObject::setBasePosition(m_player->getPosition());
|
||||
m_player->setPlayerSAO(this);
|
||||
m_player->peer_id = m_peer_id;
|
||||
m_last_good_position = m_player->getPosition();
|
||||
m_last_good_position_age = 0.0;
|
||||
}
|
||||
|
||||
// Called before removing from environment
|
||||
void PlayerSAO::removingFromEnvironment()
|
||||
{
|
||||
ServerActiveObject::removingFromEnvironment();
|
||||
if(m_player->getPlayerSAO() == this)
|
||||
{
|
||||
m_player->setPlayerSAO(NULL);
|
||||
m_player->peer_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerSAO::isStaticAllowed() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayerSAO::unlimitedTransferDistance() const
|
||||
{
|
||||
return g_settings->getBool("unlimited_player_transfer_distance");
|
||||
}
|
||||
|
||||
std::string PlayerSAO::getClientInitializationData()
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
writeU8(os, 0); // version
|
||||
os<<serializeString(m_player->getName()); // name
|
||||
writeU8(os, 1); // is_player
|
||||
writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
|
||||
writeF1000(os, m_player->getYaw());
|
||||
writeS16(os, getHP());
|
||||
writeU8(os, 2); // number of messages stuffed in here
|
||||
os<<serializeLongString(getPropertyPacket()); // message 1
|
||||
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string PlayerSAO::getStaticData()
|
||||
{
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
|
||||
void PlayerSAO::step(float dtime, bool send_recommended)
|
||||
{
|
||||
if(!m_properties_sent)
|
||||
{
|
||||
m_properties_sent = true;
|
||||
std::string str = getPropertyPacket();
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
m_time_from_last_punch += dtime;
|
||||
|
||||
if(m_is_singleplayer)
|
||||
{
|
||||
m_last_good_position = m_player->getPosition();
|
||||
m_last_good_position_age = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Check player movements
|
||||
|
||||
NOTE: Actually the server should handle player physics like the
|
||||
client does and compare player's position to what is calculated
|
||||
on our side. This is required when eg. players fly due to an
|
||||
explosion.
|
||||
*/
|
||||
|
||||
float player_max_speed = 0;
|
||||
float player_max_speed_up = 0;
|
||||
if(m_privs.count("fast") != 0){
|
||||
// Fast speed
|
||||
player_max_speed = BS * 20;
|
||||
player_max_speed_up = BS * 20;
|
||||
} else {
|
||||
// Normal speed
|
||||
player_max_speed = BS * 4.0;
|
||||
player_max_speed_up = BS * 4.0;
|
||||
}
|
||||
// Tolerance
|
||||
player_max_speed *= 2.5;
|
||||
player_max_speed_up *= 2.5;
|
||||
|
||||
m_last_good_position_age += dtime;
|
||||
if(m_last_good_position_age >= 1.0){
|
||||
float age = m_last_good_position_age;
|
||||
v3f diff = (m_player->getPosition() - m_last_good_position);
|
||||
float d_vert = diff.Y;
|
||||
diff.Y = 0;
|
||||
float d_horiz = diff.getLength();
|
||||
/*infostream<<m_player->getName()<<"'s horizontal speed is "
|
||||
<<(d_horiz/age)<<std::endl;*/
|
||||
if(d_horiz <= age * player_max_speed &&
|
||||
(d_vert < 0 || d_vert < age * player_max_speed_up)){
|
||||
m_last_good_position = m_player->getPosition();
|
||||
} else {
|
||||
actionstream<<"Player "<<m_player->getName()
|
||||
<<" moved too fast; resetting position"
|
||||
<<std::endl;
|
||||
m_player->setPosition(m_last_good_position);
|
||||
m_teleported = true;
|
||||
}
|
||||
m_last_good_position_age = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(send_recommended == false)
|
||||
return;
|
||||
|
||||
if(m_position_not_sent)
|
||||
{
|
||||
m_position_not_sent = false;
|
||||
float update_interval = m_env->getSendRecommendedInterval();
|
||||
std::string str = gob_cmd_update_position(
|
||||
m_player->getPosition() + v3f(0,BS*1,0),
|
||||
v3f(0,0,0),
|
||||
v3f(0,0,0),
|
||||
m_player->getYaw(),
|
||||
true,
|
||||
false,
|
||||
update_interval
|
||||
);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
if(m_wielded_item_not_sent)
|
||||
{
|
||||
m_wielded_item_not_sent = false;
|
||||
// GenericCAO has no special way to show this
|
||||
}
|
||||
|
||||
if(m_armor_groups_sent == false){
|
||||
m_armor_groups_sent = true;
|
||||
std::string str = gob_cmd_update_armor_groups(
|
||||
m_armor_groups);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerSAO::setBasePosition(const v3f &position)
|
||||
{
|
||||
ServerActiveObject::setBasePosition(position);
|
||||
m_position_not_sent = true;
|
||||
}
|
||||
|
||||
void PlayerSAO::setPos(v3f pos)
|
||||
{
|
||||
m_player->setPosition(pos);
|
||||
// Movement caused by this command is always valid
|
||||
m_last_good_position = pos;
|
||||
m_last_good_position_age = 0;
|
||||
// Force position change on client
|
||||
m_teleported = true;
|
||||
}
|
||||
|
||||
void PlayerSAO::moveTo(v3f pos, bool continuous)
|
||||
{
|
||||
m_player->setPosition(pos);
|
||||
// Movement caused by this command is always valid
|
||||
m_last_good_position = pos;
|
||||
m_last_good_position_age = 0;
|
||||
// Force position change on client
|
||||
m_teleported = true;
|
||||
}
|
||||
|
||||
int PlayerSAO::punch(v3f dir,
|
||||
const ToolCapabilities *toolcap,
|
||||
ServerActiveObject *puncher,
|
||||
float time_from_last_punch)
|
||||
{
|
||||
if(!toolcap)
|
||||
return 0;
|
||||
|
||||
// No effect if PvP disabled
|
||||
if(g_settings->getBool("enable_pvp") == false){
|
||||
if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
|
||||
std::string str = gob_cmd_punched(0, getHP());
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
HitParams hitparams = getHitParams(m_armor_groups, toolcap,
|
||||
time_from_last_punch);
|
||||
|
||||
actionstream<<"Player "<<m_player->getName()<<" punched by "
|
||||
<<puncher->getDescription()<<", damage "<<hitparams.hp
|
||||
<<" HP"<<std::endl;
|
||||
|
||||
setHP(getHP() - hitparams.hp);
|
||||
|
||||
if(hitparams.hp != 0)
|
||||
{
|
||||
std::string str = gob_cmd_punched(hitparams.hp, getHP());
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
return hitparams.wear;
|
||||
}
|
||||
|
||||
void PlayerSAO::rightClick(ServerActiveObject *clicker)
|
||||
{
|
||||
}
|
||||
|
||||
s16 PlayerSAO::getHP() const
|
||||
{
|
||||
return m_player->hp;
|
||||
}
|
||||
|
||||
void PlayerSAO::setHP(s16 hp)
|
||||
{
|
||||
s16 oldhp = m_player->hp;
|
||||
|
||||
if(hp < 0)
|
||||
hp = 0;
|
||||
else if(hp > PLAYER_MAX_HP)
|
||||
hp = PLAYER_MAX_HP;
|
||||
|
||||
if(hp < oldhp && g_settings->getBool("enable_damage") == false)
|
||||
{
|
||||
m_hp_not_sent = true; // fix wrong prediction on client
|
||||
return;
|
||||
}
|
||||
|
||||
m_player->hp = hp;
|
||||
|
||||
if(hp != oldhp)
|
||||
m_hp_not_sent = true;
|
||||
|
||||
// On death or reincarnation send an active object message
|
||||
if((hp == 0) != (oldhp == 0))
|
||||
{
|
||||
// Will send new is_visible value based on (getHP()!=0)
|
||||
m_properties_sent = false;
|
||||
// Send new HP
|
||||
std::string str = gob_cmd_punched(0, getHP());
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
|
||||
{
|
||||
m_armor_groups = armor_groups;
|
||||
m_armor_groups_sent = false;
|
||||
}
|
||||
|
||||
ObjectProperties* PlayerSAO::accessObjectProperties()
|
||||
{
|
||||
return &m_prop;
|
||||
}
|
||||
|
||||
void PlayerSAO::notifyObjectPropertiesModified()
|
||||
{
|
||||
m_properties_sent = false;
|
||||
}
|
||||
|
||||
Inventory* PlayerSAO::getInventory()
|
||||
{
|
||||
return m_inventory;
|
||||
}
|
||||
const Inventory* PlayerSAO::getInventory() const
|
||||
{
|
||||
return m_inventory;
|
||||
}
|
||||
|
||||
InventoryLocation PlayerSAO::getInventoryLocation() const
|
||||
{
|
||||
InventoryLocation loc;
|
||||
loc.setPlayer(m_player->getName());
|
||||
return loc;
|
||||
}
|
||||
|
||||
void PlayerSAO::setInventoryModified()
|
||||
{
|
||||
m_inventory_not_sent = true;
|
||||
}
|
||||
|
||||
std::string PlayerSAO::getWieldList() const
|
||||
{
|
||||
return "main";
|
||||
}
|
||||
|
||||
int PlayerSAO::getWieldIndex() const
|
||||
{
|
||||
return m_wield_index;
|
||||
}
|
||||
|
||||
void PlayerSAO::setWieldIndex(int i)
|
||||
{
|
||||
if(i != m_wield_index)
|
||||
{
|
||||
m_wield_index = i;
|
||||
m_wielded_item_not_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerSAO::disconnected()
|
||||
{
|
||||
m_peer_id = 0;
|
||||
m_removed = true;
|
||||
if(m_player->getPlayerSAO() == this)
|
||||
{
|
||||
m_player->setPlayerSAO(NULL);
|
||||
m_player->peer_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerSAO::createCreativeInventory()
|
||||
{
|
||||
if(m_inventory != &m_player->inventory)
|
||||
delete m_inventory;
|
||||
|
||||
m_inventory = new Inventory(m_player->inventory);
|
||||
m_inventory->clearContents();
|
||||
scriptapi_get_creative_inventory(m_env->getLua(), this);
|
||||
}
|
||||
|
||||
std::string PlayerSAO::getPropertyPacket()
|
||||
{
|
||||
m_prop.is_visible = (getHP() != 0);
|
||||
return gob_cmd_set_properties(m_prop);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,18 +23,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "serverobject.h"
|
||||
#include "content_object.h"
|
||||
#include "itemgroup.h"
|
||||
#include "player.h"
|
||||
#include "object_properties.h"
|
||||
|
||||
ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
|
||||
const std::string itemstring);
|
||||
|
||||
/*
|
||||
LuaEntitySAO
|
||||
|
||||
This is the only SAO that needs to have a bunch of it's internals exposed.
|
||||
LuaEntitySAO needs some internals exposed.
|
||||
*/
|
||||
|
||||
struct LuaEntityProperties;
|
||||
|
||||
class LuaEntitySAO : public ServerActiveObject
|
||||
{
|
||||
public:
|
||||
@@ -42,7 +40,9 @@ class LuaEntitySAO : public ServerActiveObject
|
||||
const std::string &name, const std::string &state);
|
||||
~LuaEntitySAO();
|
||||
u8 getType() const
|
||||
{return ACTIVEOBJECT_TYPE_LUAENTITY;}
|
||||
{ return ACTIVEOBJECT_TYPE_LUAENTITY; }
|
||||
u8 getSendType() const
|
||||
{ return ACTIVEOBJECT_TYPE_GENERIC; }
|
||||
virtual void addedToEnvironment();
|
||||
static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
|
||||
const std::string &data);
|
||||
@@ -59,7 +59,10 @@ class LuaEntitySAO : public ServerActiveObject
|
||||
float getMinimumSavedMovement();
|
||||
std::string getDescription();
|
||||
void setHP(s16 hp);
|
||||
s16 getHP();
|
||||
s16 getHP() const;
|
||||
void setArmorGroups(const ItemGroupList &armor_groups);
|
||||
ObjectProperties* accessObjectProperties();
|
||||
void notifyObjectPropertiesModified();
|
||||
/* LuaEntitySAO-specific */
|
||||
void setVelocity(v3f velocity);
|
||||
v3f getVelocity();
|
||||
@@ -71,21 +74,22 @@ class LuaEntitySAO : public ServerActiveObject
|
||||
void setSprite(v2s16 p, int num_frames, float framelength,
|
||||
bool select_horiz_by_yawpitch);
|
||||
std::string getName();
|
||||
void setArmorGroups(const ItemGroupList &armor_groups);
|
||||
private:
|
||||
std::string getPropertyPacket();
|
||||
void sendPosition(bool do_interpolate, bool is_movement_end);
|
||||
|
||||
std::string m_init_name;
|
||||
std::string m_init_state;
|
||||
bool m_registered;
|
||||
struct LuaEntityProperties *m_prop;
|
||||
struct ObjectProperties m_prop;
|
||||
|
||||
s16 m_hp;
|
||||
v3f m_velocity;
|
||||
v3f m_acceleration;
|
||||
float m_yaw;
|
||||
ItemGroupList m_armor_groups;
|
||||
|
||||
|
||||
bool m_properties_sent;
|
||||
float m_last_sent_yaw;
|
||||
v3f m_last_sent_position;
|
||||
v3f m_last_sent_velocity;
|
||||
@@ -94,5 +98,124 @@ class LuaEntitySAO : public ServerActiveObject
|
||||
bool m_armor_groups_sent;
|
||||
};
|
||||
|
||||
/*
|
||||
PlayerSAO needs some internals exposed.
|
||||
*/
|
||||
|
||||
class PlayerSAO : public ServerActiveObject
|
||||
{
|
||||
public:
|
||||
PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
|
||||
const std::set<std::string> &privs, bool is_singleplayer);
|
||||
~PlayerSAO();
|
||||
u8 getType() const
|
||||
{ return ACTIVEOBJECT_TYPE_PLAYER; }
|
||||
u8 getSendType() const
|
||||
{ return ACTIVEOBJECT_TYPE_GENERIC; }
|
||||
std::string getDescription();
|
||||
|
||||
/*
|
||||
Active object <-> environment interface
|
||||
*/
|
||||
|
||||
void addedToEnvironment();
|
||||
void removingFromEnvironment();
|
||||
bool isStaticAllowed() const;
|
||||
bool unlimitedTransferDistance() const;
|
||||
std::string getClientInitializationData();
|
||||
std::string getStaticData();
|
||||
void step(float dtime, bool send_recommended);
|
||||
void setBasePosition(const v3f &position);
|
||||
void setPos(v3f pos);
|
||||
void moveTo(v3f pos, bool continuous);
|
||||
|
||||
/*
|
||||
Interaction interface
|
||||
*/
|
||||
|
||||
int punch(v3f dir,
|
||||
const ToolCapabilities *toolcap,
|
||||
ServerActiveObject *puncher,
|
||||
float time_from_last_punch);
|
||||
void rightClick(ServerActiveObject *clicker);
|
||||
s16 getHP() const;
|
||||
void setHP(s16 hp);
|
||||
|
||||
void setArmorGroups(const ItemGroupList &armor_groups);
|
||||
ObjectProperties* accessObjectProperties();
|
||||
void notifyObjectPropertiesModified();
|
||||
|
||||
/*
|
||||
Inventory interface
|
||||
*/
|
||||
|
||||
Inventory* getInventory();
|
||||
const Inventory* getInventory() const;
|
||||
InventoryLocation getInventoryLocation() const;
|
||||
void setInventoryModified();
|
||||
std::string getWieldList() const;
|
||||
int getWieldIndex() const;
|
||||
void setWieldIndex(int i);
|
||||
|
||||
/*
|
||||
PlayerSAO-specific
|
||||
*/
|
||||
|
||||
void disconnected();
|
||||
|
||||
void createCreativeInventory();
|
||||
|
||||
Player* getPlayer()
|
||||
{
|
||||
return m_player;
|
||||
}
|
||||
u16 getPeerID() const
|
||||
{
|
||||
return m_peer_id;
|
||||
}
|
||||
v3f getLastGoodPosition() const
|
||||
{
|
||||
return m_last_good_position;
|
||||
}
|
||||
float resetTimeFromLastPunch()
|
||||
{
|
||||
float r = m_time_from_last_punch;
|
||||
m_time_from_last_punch = 0.0;
|
||||
return r;
|
||||
}
|
||||
void updatePrivileges(const std::set<std::string> &privs,
|
||||
bool is_singleplayer)
|
||||
{
|
||||
m_privs = privs;
|
||||
m_is_singleplayer = is_singleplayer;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string getPropertyPacket();
|
||||
|
||||
Player *m_player;
|
||||
u16 m_peer_id;
|
||||
Inventory *m_inventory;
|
||||
v3f m_last_good_position;
|
||||
float m_last_good_position_age;
|
||||
float m_time_from_last_punch;
|
||||
int m_wield_index;
|
||||
bool m_position_not_sent;
|
||||
ItemGroupList m_armor_groups;
|
||||
bool m_armor_groups_sent;
|
||||
bool m_properties_sent;
|
||||
struct ObjectProperties m_prop;
|
||||
// Cached privileges for enforcement
|
||||
std::set<std::string> m_privs;
|
||||
bool m_is_singleplayer;
|
||||
|
||||
public:
|
||||
// Some flags used by Server
|
||||
bool m_teleported;
|
||||
bool m_inventory_not_sent;
|
||||
bool m_hp_not_sent;
|
||||
bool m_wielded_item_not_sent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -70,10 +70,14 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("wanted_fps", "30");
|
||||
settings->setDefault("fps_max", "60");
|
||||
// A bit more than the server will send around the player, to make fog blend well
|
||||
settings->setDefault("viewing_range_nodes_min", "35");
|
||||
settings->setDefault("viewing_range_nodes_max", "160");
|
||||
settings->setDefault("viewing_range_nodes_min", "35");
|
||||
settings->setDefault("screenW", "800");
|
||||
settings->setDefault("screenH", "600");
|
||||
settings->setDefault("fullscreen", "false");
|
||||
settings->setDefault("fullscreen_bpp", "24");
|
||||
settings->setDefault("fsaa", "0");
|
||||
settings->setDefault("vsync", "false");
|
||||
settings->setDefault("address", "");
|
||||
settings->setDefault("random_input", "false");
|
||||
settings->setDefault("client_unload_unused_data_timeout", "600");
|
||||
@@ -81,7 +85,7 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("fov", "72");
|
||||
settings->setDefault("view_bobbing", "true");
|
||||
settings->setDefault("new_style_water", "false");
|
||||
settings->setDefault("new_style_leaves", "false");
|
||||
settings->setDefault("new_style_leaves", "true");
|
||||
settings->setDefault("smooth_lighting", "true");
|
||||
settings->setDefault("enable_texture_atlas", "true");
|
||||
settings->setDefault("texture_path", "");
|
||||
@@ -94,10 +98,12 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("enable_clouds", "true");
|
||||
settings->setDefault("screenshot_path", ".");
|
||||
settings->setDefault("view_bobbing_amount", "1.0");
|
||||
settings->setDefault("enable_3d_clouds", "false");
|
||||
settings->setDefault("enable_3d_clouds", "true");
|
||||
settings->setDefault("opaque_water", "false");
|
||||
settings->setDefault("console_color", "(0,0,0)");
|
||||
settings->setDefault("console_alpha", "200");
|
||||
settings->setDefault("enable_sound", "true");
|
||||
settings->setDefault("sound_volume", "0.8");
|
||||
|
||||
// Server stuff
|
||||
// "map-dir" doesn't exist by default.
|
||||
@@ -111,7 +117,7 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("fixed_map_seed", "");
|
||||
settings->setDefault("give_initial_stuff", "false");
|
||||
settings->setDefault("default_password", "");
|
||||
settings->setDefault("default_privs", "build, shout");
|
||||
settings->setDefault("default_privs", "interact, shout");
|
||||
settings->setDefault("unlimited_player_transfer_distance", "true");
|
||||
settings->setDefault("enable_pvp", "true");
|
||||
|
||||
@@ -123,14 +129,13 @@ void set_default_settings(Settings *settings)
|
||||
// This causes frametime jitter on client side, or does it?
|
||||
settings->setDefault("max_simultaneous_block_sends_per_client", "4");
|
||||
settings->setDefault("max_simultaneous_block_sends_server_total", "20");
|
||||
settings->setDefault("max_block_send_distance", "10");
|
||||
settings->setDefault("max_block_generate_distance", "6");
|
||||
settings->setDefault("max_block_send_distance", "9");
|
||||
settings->setDefault("max_block_generate_distance", "7");
|
||||
settings->setDefault("time_send_interval", "5");
|
||||
settings->setDefault("time_speed", "96");
|
||||
settings->setDefault("time_speed", "72");
|
||||
settings->setDefault("server_unload_unused_data_timeout", "29");
|
||||
settings->setDefault("server_map_save_interval", "5.3");
|
||||
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
|
||||
settings->setDefault("enable_experimental", "false");
|
||||
settings->setDefault("dedicated_server_step", "0.05");
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "nodemetadata.h"
|
||||
#include "main.h" // For g_settings, g_profiler
|
||||
#include "gamedef.h"
|
||||
#include "serverremoteplayer.h"
|
||||
#ifndef SERVER
|
||||
#include "clientmap.h"
|
||||
#include "localplayer.h"
|
||||
#endif
|
||||
#include "daynightratio.h"
|
||||
|
||||
@@ -368,7 +368,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
|
||||
//infostream<<"Checking player file "<<path<<std::endl;
|
||||
|
||||
// Load player to see what is its name
|
||||
ServerRemotePlayer testplayer(this);
|
||||
RemotePlayer testplayer(m_gamedef);
|
||||
{
|
||||
// Open file and deserialize
|
||||
std::ifstream is(path.c_str(), std::ios_base::binary);
|
||||
@@ -482,7 +482,7 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
|
||||
//infostream<<"Checking player file "<<path<<std::endl;
|
||||
|
||||
// Load player to see what is its name
|
||||
ServerRemotePlayer testplayer(this);
|
||||
RemotePlayer testplayer(m_gamedef);
|
||||
{
|
||||
// Open file and deserialize
|
||||
std::ifstream is(path.c_str(), std::ios_base::binary);
|
||||
@@ -510,12 +510,10 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
|
||||
if(player == NULL)
|
||||
{
|
||||
//infostream<<"Is a new player"<<std::endl;
|
||||
player = new ServerRemotePlayer(this);
|
||||
player = new RemotePlayer(m_gamedef);
|
||||
newplayer = true;
|
||||
}
|
||||
|
||||
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
|
||||
|
||||
// Load player
|
||||
{
|
||||
verbosestream<<"Reading player "<<testplayer.getName()<<" from "
|
||||
@@ -527,9 +525,7 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
|
||||
infostream<<"Failed to read "<<path<<std::endl;
|
||||
continue;
|
||||
}
|
||||
srp->deSerialize(is);
|
||||
srp->m_last_good_position = srp->getBasePosition();
|
||||
srp->m_last_good_position_age = 0;
|
||||
player->deSerialize(is);
|
||||
}
|
||||
|
||||
if(newplayer)
|
||||
@@ -637,13 +633,15 @@ class ABMHandler
|
||||
i->timer -= trigger_interval;
|
||||
actual_interval = trigger_interval;
|
||||
}
|
||||
ActiveABM aabm;
|
||||
aabm.abm = abm;
|
||||
float intervals = actual_interval / trigger_interval;
|
||||
if(intervals == 0)
|
||||
continue;
|
||||
float chance = abm->getTriggerChance();
|
||||
if(chance == 0)
|
||||
chance = 1;
|
||||
aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
|
||||
ActiveABM aabm;
|
||||
aabm.abm = abm;
|
||||
aabm.chance = chance / intervals;
|
||||
if(aabm.chance == 0)
|
||||
aabm.chance = 1;
|
||||
// Trigger neighbors
|
||||
@@ -651,27 +649,30 @@ class ABMHandler
|
||||
= abm->getRequiredNeighbors();
|
||||
for(std::set<std::string>::iterator
|
||||
i = required_neighbors_s.begin();
|
||||
i != required_neighbors_s.end(); i++){
|
||||
content_t c = ndef->getId(*i);
|
||||
if(c == CONTENT_IGNORE)
|
||||
continue;
|
||||
aabm.required_neighbors.insert(c);
|
||||
i != required_neighbors_s.end(); i++)
|
||||
{
|
||||
ndef->getIds(*i, aabm.required_neighbors);
|
||||
}
|
||||
// Trigger contents
|
||||
std::set<std::string> contents_s = abm->getTriggerContents();
|
||||
for(std::set<std::string>::iterator
|
||||
i = contents_s.begin(); i != contents_s.end(); i++){
|
||||
content_t c = ndef->getId(*i);
|
||||
if(c == CONTENT_IGNORE)
|
||||
continue;
|
||||
std::map<content_t, std::list<ActiveABM> >::iterator j;
|
||||
j = m_aabms.find(c);
|
||||
if(j == m_aabms.end()){
|
||||
std::list<ActiveABM> aabmlist;
|
||||
m_aabms[c] = aabmlist;
|
||||
i = contents_s.begin(); i != contents_s.end(); i++)
|
||||
{
|
||||
std::set<content_t> ids;
|
||||
ndef->getIds(*i, ids);
|
||||
for(std::set<content_t>::const_iterator k = ids.begin();
|
||||
k != ids.end(); k++)
|
||||
{
|
||||
content_t c = *k;
|
||||
std::map<content_t, std::list<ActiveABM> >::iterator j;
|
||||
j = m_aabms.find(c);
|
||||
if(j == m_aabms.end()){
|
||||
std::list<ActiveABM> aabmlist;
|
||||
m_aabms[c] = aabmlist;
|
||||
j = m_aabms.find(c);
|
||||
}
|
||||
j->second.push_back(aabm);
|
||||
}
|
||||
j->second.push_back(aabm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -772,18 +773,11 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
|
||||
// Activate stored objects
|
||||
activateObjects(block);
|
||||
|
||||
// Run node metadata
|
||||
bool changed = block->m_node_metadata->step((float)dtime_s);
|
||||
if(changed)
|
||||
{
|
||||
MapEditEvent event;
|
||||
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
|
||||
event.p = block->getPos();
|
||||
m_map->dispatchEvent(&event);
|
||||
|
||||
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
||||
"node metadata modified in activateBlock");
|
||||
}
|
||||
// Run node timers
|
||||
std::map<v3s16, f32> elapsed_timers =
|
||||
block->m_node_timers.step((float)dtime_s);
|
||||
if(!elapsed_timers.empty())
|
||||
errorstream<<"Node timers don't work yet!"<<std::endl;
|
||||
|
||||
/* Handle ActiveBlockModifiers */
|
||||
ABMHandler abmhandler(m_abms, dtime_s, this, false);
|
||||
@@ -1063,18 +1057,11 @@ void ServerEnvironment::step(float dtime)
|
||||
block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
|
||||
"Timestamp older than 60s (step)");
|
||||
|
||||
// Run node metadata
|
||||
bool changed = block->m_node_metadata->step(dtime);
|
||||
if(changed)
|
||||
{
|
||||
MapEditEvent event;
|
||||
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
|
||||
event.p = p;
|
||||
m_map->dispatchEvent(&event);
|
||||
|
||||
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
||||
"node metadata modified in step");
|
||||
}
|
||||
// Run node timers
|
||||
std::map<v3s16, f32> elapsed_timers =
|
||||
block->m_node_timers.step(dtime);
|
||||
if(!elapsed_timers.empty())
|
||||
errorstream<<"Node timers don't work yet!"<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1747,6 +1734,15 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
|
||||
force_delete = true;
|
||||
} else {
|
||||
u16 new_id = pending_delete ? id : 0;
|
||||
// If static counterpart already exists, remove it first.
|
||||
// This shouldn't happen, but happens rarely for some
|
||||
// unknown reason. Unsuccessful attempts have been made to
|
||||
// find said reason.
|
||||
if(new_id && block->m_static_objects.m_active.find(new_id)){
|
||||
infostream<<"ServerEnv: WARNING: Performing hack #83274"
|
||||
<<std::endl;
|
||||
block->m_static_objects.remove(new_id);
|
||||
}
|
||||
block->m_static_objects.insert(new_id, s_obj);
|
||||
|
||||
// Only mark block as modified if data changed considerably
|
||||
@@ -1890,7 +1886,8 @@ void ClientEnvironment::step(float dtime)
|
||||
stepTimeOfDay(dtime);
|
||||
|
||||
// Get some settings
|
||||
bool free_move = g_settings->getBool("free_move");
|
||||
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
|
||||
bool free_move = fly_allowed && g_settings->getBool("free_move");
|
||||
|
||||
// Get local player
|
||||
LocalPlayer *lplayer = getLocalPlayer();
|
||||
@@ -2004,13 +2001,15 @@ void ClientEnvironment::step(float dtime)
|
||||
if(info.t == COLLISION_FALL)
|
||||
{
|
||||
//f32 tolerance = BS*10; // 2 without damage
|
||||
f32 tolerance = BS*12; // 3 without damage
|
||||
//f32 tolerance = BS*12; // 3 without damage
|
||||
f32 tolerance = BS*14; // 5 without damage
|
||||
f32 factor = 1;
|
||||
if(info.speed > tolerance)
|
||||
{
|
||||
f32 damage_f = (info.speed - tolerance)/BS*factor;
|
||||
u16 damage = (u16)(damage_f+0.5);
|
||||
damageLocalPlayer(damage, true);
|
||||
if(damage != 0)
|
||||
damageLocalPlayer(damage, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2074,7 +2073,7 @@ void ClientEnvironment::step(float dtime)
|
||||
catch(InvalidPositionException &e){
|
||||
light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
|
||||
}
|
||||
player->updateLight(light);
|
||||
player->light = light;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2226,8 +2225,19 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
|
||||
|
||||
obj->setId(id);
|
||||
|
||||
obj->initialize(init_data);
|
||||
|
||||
try
|
||||
{
|
||||
obj->initialize(init_data);
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
errorstream<<"ClientEnvironment::addActiveObject():"
|
||||
<<" id="<<id<<" type="<<type
|
||||
<<": SerializationError in initialize(),"
|
||||
<<" init_data="<<serializeJsonString(init_data)
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
addActiveObject(obj);
|
||||
}
|
||||
|
||||
@@ -2258,7 +2268,18 @@ void ClientEnvironment::processActiveObjectMessage(u16 id,
|
||||
<<std::endl;
|
||||
return;
|
||||
}
|
||||
obj->processMessage(data);
|
||||
try
|
||||
{
|
||||
obj->processMessage(data);
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
errorstream<<"ClientEnvironment::processActiveObjectMessage():"
|
||||
<<" id="<<id<<" type="<<obj->getType()
|
||||
<<" SerializationError in processMessage(),"
|
||||
<<" message="<<serializeJsonString(data)
|
||||
<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -125,7 +125,7 @@ HeightPoint ground_height(u64 seed, v2s16 p2d)
|
||||
hp.ma = (4)*BS;
|
||||
/*hp.gh = BS*base_rock_level_2d(seed, p2d);
|
||||
hp.ma = BS*get_mud_add_amount(seed, p2d);*/
|
||||
hp.have_sand = mapgen::get_have_sand(seed, p2d);
|
||||
hp.have_sand = mapgen::get_have_beach(seed, p2d);
|
||||
if(hp.gh > BS*WATER_LEVEL)
|
||||
hp.tree_amount = mapgen::tree_amount_2d(seed, p2d);
|
||||
else
|
||||
|
||||
@@ -214,6 +214,7 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
std::vector<DirListNode> GetDirListing(std::string pathstring)
|
||||
{
|
||||
|
||||
126
src/game.cpp
126
src/game.cpp
@@ -103,7 +103,9 @@ struct TextDestNodeMetadata : public TextDest
|
||||
std::string ntext = wide_to_narrow(text);
|
||||
infostream<<"Changing text of a sign node: "
|
||||
<<ntext<<std::endl;
|
||||
m_client->sendSignNodeText(m_p, ntext);
|
||||
std::map<std::string, std::string> fields;
|
||||
fields["text"] = ntext;
|
||||
m_client->sendNodemetaFields(m_p, "", fields);
|
||||
}
|
||||
|
||||
v3s16 m_p;
|
||||
@@ -130,6 +132,28 @@ class MainRespawnInitiator: public IRespawnInitiator
|
||||
Client *m_client;
|
||||
};
|
||||
|
||||
/* Form update callback */
|
||||
|
||||
class NodeMetadataFormSource: public IFormSource
|
||||
{
|
||||
public:
|
||||
NodeMetadataFormSource(ClientMap *map, v3s16 p):
|
||||
m_map(map),
|
||||
m_p(p)
|
||||
{
|
||||
}
|
||||
std::string getForm()
|
||||
{
|
||||
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
|
||||
if(!meta)
|
||||
return "";
|
||||
return meta->getString("formspec");
|
||||
}
|
||||
|
||||
ClientMap *m_map;
|
||||
v3s16 m_p;
|
||||
};
|
||||
|
||||
/*
|
||||
Hotbar draw routine
|
||||
*/
|
||||
@@ -955,10 +979,14 @@ void the_game(
|
||||
ISoundManager *sound = NULL;
|
||||
bool sound_is_dummy = false;
|
||||
#if USE_SOUND
|
||||
infostream<<"Attempting to use OpenAL audio"<<std::endl;
|
||||
sound = createOpenALSoundManager(&soundfetcher);
|
||||
if(!sound)
|
||||
infostream<<"Failed to initialize OpenAL audio"<<std::endl;
|
||||
if(g_settings->getBool("enable_sound")){
|
||||
infostream<<"Attempting to use OpenAL audio"<<std::endl;
|
||||
sound = createOpenALSoundManager(&soundfetcher);
|
||||
if(!sound)
|
||||
infostream<<"Failed to initialize OpenAL audio"<<std::endl;
|
||||
} else {
|
||||
infostream<<"Sound disabled."<<std::endl;
|
||||
}
|
||||
#endif
|
||||
if(!sound){
|
||||
infostream<<"Using dummy audio."<<std::endl;
|
||||
@@ -1563,24 +1591,19 @@ void the_game(
|
||||
|
||||
GUIInventoryMenu *menu =
|
||||
new GUIInventoryMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, v2s16(8,7),
|
||||
&g_menumgr,
|
||||
&client, gamedef);
|
||||
|
||||
InventoryLocation inventoryloc;
|
||||
inventoryloc.setCurrentPlayer();
|
||||
|
||||
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", inventoryloc, "main",
|
||||
v2s32(0, 3), v2s32(8, 4)));
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", inventoryloc, "craft",
|
||||
v2s32(3, 0), v2s32(3, 3)));
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", inventoryloc, "craftpreview",
|
||||
v2s32(7, 1), v2s32(1, 1)));
|
||||
|
||||
menu->setDrawSpec(draw_spec);
|
||||
menu->setFormSpec(
|
||||
"invsize[8,7.5;]"
|
||||
//"image[1,0.6;1,2;player.png]"
|
||||
"list[current_player;main;0,3.5;8,4;]"
|
||||
"list[current_player;craft;3,0;3,3;]"
|
||||
"list[current_player;craftpreview;7,1;1,1;]"
|
||||
, inventoryloc);
|
||||
|
||||
menu->drop();
|
||||
}
|
||||
@@ -1636,6 +1659,8 @@ void the_game(
|
||||
g_settings->set("free_move","true");
|
||||
statustext = L"free_move enabled";
|
||||
statustext_time = 0;
|
||||
if(!client.checkPrivilege("fly"))
|
||||
statustext += L" (note: no 'fly' privilege)";
|
||||
}
|
||||
}
|
||||
else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
|
||||
@@ -1651,6 +1676,8 @@ void the_game(
|
||||
g_settings->set("fast_move","true");
|
||||
statustext = L"fast_move enabled";
|
||||
statustext_time = 0;
|
||||
if(!client.checkPrivilege("fast"))
|
||||
statustext += L" (note: no 'fast' privilege)";
|
||||
}
|
||||
}
|
||||
else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
|
||||
@@ -2076,8 +2103,9 @@ void the_game(
|
||||
// Update sound listener
|
||||
sound->updateListener(camera.getCameraNode()->getPosition(),
|
||||
v3f(0,0,0), // velocity
|
||||
camera.getCameraNode()->getTarget(),
|
||||
camera.getDirection(),
|
||||
camera.getCameraNode()->getUpVector());
|
||||
sound->setListenerGain(g_settings->getFloat("sound_volume"));
|
||||
|
||||
/*
|
||||
Update sound maker
|
||||
@@ -2186,7 +2214,7 @@ void the_game(
|
||||
ClientMap &map = client.getEnv().getClientMap();
|
||||
NodeMetadata *meta = map.getNodeMetadata(nodepos);
|
||||
if(meta){
|
||||
infotext = narrow_to_wide(meta->infoText());
|
||||
infotext = narrow_to_wide(meta->getString("infotext"));
|
||||
} else {
|
||||
MapNode n = map.getNode(nodepos);
|
||||
if(nodedef->get(n).tname_tiles[0] == "unknown_block.png"){
|
||||
@@ -2311,35 +2339,8 @@ void the_game(
|
||||
{
|
||||
infostream<<"Ground right-clicked"<<std::endl;
|
||||
|
||||
// If metadata provides an inventory view, activate it
|
||||
if(meta && meta->getInventoryDrawSpecString() != "" && !random_input)
|
||||
{
|
||||
infostream<<"Launching custom inventory view"<<std::endl;
|
||||
|
||||
InventoryLocation inventoryloc;
|
||||
inventoryloc.setNodeMeta(nodepos);
|
||||
|
||||
|
||||
/*
|
||||
Create menu
|
||||
*/
|
||||
|
||||
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
|
||||
v2s16 invsize =
|
||||
GUIInventoryMenu::makeDrawSpecArrayFromString(
|
||||
draw_spec,
|
||||
meta->getInventoryDrawSpecString(),
|
||||
inventoryloc);
|
||||
|
||||
GUIInventoryMenu *menu =
|
||||
new GUIInventoryMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, invsize,
|
||||
&client, gamedef);
|
||||
menu->setDrawSpec(draw_spec);
|
||||
menu->drop();
|
||||
}
|
||||
// If metadata provides text input, activate text input
|
||||
else if(meta && meta->allowsTextInput() && !random_input)
|
||||
// sign special case, at least until formspec is properly implemented
|
||||
if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
|
||||
{
|
||||
infostream<<"Launching metadata text input"<<std::endl;
|
||||
|
||||
@@ -2347,12 +2348,32 @@ void the_game(
|
||||
|
||||
TextDest *dest = new TextDestNodeMetadata(nodepos, &client);
|
||||
|
||||
std::wstring wtext = narrow_to_wide(meta->getText());
|
||||
std::wstring wtext = narrow_to_wide(meta->getString("text"));
|
||||
|
||||
(new GUITextInputMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, dest,
|
||||
wtext))->drop();
|
||||
}
|
||||
// If metadata provides an inventory view, activate it
|
||||
else if(meta && meta->getString("formspec") != "" && !random_input)
|
||||
{
|
||||
infostream<<"Launching custom inventory view"<<std::endl;
|
||||
|
||||
InventoryLocation inventoryloc;
|
||||
inventoryloc.setNodeMeta(nodepos);
|
||||
|
||||
/* Create menu */
|
||||
|
||||
GUIInventoryMenu *menu =
|
||||
new GUIInventoryMenu(guienv, guiroot, -1,
|
||||
&g_menumgr,
|
||||
&client, gamedef);
|
||||
menu->setFormSpec(meta->getString("formspec"),
|
||||
inventoryloc);
|
||||
menu->setFormSource(new NodeMetadataFormSource(
|
||||
&client.getEnv().getClientMap(), nodepos));
|
||||
menu->drop();
|
||||
}
|
||||
// Otherwise report right click to server
|
||||
else
|
||||
{
|
||||
@@ -2596,11 +2617,12 @@ void the_game(
|
||||
char temptext[300];
|
||||
snprintf(temptext, 300,
|
||||
"(% .1f, % .1f, % .1f)"
|
||||
" (yaw = %.1f)",
|
||||
" (yaw = %.1f) (seed = %lli)",
|
||||
player_position.X/BS,
|
||||
player_position.Y/BS,
|
||||
player_position.Z/BS,
|
||||
wrapDegrees_0_360(camera_yaw));
|
||||
wrapDegrees_0_360(camera_yaw),
|
||||
client.getMapSeed());
|
||||
|
||||
guitext2->setText(narrow_to_wide(temptext).c_str());
|
||||
guitext2->setVisible(true);
|
||||
|
||||
@@ -50,10 +50,15 @@ class IGameDef
|
||||
|
||||
// Used for keeping track of names/ids of unknown nodes
|
||||
virtual u16 allocateUnknownNodeId(const std::string &name)=0;
|
||||
|
||||
|
||||
// Only usable on the client
|
||||
virtual ISoundManager* getSoundManager()=0;
|
||||
virtual MtEventManager* getEventManager()=0;
|
||||
|
||||
// Used on the client
|
||||
virtual bool checkLocalPrivilege(const std::string &priv)
|
||||
{ return false; }
|
||||
|
||||
// Shorthands
|
||||
IItemDefManager* idef(){return getItemDefManager();}
|
||||
INodeDefManager* ndef(){return getNodeDefManager();}
|
||||
|
||||
120
src/genericobject.cpp
Normal file
120
src/genericobject.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 "genericobject.h"
|
||||
#include "utility.h"
|
||||
#include <sstream>
|
||||
|
||||
std::string gob_cmd_set_properties(const ObjectProperties &prop)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
writeU8(os, GENERIC_CMD_SET_PROPERTIES);
|
||||
prop.serialize(os);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
ObjectProperties gob_read_set_properties(std::istream &is)
|
||||
{
|
||||
ObjectProperties prop;
|
||||
prop.deSerialize(is);
|
||||
return prop;
|
||||
}
|
||||
|
||||
std::string gob_cmd_update_position(
|
||||
v3f position,
|
||||
v3f velocity,
|
||||
v3f acceleration,
|
||||
f32 yaw,
|
||||
bool do_interpolate,
|
||||
bool is_movement_end,
|
||||
f32 update_interval
|
||||
){
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, GENERIC_CMD_UPDATE_POSITION);
|
||||
// pos
|
||||
writeV3F1000(os, position);
|
||||
// velocity
|
||||
writeV3F1000(os, velocity);
|
||||
// acceleration
|
||||
writeV3F1000(os, acceleration);
|
||||
// yaw
|
||||
writeF1000(os, yaw);
|
||||
// do_interpolate
|
||||
writeU8(os, do_interpolate);
|
||||
// is_end_position (for interpolation)
|
||||
writeU8(os, is_movement_end);
|
||||
// update_interval (for interpolation)
|
||||
writeF1000(os, update_interval);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_set_texture_mod(const std::string &mod)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, GENERIC_CMD_SET_TEXTURE_MOD);
|
||||
// parameters
|
||||
os<<serializeString(mod);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_set_sprite(
|
||||
v2s16 p,
|
||||
u16 num_frames,
|
||||
f32 framelength,
|
||||
bool select_horiz_by_yawpitch
|
||||
){
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, GENERIC_CMD_SET_SPRITE);
|
||||
// parameters
|
||||
writeV2S16(os, p);
|
||||
writeU16(os, num_frames);
|
||||
writeF1000(os, framelength);
|
||||
writeU8(os, select_horiz_by_yawpitch);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_punched(s16 damage, s16 result_hp)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, GENERIC_CMD_PUNCHED);
|
||||
// damage
|
||||
writeS16(os, damage);
|
||||
// result_hp
|
||||
writeS16(os, result_hp);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
writeU8(os, GENERIC_CMD_UPDATE_ARMOR_GROUPS);
|
||||
writeU16(os, armor_groups.size());
|
||||
for(ItemGroupList::const_iterator i = armor_groups.begin();
|
||||
i != armor_groups.end(); i++){
|
||||
os<<serializeString(i->first);
|
||||
writeS16(os, i->second);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
||||
63
src/genericobject.h
Normal file
63
src/genericobject.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 GENERICOBJECT_HEADER
|
||||
#define GENERICOBJECT_HEADER
|
||||
|
||||
#include <string>
|
||||
#include "irrlichttypes.h"
|
||||
#include <iostream>
|
||||
|
||||
#define GENERIC_CMD_SET_PROPERTIES 0
|
||||
#define GENERIC_CMD_UPDATE_POSITION 1
|
||||
#define GENERIC_CMD_SET_TEXTURE_MOD 2
|
||||
#define GENERIC_CMD_SET_SPRITE 3
|
||||
#define GENERIC_CMD_PUNCHED 4
|
||||
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 5
|
||||
|
||||
#include "object_properties.h"
|
||||
std::string gob_cmd_set_properties(const ObjectProperties &prop);
|
||||
ObjectProperties gob_read_set_properties(std::istream &is);
|
||||
|
||||
std::string gob_cmd_update_position(
|
||||
v3f position,
|
||||
v3f velocity,
|
||||
v3f acceleration,
|
||||
f32 yaw,
|
||||
bool do_interpolate,
|
||||
bool is_movement_end,
|
||||
f32 update_interval
|
||||
);
|
||||
|
||||
std::string gob_cmd_set_texture_mod(const std::string &mod);
|
||||
|
||||
std::string gob_cmd_set_sprite(
|
||||
v2s16 p,
|
||||
u16 num_frames,
|
||||
f32 framelength,
|
||||
bool select_horiz_by_yawpitch
|
||||
);
|
||||
|
||||
std::string gob_cmd_punched(s16 damage, s16 result_hp);
|
||||
|
||||
#include "itemgroup.h"
|
||||
std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <IGUIStaticText.h>
|
||||
#include <IGUIFont.h>
|
||||
#include "log.h"
|
||||
#include "tile.h" // ITextureSource
|
||||
|
||||
void drawItemStack(video::IVideoDriver *driver,
|
||||
gui::IGUIFont *font,
|
||||
@@ -123,27 +124,26 @@ void drawItemStack(video::IVideoDriver *driver,
|
||||
GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
v2s16 menu_size,
|
||||
InventoryManager *invmgr,
|
||||
IGameDef *gamedef
|
||||
):
|
||||
):
|
||||
GUIModalMenu(env, parent, id, menumgr),
|
||||
m_menu_size(menu_size),
|
||||
m_invmgr(invmgr),
|
||||
m_gamedef(gamedef)
|
||||
m_gamedef(gamedef),
|
||||
m_form_src(NULL),
|
||||
m_selected_item(NULL),
|
||||
m_selected_amount(0),
|
||||
m_selected_dragging(false),
|
||||
m_tooltip_element(NULL)
|
||||
{
|
||||
m_selected_item = NULL;
|
||||
m_selected_amount = 0;
|
||||
m_selected_dragging = false;
|
||||
m_tooltip_element = NULL;
|
||||
}
|
||||
|
||||
GUIInventoryMenu::~GUIInventoryMenu()
|
||||
{
|
||||
removeChildren();
|
||||
|
||||
if(m_selected_item)
|
||||
delete m_selected_item;
|
||||
delete m_selected_item;
|
||||
delete m_form_src;
|
||||
}
|
||||
|
||||
void GUIInventoryMenu::removeChildren()
|
||||
@@ -178,60 +178,96 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
|
||||
// Remove children
|
||||
removeChildren();
|
||||
|
||||
/*padding = v2s32(24,24);
|
||||
spacing = v2s32(60,56);
|
||||
imgsize = v2s32(48,48);*/
|
||||
|
||||
padding = v2s32(screensize.Y/40, screensize.Y/40);
|
||||
spacing = v2s32(screensize.Y/12, screensize.Y/13);
|
||||
imgsize = v2s32(screensize.Y/15, screensize.Y/15);
|
||||
|
||||
v2s32 size(100,100);
|
||||
s32 helptext_h = 15;
|
||||
|
||||
v2s32 size(
|
||||
padding.X*2+spacing.X*(m_menu_size.X-1)+imgsize.X,
|
||||
padding.Y*2+spacing.Y*(m_menu_size.Y-1)+imgsize.Y + helptext_h
|
||||
);
|
||||
|
||||
core::rect<s32> rect(
|
||||
screensize.X/2 - size.X/2,
|
||||
screensize.Y/2 - size.Y/2,
|
||||
screensize.X/2 + size.X/2,
|
||||
screensize.Y/2 + size.Y/2
|
||||
);
|
||||
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
core::rect<s32> rect;
|
||||
v2s32 basepos = getBasePos();
|
||||
|
||||
m_draw_spec.clear();
|
||||
for(u16 i=0; i<m_init_draw_spec.size(); i++)
|
||||
/* Convert m_init_draw_spec to m_inventorylists */
|
||||
|
||||
m_inventorylists.clear();
|
||||
m_images.clear();
|
||||
|
||||
Strfnd f(m_formspec_string);
|
||||
while(f.atend() == false)
|
||||
{
|
||||
DrawSpec &s = m_init_draw_spec[i];
|
||||
if(s.type == "list")
|
||||
std::string type = trim(f.next("["));
|
||||
if(type == "invsize")
|
||||
{
|
||||
m_draw_spec.push_back(ListDrawSpec(s.name, s.subname,
|
||||
basepos + v2s32(spacing.X*s.pos.X, spacing.Y*s.pos.Y),
|
||||
s.geom));
|
||||
v2f invsize;
|
||||
invsize.X = stof(f.next(","));
|
||||
invsize.Y = stof(f.next(";"));
|
||||
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
|
||||
f.next("]");
|
||||
|
||||
padding = v2s32(screensize.Y/40, screensize.Y/40);
|
||||
spacing = v2s32(screensize.Y/12, screensize.Y/13);
|
||||
imgsize = v2s32(screensize.Y/15, screensize.Y/15);
|
||||
size = v2s32(
|
||||
padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
|
||||
padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
|
||||
);
|
||||
rect = core::rect<s32>(
|
||||
screensize.X/2 - size.X/2,
|
||||
screensize.Y/2 - size.Y/2,
|
||||
screensize.X/2 + size.X/2,
|
||||
screensize.Y/2 + size.Y/2
|
||||
);
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
basepos = getBasePos();
|
||||
}
|
||||
else if(type == "list")
|
||||
{
|
||||
std::string name = f.next(";");
|
||||
InventoryLocation loc;
|
||||
if(name == "context" || name == "current_name")
|
||||
loc = m_current_inventory_location;
|
||||
else
|
||||
loc.deSerialize(name);
|
||||
std::string listname = f.next(";");
|
||||
v2s32 pos = basepos;
|
||||
pos.X += stof(f.next(",")) * (float)spacing.X;
|
||||
pos.Y += stof(f.next(";")) * (float)spacing.Y;
|
||||
v2s32 geom;
|
||||
geom.X = stoi(f.next(","));
|
||||
geom.Y = stoi(f.next(";"));
|
||||
infostream<<"list inv="<<name<<", listname="<<listname
|
||||
<<", pos=("<<pos.X<<","<<pos.Y<<")"
|
||||
<<", geom=("<<geom.X<<","<<geom.Y<<")"
|
||||
<<std::endl;
|
||||
f.next("]");
|
||||
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
|
||||
}
|
||||
else if(type == "image")
|
||||
{
|
||||
v2s32 pos = basepos;
|
||||
pos.X += stof(f.next(",")) * (float)spacing.X;
|
||||
pos.Y += stof(f.next(";")) * (float)spacing.Y;
|
||||
v2s32 geom;
|
||||
geom.X = stof(f.next(",")) * (float)imgsize.X;
|
||||
geom.Y = stof(f.next(";")) * (float)imgsize.Y;
|
||||
std::string name = f.next("]");
|
||||
infostream<<"image name="<<name
|
||||
<<", pos=("<<pos.X<<","<<pos.Y<<")"
|
||||
<<", geom=("<<geom.X<<","<<geom.Y<<")"
|
||||
<<std::endl;
|
||||
m_images.push_back(ImageDrawSpec(name, pos, geom));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore others
|
||||
std::string ts = f.next("]");
|
||||
infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
|
||||
<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
m_draw_spec.clear();
|
||||
m_draw_spec.push_back(ListDrawSpec("main",
|
||||
basepos + v2s32(spacing.X*0, spacing.Y*3), v2s32(8, 4)));
|
||||
m_draw_spec.push_back(ListDrawSpec("craft",
|
||||
basepos + v2s32(spacing.X*3, spacing.Y*0), v2s32(3, 3)));
|
||||
m_draw_spec.push_back(ListDrawSpec("craftresult",
|
||||
basepos + v2s32(spacing.X*7, spacing.Y*1), v2s32(1, 1)));
|
||||
*/
|
||||
|
||||
// Add children
|
||||
{
|
||||
core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
|
||||
rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
|
||||
size.Y-rect.getHeight()-15);
|
||||
size.Y-rect.getHeight()-5);
|
||||
const wchar_t *text =
|
||||
L"Left click: Move all items, Right click: Move single item";
|
||||
Environment->addStaticText(text, rect, false, true, this, 256);
|
||||
@@ -253,9 +289,9 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
|
||||
{
|
||||
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
|
||||
|
||||
for(u32 i=0; i<m_draw_spec.size(); i++)
|
||||
for(u32 i=0; i<m_inventorylists.size(); i++)
|
||||
{
|
||||
const ListDrawSpec &s = m_draw_spec[i];
|
||||
const ListDrawSpec &s = m_inventorylists[i];
|
||||
|
||||
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
|
||||
{
|
||||
@@ -284,8 +320,21 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
|
||||
font = skin->getFont();
|
||||
|
||||
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
||||
assert(inv);
|
||||
if(!inv){
|
||||
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
|
||||
<<"The inventory location "
|
||||
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
|
||||
<<std::endl;
|
||||
return;
|
||||
}
|
||||
InventoryList *ilist = inv->getList(s.listname);
|
||||
if(!ilist){
|
||||
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
|
||||
<<"The inventory list \""<<s.listname<<"\" @ \""
|
||||
<<s.inventoryloc.dump()<<"\" doesn't exist"
|
||||
<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
|
||||
|
||||
@@ -380,6 +429,14 @@ void GUIInventoryMenu::drawSelectedItem()
|
||||
|
||||
void GUIInventoryMenu::drawMenu()
|
||||
{
|
||||
if(m_form_src){
|
||||
std::string newform = m_form_src->getForm();
|
||||
if(newform != m_formspec_string){
|
||||
m_formspec_string = newform;
|
||||
regenerateGui(m_screensize_old);
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectedItem();
|
||||
|
||||
gui::IGUISkin* skin = Environment->getSkin();
|
||||
@@ -399,9 +456,26 @@ void GUIInventoryMenu::drawMenu()
|
||||
*/
|
||||
|
||||
for(int phase=0; phase<=1; phase++)
|
||||
for(u32 i=0; i<m_draw_spec.size(); i++)
|
||||
for(u32 i=0; i<m_inventorylists.size(); i++)
|
||||
{
|
||||
drawList(m_draw_spec[i], phase);
|
||||
drawList(m_inventorylists[i], phase);
|
||||
}
|
||||
|
||||
for(u32 i=0; i<m_images.size(); i++)
|
||||
{
|
||||
const ImageDrawSpec &spec = m_images[i];
|
||||
video::ITexture *texture =
|
||||
m_gamedef->tsrc()->getTextureRaw(spec.name);
|
||||
// Image size on screen
|
||||
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
|
||||
// Image rectangle on screen
|
||||
core::rect<s32> rect = imgrect + spec.pos;
|
||||
const video::SColor color(255,255,255,255);
|
||||
const video::SColor colors[] = {color,color,color,color};
|
||||
driver->draw2DImage(texture, rect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getOriginalSize())),
|
||||
NULL/*&AbsoluteClippingRect*/, colors, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -450,9 +524,9 @@ void GUIInventoryMenu::updateSelectedItem()
|
||||
// If craftresult is nonempty and nothing else is selected, select it now.
|
||||
if(!m_selected_item)
|
||||
{
|
||||
for(u32 i=0; i<m_draw_spec.size(); i++)
|
||||
for(u32 i=0; i<m_inventorylists.size(); i++)
|
||||
{
|
||||
const ListDrawSpec &s = m_draw_spec[i];
|
||||
const ListDrawSpec &s = m_inventorylists[i];
|
||||
if(s.listname == "craftpreview")
|
||||
{
|
||||
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
||||
@@ -526,16 +600,35 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
u32 s_count = 0;
|
||||
|
||||
if(s.isValid())
|
||||
{
|
||||
do{ // breakable
|
||||
inv_s = m_invmgr->getInventory(s.inventoryloc);
|
||||
assert(inv_s);
|
||||
|
||||
if(!inv_s){
|
||||
errorstream<<"InventoryMenu: The selected inventory location "
|
||||
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
|
||||
<<std::endl;
|
||||
s.i = -1; // make it invalid again
|
||||
break;
|
||||
}
|
||||
|
||||
InventoryList *list = inv_s->getList(s.listname);
|
||||
if(list != NULL && (u32) s.i < list->getSize())
|
||||
s_count = list->getItem(s.i).count;
|
||||
else
|
||||
if(list == NULL){
|
||||
errorstream<<"InventoryMenu: The selected inventory list \""
|
||||
<<s.listname<<"\" does not exist"<<std::endl;
|
||||
s.i = -1; // make it invalid again
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if((u32)s.i >= list->getSize()){
|
||||
errorstream<<"InventoryMenu: The selected inventory list \""
|
||||
<<s.listname<<"\" is too small (i="<<s.i<<", size="
|
||||
<<list->getSize()<<")"<<std::endl;
|
||||
s.i = -1; // make it invalid again
|
||||
break;
|
||||
}
|
||||
|
||||
s_count = list->getItem(s.i).count;
|
||||
}while(0);
|
||||
|
||||
bool identical = (m_selected_item != NULL) && s.isValid() &&
|
||||
(inv_selected == inv_s) &&
|
||||
@@ -788,88 +881,3 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
||||
/*
|
||||
Here is an example traditional set-up sequence for a DrawSpec list:
|
||||
|
||||
std::string furnace_inv_id = "nodemetadata:0,1,2";
|
||||
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", furnace_inv_id, "fuel",
|
||||
v2s32(2, 3), v2s32(1, 1)));
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", furnace_inv_id, "src",
|
||||
v2s32(2, 1), v2s32(1, 1)));
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", furnace_inv_id, "dst",
|
||||
v2s32(5, 1), v2s32(2, 2)));
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
"list", "current_player", "main",
|
||||
v2s32(0, 5), v2s32(8, 4)));
|
||||
setDrawSpec(draw_spec);
|
||||
|
||||
Here is the string for creating the same DrawSpec list (a single line,
|
||||
spread to multiple lines here):
|
||||
|
||||
GUIInventoryMenu::makeDrawSpecArrayFromString(
|
||||
draw_spec,
|
||||
"nodemetadata:0,1,2",
|
||||
"invsize[8,9;]"
|
||||
"list[current_name;fuel;2,3;1,1;]"
|
||||
"list[current_name;src;2,1;1,1;]"
|
||||
"list[current_name;dst;5,1;2,2;]"
|
||||
"list[current_player;main;0,5;8,4;]");
|
||||
|
||||
Returns inventory menu size defined by invsize[].
|
||||
*/
|
||||
v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
|
||||
core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
|
||||
const std::string &data,
|
||||
const InventoryLocation ¤t_location)
|
||||
{
|
||||
v2s16 invsize(8,9);
|
||||
Strfnd f(data);
|
||||
while(f.atend() == false)
|
||||
{
|
||||
std::string type = trim(f.next("["));
|
||||
//infostream<<"type="<<type<<std::endl;
|
||||
if(type == "list")
|
||||
{
|
||||
std::string name = f.next(";");
|
||||
InventoryLocation loc;
|
||||
if(name == "current_name")
|
||||
loc = current_location;
|
||||
else
|
||||
loc.deSerialize(name);
|
||||
std::string subname = f.next(";");
|
||||
s32 pos_x = stoi(f.next(","));
|
||||
s32 pos_y = stoi(f.next(";"));
|
||||
s32 geom_x = stoi(f.next(","));
|
||||
s32 geom_y = stoi(f.next(";"));
|
||||
infostream<<"list name="<<name<<", subname="<<subname
|
||||
<<", pos=("<<pos_x<<","<<pos_y<<")"
|
||||
<<", geom=("<<geom_x<<","<<geom_y<<")"
|
||||
<<std::endl;
|
||||
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
|
||||
type, loc, subname,
|
||||
v2s32(pos_x,pos_y),v2s32(geom_x,geom_y)));
|
||||
f.next("]");
|
||||
}
|
||||
else if(type == "invsize")
|
||||
{
|
||||
invsize.X = stoi(f.next(","));
|
||||
invsize.Y = stoi(f.next(";"));
|
||||
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
|
||||
f.next("]");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore others
|
||||
std::string ts = f.next("]");
|
||||
infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
|
||||
<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return invsize;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
class IGameDef;
|
||||
class InventoryManager;
|
||||
|
||||
class IFormSource
|
||||
{
|
||||
public:
|
||||
virtual ~IFormSource(){}
|
||||
virtual std::string getForm() = 0;
|
||||
};
|
||||
|
||||
void drawItemStack(video::IVideoDriver *driver,
|
||||
gui::IGUIFont *font,
|
||||
const ItemStack &item,
|
||||
@@ -70,12 +77,12 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
}
|
||||
ListDrawSpec(const InventoryLocation &a_inventoryloc,
|
||||
const std::string &a_listname,
|
||||
v2s32 a_pos, v2s32 a_geom)
|
||||
v2s32 a_pos, v2s32 a_geom):
|
||||
inventoryloc(a_inventoryloc),
|
||||
listname(a_listname),
|
||||
pos(a_pos),
|
||||
geom(a_geom)
|
||||
{
|
||||
inventoryloc = a_inventoryloc;
|
||||
listname = a_listname;
|
||||
pos = a_pos;
|
||||
geom = a_geom;
|
||||
}
|
||||
|
||||
InventoryLocation inventoryloc;
|
||||
@@ -83,50 +90,45 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
v2s32 pos;
|
||||
v2s32 geom;
|
||||
};
|
||||
public:
|
||||
struct DrawSpec
|
||||
{
|
||||
DrawSpec()
|
||||
{
|
||||
}
|
||||
DrawSpec(const std::string &a_type,
|
||||
const InventoryLocation &a_name,
|
||||
const std::string &a_subname,
|
||||
v2s32 a_pos,
|
||||
v2s32 a_geom)
|
||||
{
|
||||
type = a_type;
|
||||
name = a_name;
|
||||
subname = a_subname;
|
||||
pos = a_pos;
|
||||
geom = a_geom;
|
||||
}
|
||||
|
||||
std::string type;
|
||||
InventoryLocation name;
|
||||
std::string subname;
|
||||
struct ImageDrawSpec
|
||||
{
|
||||
ImageDrawSpec()
|
||||
{
|
||||
}
|
||||
ImageDrawSpec(const std::string &a_name,
|
||||
v2s32 a_pos, v2s32 a_geom):
|
||||
name(a_name),
|
||||
pos(a_pos),
|
||||
geom(a_geom)
|
||||
{
|
||||
}
|
||||
std::string name;
|
||||
v2s32 pos;
|
||||
v2s32 geom;
|
||||
};
|
||||
|
||||
// See .cpp for format
|
||||
static v2s16 makeDrawSpecArrayFromString(
|
||||
core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
|
||||
const std::string &data,
|
||||
const InventoryLocation ¤t_location);
|
||||
|
||||
public:
|
||||
GUIInventoryMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
v2s16 menu_size,
|
||||
InventoryManager *invmgr,
|
||||
IGameDef *gamedef
|
||||
);
|
||||
~GUIInventoryMenu();
|
||||
|
||||
void setDrawSpec(core::array<DrawSpec> &init_draw_spec)
|
||||
void setFormSpec(const std::string &formspec_string,
|
||||
InventoryLocation current_inventory_location)
|
||||
{
|
||||
m_init_draw_spec = init_draw_spec;
|
||||
m_formspec_string = formspec_string;
|
||||
m_current_inventory_location = current_inventory_location;
|
||||
regenerateGui(m_screensize_old);
|
||||
}
|
||||
|
||||
// form_src is deleted by this GUIInventoryMenu
|
||||
void setFormSource(IFormSource *form_src)
|
||||
{
|
||||
m_form_src = form_src;
|
||||
}
|
||||
|
||||
void removeChildren();
|
||||
@@ -149,8 +151,6 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
return padding + AbsoluteRect.UpperLeftCorner;
|
||||
}
|
||||
|
||||
v2s16 m_menu_size;
|
||||
|
||||
v2s32 padding;
|
||||
v2s32 spacing;
|
||||
v2s32 imgsize;
|
||||
@@ -158,8 +158,12 @@ class GUIInventoryMenu : public GUIModalMenu
|
||||
InventoryManager *m_invmgr;
|
||||
IGameDef *m_gamedef;
|
||||
|
||||
core::array<DrawSpec> m_init_draw_spec;
|
||||
core::array<ListDrawSpec> m_draw_spec;
|
||||
std::string m_formspec_string;
|
||||
InventoryLocation m_current_inventory_location;
|
||||
IFormSource *m_form_src;
|
||||
|
||||
core::array<ListDrawSpec> m_inventorylists;
|
||||
core::array<ImageDrawSpec> m_images;
|
||||
|
||||
ItemSpec *m_selected_item;
|
||||
u32 m_selected_amount;
|
||||
|
||||
@@ -590,7 +590,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
rect += m_topleft_client + v2s32(130+14, 50+35);
|
||||
Environment->addStaticText(narrow_to_wide(
|
||||
"Minetest-c55 " VERSION_STRING "\n"
|
||||
"http://c55.me/minetest/\n"
|
||||
"http://minetest.net/\n"
|
||||
"\n"
|
||||
"by Perttu Ahola <celeron55@gmail.com>\n"
|
||||
"and contributors"
|
||||
|
||||
@@ -458,6 +458,11 @@ void InventoryList::setSize(u32 newsize)
|
||||
m_size = newsize;
|
||||
}
|
||||
|
||||
void InventoryList::setName(const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void InventoryList::serialize(std::ostream &os) const
|
||||
{
|
||||
//os.imbue(std::locale("C"));
|
||||
@@ -795,6 +800,18 @@ void Inventory::clear()
|
||||
m_lists.clear();
|
||||
}
|
||||
|
||||
void Inventory::clearContents()
|
||||
{
|
||||
for(u32 i=0; i<m_lists.size(); i++)
|
||||
{
|
||||
InventoryList *list = m_lists[i];
|
||||
for(u32 j=0; j<list->getSize(); j++)
|
||||
{
|
||||
list->deleteItem(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Inventory::Inventory(IItemDefManager *itemdef)
|
||||
{
|
||||
m_itemdef = itemdef;
|
||||
@@ -903,6 +920,17 @@ InventoryList * Inventory::getList(const std::string &name)
|
||||
return m_lists[i];
|
||||
}
|
||||
|
||||
std::vector<const InventoryList*> Inventory::getLists()
|
||||
{
|
||||
std::vector<const InventoryList*> lists;
|
||||
for(u32 i=0; i<m_lists.size(); i++)
|
||||
{
|
||||
InventoryList *list = m_lists[i];
|
||||
lists.push_back(list);
|
||||
}
|
||||
return lists;
|
||||
}
|
||||
|
||||
bool Inventory::deleteList(const std::string &name)
|
||||
{
|
||||
s32 i = getListIndex(name);
|
||||
|
||||
@@ -176,6 +176,7 @@ class InventoryList
|
||||
~InventoryList();
|
||||
void clearItems();
|
||||
void setSize(u32 newsize);
|
||||
void setName(const std::string &name);
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
@@ -250,6 +251,7 @@ class Inventory
|
||||
~Inventory();
|
||||
|
||||
void clear();
|
||||
void clearContents();
|
||||
|
||||
Inventory(IItemDefManager *itemdef);
|
||||
Inventory(const Inventory &other);
|
||||
@@ -261,6 +263,7 @@ class Inventory
|
||||
InventoryList * addList(const std::string &name, u32 size);
|
||||
InventoryList * getList(const std::string &name);
|
||||
const InventoryList * getList(const std::string &name) const;
|
||||
std::vector<const InventoryList*> getLists();
|
||||
bool deleteList(const std::string &name);
|
||||
// A shorthand for adding items. Returns leftover item (possibly empty).
|
||||
ItemStack addItem(const std::string &listname, const ItemStack &newitem)
|
||||
|
||||
@@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "utility.h"
|
||||
#include "craftdef.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
/*
|
||||
InventoryLocation
|
||||
*/
|
||||
@@ -197,27 +199,90 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
||||
<<", to_list=\""<<to_list<<"\""<<std::endl;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
This performs the actual movement
|
||||
|
||||
// Handle node metadata move
|
||||
if(from_inv.type == InventoryLocation::NODEMETA &&
|
||||
to_inv.type == InventoryLocation::NODEMETA &&
|
||||
from_inv.p != to_inv.p)
|
||||
{
|
||||
errorstream<<"Directly moving items between two nodes is "
|
||||
<<"disallowed."<<std::endl;
|
||||
return;
|
||||
}
|
||||
else if(from_inv.type == InventoryLocation::NODEMETA &&
|
||||
to_inv.type == InventoryLocation::NODEMETA &&
|
||||
from_inv.p == to_inv.p)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
infostream<<player->getDescription()<<" moving "<<count0
|
||||
<<" items inside node at "<<PP(from_inv.p)<<std::endl;
|
||||
scriptapi_node_on_metadata_inventory_move(L, from_inv.p,
|
||||
from_list, from_i, to_list, to_i, count0, player);
|
||||
}
|
||||
// Handle node metadata take
|
||||
else if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
infostream<<player->getDescription()<<" taking "<<count0
|
||||
<<" items from node at "<<PP(from_inv.p)<<std::endl;
|
||||
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
|
||||
L, from_inv.p, from_list, from_i, count0, player);
|
||||
if(return_stack.count == 0)
|
||||
infostream<<"Node metadata gave no items"<<std::endl;
|
||||
return_stack = list_to->addItem(to_i, return_stack);
|
||||
list_to->addItem(return_stack); // Force return of everything
|
||||
}
|
||||
// Handle node metadata offer
|
||||
else if(to_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
ItemStack offer_stack = list_from->takeItem(from_i, count0);
|
||||
infostream<<player->getDescription()<<" offering "
|
||||
<<offer_stack.count<<" items to node at "
|
||||
<<PP(to_inv.p)<<std::endl;
|
||||
ItemStack reject_stack = scriptapi_node_on_metadata_inventory_offer(
|
||||
L, to_inv.p, to_list, to_i, offer_stack, player);
|
||||
if(reject_stack.count == offer_stack.count)
|
||||
infostream<<"Node metadata rejected all items"<<std::endl;
|
||||
else if(reject_stack.count != 0)
|
||||
infostream<<"Node metadata rejected some items"<<std::endl;
|
||||
reject_stack = list_from->addItem(from_i, reject_stack);
|
||||
list_from->addItem(reject_stack); // Force return of everything
|
||||
}
|
||||
// Handle regular move
|
||||
else
|
||||
{
|
||||
/*
|
||||
This performs the actual movement
|
||||
|
||||
If something is wrong (source item is empty, destination is the
|
||||
same as source), nothing happens
|
||||
*/
|
||||
list_from->moveItem(from_i, list_to, to_i, count);
|
||||
If something is wrong (source item is empty, destination is the
|
||||
same as source), nothing happens
|
||||
*/
|
||||
list_from->moveItem(from_i, list_to, to_i, count);
|
||||
|
||||
infostream<<"IMoveAction::apply(): moved "
|
||||
<<" count="<<count
|
||||
<<" from inv=\""<<from_inv.dump()<<"\""
|
||||
<<" list=\""<<from_list<<"\""
|
||||
<<" i="<<from_i
|
||||
<<" to inv=\""<<to_inv.dump()<<"\""
|
||||
<<" list=\""<<to_list<<"\""
|
||||
<<" i="<<to_i
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
mgr->setInventoryModified(from_inv);
|
||||
if(inv_from != inv_to)
|
||||
mgr->setInventoryModified(to_inv);
|
||||
|
||||
infostream<<"IMoveAction::apply(): moved at "
|
||||
<<" count="<<count<<"\""
|
||||
<<" from inv=\""<<from_inv.dump()<<"\""
|
||||
<<" list=\""<<from_list<<"\""
|
||||
<<" i="<<from_i
|
||||
<<" to inv=\""<<to_inv.dump()<<"\""
|
||||
<<" list=\""<<to_list<<"\""
|
||||
<<" i="<<to_i
|
||||
<<std::endl;
|
||||
}
|
||||
|
||||
void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
|
||||
@@ -297,12 +362,31 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
||||
return;
|
||||
}
|
||||
|
||||
// Take item from source list
|
||||
ItemStack item1;
|
||||
if(count == 0)
|
||||
item1 = list_from->changeItem(from_i, ItemStack());
|
||||
|
||||
// Handle node metadata take
|
||||
if(from_inv.type == InventoryLocation::NODEMETA)
|
||||
{
|
||||
lua_State *L = player->getEnv()->getLua();
|
||||
int count0 = count;
|
||||
if(count0 == 0)
|
||||
count0 = list_from->getItem(from_i).count;
|
||||
infostream<<player->getDescription()<<" dropping "<<count0
|
||||
<<" items from node at "<<PP(from_inv.p)<<std::endl;
|
||||
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
|
||||
L, from_inv.p, from_list, from_i, count0, player);
|
||||
if(return_stack.count == 0)
|
||||
infostream<<"Node metadata gave no items"<<std::endl;
|
||||
item1 = return_stack;
|
||||
}
|
||||
else
|
||||
item1 = list_from->takeItem(from_i, count);
|
||||
{
|
||||
// Take item from source list
|
||||
if(count == 0)
|
||||
item1 = list_from->changeItem(from_i, ItemStack());
|
||||
else
|
||||
item1 = list_from->takeItem(from_i, count);
|
||||
}
|
||||
|
||||
// Drop the item and apply the returned ItemStack
|
||||
ItemStack item2 = item1;
|
||||
|
||||
@@ -20,6 +20,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "light.h"
|
||||
|
||||
#if 1
|
||||
// Middle-raised variation of a_n+1 = a_n * 0.786
|
||||
// Length of LIGHT_MAX+1 means LIGHT_MAX is the last value.
|
||||
// LIGHT_SUN is read as LIGHT_MAX from here.
|
||||
u8 light_decode_table[LIGHT_MAX+1] =
|
||||
{
|
||||
8,
|
||||
11+2,
|
||||
14+7,
|
||||
18+10,
|
||||
22+15,
|
||||
29+20,
|
||||
37+20,
|
||||
47+15,
|
||||
60+10,
|
||||
76+7,
|
||||
97+5,
|
||||
123+2,
|
||||
157,
|
||||
200,
|
||||
255,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/*
|
||||
Made using this and:
|
||||
- adding 220 as the second last one
|
||||
|
||||
638
src/localplayer.cpp
Normal file
638
src/localplayer.cpp
Normal file
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 "localplayer.h"
|
||||
#include "main.h" // For g_settings
|
||||
#include "event.h"
|
||||
#include "collision.h"
|
||||
#include "gamedef.h"
|
||||
#include "nodedef.h"
|
||||
#include "settings.h"
|
||||
#include "map.h"
|
||||
|
||||
/*
|
||||
LocalPlayer
|
||||
*/
|
||||
|
||||
LocalPlayer::LocalPlayer(IGameDef *gamedef):
|
||||
Player(gamedef),
|
||||
m_sneak_node(32767,32767,32767),
|
||||
m_sneak_node_exists(false)
|
||||
{
|
||||
// Initialize hp to 0, so that no hearts will be shown if server
|
||||
// doesn't support health points
|
||||
hp = 0;
|
||||
}
|
||||
|
||||
LocalPlayer::~LocalPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
|
||||
core::list<CollisionInfo> *collision_info)
|
||||
{
|
||||
INodeDefManager *nodemgr = m_gamedef->ndef();
|
||||
|
||||
v3f position = getPosition();
|
||||
v3f oldpos = position;
|
||||
v3s16 oldpos_i = floatToInt(oldpos, BS);
|
||||
|
||||
v3f old_speed = m_speed;
|
||||
|
||||
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
|
||||
<<oldpos_i.Z<<")"<<std::endl;*/
|
||||
|
||||
/*
|
||||
Calculate new position
|
||||
*/
|
||||
position += m_speed * dtime;
|
||||
|
||||
// Skip collision detection if a special movement mode is used
|
||||
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
|
||||
bool free_move = fly_allowed && g_settings->getBool("free_move");
|
||||
if(free_move)
|
||||
{
|
||||
setPosition(position);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Collision detection
|
||||
*/
|
||||
|
||||
// Player position in nodes
|
||||
v3s16 pos_i = floatToInt(position, BS);
|
||||
|
||||
/*
|
||||
Check if player is in water (the oscillating value)
|
||||
*/
|
||||
try{
|
||||
// If in water, the threshold of coming out is at higher y
|
||||
if(in_water)
|
||||
{
|
||||
v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
|
||||
in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
|
||||
}
|
||||
// If not in water, the threshold of going in is at lower y
|
||||
else
|
||||
{
|
||||
v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
|
||||
in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
|
||||
}
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
in_water = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if player is in water (the stable value)
|
||||
*/
|
||||
try{
|
||||
v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
|
||||
in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
in_water_stable = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if player is climbing
|
||||
*/
|
||||
|
||||
try {
|
||||
v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
|
||||
v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
|
||||
is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
|
||||
nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
is_climbing = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Collision uncertainty radius
|
||||
Make it a bit larger than the maximum distance of movement
|
||||
*/
|
||||
//f32 d = pos_max_d * 1.1;
|
||||
// A fairly large value in here makes moving smoother
|
||||
f32 d = 0.15*BS;
|
||||
|
||||
// This should always apply, otherwise there are glitches
|
||||
assert(d > pos_max_d);
|
||||
|
||||
float player_radius = BS*0.30;
|
||||
float player_height = BS*1.55;
|
||||
|
||||
// Maximum distance over border for sneaking
|
||||
f32 sneak_max = BS*0.4;
|
||||
|
||||
/*
|
||||
If sneaking, player has larger collision radius to keep from
|
||||
falling
|
||||
*/
|
||||
/*if(control.sneak)
|
||||
player_radius = sneak_max + d*1.1;*/
|
||||
|
||||
/*
|
||||
If sneaking, keep in range from the last walked node and don't
|
||||
fall off from it
|
||||
*/
|
||||
if(control.sneak && m_sneak_node_exists)
|
||||
{
|
||||
f32 maxd = 0.5*BS + sneak_max;
|
||||
v3f lwn_f = intToFloat(m_sneak_node, BS);
|
||||
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
|
||||
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
|
||||
|
||||
f32 min_y = lwn_f.Y + 0.5*BS;
|
||||
if(position.Y < min_y)
|
||||
{
|
||||
position.Y = min_y;
|
||||
|
||||
//v3f old_speed = m_speed;
|
||||
|
||||
if(m_speed.Y < 0)
|
||||
m_speed.Y = 0;
|
||||
|
||||
/*if(collision_info)
|
||||
{
|
||||
// Report fall collision
|
||||
if(old_speed.Y < m_speed.Y - 0.1)
|
||||
{
|
||||
CollisionInfo info;
|
||||
info.t = COLLISION_FALL;
|
||||
info.speed = m_speed.Y - old_speed.Y;
|
||||
collision_info->push_back(info);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate player collision box (new and old)
|
||||
*/
|
||||
core::aabbox3d<f32> playerbox(
|
||||
position.X - player_radius,
|
||||
position.Y - 0.0,
|
||||
position.Z - player_radius,
|
||||
position.X + player_radius,
|
||||
position.Y + player_height,
|
||||
position.Z + player_radius
|
||||
);
|
||||
core::aabbox3d<f32> playerbox_old(
|
||||
oldpos.X - player_radius,
|
||||
oldpos.Y - 0.0,
|
||||
oldpos.Z - player_radius,
|
||||
oldpos.X + player_radius,
|
||||
oldpos.Y + player_height,
|
||||
oldpos.Z + player_radius
|
||||
);
|
||||
|
||||
/*
|
||||
If the player's feet touch the topside of any node, this is
|
||||
set to true.
|
||||
|
||||
Player is allowed to jump when this is true.
|
||||
*/
|
||||
bool touching_ground_was = touching_ground;
|
||||
touching_ground = false;
|
||||
|
||||
/*std::cout<<"Checking collisions for ("
|
||||
<<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
|
||||
<<") -> ("
|
||||
<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
|
||||
<<"):"<<std::endl;*/
|
||||
|
||||
bool standing_on_unloaded = false;
|
||||
|
||||
/*
|
||||
Go through every node around the player
|
||||
*/
|
||||
for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
|
||||
for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
|
||||
for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
|
||||
{
|
||||
bool is_unloaded = false;
|
||||
try{
|
||||
// Player collides into walkable nodes
|
||||
if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
|
||||
continue;
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
is_unloaded = true;
|
||||
// Doing nothing here will block the player from
|
||||
// walking over map borders
|
||||
}
|
||||
|
||||
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
|
||||
|
||||
/*
|
||||
See if the player is touching ground.
|
||||
|
||||
Player touches ground if player's minimum Y is near node's
|
||||
maximum Y and player's X-Z-area overlaps with the node's
|
||||
X-Z-area.
|
||||
|
||||
Use 0.15*BS so that it is easier to get on a node.
|
||||
*/
|
||||
if(
|
||||
//fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
|
||||
fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
|
||||
&& nodebox.MaxEdge.X-d > playerbox.MinEdge.X
|
||||
&& nodebox.MinEdge.X+d < playerbox.MaxEdge.X
|
||||
&& nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
|
||||
&& nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
|
||||
){
|
||||
touching_ground = true;
|
||||
if(is_unloaded)
|
||||
standing_on_unloaded = true;
|
||||
}
|
||||
|
||||
// If player doesn't intersect with node, ignore node.
|
||||
if(playerbox.intersectsWithBox(nodebox) == false)
|
||||
continue;
|
||||
|
||||
/*
|
||||
Go through every axis
|
||||
*/
|
||||
v3f dirs[3] = {
|
||||
v3f(0,0,1), // back-front
|
||||
v3f(0,1,0), // top-bottom
|
||||
v3f(1,0,0), // right-left
|
||||
};
|
||||
for(u16 i=0; i<3; i++)
|
||||
{
|
||||
/*
|
||||
Calculate values along the axis
|
||||
*/
|
||||
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
|
||||
f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
|
||||
f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
|
||||
|
||||
/*
|
||||
Check collision for the axis.
|
||||
Collision happens when player is going through a surface.
|
||||
*/
|
||||
/*f32 neg_d = d;
|
||||
f32 pos_d = d;
|
||||
// Make it easier to get on top of a node
|
||||
if(i == 1)
|
||||
neg_d = 0.15*BS;
|
||||
bool negative_axis_collides =
|
||||
(nodemax > playermin && nodemax <= playermin_old + neg_d
|
||||
&& m_speed.dotProduct(dirs[i]) < 0);
|
||||
bool positive_axis_collides =
|
||||
(nodemin < playermax && nodemin >= playermax_old - pos_d
|
||||
&& m_speed.dotProduct(dirs[i]) > 0);*/
|
||||
bool negative_axis_collides =
|
||||
(nodemax > playermin && nodemax <= playermin_old + d
|
||||
&& m_speed.dotProduct(dirs[i]) < 0);
|
||||
bool positive_axis_collides =
|
||||
(nodemin < playermax && nodemin >= playermax_old - d
|
||||
&& m_speed.dotProduct(dirs[i]) > 0);
|
||||
bool main_axis_collides =
|
||||
negative_axis_collides || positive_axis_collides;
|
||||
|
||||
/*
|
||||
Check overlap of player and node in other axes
|
||||
*/
|
||||
bool other_axes_overlap = true;
|
||||
for(u16 j=0; j<3; j++)
|
||||
{
|
||||
if(j == i)
|
||||
continue;
|
||||
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
|
||||
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
|
||||
f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
|
||||
f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
|
||||
if(!(nodemax - d > playermin && nodemin + d < playermax))
|
||||
{
|
||||
other_axes_overlap = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If this is a collision, revert the position in the main
|
||||
direction.
|
||||
*/
|
||||
if(other_axes_overlap && main_axis_collides)
|
||||
{
|
||||
//v3f old_speed = m_speed;
|
||||
|
||||
m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
|
||||
position -= position.dotProduct(dirs[i]) * dirs[i];
|
||||
position += oldpos.dotProduct(dirs[i]) * dirs[i];
|
||||
|
||||
/*if(collision_info)
|
||||
{
|
||||
// Report fall collision
|
||||
if(old_speed.Y < m_speed.Y - 0.1)
|
||||
{
|
||||
CollisionInfo info;
|
||||
info.t = COLLISION_FALL;
|
||||
info.speed = m_speed.Y - old_speed.Y;
|
||||
collision_info->push_back(info);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
} // xyz
|
||||
|
||||
/*
|
||||
Check the nodes under the player to see from which node the
|
||||
player is sneaking from, if any.
|
||||
*/
|
||||
{
|
||||
v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
|
||||
v2f player_p2df(position.X, position.Z);
|
||||
f32 min_distance_f = 100000.0*BS;
|
||||
// If already seeking from some node, compare to it.
|
||||
/*if(m_sneak_node_exists)
|
||||
{
|
||||
v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
|
||||
v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
|
||||
f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
|
||||
f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
|
||||
// Ignore if player is not on the same level (likely dropped)
|
||||
if(d_vert_f < 0.15*BS)
|
||||
min_distance_f = d_horiz_f;
|
||||
}*/
|
||||
v3s16 new_sneak_node = m_sneak_node;
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
{
|
||||
v3s16 p = pos_i_bottom + v3s16(x,0,z);
|
||||
v3f pf = intToFloat(p, BS);
|
||||
v2f node_p2df(pf.X, pf.Z);
|
||||
f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
|
||||
f32 max_axis_distance_f = MYMAX(
|
||||
fabs(player_p2df.X-node_p2df.X),
|
||||
fabs(player_p2df.Y-node_p2df.Y));
|
||||
|
||||
if(distance_f > min_distance_f ||
|
||||
max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
|
||||
continue;
|
||||
|
||||
try{
|
||||
// The node to be sneaked on has to be walkable
|
||||
if(nodemgr->get(map.getNode(p)).walkable == false)
|
||||
continue;
|
||||
// And the node above it has to be nonwalkable
|
||||
if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
|
||||
continue;
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
min_distance_f = distance_f;
|
||||
new_sneak_node = p;
|
||||
}
|
||||
|
||||
bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
|
||||
|
||||
if(control.sneak && m_sneak_node_exists)
|
||||
{
|
||||
if(sneak_node_found)
|
||||
m_sneak_node = new_sneak_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sneak_node = new_sneak_node;
|
||||
m_sneak_node_exists = sneak_node_found;
|
||||
}
|
||||
|
||||
/*
|
||||
If sneaking, the player's collision box can be in air, so
|
||||
this has to be set explicitly
|
||||
*/
|
||||
if(sneak_node_found && control.sneak)
|
||||
touching_ground = true;
|
||||
}
|
||||
|
||||
/*
|
||||
Set new position
|
||||
*/
|
||||
setPosition(position);
|
||||
|
||||
/*
|
||||
Report collisions
|
||||
*/
|
||||
if(collision_info)
|
||||
{
|
||||
// Report fall collision
|
||||
if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
|
||||
{
|
||||
CollisionInfo info;
|
||||
info.t = COLLISION_FALL;
|
||||
info.speed = m_speed.Y - old_speed.Y;
|
||||
collision_info->push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
if(!touching_ground_was && touching_ground){
|
||||
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
|
||||
m_gamedef->event()->put(e);
|
||||
}
|
||||
|
||||
{
|
||||
camera_barely_in_ceiling = false;
|
||||
v3s16 camera_np = floatToInt(getEyePosition(), BS);
|
||||
MapNode n = map.getNodeNoEx(camera_np);
|
||||
if(n.getContent() != CONTENT_IGNORE){
|
||||
if(nodemgr->get(n).walkable){
|
||||
camera_barely_in_ceiling = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
|
||||
{
|
||||
move(dtime, map, pos_max_d, NULL);
|
||||
}
|
||||
|
||||
void LocalPlayer::applyControl(float dtime)
|
||||
{
|
||||
// Clear stuff
|
||||
swimming_up = false;
|
||||
|
||||
// Random constants
|
||||
f32 walk_acceleration = 4.0 * BS;
|
||||
f32 walkspeed_max = 4.0 * BS;
|
||||
|
||||
setPitch(control.pitch);
|
||||
setYaw(control.yaw);
|
||||
|
||||
v3f move_direction = v3f(0,0,1);
|
||||
move_direction.rotateXZBy(getYaw());
|
||||
|
||||
v3f speed = v3f(0,0,0);
|
||||
|
||||
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
|
||||
bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
|
||||
|
||||
bool free_move = fly_allowed && g_settings->getBool("free_move");
|
||||
bool fast_move = fast_allowed && g_settings->getBool("fast_move");
|
||||
bool continuous_forward = g_settings->getBool("continuous_forward");
|
||||
|
||||
if(free_move || is_climbing)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = 0;
|
||||
setSpeed(speed);
|
||||
}
|
||||
|
||||
// Whether superspeed mode is used or not
|
||||
bool superspeed = false;
|
||||
|
||||
// If free movement and fast movement, always move fast
|
||||
if(free_move && fast_move)
|
||||
superspeed = true;
|
||||
|
||||
// Auxiliary button 1 (E)
|
||||
if(control.aux1)
|
||||
{
|
||||
if(free_move)
|
||||
{
|
||||
// In free movement mode, aux1 descends
|
||||
v3f speed = getSpeed();
|
||||
if(fast_move)
|
||||
speed.Y = -20*BS;
|
||||
else
|
||||
speed.Y = -walkspeed_max;
|
||||
setSpeed(speed);
|
||||
}
|
||||
else if(is_climbing)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = -3*BS;
|
||||
setSpeed(speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not free movement but fast is allowed, aux1 is
|
||||
// "Turbo button"
|
||||
if(fast_move)
|
||||
superspeed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(continuous_forward)
|
||||
speed += move_direction;
|
||||
|
||||
if(control.up)
|
||||
{
|
||||
if(continuous_forward)
|
||||
superspeed = true;
|
||||
else
|
||||
speed += move_direction;
|
||||
}
|
||||
if(control.down)
|
||||
{
|
||||
speed -= move_direction;
|
||||
}
|
||||
if(control.left)
|
||||
{
|
||||
speed += move_direction.crossProduct(v3f(0,1,0));
|
||||
}
|
||||
if(control.right)
|
||||
{
|
||||
speed += move_direction.crossProduct(v3f(0,-1,0));
|
||||
}
|
||||
if(control.jump)
|
||||
{
|
||||
if(free_move)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
if(fast_move)
|
||||
speed.Y = 20*BS;
|
||||
else
|
||||
speed.Y = walkspeed_max;
|
||||
setSpeed(speed);
|
||||
}
|
||||
else if(touching_ground)
|
||||
{
|
||||
/*
|
||||
NOTE: The d value in move() affects jump height by
|
||||
raising the height at which the jump speed is kept
|
||||
at its starting value
|
||||
*/
|
||||
v3f speed = getSpeed();
|
||||
if(speed.Y >= -0.5*BS)
|
||||
{
|
||||
speed.Y = 6.5*BS;
|
||||
setSpeed(speed);
|
||||
|
||||
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
|
||||
m_gamedef->event()->put(e);
|
||||
}
|
||||
}
|
||||
// Use the oscillating value for getting out of water
|
||||
// (so that the player doesn't fly on the surface)
|
||||
else if(in_water)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = 1.5*BS;
|
||||
setSpeed(speed);
|
||||
swimming_up = true;
|
||||
}
|
||||
else if(is_climbing)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = 3*BS;
|
||||
setSpeed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
// The speed of the player (Y is ignored)
|
||||
if(superspeed)
|
||||
speed = speed.normalize() * walkspeed_max * 5.0;
|
||||
else if(control.sneak)
|
||||
speed = speed.normalize() * walkspeed_max / 3.0;
|
||||
else
|
||||
speed = speed.normalize() * walkspeed_max;
|
||||
|
||||
f32 inc = walk_acceleration * BS * dtime;
|
||||
|
||||
// Faster acceleration if fast and free movement
|
||||
if(free_move && fast_move)
|
||||
inc = walk_acceleration * BS * dtime * 10;
|
||||
|
||||
// Accelerate to target speed with maximum increment
|
||||
accelerate(speed, inc);
|
||||
}
|
||||
|
||||
v3s16 LocalPlayer::getStandingNodePos()
|
||||
{
|
||||
if(m_sneak_node_exists)
|
||||
return m_sneak_node;
|
||||
return floatToInt(getPosition(), BS);
|
||||
}
|
||||
|
||||
101
src/localplayer.h
Normal file
101
src/localplayer.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 LOCALPLAYER_HEADER
|
||||
#define LOCALPLAYER_HEADER
|
||||
|
||||
#include "player.h"
|
||||
|
||||
struct PlayerControl
|
||||
{
|
||||
PlayerControl()
|
||||
{
|
||||
up = false;
|
||||
down = false;
|
||||
left = false;
|
||||
right = false;
|
||||
jump = false;
|
||||
aux1 = false;
|
||||
sneak = false;
|
||||
pitch = 0;
|
||||
yaw = 0;
|
||||
}
|
||||
PlayerControl(
|
||||
bool a_up,
|
||||
bool a_down,
|
||||
bool a_left,
|
||||
bool a_right,
|
||||
bool a_jump,
|
||||
bool a_aux1,
|
||||
bool a_sneak,
|
||||
float a_pitch,
|
||||
float a_yaw
|
||||
)
|
||||
{
|
||||
up = a_up;
|
||||
down = a_down;
|
||||
left = a_left;
|
||||
right = a_right;
|
||||
jump = a_jump;
|
||||
aux1 = a_aux1;
|
||||
sneak = a_sneak;
|
||||
pitch = a_pitch;
|
||||
yaw = a_yaw;
|
||||
}
|
||||
bool up;
|
||||
bool down;
|
||||
bool left;
|
||||
bool right;
|
||||
bool jump;
|
||||
bool aux1;
|
||||
bool sneak;
|
||||
float pitch;
|
||||
float yaw;
|
||||
};
|
||||
|
||||
class LocalPlayer : public Player
|
||||
{
|
||||
public:
|
||||
LocalPlayer(IGameDef *gamedef);
|
||||
virtual ~LocalPlayer();
|
||||
|
||||
bool isLocal() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void move(f32 dtime, Map &map, f32 pos_max_d,
|
||||
core::list<CollisionInfo> *collision_info);
|
||||
void move(f32 dtime, Map &map, f32 pos_max_d);
|
||||
|
||||
void applyControl(float dtime);
|
||||
|
||||
v3s16 getStandingNodePos();
|
||||
|
||||
PlayerControl control;
|
||||
|
||||
private:
|
||||
// This is used for determining the sneaking range
|
||||
v3s16 m_sneak_node;
|
||||
// Whether the player is allowed to sneak
|
||||
bool m_sneak_node_exists;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -159,4 +159,5 @@ std::ostream actionstream(&actionbuf);
|
||||
std::ostream infostream(&infobuf);
|
||||
std::ostream verbosestream(&verbosebuf);
|
||||
|
||||
bool log_trace_level_enabled = false;
|
||||
|
||||
|
||||
@@ -69,5 +69,10 @@ extern std::ostream actionstream;
|
||||
extern std::ostream infostream;
|
||||
extern std::ostream verbosestream;
|
||||
|
||||
extern bool log_trace_level_enabled;
|
||||
|
||||
#define TRACESTREAM(x){ if(log_trace_level_enabled) verbosestream x; }
|
||||
#define TRACEDO(x){ if(log_trace_level_enabled){ x ;} }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -89,6 +89,11 @@ if(LUA_ANSI)
|
||||
set(COMMON_CFLAGS "${COMMON_CFLAGS} -DLUA_ANSI")
|
||||
endif(LUA_ANSI)
|
||||
|
||||
#
|
||||
# COMMON_CFLAGS has no effect without this line
|
||||
#
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_CFLAGS}")
|
||||
|
||||
#
|
||||
# standard flags to use for each build type.
|
||||
#
|
||||
|
||||
@@ -20,29 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef LUAENTITY_COMMON_HEADER
|
||||
#define LUAENTITY_COMMON_HEADER
|
||||
|
||||
#include <string>
|
||||
#include "irrlichttypes.h"
|
||||
#include <iostream>
|
||||
|
||||
struct LuaEntityProperties
|
||||
{
|
||||
// Values are BS=1
|
||||
s16 hp_max;
|
||||
bool physical;
|
||||
float weight;
|
||||
core::aabbox3d<f32> collisionbox;
|
||||
std::string visual;
|
||||
v2f visual_size;
|
||||
core::array<std::string> textures;
|
||||
v2s16 spritediv;
|
||||
v2s16 initial_sprite_basepos;
|
||||
|
||||
LuaEntityProperties();
|
||||
std::string dump();
|
||||
void serialize(std::ostream &os);
|
||||
void deSerialize(std::istream &is);
|
||||
};
|
||||
|
||||
#define LUAENTITY_CMD_UPDATE_POSITION 0
|
||||
#define LUAENTITY_CMD_SET_TEXTURE_MOD 1
|
||||
#define LUAENTITY_CMD_SET_SPRITE 2
|
||||
|
||||
127
src/main.cpp
127
src/main.cpp
@@ -790,7 +790,7 @@ int main(int argc, char *argv[])
|
||||
allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING,
|
||||
"Same as --world (deprecated)"));
|
||||
allowed_options.insert("world", ValueSpec(VALUETYPE_STRING,
|
||||
"Set world path (implies local game)"));
|
||||
"Set world path (implies local game) ('list' lists all)"));
|
||||
allowed_options.insert("worldname", ValueSpec(VALUETYPE_STRING,
|
||||
"Set world by name (implies local game)"));
|
||||
allowed_options.insert("info", ValueSpec(VALUETYPE_FLAG,
|
||||
@@ -854,8 +854,9 @@ int main(int argc, char *argv[])
|
||||
// If trace is enabled, enable logging of certain things
|
||||
if(cmd_args.getFlag("trace")){
|
||||
dstream<<"Enabling trace level debug output"<<std::endl;
|
||||
dout_con_ptr = &verbosestream;
|
||||
socket_enable_debug_output = true;
|
||||
log_trace_level_enabled = true;
|
||||
dout_con_ptr = &verbosestream; // this is somewhat old crap
|
||||
socket_enable_debug_output = true; // socket doesn't use log.h
|
||||
}
|
||||
// In certain cases, output info level on stderr
|
||||
if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
|
||||
@@ -909,6 +910,14 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
// List worlds if requested
|
||||
if(cmd_args.exists("world") && cmd_args.get("world") == "list"){
|
||||
dstream<<"Available worlds:"<<std::endl;
|
||||
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
|
||||
print_worldspecs(worldspecs, dstream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Print startup message
|
||||
infostream<<PROJECT_NAME<<
|
||||
" with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
|
||||
@@ -1029,6 +1038,31 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// If a world name was specified, convert it to a path
|
||||
if(commanded_worldname != ""){
|
||||
// Get information about available worlds
|
||||
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
|
||||
bool found = false;
|
||||
for(u32 i=0; i<worldspecs.size(); i++){
|
||||
std::string name = worldspecs[i].name;
|
||||
if(name == commanded_worldname){
|
||||
if(commanded_world != ""){
|
||||
dstream<<"--worldname takes precedence over previously "
|
||||
<<"selected world."<<std::endl;
|
||||
}
|
||||
commanded_world = worldspecs[i].path;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
dstream<<"World '"<<commanded_worldname<<"' not "
|
||||
<<"available. Available worlds:"<<std::endl;
|
||||
print_worldspecs(worldspecs, dstream);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Gamespec
|
||||
SubgameSpec commanded_gamespec;
|
||||
if(cmd_args.exists("gameid")){
|
||||
@@ -1066,25 +1100,6 @@ int main(int argc, char *argv[])
|
||||
infostream<<"Using commanded world path ["<<world_path<<"]"
|
||||
<<std::endl;
|
||||
}
|
||||
// If a world name was specified, select it
|
||||
else if(commanded_worldname != ""){
|
||||
// Get information about available worlds
|
||||
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
|
||||
world_path = "";
|
||||
for(u32 i=0; i<worldspecs.size(); i++){
|
||||
std::string name = worldspecs[i].name;
|
||||
if(name == commanded_worldname){
|
||||
world_path = worldspecs[i].path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(world_path == ""){
|
||||
dstream<<"World '"<<commanded_worldname<<"' not "
|
||||
<<"available. Available worlds:"<<std::endl;
|
||||
print_worldspecs(worldspecs, dstream);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// No world was specified; try to select it automatically
|
||||
else
|
||||
{
|
||||
@@ -1135,43 +1150,44 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
verbosestream<<"Using world path ["<<world_path<<"]"<<std::endl;
|
||||
|
||||
// We need a gameid.
|
||||
std::string gameid;
|
||||
verbosestream<<"Determining gameid"<<std::endl;
|
||||
// We need a gamespec.
|
||||
SubgameSpec gamespec;
|
||||
verbosestream<<"Determining gameid/gamespec"<<std::endl;
|
||||
// If world doesn't exist
|
||||
if(!getWorldExists(world_path))
|
||||
{
|
||||
// Try to take gamespec from command line
|
||||
if(commanded_gamespec.isValid()){
|
||||
gameid = commanded_gamespec.id;
|
||||
infostream<<"Using commanded gameid ["<<gameid<<"]"<<std::endl;
|
||||
gamespec = commanded_gamespec;
|
||||
infostream<<"Using commanded gameid ["<<gamespec.id<<"]"<<std::endl;
|
||||
}
|
||||
// Otherwise we will be using "minetest"
|
||||
else{
|
||||
gameid = g_settings->get("default_game");
|
||||
infostream<<"Using default gameid ["<<gameid<<"]"<<std::endl;
|
||||
gamespec = findSubgame(g_settings->get("default_game"));
|
||||
infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl;
|
||||
}
|
||||
}
|
||||
// If world exists
|
||||
// World exists
|
||||
else
|
||||
{
|
||||
// Otherwise read from the world
|
||||
std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
|
||||
gameid = world_gameid;
|
||||
if(commanded_gamespec.isValid() &&
|
||||
commanded_gamespec.id != world_gameid){
|
||||
gameid = commanded_gamespec.id;
|
||||
errorstream<<"WARNING: Using commanded gameid ["<<gameid<<"]"
|
||||
<<" instead of world gameid ["<<world_gameid
|
||||
<<"]"<<std::endl;
|
||||
// If commanded to use a gameid, do so
|
||||
if(commanded_gamespec.isValid()){
|
||||
gamespec = commanded_gamespec;
|
||||
if(commanded_gamespec.id != world_gameid){
|
||||
errorstream<<"WARNING: Using commanded gameid ["
|
||||
<<gamespec.id<<"]"<<" instead of world gameid ["
|
||||
<<world_gameid<<"]"<<std::endl;
|
||||
}
|
||||
} else{
|
||||
infostream<<"Using world gameid ["<<gameid<<"]"<<std::endl;
|
||||
// If world contains an embedded game, use it;
|
||||
// Otherwise find world from local system.
|
||||
gamespec = findWorldSubgame(world_path);
|
||||
infostream<<"Using world gameid ["<<gamespec.id<<"]"<<std::endl;
|
||||
}
|
||||
}
|
||||
verbosestream<<"Finding subgame ["<<gameid<<"]"<<std::endl;
|
||||
SubgameSpec gamespec = findSubgame(gameid);
|
||||
if(!gamespec.isValid()){
|
||||
errorstream<<"Subgame ["<<gameid<<"] could not be found."
|
||||
errorstream<<"Subgame ["<<gamespec.id<<"] could not be found."
|
||||
<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
@@ -1198,8 +1214,6 @@ int main(int argc, char *argv[])
|
||||
address = "";
|
||||
else if(cmd_args.exists("address"))
|
||||
address = cmd_args.get("address");
|
||||
else if(cmd_args.exists("world"))
|
||||
address = "";
|
||||
|
||||
std::string playername = g_settings->get("name");
|
||||
if(cmd_args.exists("name"))
|
||||
@@ -1213,10 +1227,16 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Resolution selection
|
||||
|
||||
bool fullscreen = false;
|
||||
bool fullscreen = g_settings->getBool("fullscreen");
|
||||
u16 screenW = g_settings->getU16("screenW");
|
||||
u16 screenH = g_settings->getU16("screenH");
|
||||
|
||||
// bpp, fsaa, vsync
|
||||
|
||||
bool vsync = g_settings->getBool("vsync");
|
||||
u16 bits = g_settings->getU16("fullscreen_bpp");
|
||||
u16 fsaa = g_settings->getU16("fsaa");
|
||||
|
||||
// Determine driver
|
||||
|
||||
video::E_DRIVER_TYPE driverType;
|
||||
@@ -1249,9 +1269,18 @@ int main(int argc, char *argv[])
|
||||
MyEventReceiver receiver;
|
||||
|
||||
IrrlichtDevice *device;
|
||||
device = createDevice(driverType,
|
||||
core::dimension2d<u32>(screenW, screenH),
|
||||
16, fullscreen, false, false, &receiver);
|
||||
|
||||
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
||||
params.DriverType = driverType;
|
||||
params.WindowSize = core::dimension2d<u32>(screenW, screenH);
|
||||
params.Bits = bits;
|
||||
params.AntiAlias = fsaa;
|
||||
params.Fullscreen = fullscreen;
|
||||
params.Stencilbuffer = false;
|
||||
params.Vsync = vsync;
|
||||
params.EventReceiver = &receiver;
|
||||
|
||||
device = createDeviceEx(params);
|
||||
|
||||
if (device == 0)
|
||||
return 1; // could not create selected driver.
|
||||
@@ -1587,7 +1616,7 @@ int main(int argc, char *argv[])
|
||||
continue;
|
||||
}
|
||||
// Load gamespec for required game
|
||||
gamespec = findSubgame(worldspec.gameid);
|
||||
gamespec = findWorldSubgame(worldspec.path);
|
||||
if(!gamespec.isValid() && !commanded_gamespec.isValid()){
|
||||
error_message = L"Could not find or load game \""
|
||||
+ narrow_to_wide(worldspec.gameid) + L"\"";
|
||||
|
||||
338
src/map.cpp
338
src/map.cpp
@@ -691,6 +691,11 @@ void Map::updateLighting(enum LightBank bank,
|
||||
|
||||
core::map<v3s16, u8> unlight_from;
|
||||
|
||||
int num_bottom_invalid = 0;
|
||||
|
||||
{
|
||||
//TimeTaker t("first stuff");
|
||||
|
||||
core::map<v3s16, MapBlock*>::Iterator i;
|
||||
i = a_blocks.getIterator();
|
||||
for(; i.atEnd() == false; i++)
|
||||
@@ -704,6 +709,7 @@ void Map::updateLighting(enum LightBank bank,
|
||||
break;
|
||||
|
||||
v3s16 pos = block->getPos();
|
||||
v3s16 posnodes = block->getPosRelative();
|
||||
modified_blocks.insert(pos, block);
|
||||
|
||||
blocks_to_update.insert(pos, block);
|
||||
@@ -718,20 +724,23 @@ void Map::updateLighting(enum LightBank bank,
|
||||
|
||||
try{
|
||||
v3s16 p(x,y,z);
|
||||
MapNode n = block->getNode(v3s16(x,y,z));
|
||||
MapNode n = block->getNode(p);
|
||||
u8 oldlight = n.getLight(bank, nodemgr);
|
||||
n.setLight(bank, 0, nodemgr);
|
||||
block->setNode(v3s16(x,y,z), n);
|
||||
block->setNode(p, n);
|
||||
|
||||
// If node sources light, add to list
|
||||
u8 source = nodemgr->get(n).light_source;
|
||||
if(source != 0)
|
||||
light_sources[p + posnodes] = true;
|
||||
|
||||
// Collect borders for unlighting
|
||||
if(x==0 || x == MAP_BLOCKSIZE-1
|
||||
if((x==0 || x == MAP_BLOCKSIZE-1
|
||||
|| y==0 || y == MAP_BLOCKSIZE-1
|
||||
|| z==0 || z == MAP_BLOCKSIZE-1)
|
||||
&& oldlight != 0)
|
||||
{
|
||||
v3s16 p_map = p + v3s16(
|
||||
MAP_BLOCKSIZE*pos.X,
|
||||
MAP_BLOCKSIZE*pos.Y,
|
||||
MAP_BLOCKSIZE*pos.Z);
|
||||
v3s16 p_map = p + posnodes;
|
||||
unlight_from.insert(p_map, oldlight);
|
||||
}
|
||||
}
|
||||
@@ -751,6 +760,9 @@ void Map::updateLighting(enum LightBank bank,
|
||||
{
|
||||
bool bottom_valid = block->propagateSunlight(light_sources);
|
||||
|
||||
if(!bottom_valid)
|
||||
num_bottom_invalid++;
|
||||
|
||||
// If bottom is valid, we're done.
|
||||
if(bottom_valid)
|
||||
break;
|
||||
@@ -783,7 +795,9 @@ void Map::updateLighting(enum LightBank bank,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Enable this to disable proper lighting for speeding up map
|
||||
generation for testing or whatever
|
||||
@@ -803,38 +817,43 @@ void Map::updateLighting(enum LightBank bank,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
{
|
||||
TimeTaker timer("unspreadLight");
|
||||
//TimeTaker timer("unspreadLight");
|
||||
unspreadLight(bank, unlight_from, light_sources, modified_blocks);
|
||||
}
|
||||
|
||||
if(debug)
|
||||
/*if(debug)
|
||||
{
|
||||
u32 diff = modified_blocks.size() - count_was;
|
||||
count_was = modified_blocks.size();
|
||||
infostream<<"unspreadLight modified "<<diff<<std::endl;
|
||||
}
|
||||
}*/
|
||||
|
||||
{
|
||||
TimeTaker timer("spreadLight");
|
||||
//TimeTaker timer("spreadLight");
|
||||
spreadLight(bank, light_sources, modified_blocks);
|
||||
}
|
||||
|
||||
if(debug)
|
||||
/*if(debug)
|
||||
{
|
||||
u32 diff = modified_blocks.size() - count_was;
|
||||
count_was = modified_blocks.size();
|
||||
infostream<<"spreadLight modified "<<diff<<std::endl;
|
||||
}
|
||||
}*/
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
{
|
||||
//MapVoxelManipulator vmanip(this);
|
||||
|
||||
|
||||
// Make a manual voxel manipulator and load all the blocks
|
||||
// that touch the requested blocks
|
||||
ManualMapVoxelManipulator vmanip(this);
|
||||
|
||||
{
|
||||
//TimeTaker timer("initialEmerge");
|
||||
|
||||
core::map<v3s16, MapBlock*>::Iterator i;
|
||||
i = blocks_to_update.getIterator();
|
||||
for(; i.atEnd() == false; i++)
|
||||
@@ -868,6 +887,7 @@ void Map::updateLighting(enum LightBank bank,
|
||||
// Lighting of block will be updated completely
|
||||
block->setLightingExpired(false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//TimeTaker timer("unSpreadLight");
|
||||
@@ -884,6 +904,7 @@ void Map::updateLighting(enum LightBank bank,
|
||||
/*infostream<<"emerge_time="<<emerge_time<<std::endl;
|
||||
emerge_time = 0;*/
|
||||
}
|
||||
#endif
|
||||
|
||||
//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
|
||||
}
|
||||
@@ -902,7 +923,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
MapBlock *block = i.getNode()->getValue();
|
||||
block->updateDayNightDiff();
|
||||
block->expireDayNightDiff();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,27 +1009,18 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
|
||||
}
|
||||
|
||||
/*
|
||||
Remove node metadata
|
||||
*/
|
||||
|
||||
removeNodeMetadata(p);
|
||||
|
||||
/*
|
||||
Set the node on the map
|
||||
*/
|
||||
|
||||
setNode(p, n);
|
||||
|
||||
/*
|
||||
Add intial metadata
|
||||
*/
|
||||
|
||||
std::string metadata_name = nodemgr->get(n).metadata_name;
|
||||
if(metadata_name != ""){
|
||||
NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
|
||||
if(!meta){
|
||||
errorstream<<"Failed to create node metadata \""
|
||||
<<metadata_name<<"\""<<std::endl;
|
||||
} else {
|
||||
setNodeMetadata(p, meta);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If node is under sunlight and doesn't let sunlight through,
|
||||
take all sunlighted nodes under it and clear light from them
|
||||
@@ -1063,7 +1075,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
MapBlock *block = i.getNode()->getValue();
|
||||
block->updateDayNightDiff();
|
||||
block->expireDayNightDiff();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1239,7 +1251,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
MapBlock *block = i.getNode()->getValue();
|
||||
block->updateDayNightDiff();
|
||||
block->expireDayNightDiff();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1331,12 +1343,12 @@ bool Map::removeNodeWithEvent(v3s16 p)
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool Map::dayNightDiffed(v3s16 blockpos)
|
||||
bool Map::getDayNightDiff(v3s16 blockpos)
|
||||
{
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(0,0,0);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
@@ -1344,21 +1356,21 @@ bool Map::dayNightDiffed(v3s16 blockpos)
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(-1,0,0);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(0,-1,0);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(0,0,-1);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
@@ -1366,21 +1378,21 @@ bool Map::dayNightDiffed(v3s16 blockpos)
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(1,0,0);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(0,1,0);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
try{
|
||||
v3s16 p = blockpos + v3s16(0,0,1);
|
||||
MapBlock *b = getBlockNoCreate(p);
|
||||
if(b->dayNightDiffed())
|
||||
if(b->getDayNightDiff())
|
||||
return true;
|
||||
}
|
||||
catch(InvalidPositionException &e){}
|
||||
@@ -1832,7 +1844,7 @@ NodeMetadata* Map::getNodeMetadata(v3s16 p)
|
||||
<<std::endl;
|
||||
return NULL;
|
||||
}
|
||||
NodeMetadata *meta = block->m_node_metadata->get(p_rel);
|
||||
NodeMetadata *meta = block->m_node_metadata.get(p_rel);
|
||||
return meta;
|
||||
}
|
||||
|
||||
@@ -1852,7 +1864,7 @@ void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
|
||||
<<std::endl;
|
||||
return;
|
||||
}
|
||||
block->m_node_metadata->set(p_rel, meta);
|
||||
block->m_node_metadata.set(p_rel, meta);
|
||||
}
|
||||
|
||||
void Map::removeNodeMetadata(v3s16 p)
|
||||
@@ -1866,36 +1878,7 @@ void Map::removeNodeMetadata(v3s16 p)
|
||||
<<std::endl;
|
||||
return;
|
||||
}
|
||||
block->m_node_metadata->remove(p_rel);
|
||||
}
|
||||
|
||||
void Map::nodeMetadataStep(float dtime,
|
||||
core::map<v3s16, MapBlock*> &changed_blocks)
|
||||
{
|
||||
/*
|
||||
NOTE:
|
||||
Currently there is no way to ensure that all the necessary
|
||||
blocks are loaded when this is run. (They might get unloaded)
|
||||
NOTE: ^- Actually, that might not be so. In a quick test it
|
||||
reloaded a block with a furnace when I walked back to it from
|
||||
a distance.
|
||||
*/
|
||||
core::map<v2s16, MapSector*>::Iterator si;
|
||||
si = m_sectors.getIterator();
|
||||
for(; si.atEnd() == false; si++)
|
||||
{
|
||||
MapSector *sector = si.getNode()->getValue();
|
||||
core::list< MapBlock * > sectorblocks;
|
||||
sector->getBlocks(sectorblocks);
|
||||
core::list< MapBlock * >::Iterator i;
|
||||
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
|
||||
{
|
||||
MapBlock *block = *i;
|
||||
bool changed = block->m_node_metadata->step(dtime);
|
||||
if(changed)
|
||||
changed_blocks[block->getPos()] = block;
|
||||
}
|
||||
}
|
||||
block->m_node_metadata.remove(p_rel);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2048,12 +2031,29 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
|
||||
{
|
||||
bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
|
||||
if(enable_mapgen_debug_info)
|
||||
infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
|
||||
<<blockpos.Z<<")"<<std::endl;
|
||||
infostream<<"initBlockMake(): "
|
||||
<<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
|
||||
<<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
|
||||
<<std::endl;
|
||||
|
||||
//s16 chunksize = 3;
|
||||
//v3s16 chunk_offset(-1,-1,-1);
|
||||
//s16 chunksize = 4;
|
||||
//v3s16 chunk_offset(-1,-1,-1);
|
||||
s16 chunksize = 5;
|
||||
v3s16 chunk_offset(-2,-2,-2);
|
||||
v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
|
||||
v3s16 blockpos_min = blockpos_div * chunksize;
|
||||
v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
|
||||
blockpos_min += chunk_offset;
|
||||
blockpos_max += chunk_offset;
|
||||
|
||||
//v3s16 extra_borders(1,1,1);
|
||||
v3s16 extra_borders(1,1,1);
|
||||
|
||||
// Do nothing if not inside limits (+-1 because of neighbors)
|
||||
if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
|
||||
blockpos_over_limit(blockpos + v3s16(1,1,1)))
|
||||
if(blockpos_over_limit(blockpos_min - extra_borders) ||
|
||||
blockpos_over_limit(blockpos_max + extra_borders))
|
||||
{
|
||||
data->no_op = true;
|
||||
return;
|
||||
@@ -2061,7 +2061,9 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
|
||||
|
||||
data->no_op = false;
|
||||
data->seed = m_seed;
|
||||
data->blockpos = blockpos;
|
||||
data->blockpos_min = blockpos_min;
|
||||
data->blockpos_max = blockpos_max;
|
||||
data->blockpos_requested = blockpos;
|
||||
data->nodedef = m_gamedef->ndef();
|
||||
|
||||
/*
|
||||
@@ -2070,17 +2072,20 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
|
||||
{
|
||||
//TimeTaker timer("initBlockMake() create area");
|
||||
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
for(s16 x=blockpos_min.X-extra_borders.X;
|
||||
x<=blockpos_max.X+extra_borders.X; x++)
|
||||
for(s16 z=blockpos_min.Z-extra_borders.Z;
|
||||
z<=blockpos_max.Z+extra_borders.Z; z++)
|
||||
{
|
||||
v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
|
||||
v2s16 sectorpos(x, z);
|
||||
// Sector metadata is loaded from disk if not already loaded.
|
||||
ServerMapSector *sector = createSector(sectorpos);
|
||||
assert(sector);
|
||||
|
||||
for(s16 y=-1; y<=1; y++)
|
||||
for(s16 y=blockpos_min.Y-extra_borders.Y;
|
||||
y<=blockpos_max.Y+extra_borders.Y; y++)
|
||||
{
|
||||
v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
|
||||
v3s16 p(x,y,z);
|
||||
//MapBlock *block = createBlock(p);
|
||||
// 1) get from memory, 2) load from disk
|
||||
MapBlock *block = emergeBlock(p, false);
|
||||
@@ -2114,8 +2119,8 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
|
||||
*/
|
||||
|
||||
// The area that contains this block and it's neighbors
|
||||
v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
|
||||
v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
|
||||
v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
|
||||
v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
|
||||
|
||||
data->vmanip = new ManualMapVoxelManipulator(this);
|
||||
//data->vmanip->setMap(this);
|
||||
@@ -2132,9 +2137,14 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
|
||||
MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
|
||||
core::map<v3s16, MapBlock*> &changed_blocks)
|
||||
{
|
||||
v3s16 blockpos = data->blockpos;
|
||||
/*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
|
||||
<<blockpos.Z<<")"<<std::endl;*/
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
v3s16 blockpos_requested = data->blockpos_requested;
|
||||
/*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
|
||||
<<blockpos_requested.Y<<","
|
||||
<<blockpos_requested.Z<<")"<<std::endl;*/
|
||||
|
||||
v3s16 extra_borders(1,1,1);
|
||||
|
||||
if(data->no_op)
|
||||
{
|
||||
@@ -2148,11 +2158,14 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
|
||||
data->vmanip.print(infostream);*/
|
||||
|
||||
// Make sure affected blocks are loaded
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
for(s16 y=-1; y<=1; y++)
|
||||
for(s16 x=blockpos_min.X-extra_borders.X;
|
||||
x<=blockpos_max.X+extra_borders.X; x++)
|
||||
for(s16 z=blockpos_min.Z-extra_borders.Z;
|
||||
z<=blockpos_max.Z+extra_borders.Z; z++)
|
||||
for(s16 y=blockpos_min.Y-extra_borders.Y;
|
||||
y<=blockpos_max.Y+extra_borders.Y; y++)
|
||||
{
|
||||
v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
|
||||
v3s16 p(x, y, z);
|
||||
// Load from disk if not already in memory
|
||||
emergeBlock(p, false);
|
||||
}
|
||||
@@ -2179,107 +2192,59 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
|
||||
v3s16 p = data->transforming_liquid.pop_front();
|
||||
m_transforming_liquid.push_back(p);
|
||||
}
|
||||
|
||||
/*
|
||||
Get central block
|
||||
*/
|
||||
MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
|
||||
assert(block);
|
||||
|
||||
/*
|
||||
Set is_underground flag for lighting with sunlight.
|
||||
|
||||
Refer to map generator heuristics.
|
||||
|
||||
NOTE: This is done in initChunkMake
|
||||
*/
|
||||
//block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
|
||||
|
||||
|
||||
/*
|
||||
Add sunlight to central block.
|
||||
This makes in-dark-spawning monsters to not flood the whole thing.
|
||||
Do not spread the light, though.
|
||||
*/
|
||||
/*core::map<v3s16, bool> light_sources;
|
||||
bool black_air_left = false;
|
||||
block->propagateSunlight(light_sources, true, &black_air_left);*/
|
||||
|
||||
/*
|
||||
NOTE: Lighting and object adding shouldn't really be here, but
|
||||
lighting is a bit tricky to move properly to makeBlock.
|
||||
TODO: Do this the right way anyway, that is, move it to makeBlock.
|
||||
- There needs to be some way for makeBlock to report back if
|
||||
the lighting update is going further down because of the
|
||||
new block blocking light
|
||||
Do stuff in central blocks
|
||||
*/
|
||||
|
||||
/*
|
||||
Update lighting
|
||||
NOTE: This takes ~60ms, TODO: Investigate why
|
||||
*/
|
||||
{
|
||||
#if 0
|
||||
TimeTaker t("finishBlockMake lighting update");
|
||||
|
||||
core::map<v3s16, MapBlock*> lighting_update_blocks;
|
||||
#if 1
|
||||
// Center block
|
||||
lighting_update_blocks.insert(block->getPos(), block);
|
||||
|
||||
/*{
|
||||
s16 x = 0;
|
||||
s16 z = 0;
|
||||
v3s16 p = block->getPos()+v3s16(x,1,z);
|
||||
lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
|
||||
}*/
|
||||
#endif
|
||||
#if 0
|
||||
// All modified blocks
|
||||
// NOTE: Should this be done? If this is not done, then the lighting
|
||||
// of the others will be updated in a different place, one by one, i
|
||||
// think... or they might not? Well, at least they are left marked as
|
||||
// "lighting expired"; it seems that is not handled at all anywhere,
|
||||
// so enabling this will slow it down A LOT because otherwise it
|
||||
// would not do this at all. This causes the black trees.
|
||||
for(core::map<v3s16, MapBlock*>::Iterator
|
||||
i = changed_blocks.getIterator();
|
||||
i.atEnd() == false; i++)
|
||||
|
||||
// Center blocks
|
||||
for(s16 x=blockpos_min.X-extra_borders.X;
|
||||
x<=blockpos_max.X+extra_borders.X; x++)
|
||||
for(s16 z=blockpos_min.Z-extra_borders.Z;
|
||||
z<=blockpos_max.Z+extra_borders.Z; z++)
|
||||
for(s16 y=blockpos_min.Y-extra_borders.Y;
|
||||
y<=blockpos_max.Y+extra_borders.Y; y++)
|
||||
{
|
||||
lighting_update_blocks.insert(i.getNode()->getKey(),
|
||||
i.getNode()->getValue());
|
||||
v3s16 p(x, y, z);
|
||||
MapBlock *block = getBlockNoCreateNoEx(p);
|
||||
assert(block);
|
||||
lighting_update_blocks.insert(block->getPos(), block);
|
||||
}
|
||||
/*// Also force-add all the upmost blocks for proper sunlight
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
{
|
||||
v3s16 p = block->getPos()+v3s16(x,1,z);
|
||||
lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
|
||||
}*/
|
||||
#endif
|
||||
|
||||
updateLighting(lighting_update_blocks, changed_blocks);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Set lighting to non-expired state in all of them.
|
||||
This is cheating, but it is not fast enough if all of them
|
||||
would actually be updated.
|
||||
*/
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 y=-1; y<=1; y++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
for(s16 x=blockpos_min.X-extra_borders.X;
|
||||
x<=blockpos_max.X+extra_borders.X; x++)
|
||||
for(s16 z=blockpos_min.Z-extra_borders.Z;
|
||||
z<=blockpos_max.Z+extra_borders.Z; z++)
|
||||
for(s16 y=blockpos_min.Y-extra_borders.Y;
|
||||
y<=blockpos_max.Y+extra_borders.Y; y++)
|
||||
{
|
||||
v3s16 p = block->getPos()+v3s16(x,y,z);
|
||||
v3s16 p(x, y, z);
|
||||
getBlockNoCreateNoEx(p)->setLightingExpired(false);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(enable_mapgen_debug_info == false)
|
||||
t.stop(true); // Hide output
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Add random objects to block
|
||||
*/
|
||||
mapgen::add_random_objects(block);
|
||||
|
||||
/*
|
||||
Go through changed blocks
|
||||
*/
|
||||
@@ -2291,18 +2256,26 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
|
||||
/*
|
||||
Update day/night difference cache of the MapBlocks
|
||||
*/
|
||||
block->updateDayNightDiff();
|
||||
block->expireDayNightDiff();
|
||||
/*
|
||||
Set block as modified
|
||||
*/
|
||||
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
||||
"finishBlockMake updateDayNightDiff");
|
||||
"finishBlockMake expireDayNightDiff");
|
||||
}
|
||||
|
||||
/*
|
||||
Set central block as generated
|
||||
Set central blocks as generated
|
||||
*/
|
||||
block->setGenerated(true);
|
||||
for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
|
||||
for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
|
||||
for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
|
||||
{
|
||||
v3s16 p(x, y, z);
|
||||
MapBlock *block = getBlockNoCreateNoEx(p);
|
||||
assert(block);
|
||||
block->setGenerated(true);
|
||||
}
|
||||
|
||||
/*
|
||||
Save changed parts of map
|
||||
@@ -2310,19 +2283,23 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
|
||||
*/
|
||||
//save(MOD_STATE_WRITE_AT_UNLOAD);
|
||||
|
||||
/*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
|
||||
<<blockpos.Z<<")"<<std::endl;*/
|
||||
/*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
|
||||
<<","<<blockpos_requested.Y<<","
|
||||
<<blockpos_requested.Z<<")"<<std::endl;*/
|
||||
#if 0
|
||||
if(enable_mapgen_debug_info)
|
||||
{
|
||||
/*
|
||||
Analyze resulting blocks
|
||||
*/
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 y=-1; y<=1; y++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
/*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
|
||||
for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
|
||||
for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
|
||||
for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
|
||||
for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
|
||||
for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
|
||||
{
|
||||
v3s16 p = block->getPos()+v3s16(x,y,z);
|
||||
v3s16 p = v3s16(x,y,z);
|
||||
MapBlock *block = getBlockNoCreateNoEx(p);
|
||||
char spos[20];
|
||||
snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
|
||||
@@ -2332,6 +2309,9 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
|
||||
}
|
||||
#endif
|
||||
|
||||
MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
|
||||
assert(block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
@@ -3477,15 +3457,15 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
|
||||
}
|
||||
catch(InvalidFilenameException &e)
|
||||
{
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
catch(FileNotGoodException &e)
|
||||
{
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
38
src/map.h
38
src/map.h
@@ -98,6 +98,38 @@ struct MapEditEvent
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
VoxelArea getArea()
|
||||
{
|
||||
switch(type){
|
||||
case MEET_ADDNODE:
|
||||
return VoxelArea(p);
|
||||
case MEET_REMOVENODE:
|
||||
return VoxelArea(p);
|
||||
case MEET_BLOCK_NODE_METADATA_CHANGED:
|
||||
{
|
||||
v3s16 np1 = p*MAP_BLOCKSIZE;
|
||||
v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1);
|
||||
return VoxelArea(np1, np2);
|
||||
}
|
||||
case MEET_OTHER:
|
||||
{
|
||||
VoxelArea a;
|
||||
for(core::map<v3s16, bool>::Iterator
|
||||
i = modified_blocks.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
v3s16 p = i.getNode()->getKey();
|
||||
v3s16 np1 = p*MAP_BLOCKSIZE;
|
||||
v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1);
|
||||
a.addPoint(np1);
|
||||
a.addPoint(np2);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
return VoxelArea();
|
||||
}
|
||||
};
|
||||
|
||||
class MapEventReceiver
|
||||
@@ -226,7 +258,7 @@ class Map /*: public NodeContainer*/
|
||||
/*
|
||||
Takes the blocks at the edges into account
|
||||
*/
|
||||
bool dayNightDiffed(v3s16 blockpos);
|
||||
bool getDayNightDiff(v3s16 blockpos);
|
||||
|
||||
//core::aabbox3d<s16> getDisplayedBlockArea();
|
||||
|
||||
@@ -277,9 +309,7 @@ class Map /*: public NodeContainer*/
|
||||
NodeMetadata* getNodeMetadata(v3s16 p);
|
||||
void setNodeMetadata(v3s16 p, NodeMetadata *meta);
|
||||
void removeNodeMetadata(v3s16 p);
|
||||
void nodeMetadataStep(float dtime,
|
||||
core::map<v3s16, MapBlock*> &changed_blocks);
|
||||
|
||||
|
||||
/*
|
||||
Misc.
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "log.h"
|
||||
#include "nameidmapping.h"
|
||||
#include "content_mapnode.h" // For legacy name-id mapping
|
||||
#include "content_nodemeta.h" // For legacy deserialization
|
||||
#ifndef SERVER
|
||||
#include "mapblock_mesh.h"
|
||||
#endif
|
||||
@@ -39,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
|
||||
m_node_metadata(new NodeMetadataList),
|
||||
m_parent(parent),
|
||||
m_pos(pos),
|
||||
m_gamedef(gamedef),
|
||||
@@ -49,6 +49,7 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
|
||||
is_underground(false),
|
||||
m_lighting_expired(true),
|
||||
m_day_night_differs(false),
|
||||
m_day_night_differs_expired(true),
|
||||
m_generated(false),
|
||||
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
|
||||
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
|
||||
@@ -78,8 +79,6 @@ MapBlock::~MapBlock()
|
||||
}
|
||||
#endif
|
||||
|
||||
delete m_node_metadata;
|
||||
|
||||
if(data)
|
||||
delete[] data;
|
||||
}
|
||||
@@ -355,9 +354,11 @@ void MapBlock::copyFrom(VoxelManipulator &dst)
|
||||
getPosRelative(), data_size);
|
||||
}
|
||||
|
||||
void MapBlock::updateDayNightDiff()
|
||||
void MapBlock::actuallyUpdateDayNightDiff()
|
||||
{
|
||||
INodeDefManager *nodemgr = m_gamedef->ndef();
|
||||
// Running this function un-expires m_day_night_differs
|
||||
m_day_night_differs_expired = false;
|
||||
|
||||
if(data == NULL)
|
||||
{
|
||||
@@ -404,6 +405,19 @@ void MapBlock::updateDayNightDiff()
|
||||
m_day_night_differs = differs;
|
||||
}
|
||||
|
||||
void MapBlock::expireDayNightDiff()
|
||||
{
|
||||
INodeDefManager *nodemgr = m_gamedef->ndef();
|
||||
|
||||
if(data == NULL){
|
||||
m_day_night_differs = false;
|
||||
m_day_night_differs_expired = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_day_night_differs_expired = true;
|
||||
}
|
||||
|
||||
s16 MapBlock::getGroundLevel(v2s16 p2d)
|
||||
{
|
||||
if(isDummy())
|
||||
@@ -545,7 +559,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
|
||||
u8 flags = 0;
|
||||
if(is_underground)
|
||||
flags |= 0x01;
|
||||
if(m_day_night_differs)
|
||||
if(getDayNightDiff())
|
||||
flags |= 0x02;
|
||||
if(m_lighting_expired)
|
||||
flags |= 0x04;
|
||||
@@ -589,7 +603,10 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
|
||||
Node metadata
|
||||
*/
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
m_node_metadata->serialize(oss);
|
||||
if(version >= 23)
|
||||
m_node_metadata.serialize(oss);
|
||||
else
|
||||
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
|
||||
compressZlib(oss.str(), os);
|
||||
|
||||
/*
|
||||
@@ -597,6 +614,10 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
|
||||
*/
|
||||
if(disk)
|
||||
{
|
||||
// Node timers
|
||||
if(version >= 23)
|
||||
m_node_timers.serialize(os);
|
||||
|
||||
// Static objects
|
||||
m_static_objects.serialize(os);
|
||||
|
||||
@@ -614,6 +635,8 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
|
||||
if(!ser_ver_supported(version))
|
||||
throw VersionMismatchException("ERROR: MapBlock format not supported");
|
||||
|
||||
m_day_night_differs_expired = false;
|
||||
|
||||
if(version <= 21)
|
||||
{
|
||||
deSerialize_pre22(is, version, disk);
|
||||
@@ -647,12 +670,17 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
decompressZlib(is, oss);
|
||||
std::istringstream iss(oss.str(), std::ios_base::binary);
|
||||
m_node_metadata->deSerialize(iss, m_gamedef);
|
||||
if(version >= 23)
|
||||
m_node_metadata.deSerialize(iss, m_gamedef);
|
||||
else
|
||||
content_nodemeta_deserialize_legacy(iss,
|
||||
&m_node_metadata, &m_node_timers,
|
||||
m_gamedef);
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
|
||||
<<" while deserializing node metadata"<<std::endl;
|
||||
<<" while deserializing node metadata: "<<e.what()<<std::endl;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -660,6 +688,10 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
|
||||
*/
|
||||
if(disk)
|
||||
{
|
||||
// Node timers
|
||||
if(version >= 23)
|
||||
m_node_timers.deSerialize(is);
|
||||
|
||||
// Static objects
|
||||
m_static_objects.deSerialize(is);
|
||||
|
||||
@@ -800,7 +832,7 @@ void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
|
||||
u8 flags = 0;
|
||||
if(is_underground)
|
||||
flags |= 0x01;
|
||||
if(m_day_night_differs)
|
||||
if(getDayNightDiff())
|
||||
flags |= 0x02;
|
||||
if(m_lighting_expired)
|
||||
flags |= 0x04;
|
||||
@@ -839,7 +871,7 @@ void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
|
||||
{
|
||||
try{
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
m_node_metadata->serialize(oss);
|
||||
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
|
||||
os<<serializeString(oss.str());
|
||||
}
|
||||
// This will happen if the string is longer than 65535
|
||||
@@ -852,7 +884,7 @@ void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
|
||||
else
|
||||
{
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
m_node_metadata->serialize(oss);
|
||||
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
|
||||
compressZlib(oss.str(), os);
|
||||
//os<<serializeLongString(oss.str());
|
||||
}
|
||||
@@ -1006,7 +1038,9 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
|
||||
{
|
||||
std::string data = deSerializeString(is);
|
||||
std::istringstream iss(data, std::ios_base::binary);
|
||||
m_node_metadata->deSerialize(iss, m_gamedef);
|
||||
content_nodemeta_deserialize_legacy(iss,
|
||||
&m_node_metadata, &m_node_timers,
|
||||
m_gamedef);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1014,7 +1048,9 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
decompressZlib(is, oss);
|
||||
std::istringstream iss(oss.str(), std::ios_base::binary);
|
||||
m_node_metadata->deSerialize(iss, m_gamedef);
|
||||
content_nodemeta_deserialize_legacy(iss,
|
||||
&m_node_metadata, &m_node_timers,
|
||||
m_gamedef);
|
||||
}
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
|
||||
@@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "constants.h"
|
||||
#include "voxel.h"
|
||||
#include "staticobject.h"
|
||||
#include "nodemetadata.h"
|
||||
#include "nodetimer.h"
|
||||
#include "modifiedstate.h"
|
||||
|
||||
class Map;
|
||||
@@ -360,10 +362,17 @@ class MapBlock /*: public NodeContainer*/
|
||||
Sets m_day_night_differs to appropriate value.
|
||||
These methods don't care about neighboring blocks.
|
||||
*/
|
||||
void updateDayNightDiff();
|
||||
void actuallyUpdateDayNightDiff();
|
||||
/*
|
||||
Call this to schedule what the previous function does to be done
|
||||
when the value is actually needed.
|
||||
*/
|
||||
void expireDayNightDiff();
|
||||
|
||||
bool dayNightDiffed()
|
||||
bool getDayNightDiff()
|
||||
{
|
||||
if(m_day_night_differs_expired)
|
||||
actuallyUpdateDayNightDiff();
|
||||
return m_day_night_differs;
|
||||
}
|
||||
|
||||
@@ -466,7 +475,8 @@ class MapBlock /*: public NodeContainer*/
|
||||
//JMutex mesh_mutex;
|
||||
#endif
|
||||
|
||||
NodeMetadataList *m_node_metadata;
|
||||
NodeMetadataList m_node_metadata;
|
||||
NodeTimerList m_node_timers;
|
||||
StaticObjectList m_static_objects;
|
||||
|
||||
private:
|
||||
@@ -517,6 +527,7 @@ class MapBlock /*: public NodeContainer*/
|
||||
|
||||
// Whether day and night lighting differs
|
||||
bool m_day_night_differs;
|
||||
bool m_day_night_differs_expired;
|
||||
|
||||
bool m_generated;
|
||||
|
||||
|
||||
@@ -183,6 +183,15 @@ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
|
||||
else
|
||||
light = l2;
|
||||
|
||||
// Boost light level for light sources
|
||||
u8 light_source = MYMAX(ndef->get(n).light_source,
|
||||
ndef->get(n2).light_source);
|
||||
//if(light_source >= light)
|
||||
//return decode_light(undiminish_light(light_source));
|
||||
if(light_source > light)
|
||||
//return decode_light(light_source);
|
||||
light = light_source;
|
||||
|
||||
// Make some nice difference to different sides
|
||||
|
||||
// This makes light come from a corner
|
||||
@@ -233,10 +242,13 @@ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
|
||||
u16 ambient_occlusion = 0;
|
||||
u16 light = 0;
|
||||
u16 light_count = 0;
|
||||
u8 light_source_max = 0;
|
||||
for(u32 i=0; i<8; i++)
|
||||
{
|
||||
MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
|
||||
const ContentFeatures &f = ndef->get(n);
|
||||
if(f.light_source > light_source_max)
|
||||
light_source_max = f.light_source;
|
||||
// Check f.solidness because fast-style leaves look
|
||||
// better this way
|
||||
if(f.param_type == CPT_LIGHT && f.solidness != 2)
|
||||
@@ -255,10 +267,23 @@ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
|
||||
|
||||
light /= light_count;
|
||||
|
||||
// Boost brightness around light sources
|
||||
if(decode_light(light_source_max) >= light)
|
||||
//return decode_light(undiminish_light(light_source_max));
|
||||
return decode_light(light_source_max);
|
||||
|
||||
if(ambient_occlusion > 4)
|
||||
{
|
||||
ambient_occlusion -= 4;
|
||||
light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
|
||||
//ambient_occlusion -= 4;
|
||||
//light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
|
||||
float light_amount = (8 - ambient_occlusion) / 4.0;
|
||||
float light_f = (float)light / 255.0;
|
||||
light_f = pow(light_f, 2.2f); // gamma -> linear space
|
||||
light_f = light_f * light_amount;
|
||||
light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
|
||||
if(light_f > 1.0)
|
||||
light_f = 1.0;
|
||||
light = 255.0 * light_f + 0.5;
|
||||
}
|
||||
|
||||
return light;
|
||||
|
||||
1556
src/mapgen.cpp
1556
src/mapgen.cpp
File diff suppressed because it is too large
Load Diff
13
src/mapgen.h
13
src/mapgen.h
@@ -36,12 +36,12 @@ namespace mapgen
|
||||
// Find out if block is completely underground
|
||||
bool block_is_underground(u64 seed, v3s16 blockpos);
|
||||
|
||||
// Get a pseudorandom seed for a position on the map
|
||||
u32 get_blockseed(u64 seed, v3s16 p);
|
||||
|
||||
// Main map generation routine
|
||||
void make_block(BlockMakeData *data);
|
||||
|
||||
// Add objects according to block content
|
||||
void add_random_objects(MapBlock *block);
|
||||
|
||||
// Add a tree
|
||||
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
|
||||
bool is_apple_tree, INodeDefManager *ndef);
|
||||
@@ -49,16 +49,17 @@ namespace mapgen
|
||||
/*
|
||||
These are used by FarMesh
|
||||
*/
|
||||
bool get_have_sand(u64 seed, v2s16 p2d);
|
||||
bool get_have_beach(u64 seed, v2s16 p2d);
|
||||
double tree_amount_2d(u64 seed, v2s16 p);
|
||||
|
||||
|
||||
struct BlockMakeData
|
||||
{
|
||||
bool no_op;
|
||||
ManualMapVoxelManipulator *vmanip; // Destructor deletes
|
||||
u64 seed;
|
||||
v3s16 blockpos;
|
||||
v3s16 blockpos_min;
|
||||
v3s16 blockpos_max;
|
||||
v3s16 blockpos_requested;
|
||||
UniqueQueue<v3s16> transforming_liquid;
|
||||
INodeDefManager *nodedef;
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "mapsector.h"
|
||||
#include "jmutexautolock.h"
|
||||
#ifndef SERVER
|
||||
#include "client.h"
|
||||
#endif
|
||||
#include "exceptions.h"
|
||||
#include "mapblock.h"
|
||||
|
||||
|
||||
111
src/mods.cpp
111
src/mods.cpp
@@ -26,6 +26,64 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "strfnd.h"
|
||||
#include "log.h"
|
||||
|
||||
static void collectMods(const std::string &modspath,
|
||||
std::queue<ModSpec> &mods_satisfied,
|
||||
core::list<ModSpec> &mods_unsorted,
|
||||
std::map<std::string, std::string> &mod_names,
|
||||
std::string indentation)
|
||||
{
|
||||
TRACESTREAM(<<indentation<<"collectMods(\""<<modspath<<"\")"<<std::endl);
|
||||
TRACEDO(indentation += " ");
|
||||
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
|
||||
for(u32 j=0; j<dirlist.size(); j++){
|
||||
if(!dirlist[j].dir)
|
||||
continue;
|
||||
std::string modname = dirlist[j].name;
|
||||
std::string modpath = modspath + DIR_DELIM + modname;
|
||||
TRACESTREAM(<<indentation<<"collectMods: "<<modname<<" at \""<<modpath<<"\""<<std::endl);
|
||||
// Handle modpacks (defined by containing modpack.txt)
|
||||
{
|
||||
std::ifstream is((modpath+DIR_DELIM+"modpack.txt").c_str(),
|
||||
std::ios_base::binary);
|
||||
if(is.good()){
|
||||
// Is a modpack
|
||||
is.close(); // We don't actually need the file
|
||||
// Recurse into it
|
||||
collectMods(modpath, mods_satisfied, mods_unsorted, mod_names, indentation);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Detect mod name conflicts
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator i;
|
||||
i = mod_names.find(modname);
|
||||
if(i != mod_names.end()){
|
||||
std::string s;
|
||||
infostream<<indentation<<"WARNING: Mod name conflict detected: "
|
||||
<<std::endl
|
||||
<<"Already loaded: "<<i->second<<std::endl
|
||||
<<"Will not load: "<<modpath<<std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
std::set<std::string> depends;
|
||||
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
|
||||
std::ios_base::binary);
|
||||
while(is.good()){
|
||||
std::string dep;
|
||||
std::getline(is, dep);
|
||||
dep = trim(dep);
|
||||
if(dep != "")
|
||||
depends.insert(dep);
|
||||
}
|
||||
ModSpec spec(modname, modpath, depends);
|
||||
mods_unsorted.push_back(spec);
|
||||
if(depends.empty())
|
||||
mods_satisfied.push(spec);
|
||||
mod_names[modname] = modpath;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a dependency-sorted list of ModSpecs
|
||||
core::list<ModSpec> getMods(core::list<std::string> &modspaths)
|
||||
throw(ModError)
|
||||
@@ -38,41 +96,7 @@ core::list<ModSpec> getMods(core::list<std::string> &modspaths)
|
||||
for(core::list<std::string>::Iterator i = modspaths.begin();
|
||||
i != modspaths.end(); i++){
|
||||
std::string modspath = *i;
|
||||
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
|
||||
for(u32 j=0; j<dirlist.size(); j++){
|
||||
if(!dirlist[j].dir)
|
||||
continue;
|
||||
std::string modname = dirlist[j].name;
|
||||
std::string modpath = modspath + DIR_DELIM + modname;
|
||||
// Detect mod name conflicts
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator i;
|
||||
i = mod_names.find(modname);
|
||||
if(i != mod_names.end()){
|
||||
std::string s;
|
||||
infostream<<"WARNING: Mod name conflict detected: "
|
||||
<<std::endl
|
||||
<<"Already loaded: "<<i->second<<std::endl
|
||||
<<"Will not load: "<<modpath<<std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
std::set<std::string> depends;
|
||||
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
|
||||
std::ios_base::binary);
|
||||
while(is.good()){
|
||||
std::string dep;
|
||||
std::getline(is, dep);
|
||||
dep = trim(dep);
|
||||
if(dep != "")
|
||||
depends.insert(dep);
|
||||
}
|
||||
ModSpec spec(modname, modpath, depends);
|
||||
mods_unsorted.push_back(spec);
|
||||
if(depends.empty())
|
||||
mods_satisfied.push(spec);
|
||||
mod_names[modname] = modpath;
|
||||
}
|
||||
collectMods(modspath, mods_satisfied, mods_unsorted, mod_names, "");
|
||||
}
|
||||
// Sort by depencencies
|
||||
while(!mods_satisfied.empty()){
|
||||
@@ -107,9 +131,26 @@ core::list<ModSpec> getMods(core::list<std::string> &modspaths)
|
||||
errs<<"."<<std::endl;
|
||||
mods_sorted.push_back(mod);
|
||||
}
|
||||
// If an error occurred, throw an exception
|
||||
if(errs.str().size() != 0){
|
||||
throw ModError(errs.str());
|
||||
}
|
||||
// Print out some debug info
|
||||
TRACESTREAM(<<"Detected mods in load order:"<<std::endl);
|
||||
for(core::list<ModSpec>::Iterator i = mods_sorted.begin();
|
||||
i != mods_sorted.end(); i++){
|
||||
ModSpec &mod = *i;
|
||||
TRACESTREAM(<<"name=\""<<mod.name<<"\" path=\""<<mod.path<<"\"");
|
||||
TRACESTREAM(<<" depends=[");
|
||||
for(std::set<std::string>::iterator i = mod.depends.begin();
|
||||
i != mod.depends.end(); i++)
|
||||
TRACESTREAM(<<" "<<(*i));
|
||||
TRACESTREAM(<<" ] unsatisfied_depends=[");
|
||||
for(std::set<std::string>::iterator i = mod.unsatisfied_depends.begin();
|
||||
i != mod.unsatisfied_depends.end(); i++)
|
||||
TRACESTREAM(<<" "<<(*i));
|
||||
TRACESTREAM(<<" ]"<<std::endl);
|
||||
}
|
||||
return mods_sorted;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,6 @@ void ContentFeatures::reset()
|
||||
diggable = true;
|
||||
climbable = false;
|
||||
buildable_to = false;
|
||||
metadata_name = "";
|
||||
liquid_type = LIQUID_NONE;
|
||||
liquid_alternative_flowing = "";
|
||||
liquid_alternative_source = "";
|
||||
@@ -194,7 +193,7 @@ void ContentFeatures::serialize(std::ostream &os)
|
||||
writeU8(os, diggable);
|
||||
writeU8(os, climbable);
|
||||
writeU8(os, buildable_to);
|
||||
os<<serializeString(metadata_name);
|
||||
os<<serializeString(""); // legacy: used to be metadata_name
|
||||
writeU8(os, liquid_type);
|
||||
os<<serializeString(liquid_alternative_flowing);
|
||||
os<<serializeString(liquid_alternative_source);
|
||||
@@ -248,7 +247,7 @@ void ContentFeatures::deSerialize(std::istream &is)
|
||||
diggable = readU8(is);
|
||||
climbable = readU8(is);
|
||||
buildable_to = readU8(is);
|
||||
metadata_name = deSerializeString(is);
|
||||
deSerializeString(is); // legacy: used to be metadata_name
|
||||
liquid_type = (enum LiquidType)readU8(is);
|
||||
liquid_alternative_flowing = deSerializeString(is);
|
||||
liquid_alternative_source = deSerializeString(is);
|
||||
@@ -380,6 +379,25 @@ class CNodeDefManager: public IWritableNodeDefManager
|
||||
getId(name, id);
|
||||
return id;
|
||||
}
|
||||
virtual void getIds(const std::string &name, std::set<content_t> &result)
|
||||
const
|
||||
{
|
||||
if(name.substr(0,6) != "group:"){
|
||||
content_t id = CONTENT_IGNORE;
|
||||
if(getId(name, id))
|
||||
result.insert(id);
|
||||
return;
|
||||
}
|
||||
std::string group = name.substr(6);
|
||||
for(u16 id=0; id<=MAX_CONTENT; id++)
|
||||
{
|
||||
const ContentFeatures &f = m_content_features[id];
|
||||
if(f.name == "") // Quickly discard undefined nodes
|
||||
continue;
|
||||
if(itemgroup_get(f.groups, group) != 0)
|
||||
result.insert(id);
|
||||
}
|
||||
}
|
||||
virtual const ContentFeatures& get(const std::string &name) const
|
||||
{
|
||||
content_t id = CONTENT_IGNORE;
|
||||
|
||||
@@ -179,8 +179,6 @@ struct ContentFeatures
|
||||
bool climbable;
|
||||
// Player can build on these
|
||||
bool buildable_to;
|
||||
// Metadata name of node (eg. "furnace")
|
||||
std::string metadata_name;
|
||||
// Whether the node is non-liquid, source liquid or flowing liquid
|
||||
enum LiquidType liquid_type;
|
||||
// If the content is liquid, this is the flowing version of the liquid.
|
||||
@@ -238,6 +236,9 @@ class INodeDefManager
|
||||
virtual const ContentFeatures& get(const MapNode &n) const=0;
|
||||
virtual bool getId(const std::string &name, content_t &result) const=0;
|
||||
virtual content_t getId(const std::string &name) const=0;
|
||||
// Allows "group:name" in addition to regular node names
|
||||
virtual void getIds(const std::string &name, std::set<content_t> &result)
|
||||
const=0;
|
||||
virtual const ContentFeatures& get(const std::string &name) const=0;
|
||||
|
||||
virtual void serialize(std::ostream &os)=0;
|
||||
@@ -254,6 +255,9 @@ class IWritableNodeDefManager : public INodeDefManager
|
||||
virtual const ContentFeatures& get(const MapNode &n) const=0;
|
||||
virtual bool getId(const std::string &name, content_t &result) const=0;
|
||||
virtual content_t getId(const std::string &name) const=0;
|
||||
// Allows "group:name" in addition to regular node names
|
||||
virtual void getIds(const std::string &name, std::set<content_t> &result)
|
||||
const=0;
|
||||
// If not found, returns the features of CONTENT_IGNORE
|
||||
virtual const ContentFeatures& get(const std::string &name) const=0;
|
||||
|
||||
|
||||
@@ -19,11 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "nodemetadata.h"
|
||||
#include "utility.h"
|
||||
#include "mapnode.h"
|
||||
#include "exceptions.h"
|
||||
#include "gamedef.h"
|
||||
#include "inventory.h"
|
||||
#include <sstream>
|
||||
#include "content_mapnode.h"
|
||||
#include "log.h"
|
||||
|
||||
/*
|
||||
@@ -31,161 +30,104 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
NodeMetadata::NodeMetadata(IGameDef *gamedef):
|
||||
m_gamedef(gamedef)
|
||||
m_stringvars(),
|
||||
m_inventory(new Inventory(gamedef->idef()))
|
||||
{
|
||||
}
|
||||
|
||||
NodeMetadata::~NodeMetadata()
|
||||
{
|
||||
delete m_inventory;
|
||||
}
|
||||
|
||||
NodeMetadata* NodeMetadata::create(const std::string &name, IGameDef *gamedef)
|
||||
void NodeMetadata::serialize(std::ostream &os) const
|
||||
{
|
||||
// Find factory function
|
||||
core::map<std::string, Factory2>::Node *n;
|
||||
n = m_names.find(name);
|
||||
if(n == NULL)
|
||||
{
|
||||
// If factory is not found, just return.
|
||||
errorstream<<"WARNING: NodeMetadata: No factory for name=\""
|
||||
<<name<<"\""<<std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Try to load the metadata. If it fails, just return.
|
||||
try
|
||||
{
|
||||
Factory2 f2 = n->getValue();
|
||||
NodeMetadata *meta = (*f2)(gamedef);
|
||||
return meta;
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
errorstream<<"NodeMetadata: SerializationError "
|
||||
<<"while creating name=\""<<name<<"\""<<std::endl;
|
||||
return NULL;
|
||||
int num_vars = m_stringvars.size();
|
||||
writeU32(os, num_vars);
|
||||
for(std::map<std::string, std::string>::const_iterator
|
||||
i = m_stringvars.begin(); i != m_stringvars.end(); i++){
|
||||
os<<serializeString(i->first);
|
||||
os<<serializeLongString(i->second);
|
||||
}
|
||||
|
||||
m_inventory->serialize(os);
|
||||
}
|
||||
|
||||
NodeMetadata* NodeMetadata::deSerialize(std::istream &is, IGameDef *gamedef)
|
||||
void NodeMetadata::deSerialize(std::istream &is)
|
||||
{
|
||||
// Read id
|
||||
u8 buf[2];
|
||||
is.read((char*)buf, 2);
|
||||
s16 id = readS16(buf);
|
||||
|
||||
// Read data
|
||||
std::string data = deSerializeString(is);
|
||||
|
||||
// Find factory function
|
||||
core::map<u16, Factory>::Node *n;
|
||||
n = m_types.find(id);
|
||||
if(n == NULL)
|
||||
{
|
||||
// If factory is not found, just return.
|
||||
infostream<<"WARNING: NodeMetadata: No factory for typeId="
|
||||
<<id<<std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Try to load the metadata. If it fails, just return.
|
||||
try
|
||||
{
|
||||
std::istringstream iss(data, std::ios_base::binary);
|
||||
|
||||
Factory f = n->getValue();
|
||||
NodeMetadata *meta = (*f)(iss, gamedef);
|
||||
return meta;
|
||||
}
|
||||
catch(SerializationError &e)
|
||||
{
|
||||
infostream<<"WARNING: NodeMetadata: ignoring SerializationError"<<std::endl;
|
||||
return NULL;
|
||||
m_stringvars.clear();
|
||||
int num_vars = readU32(is);
|
||||
for(int i=0; i<num_vars; i++){
|
||||
std::string name = deSerializeString(is);
|
||||
std::string var = deSerializeLongString(is);
|
||||
m_stringvars[name] = var;
|
||||
}
|
||||
|
||||
m_inventory->deSerialize(is);
|
||||
}
|
||||
|
||||
void NodeMetadata::serialize(std::ostream &os)
|
||||
void NodeMetadata::clear()
|
||||
{
|
||||
u8 buf[2];
|
||||
writeU16(buf, typeId());
|
||||
os.write((char*)buf, 2);
|
||||
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
serializeBody(oss);
|
||||
os<<serializeString(oss.str());
|
||||
}
|
||||
|
||||
void NodeMetadata::registerType(u16 id, const std::string &name, Factory f,
|
||||
Factory2 f2)
|
||||
{
|
||||
{ // typeId
|
||||
core::map<u16, Factory>::Node *n;
|
||||
n = m_types.find(id);
|
||||
if(!n)
|
||||
m_types.insert(id, f);
|
||||
}
|
||||
{ // typeName
|
||||
core::map<std::string, Factory2>::Node *n;
|
||||
n = m_names.find(name);
|
||||
if(!n)
|
||||
m_names.insert(name, f2);
|
||||
}
|
||||
m_stringvars.clear();
|
||||
m_inventory->clear();
|
||||
}
|
||||
|
||||
/*
|
||||
NodeMetadataList
|
||||
*/
|
||||
|
||||
void NodeMetadataList::serialize(std::ostream &os)
|
||||
void NodeMetadataList::serialize(std::ostream &os) const
|
||||
{
|
||||
u8 buf[6];
|
||||
|
||||
u16 version = 1;
|
||||
writeU16(buf, version);
|
||||
os.write((char*)buf, 2);
|
||||
/*
|
||||
Version 0 is a placeholder for "nothing to see here; go away."
|
||||
*/
|
||||
|
||||
if(m_data.size() == 0){
|
||||
writeU8(os, 0); // version
|
||||
return;
|
||||
}
|
||||
|
||||
writeU8(os, 1); // version
|
||||
|
||||
u16 count = m_data.size();
|
||||
writeU16(buf, count);
|
||||
os.write((char*)buf, 2);
|
||||
writeU16(os, count);
|
||||
|
||||
for(core::map<v3s16, NodeMetadata*>::Iterator
|
||||
i = m_data.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
for(std::map<v3s16, NodeMetadata*>::const_iterator
|
||||
i = m_data.begin();
|
||||
i != m_data.end(); i++)
|
||||
{
|
||||
v3s16 p = i.getNode()->getKey();
|
||||
NodeMetadata *data = i.getNode()->getValue();
|
||||
|
||||
v3s16 p = i->first;
|
||||
NodeMetadata *data = i->second;
|
||||
|
||||
u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
|
||||
writeU16(buf, p16);
|
||||
os.write((char*)buf, 2);
|
||||
writeU16(os, p16);
|
||||
|
||||
data->serialize(os);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NodeMetadataList::deSerialize(std::istream &is, IGameDef *gamedef)
|
||||
{
|
||||
m_data.clear();
|
||||
|
||||
u8 buf[6];
|
||||
u8 version = readU8(is);
|
||||
|
||||
is.read((char*)buf, 2);
|
||||
u16 version = readU16(buf);
|
||||
if(version == 0){
|
||||
// Nothing
|
||||
return;
|
||||
}
|
||||
|
||||
if(version > 1)
|
||||
{
|
||||
if(version != 1){
|
||||
infostream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
|
||||
<<std::endl;
|
||||
throw SerializationError("NodeMetadataList::deSerialize");
|
||||
}
|
||||
|
||||
is.read((char*)buf, 2);
|
||||
u16 count = readU16(buf);
|
||||
|
||||
|
||||
u16 count = readU16(is);
|
||||
|
||||
for(u16 i=0; i<count; i++)
|
||||
{
|
||||
is.read((char*)buf, 2);
|
||||
u16 p16 = readU16(buf);
|
||||
u16 p16 = readU16(is);
|
||||
|
||||
v3s16 p(0,0,0);
|
||||
p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
|
||||
@@ -193,43 +135,33 @@ void NodeMetadataList::deSerialize(std::istream &is, IGameDef *gamedef)
|
||||
p.Y += p16 / MAP_BLOCKSIZE;
|
||||
p16 -= p.Y * MAP_BLOCKSIZE;
|
||||
p.X += p16;
|
||||
|
||||
NodeMetadata *data = NodeMetadata::deSerialize(is, gamedef);
|
||||
|
||||
if(data == NULL)
|
||||
continue;
|
||||
|
||||
if(m_data.find(p))
|
||||
if(m_data.find(p) != m_data.end())
|
||||
{
|
||||
infostream<<"WARNING: NodeMetadataList::deSerialize(): "
|
||||
<<"already set data at position"
|
||||
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
|
||||
<<std::endl;
|
||||
delete data;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_data.insert(p, data);
|
||||
NodeMetadata *data = new NodeMetadata(gamedef);
|
||||
data->deSerialize(is);
|
||||
m_data[p] = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NodeMetadataList::~NodeMetadataList()
|
||||
{
|
||||
for(core::map<v3s16, NodeMetadata*>::Iterator
|
||||
i = m_data.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
delete i.getNode()->getValue();
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
NodeMetadata* NodeMetadataList::get(v3s16 p)
|
||||
{
|
||||
core::map<v3s16, NodeMetadata*>::Node *n;
|
||||
n = m_data.find(p);
|
||||
if(n == NULL)
|
||||
std::map<v3s16, NodeMetadata*>::const_iterator n = m_data.find(p);
|
||||
if(n == m_data.end())
|
||||
return NULL;
|
||||
return n->getValue();
|
||||
return n->second;
|
||||
}
|
||||
|
||||
void NodeMetadataList::remove(v3s16 p)
|
||||
@@ -238,29 +170,23 @@ void NodeMetadataList::remove(v3s16 p)
|
||||
if(olddata)
|
||||
{
|
||||
delete olddata;
|
||||
m_data.remove(p);
|
||||
m_data.erase(p);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
|
||||
{
|
||||
remove(p);
|
||||
m_data.insert(p, d);
|
||||
m_data.insert(std::make_pair(p, d));
|
||||
}
|
||||
|
||||
bool NodeMetadataList::step(float dtime)
|
||||
void NodeMetadataList::clear()
|
||||
{
|
||||
bool something_changed = false;
|
||||
for(core::map<v3s16, NodeMetadata*>::Iterator
|
||||
i = m_data.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
for(std::map<v3s16, NodeMetadata*>::iterator
|
||||
i = m_data.begin();
|
||||
i != m_data.end(); i++)
|
||||
{
|
||||
v3s16 p = i.getNode()->getKey();
|
||||
NodeMetadata *meta = i.getNode()->getValue();
|
||||
bool changed = meta->step(dtime);
|
||||
if(changed)
|
||||
something_changed = true;
|
||||
delete i->second;
|
||||
}
|
||||
return something_changed;
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irrlichttypes.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
NodeMetadata stores arbitary amounts of data for special blocks.
|
||||
@@ -39,77 +40,47 @@ class IGameDef;
|
||||
class NodeMetadata
|
||||
{
|
||||
public:
|
||||
typedef NodeMetadata* (*Factory)(std::istream&, IGameDef *gamedef);
|
||||
typedef NodeMetadata* (*Factory2)(IGameDef *gamedef);
|
||||
|
||||
NodeMetadata(IGameDef *gamedef);
|
||||
virtual ~NodeMetadata();
|
||||
~NodeMetadata();
|
||||
|
||||
static NodeMetadata* create(const std::string &name, IGameDef *gamedef);
|
||||
static NodeMetadata* deSerialize(std::istream &is, IGameDef *gamedef);
|
||||
void serialize(std::ostream &os);
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
virtual u16 typeId() const = 0;
|
||||
virtual const char* typeName() const = 0;
|
||||
virtual NodeMetadata* clone(IGameDef *gamedef) = 0;
|
||||
virtual void serializeBody(std::ostream &os) = 0;
|
||||
void clear();
|
||||
|
||||
// Called on client-side; shown on screen when pointed at
|
||||
virtual std::string infoText() {return "";}
|
||||
|
||||
//
|
||||
virtual Inventory* getInventory() {return NULL;}
|
||||
// Called always after the inventory is modified, before the changes
|
||||
// are copied elsewhere
|
||||
virtual void inventoryModified(){}
|
||||
// Generic key/value store
|
||||
std::string getString(const std::string &name) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator i;
|
||||
i = m_stringvars.find(name);
|
||||
if(i == m_stringvars.end())
|
||||
return "";
|
||||
return i->second;
|
||||
}
|
||||
void setString(const std::string &name, const std::string &var)
|
||||
{
|
||||
if(var.empty())
|
||||
m_stringvars.erase(name);
|
||||
else
|
||||
m_stringvars[name] = var;
|
||||
}
|
||||
std::map<std::string, std::string> getStrings() const
|
||||
{
|
||||
return m_stringvars;
|
||||
}
|
||||
|
||||
// A step in time. Shall return true if metadata changed.
|
||||
virtual bool step(float dtime) {return false;}
|
||||
// The inventory
|
||||
Inventory* getInventory()
|
||||
{
|
||||
return m_inventory;
|
||||
}
|
||||
|
||||
// Whether the related node and this metadata cannot be removed
|
||||
virtual bool nodeRemovalDisabled(){return false;}
|
||||
// If non-empty, player can interact by using an inventory view
|
||||
// See format in guiInventoryMenu.cpp.
|
||||
virtual std::string getInventoryDrawSpecString(){return "";}
|
||||
|
||||
// If true, player can interact by writing text
|
||||
virtual bool allowsTextInput(){ return false; }
|
||||
// Get old text for player interaction
|
||||
virtual std::string getText(){ return ""; }
|
||||
// Set player-written text
|
||||
virtual void setText(const std::string &t){}
|
||||
|
||||
// If returns non-empty, only given player can modify text/inventory
|
||||
virtual std::string getOwner(){ return std::string(""); }
|
||||
// The name of the player who placed the node
|
||||
virtual void setOwner(std::string t){}
|
||||
|
||||
/* Interface for GenericNodeMetadata */
|
||||
|
||||
virtual void setInfoText(const std::string &text){};
|
||||
virtual void setInventoryDrawSpec(const std::string &text){};
|
||||
virtual void setAllowTextInput(bool b){};
|
||||
|
||||
virtual void setRemovalDisabled(bool b){};
|
||||
virtual void setEnforceOwner(bool b){};
|
||||
|
||||
virtual bool isInventoryModified(){return false;};
|
||||
virtual void resetInventoryModified(){};
|
||||
virtual bool isTextModified(){return false;};
|
||||
virtual void resetTextModified(){};
|
||||
|
||||
virtual void setString(const std::string &name, const std::string &var){}
|
||||
virtual std::string getString(const std::string &name){return "";}
|
||||
|
||||
protected:
|
||||
static void registerType(u16 id, const std::string &name, Factory f,
|
||||
Factory2 f2);
|
||||
IGameDef *m_gamedef;
|
||||
private:
|
||||
static core::map<u16, Factory> m_types;
|
||||
static core::map<std::string, Factory2> m_names;
|
||||
std::map<std::string, std::string> m_stringvars;
|
||||
Inventory *m_inventory;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
List of metadata of all the nodes of a block
|
||||
*/
|
||||
@@ -119,7 +90,7 @@ class NodeMetadataList
|
||||
public:
|
||||
~NodeMetadataList();
|
||||
|
||||
void serialize(std::ostream &os);
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is, IGameDef *gamedef);
|
||||
|
||||
// Get pointer to data
|
||||
@@ -128,12 +99,11 @@ class NodeMetadataList
|
||||
void remove(v3s16 p);
|
||||
// Deletes old data and sets a new one
|
||||
void set(v3s16 p, NodeMetadata *d);
|
||||
// Deletes all
|
||||
void clear();
|
||||
|
||||
// A step in time. Returns true if something changed.
|
||||
bool step(float dtime);
|
||||
|
||||
private:
|
||||
core::map<v3s16, NodeMetadata*> m_data;
|
||||
std::map<v3s16, NodeMetadata*> m_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
141
src/nodetimer.cpp
Normal file
141
src/nodetimer.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 "nodetimer.h"
|
||||
#include "utility.h"
|
||||
#include "log.h"
|
||||
|
||||
/*
|
||||
NodeTimer
|
||||
*/
|
||||
|
||||
void NodeTimer::serialize(std::ostream &os) const
|
||||
{
|
||||
writeF1000(os, duration);
|
||||
writeF1000(os, elapsed);
|
||||
}
|
||||
|
||||
void NodeTimer::deSerialize(std::istream &is)
|
||||
{
|
||||
duration = readF1000(is);
|
||||
elapsed = readF1000(is);
|
||||
}
|
||||
|
||||
/*
|
||||
NodeTimerList
|
||||
*/
|
||||
|
||||
void NodeTimerList::serialize(std::ostream &os) const
|
||||
{
|
||||
/*
|
||||
Version 0 is a placeholder for "nothing to see here; go away."
|
||||
*/
|
||||
|
||||
if(m_data.size() == 0){
|
||||
writeU8(os, 0); // version
|
||||
return;
|
||||
}
|
||||
|
||||
writeU8(os, 1); // version
|
||||
writeU16(os, m_data.size());
|
||||
|
||||
for(std::map<v3s16, NodeTimer>::const_iterator
|
||||
i = m_data.begin();
|
||||
i != m_data.end(); i++){
|
||||
v3s16 p = i->first;
|
||||
NodeTimer t = i->second;
|
||||
|
||||
u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
|
||||
writeU16(os, p16);
|
||||
t.serialize(os);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTimerList::deSerialize(std::istream &is)
|
||||
{
|
||||
m_data.clear();
|
||||
|
||||
u8 version = readU8(is);
|
||||
if(version == 0)
|
||||
return;
|
||||
if(version != 1)
|
||||
throw SerializationError("unsupported NodeTimerList version");
|
||||
|
||||
u16 count = readU16(is);
|
||||
|
||||
for(u16 i=0; i<count; i++)
|
||||
{
|
||||
u16 p16 = readU16(is);
|
||||
|
||||
v3s16 p(0,0,0);
|
||||
p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
|
||||
p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
|
||||
p.Y += p16 / MAP_BLOCKSIZE;
|
||||
p16 -= p.Y * MAP_BLOCKSIZE;
|
||||
p.X += p16;
|
||||
|
||||
NodeTimer t;
|
||||
t.deSerialize(is);
|
||||
|
||||
if(t.duration <= 0)
|
||||
{
|
||||
infostream<<"WARNING: NodeTimerList::deSerialize(): "
|
||||
<<"invalid data at position"
|
||||
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
|
||||
<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(m_data.find(p) != m_data.end())
|
||||
{
|
||||
infostream<<"WARNING: NodeTimerList::deSerialize(): "
|
||||
<<"already set data at position"
|
||||
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
|
||||
<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_data.insert(std::make_pair(p, t));
|
||||
}
|
||||
}
|
||||
|
||||
std::map<v3s16, f32> NodeTimerList::step(float dtime)
|
||||
{
|
||||
std::map<v3s16, f32> elapsed_timers;
|
||||
// Increment timers
|
||||
for(std::map<v3s16, NodeTimer>::iterator
|
||||
i = m_data.begin();
|
||||
i != m_data.end(); i++){
|
||||
v3s16 p = i->first;
|
||||
NodeTimer t = i->second;
|
||||
t.elapsed += dtime;
|
||||
if(t.elapsed >= t.duration)
|
||||
elapsed_timers.insert(std::make_pair(p, t.elapsed));
|
||||
else
|
||||
i->second = t;
|
||||
}
|
||||
// Delete elapsed timers
|
||||
for(std::map<v3s16, f32>::const_iterator
|
||||
i = elapsed_timers.begin();
|
||||
i != elapsed_timers.end(); i++){
|
||||
v3s16 p = i->first;
|
||||
m_data.erase(p);
|
||||
}
|
||||
return elapsed_timers;
|
||||
}
|
||||
91
src/nodetimer.h
Normal file
91
src/nodetimer.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 NODETIMER_HEADER
|
||||
#define NODETIMER_HEADER
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
NodeTimer provides per-node timed callback functionality.
|
||||
Can be used for:
|
||||
- Furnaces, to keep the fire burnin'
|
||||
- "activated" nodes that snap back to their original state
|
||||
after a fixed amount of time (mesecons buttons, for example)
|
||||
*/
|
||||
|
||||
class NodeTimer
|
||||
{
|
||||
public:
|
||||
NodeTimer(): duration(0.), elapsed(0.) {}
|
||||
NodeTimer(f32 duration_, f32 elapsed_):
|
||||
duration(duration_), elapsed(elapsed_) {}
|
||||
~NodeTimer() {}
|
||||
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
f32 duration;
|
||||
f32 elapsed;
|
||||
};
|
||||
|
||||
/*
|
||||
List of timers of all the nodes of a block
|
||||
*/
|
||||
|
||||
class NodeTimerList
|
||||
{
|
||||
public:
|
||||
NodeTimerList() {}
|
||||
~NodeTimerList() {}
|
||||
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
// Get timer
|
||||
NodeTimer get(v3s16 p){
|
||||
std::map<v3s16, NodeTimer>::iterator n = m_data.find(p);
|
||||
if(n == m_data.end())
|
||||
return NodeTimer();
|
||||
return n->second;
|
||||
}
|
||||
// Deletes timer
|
||||
void remove(v3s16 p){
|
||||
m_data.erase(p);
|
||||
}
|
||||
// Deletes old timer and sets a new one
|
||||
void set(v3s16 p, NodeTimer t){
|
||||
m_data[p] = t;
|
||||
}
|
||||
// Deletes all timers
|
||||
void clear(){
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
// A step in time. Returns map of elapsed timers.
|
||||
std::map<v3s16, f32> step(float dtime);
|
||||
|
||||
private:
|
||||
std::map<v3s16, NodeTimer> m_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2012 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 General Public License as published by
|
||||
@@ -17,14 +17,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luaentity_common.h"
|
||||
|
||||
#include "object_properties.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
#define PP2(x) "("<<(x).X<<","<<(x).Y<<")"
|
||||
|
||||
LuaEntityProperties::LuaEntityProperties():
|
||||
ObjectProperties::ObjectProperties():
|
||||
hp_max(1),
|
||||
physical(false),
|
||||
weight(5),
|
||||
@@ -32,12 +31,15 @@ LuaEntityProperties::LuaEntityProperties():
|
||||
visual("sprite"),
|
||||
visual_size(1,1),
|
||||
spritediv(1,1),
|
||||
initial_sprite_basepos(0,0)
|
||||
initial_sprite_basepos(0,0),
|
||||
is_visible(true),
|
||||
makes_footstep_sound(false),
|
||||
automatic_rotate(0)
|
||||
{
|
||||
textures.push_back("unknown_object.png");
|
||||
}
|
||||
|
||||
std::string LuaEntityProperties::dump()
|
||||
std::string ObjectProperties::dump()
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
os<<"hp_max="<<hp_max;
|
||||
@@ -53,10 +55,13 @@ std::string LuaEntityProperties::dump()
|
||||
os<<"]";
|
||||
os<<", spritediv="<<PP2(spritediv);
|
||||
os<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos);
|
||||
os<<", is_visible="<<is_visible;
|
||||
os<<", makes_footstep_sound="<<makes_footstep_sound;
|
||||
os<<", automatic_rotate="<<automatic_rotate;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void LuaEntityProperties::serialize(std::ostream &os)
|
||||
void ObjectProperties::serialize(std::ostream &os) const
|
||||
{
|
||||
writeU8(os, 1); // version
|
||||
writeS16(os, hp_max);
|
||||
@@ -72,13 +77,16 @@ void LuaEntityProperties::serialize(std::ostream &os)
|
||||
}
|
||||
writeV2S16(os, spritediv);
|
||||
writeV2S16(os, initial_sprite_basepos);
|
||||
writeU8(os, is_visible);
|
||||
writeU8(os, makes_footstep_sound);
|
||||
writeF1000(os, automatic_rotate);
|
||||
}
|
||||
|
||||
void LuaEntityProperties::deSerialize(std::istream &is)
|
||||
void ObjectProperties::deSerialize(std::istream &is)
|
||||
{
|
||||
int version = readU8(is);
|
||||
if(version != 1) throw SerializationError(
|
||||
"unsupported LuaEntityProperties version");
|
||||
"unsupported ObjectProperties version");
|
||||
hp_max = readS16(is);
|
||||
physical = readU8(is);
|
||||
weight = readF1000(is);
|
||||
@@ -93,6 +101,11 @@ void LuaEntityProperties::deSerialize(std::istream &is)
|
||||
}
|
||||
spritediv = readV2S16(is);
|
||||
initial_sprite_basepos = readV2S16(is);
|
||||
is_visible = readU8(is);
|
||||
makes_footstep_sound = readU8(is);
|
||||
try{
|
||||
automatic_rotate = readF1000(is);
|
||||
}catch(SerializationError &e){}
|
||||
}
|
||||
|
||||
|
||||
50
src/object_properties.h
Normal file
50
src/object_properties.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2012 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 OBJECT_PROPERTIES_HEADER
|
||||
#define OBJECT_PROPERTIES_HEADER
|
||||
|
||||
#include <string>
|
||||
#include "irrlichttypes.h"
|
||||
#include <iostream>
|
||||
|
||||
struct ObjectProperties
|
||||
{
|
||||
// Values are BS=1
|
||||
s16 hp_max;
|
||||
bool physical;
|
||||
float weight;
|
||||
core::aabbox3d<f32> collisionbox;
|
||||
std::string visual;
|
||||
v2f visual_size;
|
||||
core::array<std::string> textures;
|
||||
v2s16 spritediv;
|
||||
v2s16 initial_sprite_basepos;
|
||||
bool is_visible;
|
||||
bool makes_footstep_sound;
|
||||
float automatic_rotate;
|
||||
|
||||
ObjectProperties();
|
||||
std::string dump();
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
637
src/player.cpp
637
src/player.cpp
@@ -18,20 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "player.h"
|
||||
#include "map.h"
|
||||
#include "connection.h"
|
||||
#include "constants.h"
|
||||
#include "utility.h"
|
||||
#ifndef SERVER
|
||||
#include <ITextSceneNode.h>
|
||||
#endif
|
||||
#include "main.h" // For g_settings
|
||||
#include "settings.h"
|
||||
#include "nodedef.h"
|
||||
#include "collision.h"
|
||||
#include "environment.h"
|
||||
#include "gamedef.h"
|
||||
#include "event.h"
|
||||
#include "connection.h" // PEER_ID_INEXISTENT
|
||||
#include "settings.h"
|
||||
#include "content_sao.h"
|
||||
|
||||
Player::Player(IGameDef *gamedef):
|
||||
touching_ground(false),
|
||||
@@ -39,9 +31,9 @@ Player::Player(IGameDef *gamedef):
|
||||
in_water_stable(false),
|
||||
is_climbing(false),
|
||||
swimming_up(false),
|
||||
camera_barely_in_ceiling(false),
|
||||
inventory(gamedef->idef()),
|
||||
inventory_backup(NULL),
|
||||
hp(20),
|
||||
hp(PLAYER_MAX_HP),
|
||||
peer_id(PEER_ID_INEXISTENT),
|
||||
// protected
|
||||
m_gamedef(gamedef),
|
||||
@@ -51,16 +43,6 @@ Player::Player(IGameDef *gamedef):
|
||||
m_position(0,0,0)
|
||||
{
|
||||
updateName("<not set>");
|
||||
resetInventory();
|
||||
}
|
||||
|
||||
Player::~Player()
|
||||
{
|
||||
delete inventory_backup;
|
||||
}
|
||||
|
||||
void Player::resetInventory()
|
||||
{
|
||||
inventory.clear();
|
||||
inventory.addList("main", PLAYER_INVENTORY_SIZE);
|
||||
inventory.addList("craft", 9);
|
||||
@@ -68,6 +50,10 @@ void Player::resetInventory()
|
||||
inventory.addList("craftresult", 1);
|
||||
}
|
||||
|
||||
Player::~Player()
|
||||
{
|
||||
}
|
||||
|
||||
// Y direction is ignored
|
||||
void Player::accelerate(v3f target_speed, f32 max_increase)
|
||||
{
|
||||
@@ -126,12 +112,7 @@ void Player::serialize(std::ostream &os)
|
||||
|
||||
os<<"PlayerArgsEnd\n";
|
||||
|
||||
// If actual inventory is backed up due to creative mode, save it
|
||||
// instead of the dummy creative mode inventory
|
||||
if(inventory_backup)
|
||||
inventory_backup->serialize(os);
|
||||
else
|
||||
inventory.serialize(os);
|
||||
inventory.serialize(os);
|
||||
}
|
||||
|
||||
void Player::deSerialize(std::istream &is)
|
||||
@@ -181,601 +162,13 @@ void Player::deSerialize(std::istream &is)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SERVER
|
||||
/*
|
||||
LocalPlayer
|
||||
RemotePlayer
|
||||
*/
|
||||
|
||||
LocalPlayer::LocalPlayer(IGameDef *gamedef):
|
||||
Player(gamedef),
|
||||
m_sneak_node(32767,32767,32767),
|
||||
m_sneak_node_exists(false)
|
||||
void RemotePlayer::setPosition(const v3f &position)
|
||||
{
|
||||
// Initialize hp to 0, so that no hearts will be shown if server
|
||||
// doesn't support health points
|
||||
hp = 0;
|
||||
Player::setPosition(position);
|
||||
if(m_sao)
|
||||
m_sao->setBasePosition(position);
|
||||
}
|
||||
|
||||
LocalPlayer::~LocalPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
|
||||
core::list<CollisionInfo> *collision_info)
|
||||
{
|
||||
INodeDefManager *nodemgr = m_gamedef->ndef();
|
||||
|
||||
v3f position = getPosition();
|
||||
v3f oldpos = position;
|
||||
v3s16 oldpos_i = floatToInt(oldpos, BS);
|
||||
|
||||
v3f old_speed = m_speed;
|
||||
|
||||
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
|
||||
<<oldpos_i.Z<<")"<<std::endl;*/
|
||||
|
||||
/*
|
||||
Calculate new position
|
||||
*/
|
||||
position += m_speed * dtime;
|
||||
|
||||
// Skip collision detection if a special movement mode is used
|
||||
bool free_move = g_settings->getBool("free_move");
|
||||
if(free_move)
|
||||
{
|
||||
setPosition(position);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Collision detection
|
||||
*/
|
||||
|
||||
// Player position in nodes
|
||||
v3s16 pos_i = floatToInt(position, BS);
|
||||
|
||||
/*
|
||||
Check if player is in water (the oscillating value)
|
||||
*/
|
||||
try{
|
||||
// If in water, the threshold of coming out is at higher y
|
||||
if(in_water)
|
||||
{
|
||||
v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
|
||||
in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
|
||||
}
|
||||
// If not in water, the threshold of going in is at lower y
|
||||
else
|
||||
{
|
||||
v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
|
||||
in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
|
||||
}
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
in_water = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if player is in water (the stable value)
|
||||
*/
|
||||
try{
|
||||
v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
|
||||
in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
in_water_stable = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if player is climbing
|
||||
*/
|
||||
|
||||
try {
|
||||
v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
|
||||
v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
|
||||
is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
|
||||
nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
is_climbing = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Collision uncertainty radius
|
||||
Make it a bit larger than the maximum distance of movement
|
||||
*/
|
||||
//f32 d = pos_max_d * 1.1;
|
||||
// A fairly large value in here makes moving smoother
|
||||
f32 d = 0.15*BS;
|
||||
|
||||
// This should always apply, otherwise there are glitches
|
||||
assert(d > pos_max_d);
|
||||
|
||||
float player_radius = BS*0.30;
|
||||
float player_height = BS*1.55;
|
||||
|
||||
// Maximum distance over border for sneaking
|
||||
f32 sneak_max = BS*0.4;
|
||||
|
||||
/*
|
||||
If sneaking, player has larger collision radius to keep from
|
||||
falling
|
||||
*/
|
||||
/*if(control.sneak)
|
||||
player_radius = sneak_max + d*1.1;*/
|
||||
|
||||
/*
|
||||
If sneaking, keep in range from the last walked node and don't
|
||||
fall off from it
|
||||
*/
|
||||
if(control.sneak && m_sneak_node_exists)
|
||||
{
|
||||
f32 maxd = 0.5*BS + sneak_max;
|
||||
v3f lwn_f = intToFloat(m_sneak_node, BS);
|
||||
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
|
||||
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
|
||||
|
||||
f32 min_y = lwn_f.Y + 0.5*BS;
|
||||
if(position.Y < min_y)
|
||||
{
|
||||
position.Y = min_y;
|
||||
|
||||
//v3f old_speed = m_speed;
|
||||
|
||||
if(m_speed.Y < 0)
|
||||
m_speed.Y = 0;
|
||||
|
||||
/*if(collision_info)
|
||||
{
|
||||
// Report fall collision
|
||||
if(old_speed.Y < m_speed.Y - 0.1)
|
||||
{
|
||||
CollisionInfo info;
|
||||
info.t = COLLISION_FALL;
|
||||
info.speed = m_speed.Y - old_speed.Y;
|
||||
collision_info->push_back(info);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate player collision box (new and old)
|
||||
*/
|
||||
core::aabbox3d<f32> playerbox(
|
||||
position.X - player_radius,
|
||||
position.Y - 0.0,
|
||||
position.Z - player_radius,
|
||||
position.X + player_radius,
|
||||
position.Y + player_height,
|
||||
position.Z + player_radius
|
||||
);
|
||||
core::aabbox3d<f32> playerbox_old(
|
||||
oldpos.X - player_radius,
|
||||
oldpos.Y - 0.0,
|
||||
oldpos.Z - player_radius,
|
||||
oldpos.X + player_radius,
|
||||
oldpos.Y + player_height,
|
||||
oldpos.Z + player_radius
|
||||
);
|
||||
|
||||
/*
|
||||
If the player's feet touch the topside of any node, this is
|
||||
set to true.
|
||||
|
||||
Player is allowed to jump when this is true.
|
||||
*/
|
||||
bool touching_ground_was = touching_ground;
|
||||
touching_ground = false;
|
||||
|
||||
/*std::cout<<"Checking collisions for ("
|
||||
<<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
|
||||
<<") -> ("
|
||||
<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
|
||||
<<"):"<<std::endl;*/
|
||||
|
||||
bool standing_on_unloaded = false;
|
||||
|
||||
/*
|
||||
Go through every node around the player
|
||||
*/
|
||||
for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
|
||||
for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
|
||||
for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
|
||||
{
|
||||
bool is_unloaded = false;
|
||||
try{
|
||||
// Player collides into walkable nodes
|
||||
if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
|
||||
continue;
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
is_unloaded = true;
|
||||
// Doing nothing here will block the player from
|
||||
// walking over map borders
|
||||
}
|
||||
|
||||
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
|
||||
|
||||
/*
|
||||
See if the player is touching ground.
|
||||
|
||||
Player touches ground if player's minimum Y is near node's
|
||||
maximum Y and player's X-Z-area overlaps with the node's
|
||||
X-Z-area.
|
||||
|
||||
Use 0.15*BS so that it is easier to get on a node.
|
||||
*/
|
||||
if(
|
||||
//fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
|
||||
fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
|
||||
&& nodebox.MaxEdge.X-d > playerbox.MinEdge.X
|
||||
&& nodebox.MinEdge.X+d < playerbox.MaxEdge.X
|
||||
&& nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
|
||||
&& nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
|
||||
){
|
||||
touching_ground = true;
|
||||
if(is_unloaded)
|
||||
standing_on_unloaded = true;
|
||||
}
|
||||
|
||||
// If player doesn't intersect with node, ignore node.
|
||||
if(playerbox.intersectsWithBox(nodebox) == false)
|
||||
continue;
|
||||
|
||||
/*
|
||||
Go through every axis
|
||||
*/
|
||||
v3f dirs[3] = {
|
||||
v3f(0,0,1), // back-front
|
||||
v3f(0,1,0), // top-bottom
|
||||
v3f(1,0,0), // right-left
|
||||
};
|
||||
for(u16 i=0; i<3; i++)
|
||||
{
|
||||
/*
|
||||
Calculate values along the axis
|
||||
*/
|
||||
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
|
||||
f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
|
||||
f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
|
||||
f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
|
||||
|
||||
/*
|
||||
Check collision for the axis.
|
||||
Collision happens when player is going through a surface.
|
||||
*/
|
||||
/*f32 neg_d = d;
|
||||
f32 pos_d = d;
|
||||
// Make it easier to get on top of a node
|
||||
if(i == 1)
|
||||
neg_d = 0.15*BS;
|
||||
bool negative_axis_collides =
|
||||
(nodemax > playermin && nodemax <= playermin_old + neg_d
|
||||
&& m_speed.dotProduct(dirs[i]) < 0);
|
||||
bool positive_axis_collides =
|
||||
(nodemin < playermax && nodemin >= playermax_old - pos_d
|
||||
&& m_speed.dotProduct(dirs[i]) > 0);*/
|
||||
bool negative_axis_collides =
|
||||
(nodemax > playermin && nodemax <= playermin_old + d
|
||||
&& m_speed.dotProduct(dirs[i]) < 0);
|
||||
bool positive_axis_collides =
|
||||
(nodemin < playermax && nodemin >= playermax_old - d
|
||||
&& m_speed.dotProduct(dirs[i]) > 0);
|
||||
bool main_axis_collides =
|
||||
negative_axis_collides || positive_axis_collides;
|
||||
|
||||
/*
|
||||
Check overlap of player and node in other axes
|
||||
*/
|
||||
bool other_axes_overlap = true;
|
||||
for(u16 j=0; j<3; j++)
|
||||
{
|
||||
if(j == i)
|
||||
continue;
|
||||
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
|
||||
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
|
||||
f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
|
||||
f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
|
||||
if(!(nodemax - d > playermin && nodemin + d < playermax))
|
||||
{
|
||||
other_axes_overlap = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If this is a collision, revert the position in the main
|
||||
direction.
|
||||
*/
|
||||
if(other_axes_overlap && main_axis_collides)
|
||||
{
|
||||
//v3f old_speed = m_speed;
|
||||
|
||||
m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
|
||||
position -= position.dotProduct(dirs[i]) * dirs[i];
|
||||
position += oldpos.dotProduct(dirs[i]) * dirs[i];
|
||||
|
||||
/*if(collision_info)
|
||||
{
|
||||
// Report fall collision
|
||||
if(old_speed.Y < m_speed.Y - 0.1)
|
||||
{
|
||||
CollisionInfo info;
|
||||
info.t = COLLISION_FALL;
|
||||
info.speed = m_speed.Y - old_speed.Y;
|
||||
collision_info->push_back(info);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
} // xyz
|
||||
|
||||
/*
|
||||
Check the nodes under the player to see from which node the
|
||||
player is sneaking from, if any.
|
||||
*/
|
||||
{
|
||||
v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
|
||||
v2f player_p2df(position.X, position.Z);
|
||||
f32 min_distance_f = 100000.0*BS;
|
||||
// If already seeking from some node, compare to it.
|
||||
/*if(m_sneak_node_exists)
|
||||
{
|
||||
v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
|
||||
v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
|
||||
f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
|
||||
f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
|
||||
// Ignore if player is not on the same level (likely dropped)
|
||||
if(d_vert_f < 0.15*BS)
|
||||
min_distance_f = d_horiz_f;
|
||||
}*/
|
||||
v3s16 new_sneak_node = m_sneak_node;
|
||||
for(s16 x=-1; x<=1; x++)
|
||||
for(s16 z=-1; z<=1; z++)
|
||||
{
|
||||
v3s16 p = pos_i_bottom + v3s16(x,0,z);
|
||||
v3f pf = intToFloat(p, BS);
|
||||
v2f node_p2df(pf.X, pf.Z);
|
||||
f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
|
||||
f32 max_axis_distance_f = MYMAX(
|
||||
fabs(player_p2df.X-node_p2df.X),
|
||||
fabs(player_p2df.Y-node_p2df.Y));
|
||||
|
||||
if(distance_f > min_distance_f ||
|
||||
max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
|
||||
continue;
|
||||
|
||||
try{
|
||||
// The node to be sneaked on has to be walkable
|
||||
if(nodemgr->get(map.getNode(p)).walkable == false)
|
||||
continue;
|
||||
// And the node above it has to be nonwalkable
|
||||
if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
|
||||
continue;
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
min_distance_f = distance_f;
|
||||
new_sneak_node = p;
|
||||
}
|
||||
|
||||
bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
|
||||
|
||||
if(control.sneak && m_sneak_node_exists)
|
||||
{
|
||||
if(sneak_node_found)
|
||||
m_sneak_node = new_sneak_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sneak_node = new_sneak_node;
|
||||
m_sneak_node_exists = sneak_node_found;
|
||||
}
|
||||
|
||||
/*
|
||||
If sneaking, the player's collision box can be in air, so
|
||||
this has to be set explicitly
|
||||
*/
|
||||
if(sneak_node_found && control.sneak)
|
||||
touching_ground = true;
|
||||
}
|
||||
|
||||
/*
|
||||
Set new position
|
||||
*/
|
||||
setPosition(position);
|
||||
|
||||
/*
|
||||
Report collisions
|
||||
*/
|
||||
if(collision_info)
|
||||
{
|
||||
// Report fall collision
|
||||
if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
|
||||
{
|
||||
CollisionInfo info;
|
||||
info.t = COLLISION_FALL;
|
||||
info.speed = m_speed.Y - old_speed.Y;
|
||||
collision_info->push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
if(!touching_ground_was && touching_ground){
|
||||
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
|
||||
m_gamedef->event()->put(e);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
|
||||
{
|
||||
move(dtime, map, pos_max_d, NULL);
|
||||
}
|
||||
|
||||
void LocalPlayer::applyControl(float dtime)
|
||||
{
|
||||
// Clear stuff
|
||||
swimming_up = false;
|
||||
|
||||
// Random constants
|
||||
f32 walk_acceleration = 4.0 * BS;
|
||||
f32 walkspeed_max = 4.0 * BS;
|
||||
|
||||
setPitch(control.pitch);
|
||||
setYaw(control.yaw);
|
||||
|
||||
v3f move_direction = v3f(0,0,1);
|
||||
move_direction.rotateXZBy(getYaw());
|
||||
|
||||
v3f speed = v3f(0,0,0);
|
||||
|
||||
bool free_move = g_settings->getBool("free_move");
|
||||
bool fast_move = g_settings->getBool("fast_move");
|
||||
bool continuous_forward = g_settings->getBool("continuous_forward");
|
||||
|
||||
if(free_move || is_climbing)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = 0;
|
||||
setSpeed(speed);
|
||||
}
|
||||
|
||||
// Whether superspeed mode is used or not
|
||||
bool superspeed = false;
|
||||
|
||||
// If free movement and fast movement, always move fast
|
||||
if(free_move && fast_move)
|
||||
superspeed = true;
|
||||
|
||||
// Auxiliary button 1 (E)
|
||||
if(control.aux1)
|
||||
{
|
||||
if(free_move)
|
||||
{
|
||||
// In free movement mode, aux1 descends
|
||||
v3f speed = getSpeed();
|
||||
if(fast_move)
|
||||
speed.Y = -20*BS;
|
||||
else
|
||||
speed.Y = -walkspeed_max;
|
||||
setSpeed(speed);
|
||||
}
|
||||
else if(is_climbing)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = -3*BS;
|
||||
setSpeed(speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not free movement but fast is allowed, aux1 is
|
||||
// "Turbo button"
|
||||
if(fast_move)
|
||||
superspeed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(continuous_forward)
|
||||
speed += move_direction;
|
||||
|
||||
if(control.up)
|
||||
{
|
||||
if(continuous_forward)
|
||||
superspeed = true;
|
||||
else
|
||||
speed += move_direction;
|
||||
}
|
||||
if(control.down)
|
||||
{
|
||||
speed -= move_direction;
|
||||
}
|
||||
if(control.left)
|
||||
{
|
||||
speed += move_direction.crossProduct(v3f(0,1,0));
|
||||
}
|
||||
if(control.right)
|
||||
{
|
||||
speed += move_direction.crossProduct(v3f(0,-1,0));
|
||||
}
|
||||
if(control.jump)
|
||||
{
|
||||
if(free_move)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
if(fast_move)
|
||||
speed.Y = 20*BS;
|
||||
else
|
||||
speed.Y = walkspeed_max;
|
||||
setSpeed(speed);
|
||||
}
|
||||
else if(touching_ground)
|
||||
{
|
||||
/*
|
||||
NOTE: The d value in move() affects jump height by
|
||||
raising the height at which the jump speed is kept
|
||||
at its starting value
|
||||
*/
|
||||
v3f speed = getSpeed();
|
||||
if(speed.Y >= -0.5*BS)
|
||||
{
|
||||
speed.Y = 6.5*BS;
|
||||
setSpeed(speed);
|
||||
|
||||
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
|
||||
m_gamedef->event()->put(e);
|
||||
}
|
||||
}
|
||||
// Use the oscillating value for getting out of water
|
||||
// (so that the player doesn't fly on the surface)
|
||||
else if(in_water)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = 1.5*BS;
|
||||
setSpeed(speed);
|
||||
swimming_up = true;
|
||||
}
|
||||
else if(is_climbing)
|
||||
{
|
||||
v3f speed = getSpeed();
|
||||
speed.Y = 3*BS;
|
||||
setSpeed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
// The speed of the player (Y is ignored)
|
||||
if(superspeed)
|
||||
speed = speed.normalize() * walkspeed_max * 5.0;
|
||||
else if(control.sneak)
|
||||
speed = speed.normalize() * walkspeed_max / 3.0;
|
||||
else
|
||||
speed = speed.normalize() * walkspeed_max;
|
||||
|
||||
f32 inc = walk_acceleration * BS * dtime;
|
||||
|
||||
// Faster acceleration if fast and free movement
|
||||
if(free_move && fast_move)
|
||||
inc = walk_acceleration * BS * dtime * 10;
|
||||
|
||||
// Accelerate to target speed with maximum increment
|
||||
accelerate(speed, inc);
|
||||
}
|
||||
|
||||
v3s16 LocalPlayer::getStandingNodePos()
|
||||
{
|
||||
if(m_sneak_node_exists)
|
||||
return m_sneak_node;
|
||||
return floatToInt(getPosition(), BS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
126
src/player.h
126
src/player.h
@@ -20,29 +20,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef PLAYER_HEADER
|
||||
#define PLAYER_HEADER
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include "inventory.h"
|
||||
|
||||
#define PLAYERNAME_SIZE 20
|
||||
|
||||
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
|
||||
|
||||
|
||||
class Map;
|
||||
class IGameDef;
|
||||
struct CollisionInfo;
|
||||
class PlayerSAO;
|
||||
|
||||
class Player
|
||||
{
|
||||
public:
|
||||
|
||||
Player(IGameDef *gamedef);
|
||||
virtual ~Player();
|
||||
virtual ~Player() = 0;
|
||||
|
||||
void resetInventory();
|
||||
|
||||
//void move(f32 dtime, Map &map);
|
||||
virtual void move(f32 dtime, Map &map, f32 pos_max_d) = 0;
|
||||
virtual void move(f32 dtime, Map &map, f32 pos_max_d)
|
||||
{}
|
||||
|
||||
v3f getSpeed()
|
||||
{
|
||||
@@ -67,9 +65,12 @@ class Player
|
||||
v3f getEyeOffset()
|
||||
{
|
||||
// This is at the height of the eyes of the current figure
|
||||
// return v3f(0, BS+BS/2, 0);
|
||||
// return v3f(0, BS*1.5, 0);
|
||||
// This is more like in minecraft
|
||||
return v3f(0,BS+(5*BS)/8,0);
|
||||
if(camera_barely_in_ceiling)
|
||||
return v3f(0,BS*1.5,0);
|
||||
else
|
||||
return v3f(0,BS*1.625,0);
|
||||
}
|
||||
|
||||
v3f getEyePosition()
|
||||
@@ -112,7 +113,7 @@ class Player
|
||||
return (m_yaw + 90.) * core::DEGTORAD;
|
||||
}
|
||||
|
||||
virtual void updateName(const char *name)
|
||||
void updateName(const char *name)
|
||||
{
|
||||
snprintf(m_name, PLAYERNAME_SIZE, "%s", name);
|
||||
}
|
||||
@@ -122,17 +123,13 @@ class Player
|
||||
return m_name;
|
||||
}
|
||||
|
||||
virtual bool isLocal() const = 0;
|
||||
virtual bool isLocal() const
|
||||
{ return false; }
|
||||
virtual PlayerSAO *getPlayerSAO()
|
||||
{ return NULL; }
|
||||
virtual void setPlayerSAO(PlayerSAO *sao)
|
||||
{ assert(0); }
|
||||
|
||||
virtual void updateLight(u8 light_at_pos)
|
||||
{
|
||||
light = light_at_pos;
|
||||
}
|
||||
|
||||
// NOTE: Use peer_id == 0 for disconnected
|
||||
/*virtual bool isClientConnected() { return false; }
|
||||
virtual void setClientConnected(bool) {}*/
|
||||
|
||||
/*
|
||||
serialize() writes a bunch of text that can contain
|
||||
any characters except a '\0', and such an ending that
|
||||
@@ -148,12 +145,12 @@ class Player
|
||||
bool in_water_stable;
|
||||
bool is_climbing;
|
||||
bool swimming_up;
|
||||
bool camera_barely_in_ceiling;
|
||||
|
||||
u8 light;
|
||||
|
||||
// In creative mode, this is the invisible backup inventory
|
||||
Inventory inventory;
|
||||
// Actual inventory is backed up here when creative mode is used
|
||||
Inventory *inventory_backup;
|
||||
|
||||
u16 hp;
|
||||
|
||||
@@ -167,87 +164,26 @@ class Player
|
||||
f32 m_yaw;
|
||||
v3f m_speed;
|
||||
v3f m_position;
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
#ifndef SERVER
|
||||
struct PlayerControl
|
||||
{
|
||||
PlayerControl()
|
||||
{
|
||||
up = false;
|
||||
down = false;
|
||||
left = false;
|
||||
right = false;
|
||||
jump = false;
|
||||
aux1 = false;
|
||||
sneak = false;
|
||||
pitch = 0;
|
||||
yaw = 0;
|
||||
}
|
||||
PlayerControl(
|
||||
bool a_up,
|
||||
bool a_down,
|
||||
bool a_left,
|
||||
bool a_right,
|
||||
bool a_jump,
|
||||
bool a_aux1,
|
||||
bool a_sneak,
|
||||
float a_pitch,
|
||||
float a_yaw
|
||||
)
|
||||
{
|
||||
up = a_up;
|
||||
down = a_down;
|
||||
left = a_left;
|
||||
right = a_right;
|
||||
jump = a_jump;
|
||||
aux1 = a_aux1;
|
||||
sneak = a_sneak;
|
||||
pitch = a_pitch;
|
||||
yaw = a_yaw;
|
||||
}
|
||||
bool up;
|
||||
bool down;
|
||||
bool left;
|
||||
bool right;
|
||||
bool jump;
|
||||
bool aux1;
|
||||
bool sneak;
|
||||
float pitch;
|
||||
float yaw;
|
||||
};
|
||||
|
||||
class LocalPlayer : public Player
|
||||
/*
|
||||
Player on the server
|
||||
*/
|
||||
class RemotePlayer : public Player
|
||||
{
|
||||
public:
|
||||
LocalPlayer(IGameDef *gamedef);
|
||||
virtual ~LocalPlayer();
|
||||
RemotePlayer(IGameDef *gamedef): Player(gamedef), m_sao(0) {}
|
||||
virtual ~RemotePlayer() {}
|
||||
|
||||
bool isLocal() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void move(f32 dtime, Map &map, f32 pos_max_d,
|
||||
core::list<CollisionInfo> *collision_info);
|
||||
void move(f32 dtime, Map &map, f32 pos_max_d);
|
||||
|
||||
void applyControl(float dtime);
|
||||
|
||||
v3s16 getStandingNodePos();
|
||||
|
||||
PlayerControl control;
|
||||
PlayerSAO *getPlayerSAO()
|
||||
{ return m_sao; }
|
||||
void setPlayerSAO(PlayerSAO *sao)
|
||||
{ m_sao = sao; }
|
||||
void setPosition(const v3f &position);
|
||||
|
||||
private:
|
||||
// This is used for determining the sneaking range
|
||||
v3s16 m_sneak_node;
|
||||
// Whether the player is allowed to sneak
|
||||
bool m_sneak_node_exists;
|
||||
PlayerSAO *m_sao;
|
||||
};
|
||||
#endif // !SERVER
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1795
src/scriptapi.cpp
1795
src/scriptapi.cpp
File diff suppressed because it is too large
Load Diff
@@ -23,13 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irrlichttypes.h"
|
||||
#include <string>
|
||||
#include "mapnode.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
class Server;
|
||||
class ServerEnvironment;
|
||||
class ServerActiveObject;
|
||||
class ServerRemotePlayer;
|
||||
typedef struct lua_State lua_State;
|
||||
struct LuaEntityProperties;
|
||||
struct ObjectProperties;
|
||||
struct ItemStack;
|
||||
struct PointedThing;
|
||||
//class IGameDef;
|
||||
@@ -51,13 +52,22 @@ bool scriptapi_on_chat_message(lua_State *L, const std::string &name,
|
||||
// On environment step
|
||||
void scriptapi_environment_step(lua_State *L, float dtime);
|
||||
// After generating a piece of map
|
||||
void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp);
|
||||
void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp,
|
||||
u32 blockseed);
|
||||
|
||||
/* misc */
|
||||
void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player);
|
||||
void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player);
|
||||
bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player);
|
||||
void scriptapi_get_creative_inventory(lua_State *L, ServerRemotePlayer *player);
|
||||
void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player);
|
||||
void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player);
|
||||
void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player);
|
||||
bool scriptapi_get_auth(lua_State *L, const std::string &playername,
|
||||
std::string *dst_password, std::set<std::string> *dst_privs);
|
||||
void scriptapi_create_auth(lua_State *L, const std::string &playername,
|
||||
const std::string &password);
|
||||
bool scriptapi_set_password(lua_State *L, const std::string &playername,
|
||||
const std::string &password);
|
||||
|
||||
/* item callbacks */
|
||||
bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
|
||||
@@ -72,6 +82,28 @@ bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
|
||||
ServerActiveObject *puncher);
|
||||
bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
|
||||
ServerActiveObject *digger);
|
||||
// Node constructor
|
||||
void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node);
|
||||
// Node destructor
|
||||
void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node);
|
||||
// Called when a metadata form returns values
|
||||
void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
|
||||
const std::string &formname,
|
||||
const std::map<std::string, std::string> &fields,
|
||||
ServerActiveObject *sender);
|
||||
// Moves items
|
||||
void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p,
|
||||
const std::string &from_list, int from_index,
|
||||
const std::string &to_list, int to_index,
|
||||
int count, ServerActiveObject *player);
|
||||
// Inserts items, returns rejected items
|
||||
ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p,
|
||||
const std::string &listname, int index, ItemStack &stack,
|
||||
ServerActiveObject *player);
|
||||
// Takes items, returns taken items
|
||||
ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p,
|
||||
const std::string &listname, int index, int count,
|
||||
ServerActiveObject *player);
|
||||
|
||||
/* luaentity */
|
||||
// Returns true if succesfully added into Lua; false otherwise.
|
||||
@@ -81,7 +113,7 @@ void scriptapi_luaentity_activate(lua_State *L, u16 id,
|
||||
void scriptapi_luaentity_rm(lua_State *L, u16 id);
|
||||
std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id);
|
||||
void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
|
||||
LuaEntityProperties *prop);
|
||||
ObjectProperties *prop);
|
||||
void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime);
|
||||
void scriptapi_luaentity_punch(lua_State *L, u16 id,
|
||||
ServerActiveObject *puncher, float time_from_last_punch,
|
||||
|
||||
@@ -57,12 +57,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
19: new content type handling
|
||||
20: many existing content types translated to extended ones
|
||||
21: dynamic content type allocation
|
||||
22: full 16-bit content types, minerals removed, facedir & wallmounted changed
|
||||
22: minerals removed, facedir & wallmounted changed
|
||||
23: NodeTimers, new node metadata format
|
||||
*/
|
||||
// This represents an uninitialized or invalid format
|
||||
#define SER_FMT_VER_INVALID 255
|
||||
// Highest supported serialization version
|
||||
#define SER_FMT_VER_HIGHEST 22
|
||||
#define SER_FMT_VER_HIGHEST 23
|
||||
// Lowest supported serialization version
|
||||
#define SER_FMT_VER_LOWEST 0
|
||||
|
||||
|
||||
921
src/server.cpp
921
src/server.cpp
File diff suppressed because it is too large
Load Diff
78
src/server.h
78
src/server.h
@@ -27,11 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "porting.h"
|
||||
#include "map.h"
|
||||
#include "inventory.h"
|
||||
#include "auth.h"
|
||||
#include "ban.h"
|
||||
#include "gamedef.h"
|
||||
#include "serialization.h" // For SER_FMT_VER_INVALID
|
||||
#include "serverremoteplayer.h"
|
||||
#include "mods.h"
|
||||
#include "inventorymanager.h"
|
||||
#include "subgame.h"
|
||||
@@ -42,6 +40,7 @@ class IWritableItemDefManager;
|
||||
class IWritableNodeDefManager;
|
||||
class IWritableCraftDefManager;
|
||||
class EventManager;
|
||||
class PlayerSAO;
|
||||
|
||||
class ServerError : public std::exception
|
||||
{
|
||||
@@ -299,26 +298,7 @@ struct ServerSoundParams
|
||||
loop(false)
|
||||
{}
|
||||
|
||||
v3f getPos(ServerEnvironment *env, bool *pos_exists) const
|
||||
{
|
||||
if(pos_exists) *pos_exists = false;
|
||||
switch(type){
|
||||
case SSP_LOCAL:
|
||||
return v3f(0,0,0);
|
||||
case SSP_POSITIONAL:
|
||||
if(pos_exists) *pos_exists = true;
|
||||
return pos;
|
||||
case SSP_OBJECT: {
|
||||
if(object == 0)
|
||||
return v3f(0,0,0);
|
||||
ServerActiveObject *sao = env->getActiveObject(object);
|
||||
if(!sao)
|
||||
return v3f(0,0,0);
|
||||
if(pos_exists) *pos_exists = true;
|
||||
return sao->getBasePosition(); }
|
||||
}
|
||||
return v3f(0,0,0);
|
||||
}
|
||||
v3f getPos(ServerEnvironment *env, bool *pos_exists) const;
|
||||
};
|
||||
|
||||
struct ServerPlayingSound
|
||||
@@ -503,7 +483,6 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
Shall be called with the environment and the connection locked.
|
||||
*/
|
||||
Inventory* getInventory(const InventoryLocation &loc);
|
||||
std::string getInventoryOwner(const InventoryLocation &loc);
|
||||
void setInventoryModified(const InventoryLocation &loc);
|
||||
|
||||
// Connection must be locked when called
|
||||
@@ -514,23 +493,16 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
m_shutdown_requested = true;
|
||||
}
|
||||
|
||||
// Envlock and conlock should be locked when calling this
|
||||
void SendMovePlayer(Player *player);
|
||||
|
||||
// Returns -1 if failed, sound handle on success
|
||||
// Envlock + conlock
|
||||
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams ¶ms);
|
||||
void stopSound(s32 handle);
|
||||
|
||||
// Thread-safe
|
||||
u64 getPlayerAuthPrivs(const std::string &name);
|
||||
void setPlayerAuthPrivs(const std::string &name, u64 privs);
|
||||
u64 getPlayerEffectivePrivs(const std::string &name);
|
||||
// Envlock + conlock
|
||||
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
|
||||
bool checkPriv(const std::string &name, const std::string &priv);
|
||||
void reportPrivsModified(const std::string &name=""); // ""=all
|
||||
|
||||
// Changes a player's password, password must be given as plaintext
|
||||
// If the player doesn't exist, a new entry is added to the auth manager
|
||||
void setPlayerPassword(const std::string &name, const std::wstring &password);
|
||||
|
||||
// Saves g_settings to configpath given at initialization
|
||||
void saveConfig();
|
||||
|
||||
@@ -580,9 +552,12 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
IWritableCraftDefManager* getWritableCraftDefManager();
|
||||
|
||||
const ModSpec* getModSpec(const std::string &modname);
|
||||
std::string getBuiltinLuaPath();
|
||||
|
||||
std::string getWorldPath(){ return m_path_world; }
|
||||
|
||||
bool isSingleplayer(){ return m_simple_singleplayer_mode; }
|
||||
|
||||
void setAsyncFatalError(const std::string &error)
|
||||
{
|
||||
m_async_fatal_error.set(error);
|
||||
@@ -619,13 +594,11 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
|
||||
// Envlock and conlock should be locked when calling these
|
||||
void SendInventory(u16 peer_id);
|
||||
// send wielded item info about player to all
|
||||
void SendWieldedItem(const ServerRemotePlayer *srp);
|
||||
// send wielded item info about all players to all players
|
||||
void SendPlayerItems();
|
||||
void SendChatMessage(u16 peer_id, const std::wstring &message);
|
||||
void BroadcastChatMessage(const std::wstring &message);
|
||||
void SendPlayerHP(Player *player);
|
||||
void SendPlayerHP(u16 peer_id);
|
||||
void SendMovePlayer(u16 peer_id);
|
||||
void SendPlayerPrivileges(u16 peer_id);
|
||||
/*
|
||||
Send a node removal/addition event to all clients except ignore_id.
|
||||
Additionally, if far_players!=NULL, players further away than
|
||||
@@ -653,8 +626,8 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
Something random
|
||||
*/
|
||||
|
||||
void DiePlayer(Player *player);
|
||||
void RespawnPlayer(Player *player);
|
||||
void DiePlayer(u16 peer_id);
|
||||
void RespawnPlayer(u16 peer_id);
|
||||
|
||||
void UpdateCrafting(u16 peer_id);
|
||||
|
||||
@@ -670,6 +643,15 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
return player->getName();
|
||||
}
|
||||
|
||||
// When called, environment mutex should be locked
|
||||
PlayerSAO* getPlayerSAO(u16 peer_id)
|
||||
{
|
||||
Player *player = m_env->getPlayer(peer_id);
|
||||
if(player == NULL)
|
||||
return NULL;
|
||||
return player->getPlayerSAO();
|
||||
}
|
||||
|
||||
/*
|
||||
Get a player from memory or creates one.
|
||||
If player is already connected, return NULL
|
||||
@@ -677,15 +659,13 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
|
||||
Call with env and con locked.
|
||||
*/
|
||||
ServerRemotePlayer *emergePlayer(const char *name, u16 peer_id);
|
||||
PlayerSAO *emergePlayer(const char *name, u16 peer_id);
|
||||
|
||||
// Locks environment and connection by its own
|
||||
struct PeerChange;
|
||||
void handlePeerChange(PeerChange &c);
|
||||
void handlePeerChanges();
|
||||
|
||||
u64 getPlayerPrivs(Player *player);
|
||||
|
||||
/*
|
||||
Variables
|
||||
*/
|
||||
@@ -724,9 +704,6 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
// Connected clients (behind the con mutex)
|
||||
core::map<u16, RemoteClient*> m_clients;
|
||||
|
||||
// User authentication
|
||||
AuthManager m_authmanager;
|
||||
|
||||
// Bann checking
|
||||
BanManager m_banmanager;
|
||||
|
||||
@@ -821,6 +798,13 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
This is behind m_env_mutex
|
||||
*/
|
||||
bool m_ignore_map_edit_events;
|
||||
/*
|
||||
If a non-empty area, map edit events contained within are left
|
||||
unsent. Done at map generation time to speed up editing of the
|
||||
generated area, as it will be sent anyway.
|
||||
This is behind m_env_mutex
|
||||
*/
|
||||
VoxelArea m_ignore_map_edit_events_area;
|
||||
/*
|
||||
If set to !=0, the incoming MapEditEvents are modified to have
|
||||
this peed id as the disabled recipient
|
||||
|
||||
@@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#include "utility.h"
|
||||
#include "settings.h"
|
||||
#include "main.h" // For g_settings
|
||||
#include "content_sao.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
@@ -37,98 +38,6 @@ void cmd_me(std::wostringstream &os,
|
||||
ctx->flags |= SEND_TO_OTHERS | SEND_NO_PREFIX;
|
||||
}
|
||||
|
||||
void cmd_privs(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if(ctx->parms.size() == 1)
|
||||
{
|
||||
// Show our own real privs, without any adjustments
|
||||
// made for admin status
|
||||
os<<L"-!- " + narrow_to_wide(privsToString(
|
||||
ctx->server->getPlayerAuthPrivs(ctx->player->getName())));
|
||||
return;
|
||||
}
|
||||
|
||||
if((ctx->privs & PRIV_PRIVS) == 0)
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
}
|
||||
|
||||
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
|
||||
if(tp == NULL)
|
||||
{
|
||||
os<<L"-!- No such player";
|
||||
return;
|
||||
}
|
||||
|
||||
os<<L"-!- " + narrow_to_wide(privsToString(ctx->server->getPlayerAuthPrivs(tp->getName())));
|
||||
}
|
||||
|
||||
void cmd_grantrevoke(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if(ctx->parms.size() != 3)
|
||||
{
|
||||
os<<L"-!- Missing parameter";
|
||||
return;
|
||||
}
|
||||
|
||||
if((ctx->privs & PRIV_PRIVS) == 0)
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
}
|
||||
|
||||
u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2]));
|
||||
if(newprivs == PRIV_INVALID)
|
||||
{
|
||||
os<<L"-!- Invalid privileges specified";
|
||||
return;
|
||||
}
|
||||
|
||||
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
|
||||
if(tp == NULL)
|
||||
{
|
||||
os<<L"-!- No such player";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string playername = wide_to_narrow(ctx->parms[1]);
|
||||
u64 privs = ctx->server->getPlayerAuthPrivs(playername);
|
||||
|
||||
if(ctx->parms[0] == L"grant"){
|
||||
privs |= newprivs;
|
||||
actionstream<<ctx->player->getName()<<" grants "
|
||||
<<wide_to_narrow(ctx->parms[2])<<" to "
|
||||
<<playername<<std::endl;
|
||||
|
||||
std::wstring msg;
|
||||
msg += narrow_to_wide(ctx->player->getName());
|
||||
msg += L" granted you the privilege \"";
|
||||
msg += ctx->parms[2];
|
||||
msg += L"\"";
|
||||
ctx->server->notifyPlayer(playername.c_str(), msg);
|
||||
} else {
|
||||
privs &= ~newprivs;
|
||||
actionstream<<ctx->player->getName()<<" revokes "
|
||||
<<wide_to_narrow(ctx->parms[2])<<" from "
|
||||
<<playername<<std::endl;
|
||||
|
||||
std::wstring msg;
|
||||
msg += narrow_to_wide(ctx->player->getName());
|
||||
msg += L" revoked from you the privilege \"";
|
||||
msg += ctx->parms[2];
|
||||
msg += L"\"";
|
||||
ctx->server->notifyPlayer(playername.c_str(), msg);
|
||||
}
|
||||
|
||||
ctx->server->setPlayerAuthPrivs(playername, privs);
|
||||
|
||||
os<<L"-!- Privileges change to ";
|
||||
os<<narrow_to_wide(privsToString(privs));
|
||||
}
|
||||
|
||||
void cmd_time(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
@@ -137,8 +46,8 @@ void cmd_time(std::wostringstream &os,
|
||||
os<<L"-!- Missing parameter";
|
||||
return;
|
||||
}
|
||||
|
||||
if((ctx->privs & PRIV_SETTIME) ==0)
|
||||
|
||||
if(!ctx->server->checkPriv(ctx->player->getName(), "settime"))
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
@@ -155,7 +64,7 @@ void cmd_time(std::wostringstream &os,
|
||||
void cmd_shutdown(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if((ctx->privs & PRIV_SERVER) ==0)
|
||||
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
@@ -170,71 +79,9 @@ void cmd_shutdown(std::wostringstream &os,
|
||||
ctx->flags |= SEND_TO_OTHERS;
|
||||
}
|
||||
|
||||
void cmd_setting(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if((ctx->privs & PRIV_SERVER) ==0)
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
}
|
||||
|
||||
/*std::string confline = wide_to_narrow(
|
||||
ctx->parms[1] + L" = " + ctx->params[2]);*/
|
||||
|
||||
std::string confline = wide_to_narrow(ctx->paramstring);
|
||||
|
||||
actionstream<<ctx->player->getName()
|
||||
<<" sets: "<<confline<<std::endl;
|
||||
|
||||
g_settings->parseConfigLine(confline);
|
||||
|
||||
ctx->server->saveConfig();
|
||||
|
||||
os<< L"-!- Setting changed and configuration saved.";
|
||||
}
|
||||
|
||||
void cmd_teleport(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if((ctx->privs & PRIV_TELEPORT) ==0)
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx->parms.size() != 2)
|
||||
{
|
||||
os<<L"-!- Missing parameter";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> coords = str_split(ctx->parms[1], L',');
|
||||
if(coords.size() != 3)
|
||||
{
|
||||
os<<L"-!- You can only specify coordinates currently";
|
||||
return;
|
||||
}
|
||||
|
||||
v3f dest(stoi(coords[0])*10, stoi(coords[1])*10, stoi(coords[2])*10);
|
||||
|
||||
actionstream<<ctx->player->getName()<<" teleports from "
|
||||
<<PP(ctx->player->getPosition()/BS)<<" to "
|
||||
<<PP(dest/BS)<<std::endl;
|
||||
|
||||
//ctx->player->setPosition(dest);
|
||||
|
||||
// Use the ServerActiveObject interface of ServerRemotePlayer
|
||||
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(ctx->player);
|
||||
srp->setPos(dest);
|
||||
ctx->server->SendMovePlayer(ctx->player);
|
||||
|
||||
os<< L"-!- Teleported.";
|
||||
}
|
||||
|
||||
void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
|
||||
{
|
||||
if((ctx->privs & PRIV_BAN) == 0)
|
||||
if(!ctx->server->checkPriv(ctx->player->getName(), "ban"))
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
@@ -281,62 +128,10 @@ void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_setclearpassword(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if((ctx->privs & PRIV_PASSWORD) == 0)
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string playername;
|
||||
std::wstring password;
|
||||
|
||||
if(ctx->parms[0] == L"setpassword")
|
||||
{
|
||||
if(ctx->parms.size() != 3)
|
||||
{
|
||||
os<<L"-!- Missing parameter";
|
||||
return;
|
||||
}
|
||||
|
||||
playername = wide_to_narrow(ctx->parms[1]);
|
||||
password = ctx->parms[2];
|
||||
|
||||
actionstream<<ctx->player->getName()<<" sets password of "
|
||||
<<playername<<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clearpassword
|
||||
|
||||
if(ctx->parms.size() != 2)
|
||||
{
|
||||
os<<L"-!- Missing parameter";
|
||||
return;
|
||||
}
|
||||
|
||||
playername = wide_to_narrow(ctx->parms[1]);
|
||||
password = L"";
|
||||
|
||||
actionstream<<ctx->player->getName()<<" clears password of"
|
||||
<<playername<<std::endl;
|
||||
}
|
||||
|
||||
ctx->server->setPlayerPassword(playername, password);
|
||||
|
||||
std::wostringstream msg;
|
||||
msg<<ctx->player->getName()<<L" changed your password";
|
||||
ctx->server->notifyPlayer(playername.c_str(), msg.str());
|
||||
|
||||
os<<L"-!- Password change for "<<narrow_to_wide(playername)<<" successful";
|
||||
}
|
||||
|
||||
void cmd_clearobjects(std::wostringstream &os,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
if((ctx->privs & PRIV_SERVER) ==0)
|
||||
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
|
||||
{
|
||||
os<<L"-!- You don't have permission to do that";
|
||||
return;
|
||||
@@ -365,47 +160,19 @@ void cmd_clearobjects(std::wostringstream &os,
|
||||
|
||||
std::wstring processServerCommand(ServerCommandContext *ctx)
|
||||
{
|
||||
|
||||
std::wostringstream os(std::ios_base::binary);
|
||||
ctx->flags = SEND_TO_SENDER; // Default, unless we change it.
|
||||
|
||||
u64 privs = ctx->privs;
|
||||
|
||||
if(ctx->parms.size() == 0 || ctx->parms[0] == L"help")
|
||||
{
|
||||
os<<L"-!- Available commands: ";
|
||||
os<<L"me status privs";
|
||||
if(privs & PRIV_SERVER)
|
||||
os<<L" shutdown setting clearobjects";
|
||||
if(privs & PRIV_SETTIME)
|
||||
os<<L" time";
|
||||
if(privs & PRIV_TELEPORT)
|
||||
os<<L" teleport";
|
||||
if(privs & PRIV_PRIVS)
|
||||
os<<L" grant revoke";
|
||||
if(privs & PRIV_BAN)
|
||||
os<<L" ban unban";
|
||||
if(privs & PRIV_PASSWORD)
|
||||
os<<L" setpassword clearpassword";
|
||||
}
|
||||
if(ctx->parms.size() == 0)
|
||||
os<<L"-!- Empty command";
|
||||
else if(ctx->parms[0] == L"status")
|
||||
cmd_status(os, ctx);
|
||||
else if(ctx->parms[0] == L"privs")
|
||||
cmd_privs(os, ctx);
|
||||
else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke")
|
||||
cmd_grantrevoke(os, ctx);
|
||||
else if(ctx->parms[0] == L"time")
|
||||
cmd_time(os, ctx);
|
||||
else if(ctx->parms[0] == L"shutdown")
|
||||
cmd_shutdown(os, ctx);
|
||||
else if(ctx->parms[0] == L"setting")
|
||||
cmd_setting(os, ctx);
|
||||
else if(ctx->parms[0] == L"teleport")
|
||||
cmd_teleport(os, ctx);
|
||||
else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban")
|
||||
cmd_banunban(os, ctx);
|
||||
else if(ctx->parms[0] == L"setpassword" || ctx->parms[0] == L"clearpassword")
|
||||
cmd_setclearpassword(os, ctx);
|
||||
else if(ctx->parms[0] == L"me")
|
||||
cmd_me(os, ctx);
|
||||
else if(ctx->parms[0] == L"clearobjects")
|
||||
|
||||
@@ -36,9 +36,6 @@ struct ServerCommandContext
|
||||
Server* server;
|
||||
ServerEnvironment *env;
|
||||
Player* player;
|
||||
// Effective privs for the player, which may be different to their
|
||||
// stored ones - e.g. if they are named in the config as an admin.
|
||||
u64 privs;
|
||||
u32 flags;
|
||||
|
||||
ServerCommandContext(
|
||||
@@ -46,10 +43,9 @@ struct ServerCommandContext
|
||||
std::wstring paramstring,
|
||||
Server* server,
|
||||
ServerEnvironment *env,
|
||||
Player* player,
|
||||
u64 privs)
|
||||
Player* player)
|
||||
: parms(parms), paramstring(paramstring),
|
||||
server(server), env(env), player(player), privs(privs)
|
||||
server(server), env(env), player(player)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "activeobject.h"
|
||||
#include "utility.h"
|
||||
#include "inventorymanager.h"
|
||||
#include "itemgroup.h"
|
||||
|
||||
/*
|
||||
|
||||
@@ -45,6 +46,7 @@ class ServerEnvironment;
|
||||
struct ItemStack;
|
||||
class Player;
|
||||
struct ToolCapabilities;
|
||||
struct ObjectProperties;
|
||||
|
||||
class ServerActiveObject : public ActiveObject
|
||||
{
|
||||
@@ -56,6 +58,9 @@ class ServerActiveObject : public ActiveObject
|
||||
ServerActiveObject(ServerEnvironment *env, v3f pos);
|
||||
virtual ~ServerActiveObject();
|
||||
|
||||
virtual u8 getSendType() const
|
||||
{ return getType(); }
|
||||
|
||||
// Called after id has been set and has been inserted in environment
|
||||
virtual void addedToEnvironment(){};
|
||||
// Called before removing from environment
|
||||
@@ -143,9 +148,16 @@ class ServerActiveObject : public ActiveObject
|
||||
{}
|
||||
virtual void setHP(s16 hp)
|
||||
{}
|
||||
virtual s16 getHP()
|
||||
virtual s16 getHP() const
|
||||
{ return 0; }
|
||||
|
||||
virtual void setArmorGroups(const ItemGroupList &armor_groups)
|
||||
{}
|
||||
virtual ObjectProperties* accessObjectProperties()
|
||||
{ return NULL; }
|
||||
virtual void notifyObjectPropertiesModified()
|
||||
{}
|
||||
|
||||
// Inventory and wielded item
|
||||
virtual Inventory* getInventory()
|
||||
{ return NULL; }
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2011 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 "serverremoteplayer.h"
|
||||
#include "main.h" // For g_settings
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#include "gamedef.h"
|
||||
#include "inventory.h"
|
||||
#include "environment.h"
|
||||
#include "tool.h"
|
||||
|
||||
ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env):
|
||||
Player(env->getGameDef()),
|
||||
ServerActiveObject(env, v3f(0,0,0)),
|
||||
m_last_good_position(0,0,0),
|
||||
m_last_good_position_age(0),
|
||||
m_wield_index(0),
|
||||
m_inventory_not_sent(false),
|
||||
m_hp_not_sent(false),
|
||||
m_is_in_environment(false),
|
||||
m_time_from_last_punch(0),
|
||||
m_position_not_sent(false)
|
||||
{
|
||||
}
|
||||
ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 peer_id_,
|
||||
const char *name_):
|
||||
Player(env->getGameDef()),
|
||||
ServerActiveObject(env, pos_),
|
||||
m_last_good_position(0,0,0),
|
||||
m_last_good_position_age(0),
|
||||
m_wield_index(0),
|
||||
m_inventory_not_sent(false),
|
||||
m_hp_not_sent(false),
|
||||
m_is_in_environment(false),
|
||||
m_time_from_last_punch(0),
|
||||
m_position_not_sent(false)
|
||||
{
|
||||
setPosition(pos_);
|
||||
peer_id = peer_id_;
|
||||
updateName(name_);
|
||||
}
|
||||
ServerRemotePlayer::~ServerRemotePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::setPosition(const v3f &position)
|
||||
{
|
||||
Player::setPosition(position);
|
||||
ServerActiveObject::setBasePosition(position);
|
||||
m_position_not_sent = true;
|
||||
}
|
||||
|
||||
Inventory* ServerRemotePlayer::getInventory()
|
||||
{
|
||||
return &inventory;
|
||||
}
|
||||
|
||||
const Inventory* ServerRemotePlayer::getInventory() const
|
||||
{
|
||||
return &inventory;
|
||||
}
|
||||
|
||||
InventoryLocation ServerRemotePlayer::getInventoryLocation() const
|
||||
{
|
||||
InventoryLocation loc;
|
||||
loc.setPlayer(getName());
|
||||
return loc;
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::setInventoryModified()
|
||||
{
|
||||
m_inventory_not_sent = true;
|
||||
}
|
||||
|
||||
std::string ServerRemotePlayer::getWieldList() const
|
||||
{
|
||||
return "main";
|
||||
}
|
||||
|
||||
int ServerRemotePlayer::getWieldIndex() const
|
||||
{
|
||||
return m_wield_index;
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::setWieldIndex(int i)
|
||||
{
|
||||
m_wield_index = i;
|
||||
}
|
||||
|
||||
/* ServerActiveObject interface */
|
||||
|
||||
void ServerRemotePlayer::addedToEnvironment()
|
||||
{
|
||||
assert(!m_is_in_environment);
|
||||
m_is_in_environment = true;
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::removingFromEnvironment()
|
||||
{
|
||||
assert(m_is_in_environment);
|
||||
m_is_in_environment = false;
|
||||
}
|
||||
|
||||
bool ServerRemotePlayer::unlimitedTransferDistance() const
|
||||
{
|
||||
return g_settings->getBool("unlimited_player_transfer_distance");
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::step(float dtime, bool send_recommended)
|
||||
{
|
||||
m_time_from_last_punch += dtime;
|
||||
|
||||
if(send_recommended == false)
|
||||
return;
|
||||
|
||||
if(m_position_not_sent)
|
||||
{
|
||||
m_position_not_sent = false;
|
||||
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command (0 = update position)
|
||||
writeU8(os, 0);
|
||||
// pos
|
||||
writeV3F1000(os, getPosition());
|
||||
// yaw
|
||||
writeF1000(os, getYaw());
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, os.str());
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ServerRemotePlayer::getClientInitializationData()
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// version
|
||||
writeU8(os, 0);
|
||||
// name
|
||||
os<<serializeString(getName());
|
||||
// pos
|
||||
writeV3F1000(os, getPosition());
|
||||
// yaw
|
||||
writeF1000(os, getYaw());
|
||||
// dead
|
||||
writeU8(os, getHP() == 0);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string ServerRemotePlayer::getStaticData()
|
||||
{
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
|
||||
int ServerRemotePlayer::punch(v3f dir,
|
||||
const ToolCapabilities *toolcap,
|
||||
ServerActiveObject *puncher,
|
||||
float time_from_last_punch)
|
||||
{
|
||||
if(!toolcap)
|
||||
return 0;
|
||||
|
||||
// No effect if PvP disabled
|
||||
if(g_settings->getBool("enable_pvp") == false){
|
||||
if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// "Material" groups of the player
|
||||
ItemGroupList groups;
|
||||
groups["choppy"] = 2;
|
||||
groups["fleshy"] = 3;
|
||||
|
||||
HitParams hitparams = getHitParams(groups, toolcap, time_from_last_punch);
|
||||
|
||||
actionstream<<"Player "<<getName()<<" punched by "
|
||||
<<puncher->getDescription()<<", damage "<<hitparams.hp
|
||||
<<" HP"<<std::endl;
|
||||
|
||||
setHP(getHP() - hitparams.hp);
|
||||
|
||||
if(hitparams.hp != 0)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command (1 = punched)
|
||||
writeU8(os, 1);
|
||||
// damage
|
||||
writeS16(os, hitparams.hp);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, os.str());
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
return hitparams.wear;
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::rightClick(ServerActiveObject *clicker)
|
||||
{
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::setPos(v3f pos)
|
||||
{
|
||||
setPosition(pos);
|
||||
// Movement caused by this command is always valid
|
||||
m_last_good_position = pos;
|
||||
m_last_good_position_age = 0;
|
||||
}
|
||||
void ServerRemotePlayer::moveTo(v3f pos, bool continuous)
|
||||
{
|
||||
setPosition(pos);
|
||||
// Movement caused by this command is always valid
|
||||
m_last_good_position = pos;
|
||||
m_last_good_position_age = 0;
|
||||
}
|
||||
|
||||
void ServerRemotePlayer::setHP(s16 hp_)
|
||||
{
|
||||
s16 oldhp = hp;
|
||||
|
||||
// FIXME: don't hardcode maximum HP, make configurable per object
|
||||
if(hp_ < 0)
|
||||
hp_ = 0;
|
||||
else if(hp_ > 20)
|
||||
hp_ = 20;
|
||||
hp = hp_;
|
||||
|
||||
if(hp != oldhp)
|
||||
m_hp_not_sent = true;
|
||||
|
||||
// On death or reincarnation send an active object message
|
||||
if((hp == 0) != (oldhp == 0))
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command (2 = update death state)
|
||||
writeU8(os, 2);
|
||||
// dead?
|
||||
writeU8(os, hp == 0);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), false, os.str());
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
}
|
||||
s16 ServerRemotePlayer::getHP()
|
||||
{
|
||||
return hp;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2011 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 SERVERREMOTEPLAYER_HEADER
|
||||
#define SERVERREMOTEPLAYER_HEADER
|
||||
|
||||
#include "player.h"
|
||||
#include "serverobject.h"
|
||||
#include "content_object.h" // Object type IDs
|
||||
|
||||
/*
|
||||
Player on the server
|
||||
*/
|
||||
|
||||
class ServerRemotePlayer : public Player, public ServerActiveObject
|
||||
{
|
||||
public:
|
||||
ServerRemotePlayer(ServerEnvironment *env);
|
||||
ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 peer_id_,
|
||||
const char *name_);
|
||||
|
||||
virtual ~ServerRemotePlayer();
|
||||
|
||||
virtual bool isLocal() const
|
||||
{ return false; }
|
||||
|
||||
virtual void move(f32 dtime, Map &map, f32 pos_max_d)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setPosition(const v3f &position);
|
||||
|
||||
/* ServerActiveObject interface */
|
||||
|
||||
u8 getType() const
|
||||
{return ACTIVEOBJECT_TYPE_PLAYER;}
|
||||
|
||||
// Called after id has been set and has been inserted in environment
|
||||
void addedToEnvironment();
|
||||
// Called before removing from environment
|
||||
void removingFromEnvironment();
|
||||
|
||||
bool environmentDeletes() const
|
||||
{ return false; }
|
||||
|
||||
virtual bool unlimitedTransferDistance() const;
|
||||
|
||||
bool isStaticAllowed() const
|
||||
{ return false; }
|
||||
|
||||
void step(float dtime, bool send_recommended);
|
||||
std::string getClientInitializationData();
|
||||
std::string getStaticData();
|
||||
int punch(v3f dir,
|
||||
const ToolCapabilities *toolcap,
|
||||
ServerActiveObject *puncher,
|
||||
float time_from_last_punch);
|
||||
void rightClick(ServerActiveObject *clicker);
|
||||
void setPos(v3f pos);
|
||||
void moveTo(v3f pos, bool continuous);
|
||||
virtual std::string getDescription()
|
||||
{return std::string("player ")+getName();}
|
||||
|
||||
virtual Inventory* getInventory();
|
||||
virtual const Inventory* getInventory() const;
|
||||
virtual InventoryLocation getInventoryLocation() const;
|
||||
virtual void setInventoryModified();
|
||||
virtual std::string getWieldList() const;
|
||||
virtual int getWieldIndex() const;
|
||||
virtual void setWieldIndex(int i);
|
||||
|
||||
virtual void setHP(s16 hp_);
|
||||
virtual s16 getHP();
|
||||
|
||||
v3f m_last_good_position;
|
||||
float m_last_good_position_age;
|
||||
int m_wield_index;
|
||||
bool m_inventory_not_sent;
|
||||
bool m_hp_not_sent;
|
||||
bool m_is_in_environment;
|
||||
// Incremented by step(), read and reset by Server
|
||||
float m_time_from_last_punch;
|
||||
|
||||
private:
|
||||
bool m_position_not_sent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -147,8 +147,8 @@ class Settings
|
||||
if(is.good() == false)
|
||||
return false;
|
||||
|
||||
infostream<<"Parsing configuration file: \""
|
||||
<<filename<<"\""<<std::endl;
|
||||
/*infostream<<"Parsing configuration file: \""
|
||||
<<filename<<"\""<<std::endl;*/
|
||||
|
||||
while(parseConfigObject(is));
|
||||
|
||||
|
||||
78
src/sky.cpp
78
src/sky.cpp
@@ -91,7 +91,7 @@ void Sky::render()
|
||||
scale.setScale(core::vector3df(viewDistance, viewDistance, viewDistance));
|
||||
|
||||
driver->setTransform(video::ETS_WORLD, translate * scale);
|
||||
|
||||
|
||||
if(m_sunlight_seen)
|
||||
{
|
||||
float sunsize = 0.07;
|
||||
@@ -108,7 +108,7 @@ void Sky::render()
|
||||
video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
|
||||
video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);
|
||||
|
||||
float nightlength = 0.41;
|
||||
float nightlength = 0.415;
|
||||
float wn = nightlength / 2;
|
||||
float wicked_time_of_day = 0;
|
||||
if(m_time_of_day > wn && m_time_of_day < 1.0 - wn)
|
||||
@@ -129,8 +129,63 @@ void Sky::render()
|
||||
const f32 o = 0.0f;
|
||||
static const u16 indices[4] = {0,1,2,3};
|
||||
video::S3DVertex vertices[4];
|
||||
|
||||
driver->setMaterial(m_materials[1]);
|
||||
|
||||
//video::SColor cloudyfogcolor(255,255,255,255);
|
||||
video::SColor cloudyfogcolor = m_bgcolor;
|
||||
//video::SColor cloudyfogcolor = m_bgcolor.getInterpolated(m_skycolor, 0.5);
|
||||
|
||||
// Draw far cloudy fog thing
|
||||
for(u32 j=0; j<4; j++)
|
||||
{
|
||||
video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45);
|
||||
vertices[0] = video::S3DVertex(-1, 0.08,-1, 0,0,1, c, t, t);
|
||||
vertices[1] = video::S3DVertex( 1, 0.08,-1, 0,0,1, c, o, t);
|
||||
vertices[2] = video::S3DVertex( 1, 0.12,-1, 0,0,1, c, o, o);
|
||||
vertices[3] = video::S3DVertex(-1, 0.12,-1, 0,0,1, c, t, o);
|
||||
for(u32 i=0; i<4; i++){
|
||||
if(j==0)
|
||||
// Don't switch
|
||||
{}
|
||||
else if(j==1)
|
||||
// Switch from -Z (south) to +X (east)
|
||||
vertices[i].Pos.rotateXZBy(90);
|
||||
else if(j==2)
|
||||
// Switch from -Z (south) to -X (west)
|
||||
vertices[i].Pos.rotateXZBy(-90);
|
||||
else
|
||||
// Switch from -Z (south) to -Z (north)
|
||||
vertices[i].Pos.rotateXZBy(-180);
|
||||
}
|
||||
driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
|
||||
}
|
||||
for(u32 j=0; j<4; j++)
|
||||
{
|
||||
video::SColor c = cloudyfogcolor;
|
||||
vertices[0] = video::S3DVertex(-1,-1.0,-1, 0,0,1, c, t, t);
|
||||
vertices[1] = video::S3DVertex( 1,-1.0,-1, 0,0,1, c, o, t);
|
||||
vertices[2] = video::S3DVertex( 1, 0.08,-1, 0,0,1, c, o, o);
|
||||
vertices[3] = video::S3DVertex(-1, 0.08,-1, 0,0,1, c, t, o);
|
||||
for(u32 i=0; i<4; i++){
|
||||
if(j==0)
|
||||
// Don't switch
|
||||
{}
|
||||
else if(j==1)
|
||||
// Switch from -Z (south) to +X (east)
|
||||
vertices[i].Pos.rotateXZBy(90);
|
||||
else if(j==2)
|
||||
// Switch from -Z (south) to -X (west)
|
||||
vertices[i].Pos.rotateXZBy(-90);
|
||||
else
|
||||
// Switch from -Z (south) to -Z (north)
|
||||
vertices[i].Pos.rotateXZBy(-180);
|
||||
}
|
||||
driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
|
||||
}
|
||||
|
||||
driver->setMaterial(m_materials[2]);
|
||||
|
||||
{
|
||||
float mid1 = 0.25;
|
||||
float mid = (wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1));
|
||||
@@ -314,10 +369,12 @@ void Sky::render()
|
||||
|
||||
for(u32 j=0; j<2; j++)
|
||||
{
|
||||
vertices[0] = video::S3DVertex(-1,-1.0,-1, 0,0,1, m_skycolor, t, t);
|
||||
vertices[1] = video::S3DVertex( 1,-1.0,-1, 0,0,1, m_skycolor, o, t);
|
||||
vertices[2] = video::S3DVertex( 1,-0.02,-1, 0,0,1, m_skycolor, o, o);
|
||||
vertices[3] = video::S3DVertex(-1,-0.02,-1, 0,0,1, m_skycolor, t, o);
|
||||
//video::SColor c = m_skycolor;
|
||||
video::SColor c = cloudyfogcolor;
|
||||
vertices[0] = video::S3DVertex(-1,-1.0,-1, 0,0,1, c, t, t);
|
||||
vertices[1] = video::S3DVertex( 1,-1.0,-1, 0,0,1, c, o, t);
|
||||
vertices[2] = video::S3DVertex( 1,-0.02,-1, 0,0,1, c, o, o);
|
||||
vertices[3] = video::S3DVertex(-1,-0.02,-1, 0,0,1, c, t, o);
|
||||
for(u32 i=0; i<4; i++){
|
||||
//if(wicked_time_of_day < 0.5)
|
||||
if(j==0)
|
||||
@@ -353,14 +410,17 @@ void Sky::update(float time_of_day, float time_brightness,
|
||||
m_time_brightness = time_brightness;
|
||||
m_sunlight_seen = sunlight_seen;
|
||||
|
||||
bool is_dawn = (time_brightness >= 0.20 && time_brightness < 0.50);
|
||||
bool is_dawn = (time_brightness >= 0.20 && time_brightness < 0.35);
|
||||
|
||||
video::SColorf bgcolor_bright_normal_f(170./255,200./255,230./255, 1.0);
|
||||
//video::SColorf bgcolor_bright_normal_f(170./255,200./255,230./255, 1.0);
|
||||
video::SColorf bgcolor_bright_normal_f(155./255,193./255,240./255, 1.0);
|
||||
video::SColorf bgcolor_bright_indoor_f(100./255,100./255,100./255, 1.0);
|
||||
//video::SColorf bgcolor_bright_dawn_f(0.666,200./255*0.7,230./255*0.5,1.0);
|
||||
//video::SColorf bgcolor_bright_dawn_f(0.666,0.549,0.220,1.0);
|
||||
//video::SColorf bgcolor_bright_dawn_f(0.666*1.2,0.549*1.0,0.220*1.0, 1.0);
|
||||
video::SColorf bgcolor_bright_dawn_f(0.666*1.2,0.549*1.0,0.220*1.2,1.0);
|
||||
//video::SColorf bgcolor_bright_dawn_f(0.666*1.2,0.549*1.0,0.220*1.2,1.0);
|
||||
video::SColorf bgcolor_bright_dawn_f
|
||||
(155./255*1.2,193./255,240./255, 1.0);
|
||||
|
||||
video::SColorf skycolor_bright_normal_f =
|
||||
video::SColor(255, 140, 186, 250);
|
||||
|
||||
@@ -58,6 +58,7 @@ class ISoundManager
|
||||
const std::string &filedata) = 0;
|
||||
|
||||
virtual void updateListener(v3f pos, v3f vel, v3f at, v3f up) = 0;
|
||||
virtual void setListenerGain(float gain) = 0;
|
||||
|
||||
// playSound functions return -1 on failure, otherwise a handle to the
|
||||
// sound. If name=="", call should be ignored without error.
|
||||
@@ -83,6 +84,7 @@ class DummySoundManager: public ISoundManager
|
||||
virtual bool loadSoundData(const std::string &name,
|
||||
const std::string &filedata) {return true;}
|
||||
void updateListener(v3f pos, v3f vel, v3f at, v3f up) {}
|
||||
void setListenerGain(float gain) {}
|
||||
int playSound(const std::string &name, bool loop,
|
||||
float volume) {return 0;}
|
||||
int playSoundAt(const std::string &name, bool loop,
|
||||
|
||||
@@ -198,12 +198,14 @@ class OpenALSoundManager: public ISoundManager
|
||||
std::map<int, PlayingSound*> m_sounds_playing;
|
||||
v3f m_listener_pos;
|
||||
public:
|
||||
bool m_is_initialized;
|
||||
OpenALSoundManager(OnDemandSoundFetcher *fetcher):
|
||||
m_fetcher(fetcher),
|
||||
m_device(NULL),
|
||||
m_context(NULL),
|
||||
m_can_vorbis(false),
|
||||
m_next_id(1)
|
||||
m_next_id(1),
|
||||
m_is_initialized(false)
|
||||
{
|
||||
ALCenum error = ALC_NO_ERROR;
|
||||
|
||||
@@ -252,6 +254,8 @@ class OpenALSoundManager: public ISoundManager
|
||||
infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
|
||||
<<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
|
||||
<<std::endl;
|
||||
|
||||
m_is_initialized = true;
|
||||
}
|
||||
|
||||
~OpenALSoundManager()
|
||||
@@ -461,10 +465,15 @@ class OpenALSoundManager: public ISoundManager
|
||||
alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
|
||||
ALfloat f[6];
|
||||
f3_set(f, at);
|
||||
f3_set(f+3, up);
|
||||
f3_set(f+3, -up);
|
||||
alListenerfv(AL_ORIENTATION, f);
|
||||
warn_if_error(alGetError(), "updateListener");
|
||||
}
|
||||
|
||||
void setListenerGain(float gain)
|
||||
{
|
||||
alListenerf(AL_GAIN, gain);
|
||||
}
|
||||
|
||||
int playSound(const std::string &name, bool loop, float volume)
|
||||
{
|
||||
@@ -519,6 +528,10 @@ class OpenALSoundManager: public ISoundManager
|
||||
|
||||
ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
|
||||
{
|
||||
return new OpenALSoundManager(fetcher);
|
||||
OpenALSoundManager *m = new OpenALSoundManager(fetcher);
|
||||
if(m->m_is_initialized)
|
||||
return m;
|
||||
delete m;
|
||||
return NULL;
|
||||
};
|
||||
|
||||
|
||||
@@ -87,6 +87,24 @@ SubgameSpec findSubgame(const std::string &id)
|
||||
return SubgameSpec(id, game_path, mods_paths, game_name);
|
||||
}
|
||||
|
||||
SubgameSpec findWorldSubgame(const std::string &world_path)
|
||||
{
|
||||
std::string world_gameid = getWorldGameId(world_path, true);
|
||||
// See if world contains an embedded game; if so, use it.
|
||||
std::string world_gamepath = world_path + DIR_DELIM + "game";
|
||||
if(fs::PathExists(world_gamepath)){
|
||||
SubgameSpec gamespec;
|
||||
gamespec.id = world_gameid;
|
||||
gamespec.path = world_gamepath;
|
||||
gamespec.mods_paths.insert(world_gamepath + DIR_DELIM + "mods");
|
||||
gamespec.name = getGameName(world_gamepath);
|
||||
if(gamespec.name == "")
|
||||
gamespec.name = "unknown";
|
||||
return gamespec;
|
||||
}
|
||||
return findSubgame(world_gameid);
|
||||
}
|
||||
|
||||
std::set<std::string> getAvailableGameIds()
|
||||
{
|
||||
std::set<std::string> gameids;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user