Compare commits

..

191 Commits

Author SHA1 Message Date
Perttu Ahola
3bea1a944e Call this 0.4.dev-20120603 2012-06-04 00:37:51 +03:00
darkrose
3e419ffb38 Add InvRef:is_empty(listname) and make chests/furnaces not diggable if not empty in minimal game 2012-06-03 22:31:02 +03:00
darkrose
a149c6ecde Add can_dig callback 2012-06-03 22:31:02 +03:00
darkrose
3f18e9a2da fix locked chest to not destroy denied items (minimal game) 2012-06-03 22:31:02 +03:00
Perttu Ahola
4b0494062e Add fire visualization to minimal furnace menu 2012-06-03 22:31:02 +03:00
Perttu Ahola
a09d86dd3c Update inventory menu from formspec on-the-fly 2012-06-03 22:31:02 +03:00
Perttu Ahola
3ccb0f691b Add texture modification [lowpart:percent:filename 2012-06-03 22:31:02 +03:00
Perttu Ahola
1709fd854a Implement image[<X>,<Y>;<W>,<H>;<texture name>] and allow floating-point values in formspec 2012-06-03 22:31:02 +03:00
Perttu Ahola
7bf446f671 Reshape inventory menu code 2012-06-03 22:31:02 +03:00
Perttu Ahola
e4bc76f55c Add a bit of unit test for inventory 2012-06-03 22:31:02 +03:00
Perttu Ahola
81c5e5200b Fix loading of legacy chests 2012-06-03 22:31:01 +03:00
Perttu Ahola
9c3830dd35 Use proper furnace cook time 2012-06-03 22:31:01 +03:00
Perttu Ahola
bf8cfce50e Add ObjRef:is_player() and modify ObjRef:get_player_name() to always return a string to aid better inter-object compatibility of code that assumes objects to be players 2012-06-03 22:31:01 +03:00
Perttu Ahola
b235e4d290 Make GUIinventoryMenu::drawList more robust with invalid data 2012-06-03 22:31:01 +03:00
darkrose
b8cf6dee3e Lua implementation of furnace with visible active state 2012-06-03 22:31:01 +03:00
Perttu Ahola
35df1ee253 Implement locked chest; add after_place_node and after_dig_node node callbacks 2012-06-03 22:31:01 +03:00
Perttu Ahola
353f9fd43c minetest.get_craft_result 2012-06-03 22:31:01 +03:00
Perttu Ahola
3899f83da8 NodeMetaRef:{to,from}_table and lua_api.txt additions 2012-06-03 22:31:01 +03:00
Perttu Ahola
5f3c70061d Properly handle dropping of items from nodes, and disallow moving items directly between nodes 2012-06-03 22:31:01 +03:00
Perttu Ahola
aba7134301 on_metadata_inventory_{move,offer,take} 2012-06-03 22:31:01 +03:00
Perttu Ahola
d7447cdf9e Implement sign using form field protocol 2012-06-03 22:31:01 +03:00
Perttu Ahola
ff8d2bbc36 Convert legacy chest inventory list "0" to "main" 2012-06-03 22:31:00 +03:00
Perttu Ahola
70c98c9972 Properly create metadata inventories in minimal 2012-06-03 22:31:00 +03:00
Perttu Ahola
45f32838b9 Improve GUIInventoryMenu error messages more 2012-06-03 22:31:00 +03:00
Perttu Ahola
7d6e80a423 Add proper error messages to GUIInventoryMenu when inventory lists are invalid 2012-06-03 22:31:00 +03:00
Perttu Ahola
f3ec1452cd Random node metadata things 2012-06-03 22:31:00 +03:00
Perttu Ahola
29f03756bd Attempt to begin to implement chests and furnace in Lua (with problems) 2012-06-03 22:31:00 +03:00
Perttu Ahola
fe8c5546f0 Add node on_construct, on_destruct callbacks and update lua_api.txt 2012-06-03 22:31:00 +03:00
darkrose
c4315a7afa Strip unneeded variables from NodeMetadata 2012-06-03 22:31:00 +03:00
Kahrl
704782c95b WIP node metadata, node timers 2012-06-03 22:31:00 +03:00
Perttu Ahola
67059e1932 Catch VersionMismatchException while loading world data 2012-06-03 20:42:49 +03:00
Perttu Ahola
e2ea711136 Check password hash validity 2012-06-03 20:32:44 +03:00
Perttu Ahola
74aa598769 Fix MSVC build (pow type overload stuff) 2012-05-31 20:22:37 +03:00
Perttu Ahola
747ff7daf8 Add support for putting modpacks directly into mod directories 2012-05-21 19:49:35 +03:00
Perttu Ahola
1f670fc688 Add better trace peudo-loglevel support 2012-05-20 18:30:30 +03:00
Perttu Ahola
388906ac70 Allow using --worldname for local game too 2012-05-20 17:41:40 +03:00
Perttu Ahola
49d9745dc4 Comment out unnecessary debug output in Settings 2012-05-20 17:19:48 +03:00
Perttu Ahola
7c15d793ac --world list 2012-05-20 17:09:46 +03:00
Perttu Ahola
d90cb3511c Better description of static_spawnpoint in minetest.conf.example 2012-05-20 16:59:13 +03:00
Perttu Ahola
5100272dc3 Update minetest.conf.example a bit 2012-05-20 14:54:02 +03:00
Perttu Ahola
afda9810f1 Tune smooth lighting a bit 2012-05-20 14:12:17 +03:00
Perttu Ahola
22df8b81b3 Log more stuff in Client::afterContentReceived() 2012-05-19 17:11:41 +03:00
Perttu Ahola
7a427c9070 Take out the "Privileges of foo are hidden from you." response of /privs 2012-05-19 12:42:55 +03:00
Perttu Ahola
a35e4bdbda Add a hack to get rid of the rare "ERROR: StaticObjectList::insert(): id already exists" failure 2012-04-15 14:37:35 +03:00
Perttu Ahola
0aeb5adb8f Fix continuing to process TOSERVER_INIT2 even if player doesn't exist 2012-04-14 18:46:10 +03:00
Perttu Ahola
9c5cc217bf Add "Registered definitions of stuff" to doc/lua_api.txt 2012-04-14 11:24:18 +03:00
Perttu Ahola
24603d7ad3 Fix executing register_chatcommand-defined commands even in the middle of a chat line 2012-04-13 17:20:48 +03:00
Perttu Ahola
1eac6ff8ff Fix unhandled InvalidPositionException 2012-04-10 18:16:03 +03:00
Perttu Ahola
8742b3dab2 basic_privs to allow granting/revoking interact_extra too 2012-04-10 00:41:28 +03:00
Perttu Ahola
93cdc9b9dc Support static_spawnpoint setting 2012-04-09 23:32:13 +03:00
Perttu Ahola
8ed74a3429 Fix ABM handling on block activation 2012-04-09 23:06:24 +03:00
Perttu Ahola
07a5e59ae7 Allow redefining minetest.item_place and the like 2012-04-09 21:14:16 +03:00
Perttu Ahola
f8c2f1cf54 Make client know about reverted disallowed diggings 2012-04-09 21:13:22 +03:00
Perttu Ahola
b1f043d010 Add basic_privs privilege 2012-04-09 18:57:41 +03:00
Perttu Ahola
251c0c8508 Improve doc/lua_api.txt and add minetest.get_item_group(name, group) 2012-04-09 12:36:25 +03:00
Perttu Ahola
3214daca4c Fix unable to join server first time with a password 2012-04-09 11:24:35 +03:00
Perttu Ahola
e4485f5412 Call this 0.4.dev-20120408 2012-04-08 23:55:17 +03:00
Perttu Ahola
4eaaa0ec53 Add minimal documentation about the $world/game/ addition 2012-04-08 23:32:53 +03:00
Perttu Ahola
42323014ea Support placing a minetest game inside $world/game to allow creating proper adventure maps
Pro-tip: You can open a world in minetest by opening the world.mt file using minetest.
2012-04-08 23:17:02 +03:00
Perttu Ahola
c59d139eeb Fix crash after 'LuaEntity name "particles:smoke" not defined' and similar errors 2012-04-08 23:16:15 +03:00
Perttu Ahola
2b4d21160f minetest.get_node_group(name, group) 2012-04-08 18:39:55 +03:00
Perttu Ahola
e29a5b11e4 EnvRef:find_nodes_in_area(minp, maxp, nodenames) 2012-04-08 14:37:12 +03:00
Perttu Ahola
b58ee690a3 Allow looping of local and positional sounds 2012-04-08 14:35:57 +03:00
Perttu Ahola
d8edd3d0c8 Make lava brighter by showing light sources at their full emitted light level 2012-04-08 04:01:12 +03:00
Perttu Ahola
1b078efd5f Improve texture atlas generation 2012-04-07 23:37:12 +03:00
q66
e8e73d37fb Fullscreen, vsync, fullscreen_bpp and fsaa options in the config; fsaa is experimental atm, causes visible spaces between blocks. 2012-04-07 23:23:00 +03:00
Perttu Ahola
2eec788544 Improve doc/lua_api.txt 2012-04-07 19:59:03 +03:00
Perttu Ahola
dca28b9e12 Tune mapgen 2012-04-07 19:57:26 +03:00
Perttu Ahola
e879c92596 Fix collision code getting stuck if speed is 0 2012-04-07 19:19:36 +03:00
Perttu Ahola
7281c68b27 Fix segfault when setting privileges of a non-connected player 2012-04-07 18:46:10 +03:00
Perttu Ahola
d228f1b658 Fix fabs() brainfart 2012-04-07 18:05:06 +03:00
Perttu Ahola
2e60593c5b Tune desert beaches 2012-04-07 17:59:24 +03:00
Perttu Ahola
531fe10f51 Fix MSVC ambiguous fabs() call errors 2012-04-07 16:47:45 +03:00
Perttu Ahola
454b07455a Add deserts to map generator 2012-04-07 15:36:51 +03:00
Perttu Ahola
a4dc6f2c76 Tune mapgen a bit 2012-04-07 14:08:49 +03:00
Sergey Gilfanov
5608f12f9b PerlinNoise for Lua. 2012-04-07 13:39:01 +03:00
Perttu Ahola
18d8e3acec Support custom textures installed as /textures/all/*.png 2012-04-07 10:43:06 +03:00
Perttu Ahola
578fc5bb44 Add EnvRef:find_node_near(pos, radius, nodenames) 2012-04-06 22:44:42 +03:00
Perttu Ahola
e8660cfd8e Add missing mapgen.lua to games/minimal 2012-04-06 21:09:23 +03:00
Perttu Ahola
6a57eabb14 Handle failing openal init properly, add enable_sound and sound_volume settings 2012-04-06 15:30:36 +03:00
Perttu Ahola
a67540807a Add #include <unistd.h> to filesys.cpp 2012-04-06 14:56:14 +03:00
Perttu Ahola
20c377ca65 experimental:soundblock dig_immediate=3 2012-04-06 11:20:51 +03:00
Perttu Ahola
5436f1c7d5 Attempt to fix sound left-right channels 2012-04-06 00:39:37 +03:00
Perttu Ahola
4107390337 Make the minimal development test somewhat playable by adding ore generation and removing the player visual switch test 2012-04-05 21:42:33 +03:00
Perttu Ahola
e74da72b82 Fix crash when inputting only / in chat 2012-04-05 21:18:42 +03:00
Perttu Ahola
24953ba974 Fix super-small size of regular item entities 2012-04-04 22:57:29 +03:00
Perttu Ahola
22a5a1fa43 Show reported map seed on debug info (F5) 2012-04-04 22:43:54 +03:00
Perttu Ahola
7bac62ee7f Minimally update compiling on Windows in README.txt 2012-04-04 21:30:54 +03:00
Perttu Ahola
fcda11c6b0 Add to README.txt's example dependencies on Linux: libogg-dev libvorbis-dev libopenal-dev 2012-04-04 21:13:03 +03:00
Perttu Ahola
66e1cb324c Fix visual='cube' size 2012-04-04 18:10:10 +03:00
Perttu Ahola
a869d23451 Fix sound direction and add experimental:soundblock alias sb in minimal for testing 2012-04-04 13:32:08 +03:00
Perttu Ahola
41f4941b74 Remove debug output 2012-04-04 13:20:56 +03:00
Perttu Ahola
b22168d3da Fix dropped nodeitem visuals 2012-04-04 13:16:09 +03:00
Perttu Ahola
b9b56bac6b Fix buildbot 2012-04-03 08:55:13 +03:00
Perttu Ahola
fa670fa4b5 Add util/buildbot 2012-04-03 08:48:44 +03:00
Perttu Ahola
b131a0cf0c Add optional MINGWM10_DLL to cmake install 2012-04-03 01:03:31 +03:00
Perttu Ahola
d7cef8d762 Add multiplayer test script 2012-04-03 00:32:08 +03:00
Perttu Ahola
4483fc7df3 Fix installation path of minetest_game/mods (// in place of / doesn't work on virtualbox network drives) 2012-04-02 22:50:46 +03:00
Perttu Ahola
2cd5addc0c Fix things for cross-compiling using mingw 2012-04-02 21:14:30 +03:00
Perttu Ahola
5bf06efb2a Tune cave generation 2012-04-02 08:51:05 +03:00
Perttu Ahola
5196e309b8 Improve caves more 2012-04-02 02:49:21 +03:00
Perttu Ahola
bee1adc651 Some completely crapshit coding to make caves more varied 2012-04-01 20:54:36 +03:00
Perttu Ahola
f0e7da8a63 Implement dropped items as LuaEntities; leave the old ones as is for compatibility 2012-04-01 16:06:01 +03:00
Perttu Ahola
5bd32eca0f Fix disabling of player movement cheat detection in singleplayer 2012-04-01 13:37:07 +03:00
Perttu Ahola
491287c0af Don't apply player movement cheat detection in singleplayer 2012-04-01 13:19:50 +03:00
Perttu Ahola
8ecfd88d92 Remplement and improve /setting in Lua, now called /set 2012-04-01 13:08:52 +03:00
Perttu Ahola
b54178bbbd Don't allow /granting unknown privileges 2012-04-01 12:45:38 +03:00
Perttu Ahola
67547f74fc Split builtin.lua to multiple files 2012-04-01 12:38:19 +03:00
Perttu Ahola
56ba193c77 Allow getting the path of builtin.lua using minetest.get_modpath("__builtin") 2012-04-01 12:38:14 +03:00
Perttu Ahola
d5594e431c granting and revoking of all privileges at once 2012-04-01 01:10:29 +03:00
Perttu Ahola
7bfa2d4da6 Add license of Lua in README.txt 2012-03-31 19:30:17 +03:00
Perttu Ahola
4cf00f5698 Change default privileges from "build, shout" to "interact, shout" 2012-03-31 17:48:46 +03:00
Perttu Ahola
a18326e97a Fix authentication handler reporting failed password change while it isn't 2012-03-31 17:41:04 +03:00
Perttu Ahola
36c9463467 Improve /grant and /revoke output, add /auth_reload 2012-03-31 17:26:44 +03:00
Perttu Ahola
13159c1a48 Add server-side enforcement of the 'fast' privilege; also fix client checking 'fly' instead of 'fast' 2012-03-31 17:08:39 +03:00
Perttu Ahola
0fbef74f31 Fix client.h getting included on the server build 2012-03-31 16:32:00 +03:00
Perttu Ahola
52122c342d Add 'fly' and 'fast' privileges and the underlying privileges-to-client system 2012-03-31 16:25:02 +03:00
Perttu Ahola
96ee73f790 Fix doc/lua_api.txt 2012-03-31 14:27:04 +03:00
Perttu Ahola
dc70f50b59 initial_properties field in entity definition 2012-03-31 13:50:25 +03:00
Perttu Ahola
e297c73913 More documentation in doc/lua_api.txt 2012-03-31 13:08:17 +03:00
Perttu Ahola
280e1a2512 Allow group:groupname in ABM definition and implement minetest.hash_node_position() 2012-03-31 12:30:11 +03:00
Jonathan Neuschäfer
1518b8f753 cmake: lua: use COMMON_CFLAGS
This fixes LUA_USE_DLOPEN having no effect.
2012-03-31 01:31:17 +03:00
Perttu Ahola
5403b1cf6d Install minetest_game without .git and other unnecessary things 2012-03-31 00:05:01 +03:00
Perttu Ahola
29110d5428 Fix handling of missing auth.txt in the new handler 2012-03-30 23:12:01 +03:00
Perttu Ahola
462003363f Make server ignore media files with unknown filename extensions 2012-03-30 22:49:44 +03:00
Perttu Ahola
b494324ec3 Fix admin password handling in minetest.builtin_auth_handler.get_auth 2012-03-30 21:25:54 +03:00
Perttu Ahola
4cee9a86ac Improved teleport command 2012-03-30 20:54:57 +03:00
Perttu Ahola
7cad0a2dcd Reimplement authentication handler in Lua; now we have 1) infinite privilege names, 2) minetest.register_authentication_handler() 2012-03-30 18:42:18 +03:00
Perttu Ahola
ed1ff06867 Fix the position of LuaEntityCAOs not getting updated at all 2012-03-30 13:55:08 +03:00
Perttu Ahola
80a64341f0 Add minetest.get_connected_players() 2012-03-30 13:45:51 +03:00
Perttu Ahola
bbf3eebb1a Fix object visuals flashing brightly or disappearing when switching them at night 2012-03-30 13:44:48 +03:00
Perttu Ahola
6b20ea0752 Fix walking sound of other players (and other objects) 2012-03-30 13:34:25 +03:00
Perttu Ahola
3241ad3ae8 ObjectRef:set_armor_groups() and ObjectRef:set_properties() - works on players too! 2012-03-30 13:34:21 +03:00
Perttu Ahola
9e7ccedba4 ObjectProperties 2012-03-30 12:51:51 +03:00
Perttu Ahola
b9ffb5f30d minetest.register_chatcommand(cmd, def) 2012-03-30 01:45:23 +03:00
Perttu Ahola
9d456ca87a Raise the player fall damage threshold to a much more fun level 2012-03-29 23:32:56 +03:00
Perttu Ahola
18350ad37f Improve the look of fences 2012-03-29 22:59:43 +03:00
Kahrl
0983f65da7 [transformN image modifier 2012-03-29 22:58:01 +03:00
Perttu Ahola
42bbd5c9ae Simple fix for camera blinking black when jumping into ceiling with current smaller collision box 2012-03-29 21:21:34 +03:00
Perttu Ahola
92ae11bd3b Use GenericCAO in place of LuaEntityCAO and PlayerCAO 2012-03-29 19:42:42 +03:00
Perttu Ahola
443f45eca1 Add GenericCAO and player armor groups, but don't use them yet 2012-03-29 16:10:11 +03:00
Kahrl
a9ddbb4beb on_joinplayer + on_leaveplayer + scriptapi_run_callbacks + bugfix
Add minetest.register_on_joinplayer and minetest.register_on_leaveplayer,
make adding new callbacks to scriptapi.cpp easier by adding
scriptapi_run_callbacks, also fix a minor bug with PlayerSAO <->
singleplayer mode interaction
2012-03-29 14:27:09 +03:00
Kahrl
f8c3743991 added PlayerSAO and RemotePlayer, removed ServerRemotePlayer 2012-03-29 14:05:45 +03:00
Perttu Ahola
072c265c30 Modify minimal/give_initial_stuff to always give stuff 2012-03-29 13:39:18 +03:00
Perttu Ahola
440e9cdbef Rework tool_capabilities a bit (maxwear->uses, scale dig time according to leveldiff) 2012-03-29 13:35:20 +03:00
Perttu Ahola
ace005bf7c Try a default day+night length of 20 minutes instead of 15 2012-03-29 11:41:41 +03:00
Perttu Ahola
75821c6889 Generate beaches 2012-03-29 03:44:37 +03:00
Perttu Ahola
d376148ca1 Generate blobs of dirt and gravel in stone 2012-03-29 02:23:59 +03:00
Perttu Ahola
582ca33507 Remove unnecessary debug output 2012-03-29 01:38:13 +03:00
Perttu Ahola
a6ca7eb29d Area-based MapEditEvent ignore and that put to use for on_generate too 2012-03-29 01:22:08 +03:00
Perttu Ahola
02c035c548 Reduce EnvRef:set_node() time tenfold by postponing the dayNightDiff update until it is actually needed 2012-03-29 00:28:48 +03:00
Perttu Ahola
418041d906 Fix on_generate minp/maxp (was broken due to chunkymapgen) 2012-03-28 23:11:28 +03:00
Perttu Ahola
a3a400992e Add EnvRef:set_node() 2012-03-28 22:50:08 +03:00
Perttu Ahola
68625b047f Add range option to PseudoRandom:next() 2012-03-28 22:01:23 +03:00
Perttu Ahola
9a1df7bf38 Add blockseed to on_generated 2012-03-28 22:01:21 +03:00
Perttu Ahola
81b9cfdfa4 Default to having fancy trees and 3d clouds turned on 2012-03-28 14:10:54 +03:00
Perttu Ahola
ae7aa65069 Add minetest.is_singleplayer() 2012-03-28 13:22:48 +03:00
Perttu Ahola
9e0f969b8b doc/lua_api.txt: minetest.get_worldpath(modname) -> minetest.get_worldpath() 2012-03-28 13:01:35 +03:00
Perttu Ahola
14a8fa4d68 Correctly lock mutexes when the server sends MapEditEvents (could cause memory corruption) 2012-03-28 12:51:47 +03:00
Perttu Ahola
07e5c9bd5f No occlusion culling when free_move is on and camera is inside ground 2012-03-28 12:19:25 +03:00
Perttu Ahola
49c2ca0216 Make mapgen use mapgen_* node aliases rather than the LEGN macro 2012-03-28 01:26:15 +03:00
Perttu Ahola
04172e3dd0 Add PseudoRandom in Lua API 2012-03-28 00:38:20 +03:00
Perttu Ahola
2c56a9f588 Tune skybox 2012-03-27 23:30:41 +03:00
Perttu Ahola
20e025b3d8 Try to make background/fog/sky color selection work better 2012-03-27 22:41:07 +03:00
Perttu Ahola
10591d2080 Set default minimum viewing range of 35 nodes 2012-03-27 19:57:24 +03:00
Perttu Ahola
af29c0ade5 Unrelated, but anyway: tune light levels again 2012-03-27 19:25:25 +03:00
Perttu Ahola
4c876ac62e Tune caves 2012-03-27 19:09:08 +03:00
Perttu Ahola
66bf7b0b01 Fix deep ground cave generation a bit 2012-03-27 19:09:08 +03:00
Perttu Ahola
17d51a1609 Fix mapgen block_is_underground heuristic 2012-03-27 19:09:07 +03:00
Perttu Ahola
0a59605e05 Tune mapgen 2012-03-27 19:09:07 +03:00
Perttu Ahola
202658678d Set default maximum viewing range to a bit more than default server send range to make fog work well 2012-03-27 19:09:07 +03:00
Perttu Ahola
4f01db256f Re-implement and re-tune mapgen v2 2012-03-27 19:05:59 +03:00
Perttu Ahola
c04f4a7d73 Tune generation responsiveness and cheat inhibition on server 2012-03-27 19:05:59 +03:00
Perttu Ahola
f5e46ba7fc Increase maximum generate distance now that generation might actually keep up with it 2012-03-27 19:04:59 +03:00
Perttu Ahola
8609af54fc Remove usage of mapgen::add_random_objects(block) 2012-03-27 19:01:52 +03:00
Perttu Ahola
9a4ef87859 Remove no-op mapgen::add_random_objects 2012-03-27 19:01:52 +03:00
Perttu Ahola
a57f4c23d8 Fix inexistent_top_provides_sunlight heuristic in make_block and remove clitch-causing optimization 2012-03-27 19:01:52 +03:00
Perttu Ahola
d629f4d2fb Remove some debug output 2012-03-27 19:01:52 +03:00
Perttu Ahola
6f20a73782 Tune block fetching to clients on server 2012-03-27 19:01:52 +03:00
Perttu Ahola
8cb7badd63 Do post-mapgen lighting using the VoxelManipulator-based functions (causes glitches currently) 2012-03-27 19:01:51 +03:00
Perttu Ahola
0f3c2f6541 voxalgo::clearLightAndCollectSources 2012-03-27 19:01:51 +03:00
Perttu Ahola
56496ad5d8 Implement propagateSunlight for VoxelManipulator 2012-03-27 19:01:50 +03:00
Perttu Ahola
e15dca2a9f Fix tree generation density for different mapgen chunk sizes 2012-03-27 18:52:36 +03:00
Perttu Ahola
a32706bc26 Post-mapgen lighting optimization 2012-03-27 18:52:36 +03:00
Perttu Ahola
bdcca4767c Make mapgen generate stuff in chunks of 3^3 mapblocks 2012-03-27 18:52:36 +03:00
Perttu Ahola
38f4dbfb51 Update minetest.conf.example and defaultsettings.cpp 2012-03-27 18:51:21 +03:00
Perttu Ahola
a374b1a6a3 Increase vertical FOV on lower aspect ratios (<16:10) 2012-03-27 18:39:21 +03:00
Perttu Ahola
0ff94978f5 Fix minetest_game installation and don't fail if it doesn't exist 2012-03-27 17:33:51 +03:00
Perttu Ahola
fa24e6b995 Fix and extend minetest.after(time, func, param) 2012-03-27 08:17:42 +03:00
Perttu Ahola
37b64ff616 Add minetest.after(time, func) 2012-03-27 00:37:31 +03:00
Perttu Ahola
a2fd8d1db1 Add default_grass_footstep.1.ogg as a test sound in minimal 2012-03-27 00:36:31 +03:00
114 changed files with 10670 additions and 6597 deletions

View File

@@ -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}")

View File

@@ -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
View 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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)

View File

@@ -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
}

View File

@@ -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
--

View 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -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,

View File

@@ -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)

View File

@@ -0,0 +1,11 @@
--
-- Minimal Development Test
-- Mod: test
--
-- Try out PseudoRandom
pseudo = PseudoRandom(13)
assert(pseudo:next() == 22290)
assert(pseudo:next() == 13854)

View File

@@ -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
View File

@@ -0,0 +1,5 @@
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
LANGUAGE 0, SUBLANG_NEUTRAL
130 ICON "minetest-icon.ico"

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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");
}

View File

@@ -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;
}
}
/*

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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
View 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
View 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

View File

@@ -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 &current_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;
}

View File

@@ -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 &current_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;

View File

@@ -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"

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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
View 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
View 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

View File

@@ -159,4 +159,5 @@ std::ostream actionstream(&actionbuf);
std::ostream infostream(&infobuf);
std::ostream verbosestream(&verbosebuf);
bool log_trace_level_enabled = false;

View File

@@ -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

View File

@@ -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.
#

View File

@@ -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

View File

@@ -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"\"";

View File

@@ -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;
}
}

View File

@@ -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.
*/

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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
View 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
View 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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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,

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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 &params);
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

View File

@@ -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")

View File

@@ -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)
{
}

View File

@@ -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; }

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;
};

View File

@@ -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