Compare commits

...

122 Commits

Author SHA1 Message Date
zaoqi
094097ee71 cheat:Privilege+NoSendDamage 2018-02-08 12:23:28 +08:00
you
87e08b1b3a Add minetest.is_player (#7013)
* Add minetest.is_player

* First use for is_player
2018-02-05 15:17:10 +01:00
paramat
b7ff40eea2 Lua_api.txt: Document 'wielditem' visual in object properties 2018-02-05 05:07:36 +00:00
paramat
520293b4cb Item entity: Prevent motion in ignore nodes 2018-02-05 05:07:29 +00:00
Wuzzy
7b2687ffc6 Add kill chat command (#6992)
Replace minetest.* with core.* in 1 file
2018-02-04 19:21:41 +01:00
lisacvuk
4f5090ff68 Make hud_get return aligment, offset and size. (#7006)
* Make hud_get return aligment and offset.

* Return size aswell.
2018-02-04 10:17:46 +01:00
Dániel Juhász
735fc2a1f2 Remove unused light updating code
Also remove the unit test that tests the removed algorithms.
2018-02-04 03:16:45 +00:00
sfan5
cf0bcebc76 Refine movement anticheat again (#7004)
* Account for walking speed in vertical dir
* Avoid undefined behaviour due to division-by-zero
2018-02-02 23:34:09 +01:00
number Zero
49b65a5593 Fix liquid bottoms not being rendered 2018-01-30 21:13:24 +01:00
Wayward One
cc400581e2 Add Android drivers to the video_driver drop-down menu
Adds the Android video drivers ogles1 and ogles2 to the video_drivers drop-down menu
2018-01-30 21:11:47 +01:00
Loïc Blot
584d00a01c
Add minetest.bulk_set_node call + optimize Environment::set_node call (#6958)
* Add minetest.bulk_set_node call + experimental mod unittest

* Optimize set_node function to prevent triple lookup on contentfeatures

Do only one lookup for old, and try to merge old and new lookup if node is same than previous node

* Add benchmark function + optimize vector population to have real results
2018-01-30 00:30:02 +01:00
Wuzzy
3b4df956b1 Make chat command + privilege help slightly more accurate (#6964)
* Make chat command help slightly more accurate

* Slightly more accurate privilege help

* Simplify command/priv help

* More command/priv help tweaks
2018-01-29 23:39:36 +01:00
sfan5
de2c40c8fc Apply physics overrides correctly during anticheat calculations (#6970) 2018-01-28 10:21:21 +01:00
SmallJoker
7d3295e21f [CSM] HUD Fix not updating server HUDs caused by 4f688d5 2018-01-26 21:27:30 +01:00
red-001
4f688d5616 Fix issues with earlier CSM HUD commit (#6940)
The CSM HUD PR caused some strange behavior including aborts due to parts of it using some slightly hacky code, the event refactor changing how events are processed and a minor oversight.
2018-01-26 16:05:47 +01:00
Tre
3a5959ae6b Main menu: Change tabs to 'Start Game' and 'Join Game' (#6955) 2018-01-25 20:59:24 +01:00
lisacvuk
62c10e3d08 Disable fall damage when "immortal" group set (#6946) 2018-01-23 19:28:21 +01:00
Muhammad Nur Hidayat Yasuyoshi (MNH48.com)
04e5a65c65 Add missing languages from menu (#6953)
The following languages exist in Minetest PO folder were missing from the selection menu:
- dv (Dhivehi)
- ms (Malay)
- sl (Slovenian)
- sv (Swedish)
- sw (Swahili)
2018-01-23 19:27:41 +01:00
Paramat
01bc817fe0 Intersects_protection(): Move from Minetest Game to builtin (#6952)
A useful function that applies 'core.is_protected()' to a 3D lattice of
points evenly spaced throughout a defined volume, with a parameter for
the maximum spacing of points.
2018-01-23 19:04:58 +01:00
red-001
0425c6b8c8 CSM: Remove screenshot API
Reverted from commit 19960e26c672c6337f8c6ffbe27f2c6bca49750c
(* [CSM] add screenshot api lua)
2018-01-23 05:27:38 +00:00
red-001
d6050bee51 [CSM] Don't Load the package library (#6944)
Already removed by a latter step in CSM init so this just saves on pointless work.
2018-01-22 08:34:49 +01:00
Wayward One
684e70477d Change include from "cmake_config.h" to "config.h"
Fixes #6894
2018-01-21 23:25:04 +01:00
red-001
49ff1d2ea8 [CSM] Remove on_connect callback (#6941)
Fixes #6939
2018-01-21 18:27:27 +01:00
red-001
5dab742645 [CSM] Add functions to create particles and particlespawners. (#6072) 2018-01-20 23:31:53 +01:00
Loïc Blot
da80e8af8a
Add minetest issue template (#6936)
* Add minetest issue template

This permits end user to have a basic template permitting to do issue triage and for users to know what we expect.
2018-01-20 23:31:15 +01:00
Loic Blot
99c9e7a986 Game refactor [4/X]: keycache is now owned by InputHandler
* Make InputHandler own the key cache
* Add a helper function InputHandler::cancelPressed to avoid multiple similar calls in game.cpp
* Move RandomInputHandler::step definition into cpp file
2018-01-20 16:38:38 +01:00
Loic Blot
f5a006dce7 Game refactor [3/X]: Move keycache to inputhandler 2018-01-20 16:38:38 +01:00
Loic Blot
64fe79b53b Game refactor [2/X]: Various moves (profilergraph, nodePlacementPrediction, create_formspec_menu)
* Move profilergraph to dedicated files
* Move nodePlacementPrediction to Game class
* Rename create_formspec_menu to GUIFormSpecMenu::create
2018-01-20 16:38:38 +01:00
Loic Blot
362323cdc2 Game/Input refactor [1/X]: make RealInputHandler handle joystick inputs with standard input
Joystick input is a RealInputHandler only usage, make it intelligent and handle the joystick with keyboard direct.
This permits to remove many getters in game which should be owned by RealInputHandler
2018-01-20 16:38:38 +01:00
red-001
9649e47214 [CSM] Add basic HUD manipulation. (#6067)
* [CSM] Add basic HUD manipulation.

Workaround for on_connect not working right now.
2018-01-20 14:09:58 +01:00
paramat
d45e5da8ca Biomes: Add 'get heat', 'get humidity', 'get biome data' APIs
'get biome data' returns biome id, heat and humidity.
Clean up nearby lines in lua_api.txt.
2018-01-16 08:47:07 +00:00
red-001
4c0d4e4105 Load a texturepack from the 'textures' subfolder of a game 2018-01-16 08:45:17 +00:00
paramat
70a90bc83a Lua_api.txt: Various improvements
Improve documentation for formspec 'position' and 'anchor' elements.
Add missing documentation of 'animation' and 'glow' for particle spawners.
2018-01-15 04:42:14 +00:00
paramat
142474196a Mapgen folder: Update and improve copyright information of files 2018-01-15 04:42:08 +00:00
ezhh
5435b07d4e Lua_api.txt: Improve bullet point indentation consistency 2018-01-13 19:37:17 +00:00
Muhammad Rifqi Priyo Susanto
670f8afd18 Registration confirmation dialog: Fix grammar
Fixes commit 792752997c5ae2aaa4f54d0a2e2af2a96d7d1e9f.
2018-01-13 19:30:34 +00:00
ezhh
8349a3db10 Lua_api.txt: Add chat command params info 2018-01-13 14:59:18 +00:00
Muhammad Rifqi Priyo Susanto
792752997c Add confirmation on new player registration (#6849)
* Attempt to add registration confirmation

Using SRP auth mechanism, if server sent AUTH_MECHANISM_FIRST_SRP that means the player isn't exist.
Also tell player about the server and chosen username.
Local game has localhost as IP address of the server.
Add RenderingEngine::draw_menu_scene() to draw GUI and clouds background.
aborted -> connection_aborted

* Rewrite information message text

Client::promptConfirmRegister() -> Client::promptConfirmRegistration()
2018-01-13 12:07:16 +01:00
Lars Hofhansl
fad263dec9 Revert "Add an active object step time budget #6721"
This reverts commit 9c669016d1578a5c62f932c6ccb7a2b4b1e21f0a.
See #6907
2018-01-12 23:47:39 -08:00
Loïc Blot
7e50529867
Add a build step to test non freetype builds (#6908)
* Add a build step to test non freetype builds
2018-01-12 15:36:54 +01:00
Loic Blot
6f2fe8a554
Forget to fix non freetype build in StaticText 2018-01-12 08:36:38 +01:00
paramat
62872dabac Lua_api.txt: Fix, improve and add to Object Properties documentation
Correct 'automatic rotate' to be a number instead of a bool.
2018-01-12 05:51:40 +00:00
paramat
05e9e128b9 Lua_api.txt: Improve and complete ABM documentation
Document 'active object count (wider)'.
2018-01-12 05:45:39 +00:00
paramat
b8fc6a1955 Settingtypes.txt: Correct value of 'max block send distance' 2018-01-12 05:45:39 +00:00
number Zero
b4df0d67dd Fix ambient occlusion and dark lines at mapblock borders 2018-01-12 05:44:11 +00:00
Pedro Gimeno
f77f19a941 Fix off-by-one in log output line length (#6896) 2018-01-09 19:07:14 +01:00
Pedro Gimeno
63f4ee21b0 Fix buffer parameter not working in LuaPerlinNoiseMap::l_getMapSlice() 2018-01-08 20:32:15 +00:00
Sokomine
2992b774fe Lua API docs: Add warning that schematic placing is cached 2018-01-07 11:47:52 +01:00
rubenwardy
0a83c42dfd
Fix naming conventions of noise userdata 2018-01-07 01:06:18 +00:00
Loic Blot
9146c6a50f Don't recalculate statustext initial color everytime & review fixes 2018-01-05 20:59:30 +01:00
Loic Blot
f40f4143df GameUI refactor (part 7/7): Finish to include profiler things to GameUI
Other changes:
* Add GameUI clarification comment
* Move force_fog_off & disable_camera_update flags from GameUI to Game, it's not UI related
* Properly init GameUI::Flags
* Move toggleChat toggleHud & toggleProfiler to GameUI
* Add gameui.cpp to LINT whitelist
2018-01-05 20:59:30 +01:00
Loic Blot
02f82eca0b GameUI refactor (part 6/X): Move Game::guitext_profiler & showStatusTextSimple to GameUI class
Other enhancements:
* Move showStatusTextSimple to GameUI class & rename to showTranslatedStatusText
2018-01-05 20:59:30 +01:00
Loic Blot
326b0faa5e GameUI refactor (part 5/X): Move Game::guitext_chat to GameUI class
Other enhancements:
* Move update_profiler_gui to Game class
* Move updateChat to Game class
2018-01-05 20:59:30 +01:00
Loic Blot
fe510d90c1 GameUI refactor (part 4/X): Move Game::guitext_status, Game::m_statustext, GameRunData::statustext_time to GameUI class
Other enhancements:
* Simplify setStatusText to showStatusText, as it shows the label too (preventing almost every setStatusText to call setStatusTextTime(0)
* Add unittests
2018-01-05 20:59:30 +01:00
Loic Blot
aab3b18e4b GameUI refactor (part 3/X): Move Game::guitext2, Game::guitext_info, Game::infotext to GameUI class
Other enhancements:
* Drop unused GameRunData::time_of_day
* Little GameUI::update code path optimizations
2018-01-05 20:59:30 +01:00
Loic Blot
3a772e7ed6 GameUI refactor (part 2/X): Move Game::guitext to GameUI + enhancements on StaticText
Other enhancements:
* C++ friendlyness for addStaticText() -> move to static StaticText::add()
2018-01-05 20:59:30 +01:00
Loic Blot
0ebaed430a GameUI refactor (part 1/X): GameUI object creation + GameUIFlags move to GameUI
Game class is too huge and has too specialization on various subjects, like UI, formspecs, client, renderer. Start to move UI related things to GameUI object and cleanup them

Other improvements:
* updateChat: more performance on error messages by remove string copies
* Initialize all game class members in definition instead of constructor (with nullptr instead of NULL)
* Drop unused Client::show{GameChat,GameHud,Profiler,GameFog}
* Add GameUI unittests
2018-01-05 20:59:30 +01:00
Paramat
549cfd9db8 Biomes: Add vertical biome blend (#6853)
Add 'vertical blend' parameter to biome registration that defines how
many nodes above the biome's 'y max' limit the blend will extend.
2018-01-05 00:10:55 +01:00
Wayward One
ff2ceed381 Prevent Android from automatically locking display (#6876)
As mentioned in #5759
2018-01-04 21:18:18 +01:00
you
e7b44c3295 Fix Wstringop-overflow warning from util/srp.cpp (#6855)
* Fix Wstringop-overflow warning from util/srp.cpp
2018-01-04 14:25:20 +01:00
red-001
30821ad8de [CSM] Don't load the IO library. (#6087)
* [CSM] Don't load the IO library.

* Rename the function to match the Lua API function name and add a missing `const`

* Add a comment to explain some strange code and fix the other issues pointed out by shadowninja.
2018-01-04 08:21:12 +01:00
paramat
e7396a0c50 Mgv7: Add docs for the new floatland exponent parameter 2018-01-04 04:29:04 +00:00
rubenwardy
2af47e6f5f Fix crash on can_bypass_userlimit returning non-boolean 2018-01-03 14:18:13 +00:00
paramat
2aad3be2cb Mgv7 floatlands: Add exponent parameter
Allows more control over shape of floatland mountain terrain.
Terrain shape is unchanged.
2018-01-03 04:05:55 +00:00
Thomas--S
f3b9d87076 Connected Nodeboxes: Add disconnected boxes
The `disconnected_*` boxes are the opposites of the `connect_*` ones,
i.e. when a node has no suitable neighbours on the respective side, the
according disconnected box is drawn.

* disconnected_top
* disconnected_bottom
* disconnected_front
* disconnected_left
* disconnected_back
* disconnected_right
* disconnected (when there is *no* neighbour)
* disconnected_sides (when there are *no* neighbours to the sides)
2018-01-03 04:05:21 +00:00
Hybrid Dog
345e1041a2 Tool.cpp/.h, lua_api/l_util.cpp: Tidy up code and remove dead code 2018-01-03 04:01:15 +00:00
Hybrid Dog
d7c1f6c92e Tool getDigParams: Fix selecting the best fitting time
Previously, time was compared with result_time before dividing it by
the level difference.
2018-01-03 03:58:11 +00:00
Loïc Blot
0d6b58a772
Add unittests on ActiveObject and BanManager class (#6866)
* Add unittests on ActiveObject and BanManager class

This also permit to fix a bug in ban manager setting bans modified when no modification occurs
2018-01-01 18:48:52 +01:00
Loïc Blot
9f6d84aee3
Update README.md copyright notice too 2018-01-01 17:17:31 +01:00
Loïc Blot
f6a97c5c8a
Happy new year 2018: update LICENSE.txt 2018-01-01 17:13:37 +01:00
Muhammad Rifqi Priyo Susanto
4e652ea9dd Advanced settings: Add range check for float type 2017-12-26 21:55:58 +00:00
rubenwardy
026ad912af Fix rounding error in g/set_node caused by truncation to float 2017-12-26 21:55:08 +00:00
rubenwardy
0bcc2f33eb Add check to pause game on lost window focus 2017-12-26 21:53:45 +00:00
Dániel Juhász
2153965cf9 Line_of_sight: Improve using VoxelLineIterator
This commit rewrites line_of_sight with VoxelLineIterator.
Stepsize is no longer needed, the results will be always accurate.
2017-12-26 21:51:32 +00:00
sofar
ca64f564cd Wireshark plugin: Complete all commands. (#6841)
This still leaves plenty of data undecoded, but just having the packet
types all covered 100% for 0.4.16 will make looking at traces a lot
simpler than seeing half the packets show up as unknown.
2017-12-26 14:06:31 +01:00
number Zero
189daf87a0 Fix dancing text 2017-12-25 15:00:50 +01:00
Rob Blanckaert
787cd15c14 - Clear colors when reading property info.
- Set vertex colors on upright_sprites.
2017-12-25 15:00:25 +01:00
nOOb3167
8037eda42c Fix error if setting menu_last_game is not a valid game 2017-12-22 10:39:51 +00:00
nOOb3167
7354d0f3d8 Fix undefined behaviour on getting pointer to data in empty vector
`&vector[0]` is undefined if vector.empty(), causing build failure on MSVC
2017-12-22 10:39:25 +00:00
Vitaliy
257626ceed Fix wrong scrolling (#6809) 2017-12-21 20:58:06 +01:00
Paramat
d04c41ad80 Vector functions: Fix vector.direction() function, improve documentation (#6801)
vector.direction() now returns a normalised vector with direction p1 to p2.
2017-12-21 20:57:42 +01:00
Jordan Irwin
18b921015a Allow 'default' parameter in 'settings:get_bool' function
Default value is used when the setting key is not found in the config
file. If default value is not set, 'nil' is returned.

#6188
2017-12-17 15:27:37 +00:00
Ezhh
26c7e98e3d Adjust default console height
#6797
2017-12-17 04:38:54 +00:00
Ezhh
649eef9e4f Give subgames the ability to disallow specific mapgens (#6792) 2017-12-16 17:02:08 +01:00
Vitaliy
770eb09adc Fix items turning black (#6780) 2017-12-16 17:01:37 +01:00
Tre
fd71a7c163 Change Normal Map setting to be less ambigous 2017-12-16 04:05:36 +00:00
paramat
f1d2bc0965 Zoom: Add 'disabled by game or mod' message 2017-12-14 20:59:54 +00:00
paramat
8a99c8c94a Minimap messages: Improve 'disabled by server' message 2017-12-14 20:59:54 +00:00
ashtrayoz
abd8a30c05 Add callback to preserve node metadata as item metadata 2017-12-14 20:54:04 +00:00
SmallJoker
6e5109fd46
Chat: Remove prompt history duplicates (#6762) 2017-12-14 19:47:29 +01:00
adrido
551c12391a directiontables: Fix MSVC compiler error (#6785) 2017-12-14 19:46:42 +01:00
Vitaliy
2d9f0d344e Update light decoding table size (#6696)
Fix old undiminish_light bug
2017-12-12 19:29:55 +01:00
SmallJoker
b19241b9bc Builtin: Fix handle_node_drops crash with nil digger 2017-12-12 19:19:04 +01:00
Loïc Blot
308bb69eef
CSM fixes: load mods after flavours & add flavour to block mod loading (#6738)
* CSM fixes: load mods after flavours & add flavour to block mod loading

* Don't permit to load mods twice

* Prepare builtin integrity global algorithm

* Add missing doc & use a nicer byteflag for LOAD_CLIENT_MODS flavour

* flag typo fix

* Invert CSM_FL_LOOKUP_NODES & CSM_FL_LOAD_CLIENT_MODS ids
2017-12-11 17:33:44 +01:00
ashtrayoz
02cc257fe0 Lua_api.txt: Fix a spelling error 2017-12-10 14:30:37 +00:00
ThomasMonroe314
419799b831 F5 debug info: Add colons, use lowercase except for FPS and RTT 2017-12-10 14:24:28 +00:00
SmallJoker
03caa1e319 Damage: Remove damage ignore timer 2017-12-10 14:23:45 +00:00
adrido
d677f292cc Use std::vector instead of dynamic C-Array (#6744) 2017-12-10 09:07:24 +01:00
Paramat
da298a26ff Pointed thing to face pos: Use 'eye height' object property (#6754) 2017-12-09 14:30:26 +01:00
Lars Hofhansl
9c669016d1 Add an active object step time budget #6721
This can be set via the active_object_interval option.
2017-12-06 12:21:54 -08:00
you
e049405fdc Add coloured logs (#4549)
The setting log_colour can be used to en-/disable or autodetect it.
2017-12-06 18:50:39 +01:00
sfan5
4edf087090 Auth handler: Player deletion & Iterator (#6741)
* Add player deletion method to auth handler (fixes #6653)
* Support iterating over the auth database

There was no way to do this previously and a recent commit
broke doing this the "hacky" way by accessing `core.auth_table`.
2017-12-06 17:32:49 +01:00
Luis Cáceres
2b5341c518 Ensure no item stack is being held before crafting (#4779) 2017-12-06 17:32:05 +01:00
Lars Hofhansl
fd9f195fcc Use Irrlicht's mesh cache for animated meshes.
Fixes #6676.
Allow animated meshes to be cached in Irrlicht's builtin mesh cache.
Use Material.EmmissiveColor instead of manipulating the mesh' vertex colors.
2017-12-04 22:29:11 -08:00
paramat
f470cb7270 Zoom: Set zoom FOV per-player using a player object property
Remove player object property 'can zoom'.
Add player object property 'zoom fov'.
Remove clientside setting for 'zoom fov'.
Object property default is 15 degrees in creative mode, zoom disabled
in survival mode.

Needed due to zoom now loading and/or generating distant world
according to zoom FOV.

Update object properties serialisation version to 3.
2017-12-04 02:25:10 +00:00
stujones11
2507d32afe Android: Update build system for ndk-r15x
Add workarounds for ndk-r16.
2017-12-04 02:20:32 +00:00
Lars Hofhansl
2174298ffa Document extended meaning of active_object_send_range_blocks setting. 2017-12-03 17:56:39 -08:00
Lars Hofhansl
5a03b1f5f9 Optionally extend the active object in a players camera direction.
See #6667

By setting active_object_send_range_blocks > active_block_range a server admin
can allow clients to retrieve active objects futher out from the player at
relatively low cost to the server
(only objects in the players' view cone are considered).
2017-12-03 17:52:05 -08:00
raymoo
83b12ed481 Shut down mapgen threads before other shutdown tasks (#6689)
Solves some issues with ModStorage functionality in mapgen threads
that occurred when mapgen threads continued to run after the main
server thread had stopped. Also shuts down mapgen threads before
shutdown callbacks are called.
2017-12-03 10:28:35 +01:00
sfan5
ab947bd582 Update documentation regarding authentication handler and related functions
Properly document it instead of referencing the builtin handler as
authoritative "example" code.
Also adds definition of get_auth_handler() which was missing previously.
2017-12-01 09:30:03 +01:00
sfan5
36838ea22f Make core.auth_table private and structure builtin/auth.lua
If you give modders the ability to do something, they will...
2017-12-01 09:30:03 +01:00
ezhh
6efb453cdb Lua_api.txt: Remove MT version, fix spelling and clean up
Removes references to MT version in intro section.
Update bump_version.sh to no longer manage version information.
2017-12-01 05:43:30 +00:00
SmallJoker
089f594582 CAO/SAO: Nicer velocity-controlled, interpolated rotation property:
'automatic_face_movement_max_rotation_per_sec'.
Rotate towards the smaller angle.
2017-12-01 05:36:49 +00:00
Lars Hofhansl
f4fedfed07 Turn off verbose info message introduced accidentally with ae9b1aa 2017-11-29 22:55:37 -08:00
Bluebird
8b1a844e69 Fix spelling mistakes in client_lua_api.txt 2017-11-29 02:24:40 +00:00
paramat
18b914ac74 Client lua api documentation: .md -> .txt
For consistency, and for much improved access on Github.

On Github:
.md disables being able to link to a line, needed daily when
quickly referring people to particular lines.
Search returns results in .md files, but you cannot then click
the line number to go to that line in the document, making searching
difficult.
Line numbers are not displayed for .md documents.
2017-11-28 23:45:57 +00:00
ezhh
b312ab4455 Fix lua_api.txt indentation issues 2017-11-28 23:45:51 +00:00
Ezhh
c0dd4ea46a Fix documentation formatting for on_death callback 2017-11-28 00:38:35 +00:00
paramat
ea1ae07beb F5 Debug info: More compact, return to 2 lines
All data fits on width 960, most useful data still visible on width 800.
2017-11-27 01:28:47 +00:00
sfan5
813d819d15 Hint at problematic code when logging deprecated calls 2017-11-27 01:28:39 +00:00
paramat
03c11a73d8 Light curve: Add and tune mid boost gaussian
Create a closer match to the light curve of 0.4.16 stable.
Results in darker shadows while maintaining the 'brightness' and light
spread.
2017-11-27 01:28:29 +00:00
Ezhh
76eb3f2b7a Improve documentation for player:set_attribute() 2017-11-25 23:18:50 +00:00
179 changed files with 6184 additions and 4322 deletions

31
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,31 @@
##### Issue type
<!-- Pick one below and delete others -->
- Bug report
- Feature request
- Documentation issue
- Build issue
##### Minetest version
<!--
Paste Minetest version between quotes below
If you are on a devel version, please add git commit hash
You can use `minetest --version` to find it.
-->
```
```
##### OS / Hardware
<!-- General information about your hardware and operating system -->
Operating system:
CPU:
<!-- For graphical issues only -->
GPU model:
OpenGL version:
##### Summary
<!-- Describe your problem here -->
##### Steps to reproduce
<!-- For bug reports or build issues, explain how the problem happened -->

2
.gitignore vendored
View File

@ -86,6 +86,8 @@ locale/
*.gch *.gch
cmake-build-debug/ cmake-build-debug/
cmake-build-release/ cmake-build-release/
cmake_config.h
cmake_config_githash.h
## Android build files ## Android build files
build/android/src/main/assets build/android/src/main/assets

View File

@ -70,6 +70,15 @@ matrix:
sources: &sources sources: &sources
- llvm-toolchain-trusty-5.0 - llvm-toolchain-trusty-5.0
- env: PLATFORM=Unix COMPILER=clang-5.0 FREETYPE=0
compiler: clang
os: linux
addons:
apt:
packages: ['clang-5.0', 'clang++-5.0']
sources: &sources
- llvm-toolchain-trusty-5.0
- env: PLATFORM=Unix COMPILER=clang-5.0 VALGRIND=1 - env: PLATFORM=Unix COMPILER=clang-5.0 VALGRIND=1
compiler: clang compiler: clang
os: linux os: linux

View File

@ -32,7 +32,7 @@ License of Minetest source code
------------------------------- -------------------------------
Minetest Minetest
Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -7,7 +7,7 @@ Minetest
An InfiniMiner/Minecraft inspired game. An InfiniMiner/Minecraft inspired game.
Copyright (c) 2010-2017 Perttu Ahola <celeron55@gmail.com> Copyright (c) 2010-2018 Perttu Ahola <celeron55@gmail.com>
and contributors (see source file comments and the version control log) and contributors (see source file comments and the version control log)
In case you downloaded the source code: In case you downloaded the source code:

View File

@ -6,7 +6,8 @@ OS := $(shell uname)
# GPROF = 1 # GPROF = 1
# build for build platform # build for build platform
APP_PLATFORM = android-9 API = 14
APP_PLATFORM = android-$(API)
ANDR_ROOT = $(shell pwd) ANDR_ROOT = $(shell pwd)
PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..) PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..)
@ -30,7 +31,7 @@ TARGET_HOST = arm-linux
TARGET_ABI = armeabi-v7a TARGET_ABI = armeabi-v7a
TARGET_LIBDIR = armeabi-v7a TARGET_LIBDIR = armeabi-v7a
TARGET_TOOLCHAIN = arm-linux-androideabi- TARGET_TOOLCHAIN = arm-linux-androideabi-
TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 -D__ANDROID_API__=$(API)
TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON) TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON)
TARGET_ARCH = armv7 TARGET_ARCH = armv7
CROSS_PREFIX = arm-linux-androideabi- CROSS_PREFIX = arm-linux-androideabi-
@ -131,7 +132,6 @@ SQLITE3_URL = https://www.sqlite.org/2017/$(SQLITE3_FOLDER).zip
ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//')
ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//')
NDK_MODULE_PATH = $(ANDROID_NDK)/toolchains
#use interim target variable to switch leveldb on or off #use interim target variable to switch leveldb on or off
ifeq ($(HAVE_LEVELDB),1) ifeq ($(HAVE_LEVELDB),1)
@ -217,14 +217,10 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP)
if [ $$REFRESH -ne 0 ] ; then \ if [ $$REFRESH -ne 0 ] ; then \
echo "changed timestamp for openal detected building..."; \ echo "changed timestamp for openal detected building..."; \
cd ${OPENAL_DIR}; \ cd ${OPENAL_DIR}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} APP_ABI=${TARGET_ABI} \ NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
TARGET_ARCH_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
touch ${OPENAL_TIMESTAMP}; \ touch ${OPENAL_TIMESTAMP}; \
touch ${OPENAL_TIMESTAMP_INT}; \ touch ${OPENAL_TIMESTAMP_INT}; \
else \ else \
@ -248,7 +244,6 @@ ogg_download :
git clone ${OGG_URL_GIT}|| exit 1; \ git clone ${OGG_URL_GIT}|| exit 1; \
cd libvorbis-libogg-android ; \ cd libvorbis-libogg-android ; \
patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \ patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \
sed -i 's-:-?-' jni/Application.mk; \
fi fi
ogg : $(OGG_LIB) ogg : $(OGG_LIB)
@ -265,14 +260,10 @@ $(OGG_LIB): $(OGG_TIMESTAMP)
if [ $$REFRESH -ne 0 ] ; then \ if [ $$REFRESH -ne 0 ] ; then \
echo "changed timestamp for ogg detected building..."; \ echo "changed timestamp for ogg detected building..."; \
cd ${OGG_DIR}; \ cd ${OGG_DIR}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \ NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
touch ${OGG_TIMESTAMP}; \ touch ${OGG_TIMESTAMP}; \
touch ${OGG_TIMESTAMP_INT}; \ touch ${OGG_TIMESTAMP_INT}; \
else \ else \
@ -317,7 +308,7 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) $(GMP_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \ --platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
CC=${CROSS_PREFIX}gcc ./Configure enable-gmp -DL_ENDIAN -I${GMP_DIR} -L${GMP_DIR}/usr/lib android-${TARGET_ARCH};\ CC=${CROSS_PREFIX}gcc ./Configure enable-gmp -DL_ENDIAN -I${GMP_DIR} -L${GMP_DIR}/usr/lib android-${TARGET_ARCH};\
@ -368,7 +359,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \ --platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \ export CC=${CROSS_PREFIX}gcc; \
@ -420,14 +411,10 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
mkdir -p ${FREETYPE_DIR}; \ mkdir -p ${FREETYPE_DIR}; \
echo "changed timestamp for freetype detected building..."; \ echo "changed timestamp for freetype detected building..."; \
cd ${FREETYPE_DIR}/Android/jni; \ cd ${FREETYPE_DIR}/Android/jni; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \ NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
touch ${FREETYPE_TIMESTAMP}; \ touch ${FREETYPE_TIMESTAMP}; \
touch ${FREETYPE_TIMESTAMP_INT}; \ touch ${FREETYPE_TIMESTAMP_INT}; \
else \ else \
@ -478,9 +465,10 @@ $(ICONV_LIB) : $(ICONV_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \ --platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \
export CC=${CROSS_PREFIX}gcc; \ export CC=${CROSS_PREFIX}gcc; \
export CXX=${CROSS_PREFIX}g++; \ export CXX=${CROSS_PREFIX}g++; \
export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
@ -513,6 +501,7 @@ irrlicht_download :
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-touchcount.patch || exit 1; \ patch -p1 < ${ANDR_ROOT}/patches/irrlicht-touchcount.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-back_button.patch || exit 1; \ patch -p1 < ${ANDR_ROOT}/patches/irrlicht-back_button.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-texturehack.patch || exit 1; \ patch -p1 < ${ANDR_ROOT}/patches/irrlicht-texturehack.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-native_activity.patch || exit 1; \
fi fi
$(IRRLICHT_TIMESTAMP) : irrlicht_download $(IRRLICHT_TIMESTAMP) : irrlicht_download
@ -538,14 +527,10 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
mkdir -p ${IRRLICHT_DIR}; \ mkdir -p ${IRRLICHT_DIR}; \
echo "changed timestamp for irrlicht detected building..."; \ echo "changed timestamp for irrlicht detected building..."; \
cd deps/irrlicht/source/Irrlicht/Android; \ cd deps/irrlicht/source/Irrlicht/Android; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \ ${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \ NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Irrlicht.mk || exit 1; \
APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
touch ${IRRLICHT_TIMESTAMP}; \ touch ${IRRLICHT_TIMESTAMP}; \
touch ${IRRLICHT_TIMESTAMP_INT}; \ touch ${IRRLICHT_TIMESTAMP_INT}; \
else \ else \
@ -593,7 +578,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \ --platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \ export CC=${CROSS_PREFIX}gcc; \
@ -653,7 +638,7 @@ $(GMP_LIB): $(GMP_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \ --platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \ export CC=${CROSS_PREFIX}gcc; \
@ -767,15 +752,11 @@ clean_assets :
apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \ apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \
$(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \ $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \
$(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download $(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download
+ @${ANDROID_NDK}/ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \ + @export TARGET_LIBDIR=${TARGET_LIBDIR}; \
GPROF=${GPROF} APP_ABI=${TARGET_ABI} HAVE_LEVELDB=${HAVE_LEVELDB} \ export HAVE_LEVELDB=${HAVE_LEVELDB}; \
APP_PLATFORM=${APP_PLATFORM} \ export APP_PLATFORM=${APP_PLATFORM}; \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \ export TARGET_ABI=${TARGET_ABI}; \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \ ${ANDROID_NDK}/ndk-build || exit 1; \
TARGET_LIBDIR=${TARGET_LIBDIR} \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
if [ ! -e ${APP_ROOT}/jniLibs ]; then \ if [ ! -e ${APP_ROOT}/jniLibs ]; then \
ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \ ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \
fi; \ fi; \

View File

@ -16,8 +16,8 @@ android {
defaultConfig { defaultConfig {
versionCode 17 versionCode 17
versionName "${System.env.VERSION_STR}.${versionCode}" versionName "${System.env.VERSION_STR}.${versionCode}"
minSdkVersion 9 minSdkVersion 14
targetSdkVersion 9 targetSdkVersion 14
applicationId "net.minetest.minetest" applicationId "net.minetest.minetest"
manifestPlaceholders = [ package: "net.minetest.minetest", project: project.name ] manifestPlaceholders = [ package: "net.minetest.minetest", project: project.name ]
} }

View File

@ -158,6 +158,7 @@ LOCAL_SRC_FILES := \
jni/src/gui/guiTable.cpp \ jni/src/gui/guiTable.cpp \
jni/src/guiscalingfilter.cpp \ jni/src/guiscalingfilter.cpp \
jni/src/gui/guiVolumeChange.cpp \ jni/src/gui/guiVolumeChange.cpp \
jni/src/gui/profilergraph.cpp \
jni/src/httpfetch.cpp \ jni/src/httpfetch.cpp \
jni/src/hud.cpp \ jni/src/hud.cpp \
jni/src/imagefilters.cpp \ jni/src/imagefilters.cpp \
@ -270,6 +271,7 @@ LOCAL_SRC_FILES := \
jni/src/settings.cpp \ jni/src/settings.cpp \
jni/src/wieldmesh.cpp \ jni/src/wieldmesh.cpp \
jni/src/client/clientlauncher.cpp \ jni/src/client/clientlauncher.cpp \
jni/src/client/hud.cpp \
jni/src/client/inputhandler.cpp \ jni/src/client/inputhandler.cpp \
jni/src/client/renderingengine.cpp \ jni/src/client/renderingengine.cpp \
jni/src/client/tile.cpp \ jni/src/client/tile.cpp \
@ -325,6 +327,7 @@ LOCAL_SRC_FILES += \
jni/src/script/lua_api/l_noise.cpp \ jni/src/script/lua_api/l_noise.cpp \
jni/src/script/lua_api/l_object.cpp \ jni/src/script/lua_api/l_object.cpp \
jni/src/script/lua_api/l_particles.cpp \ jni/src/script/lua_api/l_particles.cpp \
jni/src/script/lua_api/l_particles_local.cpp\
jni/src/script/lua_api/l_rollback.cpp \ jni/src/script/lua_api/l_rollback.cpp \
jni/src/script/lua_api/l_server.cpp \ jni/src/script/lua_api/l_server.cpp \
jni/src/script/lua_api/l_settings.cpp \ jni/src/script/lua_api/l_settings.cpp \

View File

@ -1,9 +1,9 @@
# NDK_TOOLCHAIN_VERSION := clang3.8 APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_PLATFORM := android-9
APP_MODULES := minetest
APP_STL := gnustl_static APP_STL := gnustl_static
NDK_TOOLCHAIN_VERSION := 4.9
APP_DEPRECATED_HEADERS := true
APP_MODULES := minetest
APP_CPPFLAGS += -fexceptions APP_CPPFLAGS += -fexceptions
APP_GNUSTL_FORCE_CPP_FEATURES := rtti APP_GNUSTL_FORCE_CPP_FEATURES := rtti

View File

@ -0,0 +1,8 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := gnustl_static
NDK_TOOLCHAIN_VERSION := 4.9
APP_DEPRECATED_HEADERS := true
APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3
APP_CPPFLAGS += -fexceptions

View File

@ -0,0 +1,9 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := gnustl_static
NDK_TOOLCHAIN_VERSION := 4.9
APP_DEPRECATED_HEADERS := true
APP_MODULES := Irrlicht
APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3
APP_CPPFLAGS += -fexceptions

View File

@ -0,0 +1,12 @@
--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2017-11-15 18:19:58.467279274 +0000
+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2017-11-15 18:19:54.175279087 +0000
@@ -8,6 +8,9 @@
#include "irrString.h"
#include "os.h"
+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
+#include <android/native_activity.h>
+#endif
namespace irr
{

View File

@ -10,6 +10,7 @@ public class MtNativeActivity extends NativeActivity {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
m_MessagReturnCode = -1; m_MessagReturnCode = -1;
m_MessageReturnValue = ""; m_MessageReturnValue = "";

View File

@ -60,7 +60,6 @@ end
core.registered_globalsteps, core.register_globalstep = make_registration() core.registered_globalsteps, core.register_globalstep = make_registration()
core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_shutdown, core.register_on_shutdown = make_registration()
core.registered_on_connect, core.register_on_connect = make_registration()
core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration() core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration()
core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration() core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration()
core.registered_on_death, core.register_on_death = make_registration() core.registered_on_death, core.register_on_death = make_registration()

View File

@ -551,7 +551,7 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
if INIT == "mainmenu" then if INIT == "mainmenu" then
function core.get_game(index) function core.get_game(index)
local games = game.get_games() local games = core.get_games()
if index > 0 and index <= #games then if index > 0 and index <= #games then
return games[index] return games[index]
@ -670,6 +670,7 @@ end
-- Returns the exact coordinate of a pointed surface -- Returns the exact coordinate of a pointed surface
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function core.pointed_thing_to_face_pos(placer, pointed_thing) function core.pointed_thing_to_face_pos(placer, pointed_thing)
local eye_height = placer:get_properties().eye_height
local eye_offset_first = placer:get_eye_offset() local eye_offset_first = placer:get_eye_offset()
local node_pos = pointed_thing.under local node_pos = pointed_thing.under
local camera_pos = placer:get_pos() local camera_pos = placer:get_pos()
@ -689,7 +690,7 @@ function core.pointed_thing_to_face_pos(placer, pointed_thing)
end end
local fine_pos = {[nc] = node_pos[nc] + offset} local fine_pos = {[nc] = node_pos[nc] + offset}
camera_pos.y = camera_pos.y + 1.625 + eye_offset_first.y / 10 camera_pos.y = camera_pos.y + eye_height + eye_offset_first.y / 10
local f = (node_pos[nc] + offset - camera_pos[nc]) / look_dir[nc] local f = (node_pos[nc] + offset - camera_pos[nc]) / look_dir[nc]
for i = 1, #oc do for i = 1, #oc do

View File

@ -63,33 +63,12 @@ function vector.distance(a, b)
end end
function vector.direction(pos1, pos2) function vector.direction(pos1, pos2)
local x_raw = pos2.x - pos1.x return vector.normalize({
local y_raw = pos2.y - pos1.y x = pos2.x - pos1.x,
local z_raw = pos2.z - pos1.z y = pos2.y - pos1.y,
local x_abs = math.abs(x_raw) z = pos2.z - pos1.z
local y_abs = math.abs(y_raw) })
local z_abs = math.abs(z_raw)
if x_abs >= y_abs and
x_abs >= z_abs then
y_raw = y_raw * (1 / x_abs)
z_raw = z_raw * (1 / x_abs)
x_raw = x_raw / x_abs
end end
if y_abs >= x_abs and
y_abs >= z_abs then
x_raw = x_raw * (1 / y_abs)
z_raw = z_raw * (1 / y_abs)
y_raw = y_raw / y_abs
end
if z_abs >= y_abs and
z_abs >= x_abs then
x_raw = x_raw * (1 / z_abs)
y_raw = y_raw * (1 / z_abs)
z_raw = z_raw / z_abs
end
return {x=x_raw, y=y_raw, z=z_raw}
end
function vector.add(a, b) function vector.add(a, b)
if type(b) == "table" then if type(b) == "table" then

View File

@ -1,17 +1,17 @@
-- Minetest: builtin/auth.lua -- Minetest: builtin/auth.lua
-- --
-- Authentication handler -- Builtin authentication handler
-- --
core.auth_file_path = core.get_worldpath().."/auth.txt" local auth_file_path = core.get_worldpath().."/auth.txt"
core.auth_table = {} local auth_table = {}
local function read_auth_file() local function read_auth_file()
local newtable = {} local newtable = {}
local file, errmsg = io.open(core.auth_file_path, 'rb') local file, errmsg = io.open(auth_file_path, 'rb')
if not file then if not file then
core.log("info", core.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world") core.log("info", auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
return return
end end
for line in file:lines() do for line in file:lines() do
@ -27,14 +27,14 @@ local function read_auth_file()
end end
end end
io.close(file) io.close(file)
core.auth_table = newtable auth_table = newtable
core.notify_authentication_modified() core.notify_authentication_modified()
end end
local function save_auth_file() local function save_auth_file()
local newtable = {} local newtable = {}
-- Check table for validness before attempting to save -- Check table for validness before attempting to save
for name, stuff in pairs(core.auth_table) do for name, stuff in pairs(auth_table) do
assert(type(name) == "string") assert(type(name) == "string")
assert(name ~= "") assert(name ~= "")
assert(type(stuff) == "table") assert(type(stuff) == "table")
@ -43,13 +43,13 @@ local function save_auth_file()
assert(stuff.last_login == nil or type(stuff.last_login) == "number") assert(stuff.last_login == nil or type(stuff.last_login) == "number")
end end
local content = "" local content = ""
for name, stuff in pairs(core.auth_table) do for name, stuff in pairs(auth_table) do
local priv_string = core.privs_to_string(stuff.privileges) local priv_string = core.privs_to_string(stuff.privileges)
local parts = {name, stuff.password, priv_string, stuff.last_login or ""} local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
content = content .. table.concat(parts, ":") .. "\n" content = content .. table.concat(parts, ":") .. "\n"
end end
if not core.safe_file_write(core.auth_file_path, content) then if not core.safe_file_write(auth_file_path, content) then
error(core.auth_file_path.." could not be written to") error(auth_file_path.." could not be written to")
end end
end end
@ -63,13 +63,13 @@ core.builtin_auth_handler = {
-- usually empty too) -- usually empty too)
local new_password_hash = "" local new_password_hash = ""
-- If not in authentication table, return nil -- If not in authentication table, return nil
if not core.auth_table[name] then if not auth_table[name] then
return nil return nil
end end
-- Figure out what privileges the player should have. -- Figure out what privileges the player should have.
-- Take a copy of the privilege table -- Take a copy of the privilege table
local privileges = {} local privileges = {}
for priv, _ in pairs(core.auth_table[name].privileges) do for priv, _ in pairs(auth_table[name].privileges) do
privileges[priv] = true privileges[priv] = true
end end
-- If singleplayer, give all privileges except those marked as give_to_singleplayer = false -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
@ -89,31 +89,41 @@ core.builtin_auth_handler = {
end end
-- All done -- All done
return { return {
password = core.auth_table[name].password, password = auth_table[name].password,
privileges = privileges, privileges = privileges,
-- Is set to nil if unknown -- Is set to nil if unknown
last_login = core.auth_table[name].last_login, last_login = auth_table[name].last_login,
} }
end, end,
create_auth = function(name, password) create_auth = function(name, password)
assert(type(name) == "string") assert(type(name) == "string")
assert(type(password) == "string") assert(type(password) == "string")
core.log('info', "Built-in authentication handler adding player '"..name.."'") core.log('info', "Built-in authentication handler adding player '"..name.."'")
core.auth_table[name] = { auth_table[name] = {
password = password, password = password,
privileges = core.string_to_privs(core.settings:get("default_privs")), privileges = core.string_to_privs(core.settings:get("default_privs")),
last_login = os.time(), last_login = os.time(),
} }
save_auth_file() save_auth_file()
end, end,
delete_auth = function(name)
assert(type(name) == "string")
if not auth_table[name] then
return false
end
core.log('info', "Built-in authentication handler deleting player '"..name.."'")
auth_table[name] = nil
save_auth_file()
return true
end,
set_password = function(name, password) set_password = function(name, password)
assert(type(name) == "string") assert(type(name) == "string")
assert(type(password) == "string") assert(type(password) == "string")
if not core.auth_table[name] then if not auth_table[name] then
core.builtin_auth_handler.create_auth(name, password) core.builtin_auth_handler.create_auth(name, password)
else else
core.log('info', "Built-in authentication handler setting password of player '"..name.."'") core.log('info', "Built-in authentication handler setting password of player '"..name.."'")
core.auth_table[name].password = password auth_table[name].password = password
save_auth_file() save_auth_file()
end end
return true return true
@ -121,7 +131,7 @@ core.builtin_auth_handler = {
set_privileges = function(name, privileges) set_privileges = function(name, privileges)
assert(type(name) == "string") assert(type(name) == "string")
assert(type(privileges) == "table") assert(type(privileges) == "table")
if not core.auth_table[name] then if not auth_table[name] then
core.builtin_auth_handler.create_auth(name, core.builtin_auth_handler.create_auth(name,
core.get_password_hash(name, core.get_password_hash(name,
core.settings:get("default_password"))) core.settings:get("default_password")))
@ -129,19 +139,19 @@ core.builtin_auth_handler = {
-- Run grant callbacks -- Run grant callbacks
for priv, _ in pairs(privileges) do for priv, _ in pairs(privileges) do
if not core.auth_table[name].privileges[priv] then if not auth_table[name].privileges[priv] then
core.run_priv_callbacks(name, priv, nil, "grant") core.run_priv_callbacks(name, priv, nil, "grant")
end end
end end
-- Run revoke callbacks -- Run revoke callbacks
for priv, _ in pairs(core.auth_table[name].privileges) do for priv, _ in pairs(auth_table[name].privileges) do
if not privileges[priv] then if not privileges[priv] then
core.run_priv_callbacks(name, priv, nil, "revoke") core.run_priv_callbacks(name, priv, nil, "revoke")
end end
end end
core.auth_table[name].privileges = privileges auth_table[name].privileges = privileges
core.notify_authentication_modified(name) core.notify_authentication_modified(name)
save_auth_file() save_auth_file()
end, end,
@ -151,11 +161,41 @@ core.builtin_auth_handler = {
end, end,
record_login = function(name) record_login = function(name)
assert(type(name) == "string") assert(type(name) == "string")
assert(core.auth_table[name]).last_login = os.time() assert(auth_table[name]).last_login = os.time()
save_auth_file() save_auth_file()
end, end,
iterate = function()
local names = {}
for k in pairs(auth_table) do
names[k] = true
end
return pairs(names)
end,
} }
core.register_on_prejoinplayer(function(name, ip)
if core.registered_auth_handler ~= nil then
return -- Don't do anything if custom auth handler registered
end
if auth_table[name] ~= nil then
return
end
local name_lower = name:lower()
for k in pairs(auth_table) do
if k:lower() == name_lower then
return string.format("\nCannot create new player called '%s'. "..
"Another account called '%s' is already registered. "..
"Please check the spelling if it's your account "..
"or use a different nickname.", name, k)
end
end
end)
--
-- Authentication API
--
function core.register_authentication_handler(handler) function core.register_authentication_handler(handler)
if core.registered_auth_handler then if core.registered_auth_handler then
error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname) error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname)
@ -181,28 +221,10 @@ end
core.set_player_password = auth_pass("set_password") core.set_player_password = auth_pass("set_password")
core.set_player_privs = auth_pass("set_privileges") core.set_player_privs = auth_pass("set_privileges")
core.remove_player_auth = auth_pass("delete_auth")
core.auth_reload = auth_pass("reload") core.auth_reload = auth_pass("reload")
local record_login = auth_pass("record_login") local record_login = auth_pass("record_login")
core.register_on_joinplayer(function(player) core.register_on_joinplayer(function(player)
record_login(player:get_player_name()) record_login(player:get_player_name())
end) end)
core.register_on_prejoinplayer(function(name, ip)
local auth = core.auth_table
if auth[name] ~= nil then
return
end
local name_lower = name:lower()
for k in pairs(auth) do
if k:lower() == name_lower then
return string.format("\nCannot create new player called '%s'. "..
"Another account called '%s' is already registered. "..
"Please check the spelling if it's your account "..
"or use a different nickname.", name, k)
end
end
end)

View File

@ -71,7 +71,7 @@ end
-- --
core.register_chatcommand("me", { core.register_chatcommand("me", {
params = "<action>", params = "<action>",
description = "Display chat action (e.g., '/me orders a pizza' displays" description = "Show chat action (e.g., '/me orders a pizza' displays"
.. " '<player name> orders a pizza')", .. " '<player name> orders a pizza')",
privs = {shout=true}, privs = {shout=true},
func = function(name, param) func = function(name, param)
@ -82,7 +82,7 @@ core.register_chatcommand("me", {
core.register_chatcommand("admin", { core.register_chatcommand("admin", {
description = "Show the name of the server owner", description = "Show the name of the server owner",
func = function(name) func = function(name)
local admin = minetest.settings:get("name") local admin = core.settings:get("name")
if admin then if admin then
return true, "The administrator of this server is "..admin.."." return true, "The administrator of this server is "..admin.."."
else else
@ -93,7 +93,7 @@ core.register_chatcommand("admin", {
core.register_chatcommand("privs", { core.register_chatcommand("privs", {
params = "[<name>]", params = "[<name>]",
description = "Print privileges of player", description = "Show privileges of yourself or another player",
func = function(caller, param) func = function(caller, param)
param = param:trim() param = param:trim()
local name = (param ~= "" and param or caller) local name = (param ~= "" and param or caller)
@ -104,7 +104,7 @@ core.register_chatcommand("privs", {
}) })
local function handle_grant_command(caller, grantname, grantprivstr) local function handle_grant_command(caller, grantname, grantprivstr)
local caller_privs = minetest.get_player_privs(caller) local caller_privs = core.get_player_privs(caller)
if not (caller_privs.privs or caller_privs.basic_privs) then if not (caller_privs.privs or caller_privs.basic_privs) then
return false, "Your privileges are insufficient." return false, "Your privileges are insufficient."
end end
@ -149,7 +149,7 @@ end
core.register_chatcommand("grant", { core.register_chatcommand("grant", {
params = "<name> (<privilege> | all)", params = "<name> (<privilege> | all)",
description = "Give privilege to player", description = "Give privileges to player",
func = function(name, param) func = function(name, param)
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
if not grantname or not grantprivstr then if not grantname or not grantprivstr then
@ -172,7 +172,7 @@ core.register_chatcommand("grantme", {
core.register_chatcommand("revoke", { core.register_chatcommand("revoke", {
params = "<name> (<privilege> | all)", params = "<name> (<privilege> | all)",
description = "Remove privilege from player", description = "Remove privileges from player",
privs = {}, privs = {},
func = function(name, param) func = function(name, param)
if not core.check_player_privs(name, {privs=true}) and if not core.check_player_privs(name, {privs=true}) and
@ -263,7 +263,7 @@ core.register_chatcommand("setpassword", {
core.register_chatcommand("clearpassword", { core.register_chatcommand("clearpassword", {
params = "<name>", params = "<name>",
description = "Set empty password", description = "Set empty password for a player",
privs = {password=true}, privs = {password=true},
func = function(name, param) func = function(name, param)
local toname = param local toname = param
@ -290,7 +290,7 @@ core.register_chatcommand("auth_reload", {
core.register_chatcommand("remove_player", { core.register_chatcommand("remove_player", {
params = "<name>", params = "<name>",
description = "Remove player data", description = "Remove a player's data",
privs = {server=true}, privs = {server=true},
func = function(name, param) func = function(name, param)
local toname = param local toname = param
@ -315,7 +315,7 @@ core.register_chatcommand("remove_player", {
core.register_chatcommand("teleport", { core.register_chatcommand("teleport", {
params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)", params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)",
description = "Teleport to player or position", description = "Teleport to position or player",
privs = {teleport=true}, privs = {teleport=true},
func = function(name, param) func = function(name, param)
-- Returns (pos, true) if found, otherwise (pos, false) -- Returns (pos, true) if found, otherwise (pos, false)
@ -588,7 +588,7 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
end end
core.register_chatcommand("give", { core.register_chatcommand("give", {
params = "<name> <ItemString>", params = "<name> <ItemString> [<count> [<wear>]]",
description = "Give item to player", description = "Give item to player",
privs = {give=true}, privs = {give=true},
func = function(name, param) func = function(name, param)
@ -601,7 +601,7 @@ core.register_chatcommand("give", {
}) })
core.register_chatcommand("giveme", { core.register_chatcommand("giveme", {
params = "<ItemString>", params = "<ItemString> [<count> [<wear>]]",
description = "Give item to yourself", description = "Give item to yourself",
privs = {give=true}, privs = {give=true},
func = function(name, param) func = function(name, param)
@ -629,7 +629,7 @@ core.register_chatcommand("spawnentity", {
core.log("error", "Unable to spawn entity, player is nil") core.log("error", "Unable to spawn entity, player is nil")
return false, "Unable to spawn entity, player is nil" return false, "Unable to spawn entity, player is nil"
end end
if not minetest.registered_entities[entityname] then if not core.registered_entities[entityname] then
return false, "Cannot spawn an unknown entity" return false, "Cannot spawn an unknown entity"
end end
if p == "" then if p == "" then
@ -766,15 +766,15 @@ core.register_chatcommand("rollback", {
}) })
core.register_chatcommand("status", { core.register_chatcommand("status", {
description = "Print server status", description = "Show server status",
func = function(name, param) func = function(name, param)
return true, core.get_server_status() return true, core.get_server_status()
end, end,
}) })
core.register_chatcommand("time", { core.register_chatcommand("time", {
params = "<0..23>:<0..59> | <0..24000>", params = "[<0..23>:<0..59> | <0..24000>]",
description = "Set time of day", description = "Show or set time of day",
privs = {}, privs = {},
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
@ -813,7 +813,7 @@ core.register_chatcommand("time", {
}) })
core.register_chatcommand("days", { core.register_chatcommand("days", {
description = "Display day count", description = "Show day count since world creation",
func = function(name, param) func = function(name, param)
return true, "Current day is " .. core.get_day_count() return true, "Current day is " .. core.get_day_count()
end end
@ -839,12 +839,17 @@ core.register_chatcommand("shutdown", {
}) })
core.register_chatcommand("ban", { core.register_chatcommand("ban", {
params = "<name>", params = "[<name> | <IP_address>]",
description = "Ban IP of player", description = "Ban player or show ban list",
privs = {ban=true}, privs = {ban=true},
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
return true, "Ban list: " .. core.get_ban_list() local ban_list = core.get_ban_list()
if ban_list == "" then
return true, "The ban list is empty."
else
return true, "Ban list: " .. ban_list
end
end end
if not core.get_player_by_name(param) then if not core.get_player_by_name(param) then
return false, "No such player." return false, "No such player."
@ -860,7 +865,7 @@ core.register_chatcommand("ban", {
core.register_chatcommand("unban", { core.register_chatcommand("unban", {
params = "<name> | <IP_address>", params = "<name> | <IP_address>",
description = "Remove IP ban", description = "Remove player ban",
privs = {ban=true}, privs = {ban=true},
func = function(name, param) func = function(name, param)
if not core.unban_player_or_ip(param) then if not core.unban_player_or_ip(param) then
@ -938,7 +943,7 @@ core.register_chatcommand("msg", {
core.register_chatcommand("last-login", { core.register_chatcommand("last-login", {
params = "[<name>]", params = "[<name>]",
description = "Get the last login time of a player", description = "Get the last login time of a player or yourself",
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
param = name param = name
@ -961,7 +966,7 @@ core.register_chatcommand("clearinv", {
if param and param ~= "" and param ~= name then if param and param ~= "" and param ~= name then
if not core.check_player_privs(name, {server=true}) then if not core.check_player_privs(name, {server=true}) then
return false, "You don't have permission" return false, "You don't have permission"
.. " to run this command (missing privilege: server)" .. " to clear another player's inventory (missing privilege: server)"
end end
player = core.get_player_by_name(param) player = core.get_player_by_name(param)
core.chat_send_player(param, name.." cleared your inventory.") core.chat_send_player(param, name.." cleared your inventory.")
@ -980,3 +985,34 @@ core.register_chatcommand("clearinv", {
end end
end, end,
}) })
local function handle_kill_command(killer, victim)
if core.settings:get_bool("enable_damage") == false then
return false, "Players can't be killed, damage has been disabled."
end
local victimref = core.get_player_by_name(victim)
if victimref == nil then
return false, string.format("Player %s is not online.", victim)
elseif victimref:get_hp() <= 0 then
if killer == victim then
return false, "You are already dead."
else
return false, string.format("%s is already dead.", victim)
end
end
if not killer == victim then
core.log("action", string.format("%s killed %s", killer, victim))
end
-- Kill victim
victimref:set_hp(0)
return true, string.format("%s has been killed.", victim)
end
core.register_chatcommand("kill", {
params = "[<name>]",
description = "Kill player or yourself",
privs = {server=true},
func = function(name, param)
return handle_kill_command(name, param == "" and name or param)
end,
})

View File

@ -150,8 +150,22 @@ end
local function drop_attached_node(p) local function drop_attached_node(p)
local n = core.get_node(p) local n = core.get_node(p)
local drops = core.get_node_drops(n, "")
local def = core.registered_items[n.name]
if def and def.preserve_metadata then
local oldmeta = core.get_meta(p):to_table().fields
-- Copy pos and node because the callback can modify them.
local pos_copy = {x=p.x, y=p.y, z=p.z}
local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
local drop_stacks = {}
for k, v in pairs(drops) do
drop_stacks[k] = ItemStack(v)
end
drops = drop_stacks
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
end
core.remove_node(p) core.remove_node(p)
for _, item in pairs(core.get_node_drops(n, "")) do for _, item in pairs(drops) do
local pos = { local pos = {
x = p.x + math.random()/2 - 0.25, x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25, y = p.y + math.random()/2 - 0.25,

View File

@ -516,7 +516,8 @@ function core.handle_node_drops(pos, drops, digger)
end end
else else
give_item = function(item) give_item = function(item)
return item -- itemstring to ItemStack for left:is_empty()
return ItemStack(item)
end end
end end
@ -578,6 +579,20 @@ function core.node_dig(pos, node, digger)
digger:set_wielded_item(wielded) digger:set_wielded_item(wielded)
end end
-- Check to see if metadata should be preserved.
if def and def.preserve_metadata then
local oldmeta = core.get_meta(pos):to_table().fields
-- Copy pos and node because the callback can modify them.
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
local drop_stacks = {}
for k, v in pairs(drops) do
drop_stacks[k] = ItemStack(v)
end
drops = drop_stacks
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
end
-- Handle drops -- Handle drops
core.handle_node_drops(pos, drops, digger) core.handle_node_drops(pos, drops, digger)

View File

@ -147,9 +147,9 @@ core.register_entity(":__builtin:item", {
}) })
local vel = self.object:getvelocity() local vel = self.object:getvelocity()
local def = node and core.registered_nodes[node.name] local def = node and core.registered_nodes[node.name]
-- Ignore is nil -> stop until the block loaded -- Avoid entity falling into ignore nodes or unloaded areas
local is_moving = (def and not def.walkable) or local is_moving = node and node.name ~= "ignore" and
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0 ((def and not def.walkable) or vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0)
local is_slippery = false local is_slippery = false
if def and def.walkable then if def and def.walkable then

View File

@ -5,12 +5,11 @@
-- --
function core.check_player_privs(name, ...) function core.check_player_privs(name, ...)
local arg_type = type(name) if core.is_player(name) then
if (arg_type == "userdata" or arg_type == "table") and
name.get_player_name then -- If it quacks like a Player...
name = name:get_player_name() name = name:get_player_name()
elseif arg_type ~= "string" then elseif type(name) ~= "string" then
error("Invalid core.check_player_privs argument type: " .. arg_type, 2) error("core.check_player_privs expects a player or playername as " ..
"argument.", 2)
end end
local requested_privs = {...} local requested_privs = {...}
@ -40,14 +39,17 @@ function core.check_player_privs(name, ...)
return true, "" return true, ""
end end
local player_list = {} local player_list = {}
function core.send_join_message(player_name) function core.send_join_message(player_name)
if not minetest.is_singleplayer() then if not minetest.is_singleplayer() then
core.chat_send_all("*** " .. player_name .. " joined the game.") core.chat_send_all("*** " .. player_name .. " joined the game.")
end end
end end
function core.send_leave_message(player_name, timed_out) function core.send_leave_message(player_name, timed_out)
local announcement = "*** " .. player_name .. " left the game." local announcement = "*** " .. player_name .. " left the game."
if timed_out then if timed_out then
@ -56,18 +58,21 @@ function core.send_leave_message(player_name, timed_out)
core.chat_send_all(announcement) core.chat_send_all(announcement)
end end
core.register_on_joinplayer(function(player) core.register_on_joinplayer(function(player)
local player_name = player:get_player_name() local player_name = player:get_player_name()
player_list[player_name] = player player_list[player_name] = player
core.send_join_message(player_name) core.send_join_message(player_name)
end) end)
core.register_on_leaveplayer(function(player, timed_out) core.register_on_leaveplayer(function(player, timed_out)
local player_name = player:get_player_name() local player_name = player:get_player_name()
player_list[player_name] = nil player_list[player_name] = nil
core.send_leave_message(player_name, timed_out) core.send_leave_message(player_name, timed_out)
end) end)
function core.get_connected_players() function core.get_connected_players()
local temp_table = {} local temp_table = {}
for index, value in pairs(player_list) do for index, value in pairs(player_list) do
@ -78,12 +83,24 @@ function core.get_connected_players()
return temp_table return temp_table
end end
function core.is_player(player)
-- a table being a player is also supported because it quacks sufficiently
-- like a player if it has the is_player function
local t = type(player)
return (t == "userdata" or t == "table") and
type(player.is_player) == "function" and player:is_player()
end
function minetest.player_exists(name) function minetest.player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil return minetest.get_auth_handler().get_auth(name) ~= nil
end end
-- Returns two position vectors representing a box of `radius` in each -- Returns two position vectors representing a box of `radius` in each
-- direction centered around the player corresponding to `player_name` -- direction centered around the player corresponding to `player_name`
function core.get_player_radius_area(player_name, radius) function core.get_player_radius_area(player_name, radius)
local player = core.get_player_by_name(player_name) local player = core.get_player_by_name(player_name)
if player == nil then if player == nil then
@ -101,10 +118,12 @@ function core.get_player_radius_area(player_name, radius)
return p1, p2 return p1, p2
end end
function core.hash_node_position(pos) function core.hash_node_position(pos)
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768 return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
end end
function core.get_position_from_hash(hash) function core.get_position_from_hash(hash)
local pos = {} local pos = {}
pos.x = (hash%65536) - 32768 pos.x = (hash%65536) - 32768
@ -115,6 +134,7 @@ function core.get_position_from_hash(hash)
return pos return pos
end end
function core.get_item_group(name, group) function core.get_item_group(name, group)
if not core.registered_items[name] or not if not core.registered_items[name] or not
core.registered_items[name].groups[group] then core.registered_items[name].groups[group] then
@ -123,11 +143,13 @@ function core.get_item_group(name, group)
return core.registered_items[name].groups[group] return core.registered_items[name].groups[group]
end end
function core.get_node_group(name, group) function core.get_node_group(name, group)
core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead") core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
return core.get_item_group(name, group) return core.get_item_group(name, group)
end end
function core.setting_get_pos(name) function core.setting_get_pos(name)
local value = core.settings:get(name) local value = core.settings:get(name)
if not value then if not value then
@ -136,17 +158,64 @@ function core.setting_get_pos(name)
return core.string_to_pos(value) return core.string_to_pos(value)
end end
-- To be overriden by protection mods -- To be overriden by protection mods
function core.is_protected(pos, name) function core.is_protected(pos, name)
return false return false
end end
function core.record_protection_violation(pos, name) function core.record_protection_violation(pos, name)
for _, func in pairs(core.registered_on_protection_violation) do for _, func in pairs(core.registered_on_protection_violation) do
func(pos, name) func(pos, name)
end end
end end
-- Checks if specified volume intersects a protected volume
function core.intersects_protection(minp, maxp, player_name, interval)
-- 'interval' is the largest allowed interval for the 3D lattice of checks.
-- Compute the optimal float step 'd' for each axis so that all corners and
-- borders are checked. 'd' will be smaller or equal to 'interval'.
-- Subtracting 1e-4 ensures that the max co-ordinate will be reached by the
-- for loop (which might otherwise not be the case due to rounding errors).
-- Default to 4
interval = interval or 4
local d = {}
for _, c in pairs({"x", "y", "z"}) do
if maxp[c] > minp[c] then
d[c] = (maxp[c] - minp[c]) /
math.ceil((maxp[c] - minp[c]) / interval) - 1e-4
elseif maxp[c] == minp[c] then
d[c] = 1 -- Any value larger than 0 to avoid division by zero
else -- maxp[c] < minp[c], print error and treat as protection intersected
minetest.log("error", "maxp < minp in 'minetest.intersects_protection()'")
return true
end
end
for zf = minp.z, maxp.z, d.z do
local z = math.floor(zf + 0.5)
for yf = minp.y, maxp.y, d.y do
local y = math.floor(yf + 0.5)
for xf = minp.x, maxp.x, d.x do
local x = math.floor(xf + 0.5)
if core.is_protected({x = x, y = y, z = z}, player_name) then
return true
end
end
end
end
return false
end
local raillike_ids = {} local raillike_ids = {}
local raillike_cur_id = 0 local raillike_cur_id = 0
function core.raillike_group(name) function core.raillike_group(name)
@ -159,7 +228,9 @@ function core.raillike_group(name)
return id return id
end end
-- HTTP callback interface -- HTTP callback interface
function core.http_add_fetch(httpenv) function core.http_add_fetch(httpenv)
httpenv.fetch = function(req, callback) httpenv.fetch = function(req, callback)
local handle = httpenv.fetch_async(req) local handle = httpenv.fetch_async(req)
@ -178,11 +249,12 @@ function core.http_add_fetch(httpenv)
return httpenv return httpenv
end end
function core.close_formspec(player_name, formname) function core.close_formspec(player_name, formname)
return minetest.show_formspec(player_name, formname, "") return minetest.show_formspec(player_name, formname, "")
end end
function core.cancel_shutdown_requests() function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1) core.request_shutdown("", false, -1)
end end

View File

@ -34,7 +34,7 @@ core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privil
core.register_privilege("privs", "Can modify privileges") core.register_privilege("privs", "Can modify privileges")
core.register_privilege("teleport", { core.register_privilege("teleport", {
description = "Can use /teleport command", description = "Can teleport self",
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
core.register_privilege("bring", { core.register_privilege("bring", {
@ -42,7 +42,7 @@ core.register_privilege("bring", {
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
core.register_privilege("settime", { core.register_privilege("settime", {
description = "Can use /time", description = "Can set the time of day using /time",
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
core.register_privilege("server", { core.register_privilege("server", {
@ -74,15 +74,15 @@ core.register_privilege("password", {
give_to_admin = true, give_to_admin = true,
}) })
core.register_privilege("fly", { core.register_privilege("fly", {
description = "Can fly using the free_move mode", description = "Can use fly mode",
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
core.register_privilege("fast", { core.register_privilege("fast", {
description = "Can walk fast using the fast_move mode", description = "Can use fast mode",
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
core.register_privilege("noclip", { core.register_privilege("noclip", {
description = "Can fly through walls", description = "Can fly through solid nodes using noclip mode",
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
core.register_privilege("rollback", { core.register_privilege("rollback", {

View File

@ -15,11 +15,42 @@
--with this program; if not, write to the Free Software Foundation, Inc., --with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local worldname = ""
local function create_world_formspec(dialogdata) local function create_world_formspec(dialogdata)
local mapgens = core.get_mapgen_names() local mapgens = core.get_mapgen_names()
local current_seed = core.settings:get("fixed_map_seed") or "" local current_seed = core.settings:get("fixed_map_seed") or ""
local current_mg = core.settings:get("mg_name") local current_mg = core.settings:get("mg_name")
local gameid = core.settings:get("menu_last_game")
local game, gameidx = nil , 0
if gameid ~= nil then
game, gameidx = gamemgr.find_by_gameid(gameid)
if gameidx == nil then
gameidx = 0
end
end
local game_by_gameidx = core.get_game(gameidx)
if game_by_gameidx ~= nil then
local gamepath = game_by_gameidx.path
local gameconfig = Settings(gamepath.."/game.conf")
local disallowed_mapgens = (gameconfig:get("disallowed_mapgens") or ""):split()
for key, value in pairs(disallowed_mapgens) do
disallowed_mapgens[key] = value:trim()
end
if disallowed_mapgens then
for i = #mapgens, 1, -1 do
if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
table.remove(mapgens, i)
end
end
end
end
local mglist = "" local mglist = ""
local selindex = 1 local selindex = 1
@ -33,22 +64,11 @@ local function create_world_formspec(dialogdata)
end end
mglist = mglist:sub(1, -2) mglist = mglist:sub(1, -2)
local gameid = core.settings:get("menu_last_game")
local game, gameidx = nil , 0
if gameid ~= nil then
game, gameidx = gamemgr.find_by_gameid(gameid)
if gameidx == nil then
gameidx = 0
end
end
current_seed = core.formspec_escape(current_seed) current_seed = core.formspec_escape(current_seed)
local retval = local retval =
"size[11.5,6.5,true]" .. "size[11.5,6.5,true]" ..
"label[2,0;" .. fgettext("World name") .. "]".. "label[2,0;" .. fgettext("World name") .. "]"..
"field[4.5,0.4;6,0.5;te_world_name;;]" .. "field[4.5,0.4;6,0.5;te_world_name;;" .. minetest.formspec_escape(worldname) .. "]" ..
"label[2,1;" .. fgettext("Seed") .. "]".. "label[2,1;" .. fgettext("Seed") .. "]"..
"field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" .. "field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" ..
@ -121,7 +141,11 @@ local function create_world_buttonhandler(this, fields)
return true return true
end end
worldname = fields.te_world_name
if fields["games"] then if fields["games"] then
local gameindex = core.get_textlist_index("games")
core.settings:set("menu_last_game", gamemgr.games[gameindex].id)
return true return true
end end
@ -135,6 +159,7 @@ end
function create_create_world_dlg(update_worldlistfilter) function create_create_world_dlg(update_worldlistfilter)
worldname = ""
local retval = dialog_create("sp_create_world", local retval = dialog_create("sp_create_world",
create_world_formspec, create_world_formspec,
create_world_buttonhandler, create_world_buttonhandler,

View File

@ -752,6 +752,18 @@ local function handle_change_setting_buttons(this, fields)
core.update_formspec(this:get_formspec()) core.update_formspec(this:get_formspec())
return true return true
end end
if setting.min and new_value < setting.min then
this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
this.data.entered_text = fields["te_setting_value"]
core.update_formspec(this:get_formspec())
return true
end
if setting.max and new_value > setting.max then
this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
this.data.entered_text = fields["te_setting_value"]
core.update_formspec(this:get_formspec())
return true
end
core.settings:set(setting.name, new_value) core.settings:set(setting.name, new_value)
elseif setting.type == "flags" then elseif setting.type == "flags" then

View File

@ -310,7 +310,7 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
return { return {
name = "local", name = "local",
caption = fgettext("Local Game"), caption = fgettext("Start Game"),
cbf_formspec = get_formspec, cbf_formspec = get_formspec,
cbf_button_handler = main_button_handler, cbf_button_handler = main_button_handler,
on_change = on_change on_change = on_change

View File

@ -344,7 +344,7 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
return { return {
name = "online", name = "online",
caption = fgettext("Play Online"), caption = fgettext("Join Game"),
cbf_formspec = get_formspec, cbf_formspec = get_formspec,
cbf_button_handler = main_button_handler, cbf_button_handler = main_button_handler,
on_change = on_change on_change = on_change

View File

@ -235,7 +235,7 @@ local function formspec(tabview, name, tabdata)
.. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" .. .. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" ..
"checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" "checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.settings:get_bool("tone_mapping")) .. "]" .. .. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
"checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";" "checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Generate Normal Maps") .. ";"
.. dump(core.settings:get_bool("generate_normalmaps")) .. "]" .. .. dump(core.settings:get_bool("generate_normalmaps")) .. "]" ..
"checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" "checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
.. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" .. .. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" ..
@ -252,7 +252,7 @@ local function formspec(tabview, name, tabdata)
"label[8.38,1.2;" .. core.colorize("#888888", "label[8.38,1.2;" .. core.colorize("#888888",
fgettext("Tone Mapping")) .. "]" .. fgettext("Tone Mapping")) .. "]" ..
"label[8.38,1.7;" .. core.colorize("#888888", "label[8.38,1.7;" .. core.colorize("#888888",
fgettext("Normal Mapping")) .. "]" .. fgettext("Generate Normal Maps")) .. "]" ..
"label[8.38,2.2;" .. core.colorize("#888888", "label[8.38,2.2;" .. core.colorize("#888888",
fgettext("Parallax Occlusion")) .. "]" .. fgettext("Parallax Occlusion")) .. "]" ..
"label[8.38,2.7;" .. core.colorize("#888888", "label[8.38,2.7;" .. core.colorize("#888888",

View File

@ -536,6 +536,9 @@ fps_max (Maximum FPS) int 60
# Maximum FPS when game is paused. # Maximum FPS when game is paused.
pause_fps_max (FPS in pause menu) int 20 pause_fps_max (FPS in pause menu) int 20
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is open.
pause_on_lost_focus (Pause on lost window focus) bool false
# View distance in nodes. # View distance in nodes.
viewing_range (Viewing range) int 100 20 4000 viewing_range (Viewing range) int 100 20 4000
@ -566,23 +569,31 @@ vsync (V-Sync) bool false
# Field of view in degrees. # Field of view in degrees.
fov (Field of view) int 72 30 160 fov (Field of view) int 72 30 160
# Field of view while zooming in degrees.
# Requires to be allowed by server-sided mods.
zoom_fov (Field of view for zoom) int 15 7 160
# Adjust the gamma encoding for the light tables. Higher numbers are brighter. # Adjust the gamma encoding for the light tables. Higher numbers are brighter.
# This setting is for the client only and is ignored by the server. # This setting is for the client only and is ignored by the server.
display_gamma (Gamma) float 1.0 0.5 3.0 display_gamma (Gamma) float 1.0 0.5 3.0
# Gradient of light curve at minimum light level.
lighting_alpha (Darkness sharpness) float 0.0 0.0 4.0 lighting_alpha (Darkness sharpness) float 0.0 0.0 4.0
lighting_beta (Lightness sharpness) float 0.0 0.0 4.0 # Gradient of light curve at maximum light level.
lighting_beta (Lightness sharpness) float 1.5 0.0 4.0
# Strength of light curve mid-boost.
lighting_boost (Light curve mid boost) float 0.2 0.0 1.0
# Center of light curve mid-boost.
lighting_boost_center (Light curve mid boost center) float 0.5 0.0 1.0
# Spread of light curve mid-boost.
# Standard deviation of the mid-boost gaussian.
lighting_boost_spread (Light curve mid boost spread) float 0.2 0.0 1.0
# Path to texture directory. All textures are first searched from here. # Path to texture directory. All textures are first searched from here.
texture_path (Texture path) path texture_path (Texture path) path
# The rendering back-end for Irrlicht. # The rendering back-end for Irrlicht.
video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl,ogles1,ogles2
# Radius of cloud area stated in number of 64 node cloud squares. # Radius of cloud area stated in number of 64 node cloud squares.
# Values larger than 26 will start to produce sharp cutoffs at cloud area corners. # Values larger than 26 will start to produce sharp cutoffs at cloud area corners.
@ -608,7 +619,7 @@ fall_bobbing_amount (Fall bobbing factor) float 0.0
3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,pageflip 3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,pageflip
# In-game chat console height, between 0.1 (10%) and 1.0 (100%). # In-game chat console height, between 0.1 (10%) and 1.0 (100%).
console_height (Console height) float 1.0 0.1 1.0 console_height (Console height) float 0.6 0.1 1.0
# In-game chat console background color (R,G,B). # In-game chat console background color (R,G,B).
console_color (Console color) string (0,0,0) console_color (Console color) string (0,0,0)
@ -990,14 +1001,20 @@ kick_msg_crash (Crash message) string This server has experienced an internal er
ask_reconnect_on_crash (Ask to reconnect after crash) bool false ask_reconnect_on_crash (Ask to reconnect after crash) bool false
# From how far clients know about objects, stated in mapblocks (16 nodes). # From how far clients know about objects, stated in mapblocks (16 nodes).
#
# Setting this larger than active_block_range will also cause the server
# to maintain active objects up to this distance in the direction the
# player is looking. (This can avoid mobs suddenly disappearing from view)
active_object_send_range_blocks (Active object send range) int 3 active_object_send_range_blocks (Active object send range) int 3
# How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes). # How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes).
# In active blocks objects are loaded and ABMs run. # In active blocks objects are loaded and ABMs run.
# This is also the minimum range in which active objects (mobs) are maintained.
# This should be configured together with active_object_range.
active_block_range (Active block range) int 3 active_block_range (Active block range) int 3
# From how far blocks are sent to clients, stated in mapblocks (16 nodes). # From how far blocks are sent to clients, stated in mapblocks (16 nodes).
max_block_send_distance (Max block send distance) int 10 max_block_send_distance (Max block send distance) int 9
# Maximum number of forceloaded mapblocks. # Maximum number of forceloaded mapblocks.
max_forceloaded_blocks (Maximum forceloaded blocks) int 16 max_forceloaded_blocks (Maximum forceloaded blocks) int 16
@ -1104,12 +1121,13 @@ server_side_occlusion_culling (Server side occlusion culling) bool true
# Restricts the access of certain client-side functions on servers # Restricts the access of certain client-side functions on servers
# Combine these byteflags below to restrict more client-side features: # Combine these byteflags below to restrict more client-side features:
# LOOKUP_NODES_LIMIT: 1 (limits get_node call client-side to csm_flavour_noderange_limit) # LOAD_CLIENT_MODS: 1 (disable client mods loading)
# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) # CHAT_MESSAGES: 2 (disable send_chat_message call client-side)
# READ_ITEMDEFS: 4 (disable get_item_def call client-side) # READ_ITEMDEFS: 4 (disable get_item_def call client-side)
# READ_NODEDEFS: 8 (disable get_node_def call client-side) # READ_NODEDEFS: 8 (disable get_node_def call client-side)
# LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to csm_flavour_noderange_limit)
# type: int # type: int
csm_flavour_limits (Client side modding flavour limits) int 3 csm_flavour_limits (Client side modding flavour limits) int 18
# If the CSM flavour for node range is enabled, get_node is limited to # If the CSM flavour for node range is enabled, get_node is limited to
# this many nodes from the player. # this many nodes from the player.
@ -1181,7 +1199,8 @@ name (Player name) string
# Set the language. Leave empty to use the system language. # Set the language. Leave empty to use the system language.
# A restart is required after changing this. # A restart is required after changing this.
language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,ky,lt,nb,nl,pl,pt,pt_BR,ro,ru,sr_Cyrl,tr,uk,zh_CN,zh_TW language (Language) enum ,be,ca,cs,da,de,dv,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,ky,lt,ms,nb,nl,pl,pt,pt_BR,ro,ru,sl,sr_Cyrl,sv,sw,tr,uk,zh_CN,zh_TW
# Level of logging to be written to debug.txt: # Level of logging to be written to debug.txt:
# - <nothing> (no logging) # - <nothing> (no logging)
@ -1193,6 +1212,13 @@ language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,
# - verbose # - verbose
debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose
# ANSI colored logs: red error log, yellow warning and grey info and verbose logs
# Note that it doesn't work on Windows
# "yes" always enables it,
# "detect" enables it when printing to terminal and
# "no" disables it
log_color (Colored logs) enum detect yes,detect,no
# IPv6 support. # IPv6 support.
enable_ipv6 (IPv6) bool true enable_ipv6 (IPv6) bool true
@ -1387,13 +1413,16 @@ mgv7_large_cave_depth (Large cave depth) int -33
# Y of upper limit of lava in large caves. # Y of upper limit of lava in large caves.
mgv7_lava_depth (Lava depth) int -256 mgv7_lava_depth (Lava depth) int -256
# Controls the density of floatland mountain terrain. # Controls the density of mountain-type floatlands.
# Is an offset added to the 'np_mountain' noise value. # Is a noise offset added to the 'mgv7_np_mountain' noise value.
mgv7_float_mount_density (Floatland mountain density) float 0.6 mgv7_float_mount_density (Floatland mountain density) float 0.6
# Typical maximum height, above and below midpoint, of floatland mountain terrain. # Typical maximum height, above and below midpoint, of floatland mountains.
mgv7_float_mount_height (Floatland mountain height) float 128.0 mgv7_float_mount_height (Floatland mountain height) float 128.0
# Alters how mountain-type floatlands taper above and below midpoint.
mgv7_float_mount_exponent (Floatland mountain exponent) float 0.75
# Y-level of floatland midpoint and lake surface. # Y-level of floatland midpoint and lake surface.
mgv7_floatland_level (Floatland level) int 1280 mgv7_floatland_level (Floatland level) int 1280

View File

@ -7,15 +7,13 @@ dofile("preview:example.lua")
core.register_on_shutdown(function() core.register_on_shutdown(function()
print("[PREVIEW] shutdown client") print("[PREVIEW] shutdown client")
end) end)
local id = nil
core.register_on_connect(function()
print("[PREVIEW] Player connection completed")
local server_info = core.get_server_info() local server_info = core.get_server_info()
print("Server version: " .. server_info.protocol_version) print("Server version: " .. server_info.protocol_version)
print("Server ip: " .. server_info.ip) print("Server ip: " .. server_info.ip)
print("Server address: " .. server_info.address) print("Server address: " .. server_info.address)
print("Server port: " .. server_info.port) print("Server port: " .. server_info.port)
mod_channel = core.mod_channel_join("experimental_preview") mod_channel = core.mod_channel_join("experimental_preview")
core.after(4, function() core.after(4, function()
@ -23,6 +21,18 @@ core.register_on_connect(function()
mod_channel:send_all("preview talk to experimental") mod_channel:send_all("preview talk to experimental")
end end
end) end)
core.after(1, function()
id = core.localplayer:hud_add({
hud_elem_type = "text",
name = "example",
number = 0xff0000,
position = {x=0, y=1},
offset = {x=8, y=-8},
text = "You are using the preview mod",
scale = {x=200, y=60},
alignment = {x=1, y=-1},
})
end) end)
core.register_on_modchannel_message(function(channel, sender, message) core.register_on_modchannel_message(function(channel, sender, message)
@ -184,3 +194,9 @@ core.register_chatcommand("privs", {
return true, core.privs_to_string(minetest.get_privilege_list()) return true, core.privs_to_string(minetest.get_privilege_list())
end, end,
}) })
core.register_chatcommand("text", {
func = function(param)
return core.localplayer:hud_change(id, "text", param)
end,
})

View File

@ -14,7 +14,7 @@ scripting in run-time loaded mods.
A mod is a self-contained bunch of scripts, textures and other related A mod is a self-contained bunch of scripts, textures and other related
things that is loaded by and interfaces with Minetest. things that is loaded by and interfaces with Minetest.
Transfering client-sided mods form the server to the client is planned, but not implemented yet. Transferring client-sided mods from the server to the client is planned, but not implemented yet.
If you see a deficiency in the API, feel free to attempt to add the If you see a deficiency in the API, feel free to attempt to add the
functionality in the engine and API. You can send such improvements as functionality in the engine and API. You can send such improvements as
@ -626,7 +626,7 @@ Minetest namespace reference
* `hash`: Full git version (only set if available), eg, "1.2.3-dev-01234567-dirty" * `hash`: Full git version (only set if available), eg, "1.2.3-dev-01234567-dirty"
Use this for informational purposes only. The information in the returned Use this for informational purposes only. The information in the returned
table does not represent the capabilities of the engine, nor is it table does not represent the capabilities of the engine, nor is it
reliable or verifyable. Compatible forks will have a different name and reliable or verifiable. Compatible forks will have a different name and
version entirely. To check for the presence of engine features, test version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example: whether the functions exported by the wanted features exist. For example:
`if minetest.check_for_falling then ... end`. `if minetest.check_for_falling then ... end`.
@ -651,8 +651,6 @@ Call these functions only at load time!
* **Warning**: If the client terminates abnormally (i.e. crashes), the registered * **Warning**: If the client terminates abnormally (i.e. crashes), the registered
callbacks **will likely not be run**. Data should be saved at callbacks **will likely not be run**. Data should be saved at
semi-frequent intervals as well as on server shutdown. semi-frequent intervals as well as on server shutdown.
* `minetest.register_on_connect(func())`
* Called at the end of client connection (when player is loaded onto map)
* `minetest.register_on_receiving_chat_message(func(message))` * `minetest.register_on_receiving_chat_message(func(message))`
* Called always when a client receive a message * Called always when a client receive a message
* Return `true` to mark the message as handled, which means that it will not be shown to chat * Return `true` to mark the message as handled, which means that it will not be shown to chat
@ -727,7 +725,7 @@ Call these functions only at load time!
* `minetest.get_node_or_nil(pos)` * `minetest.get_node_or_nil(pos)`
* Returns the node at the given position as table in the format * Returns the node at the given position as table in the format
`{name="node_name", param1=0, param2=0}`, returns `nil` `{name="node_name", param1=0, param2=0}`, returns `nil`
for unloaded areas or flavour limited areas. for unloaded areas or flavor limited areas.
* `minetest.find_node_near(pos, radius, nodenames, [search_center])`: returns pos or `nil` * `minetest.find_node_near(pos, radius, nodenames, [search_center])`: returns pos or `nil`
* `radius`: using a maximum metric * `radius`: using a maximum metric
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
@ -754,7 +752,7 @@ Call these functions only at load time!
### Privileges ### Privileges
* `minetest.get_privilege_list()` * `minetest.get_privilege_list()`
* Returns a list of privileges the currect player has in the format `{priv1=true,...}` * Returns a list of privileges the current player has in the format `{priv1=true,...}`
* `minetest.string_to_privs(str)`: returns `{priv1=true,...}` * `minetest.string_to_privs(str)`: returns `{priv1=true,...}`
* `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."` * `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."`
* Convert between two privilege representations * Convert between two privilege representations
@ -765,8 +763,6 @@ Call these functions only at load time!
* `minetest.disconnect()` * `minetest.disconnect()`
* Disconnect from the server and exit to main menu. * Disconnect from the server and exit to main menu.
* Returns `false` if the client is already disconnecting otherwise returns `true`. * Returns `false` if the client is already disconnecting otherwise returns `true`.
* `minetest.take_screenshot()`
* Take a screenshot.
* `minetest.get_server_info()` * `minetest.get_server_info()`
* Returns [server info](#server-info). * Returns [server info](#server-info).
* `minetest.send_respawn()` * `minetest.send_respawn()`
@ -784,8 +780,16 @@ Call these functions only at load time!
* Client joins channel `channel_name`, and creates it, if necessary. You * Client joins channel `channel_name`, and creates it, if necessary. You
should listen from incoming messages with `minetest.register_on_modchannel_message` should listen from incoming messages with `minetest.register_on_modchannel_message`
call to receive incoming messages. Warning, this function is asynchronous. call to receive incoming messages. Warning, this function is asynchronous.
* You should use a minetest.register_on_connect(function() ... end) to perform
a successful channel join on client startup. ### Particles
* `minetest.add_particle(particle definition)`
* `minetest.add_particlespawner(particlespawner definition)`
* Add a `ParticleSpawner`, an object that spawns an amount of particles over `time` seconds
* Returns an `id`, and -1 if adding didn't succeed
* `minetest.delete_particlespawner(id)`
* Delete `ParticleSpawner` with `id` (return value from `minetest.add_particlespawner`)
### Misc. ### Misc.
* `minetest.parse_json(string[, nullvalue])`: returns something * `minetest.parse_json(string[, nullvalue])`: returns something
@ -870,9 +874,9 @@ An interface to use mod channels on client and server
* No more incoming or outgoing messages can be sent to this channel from client mods. * No more incoming or outgoing messages can be sent to this channel from client mods.
* This invalidate all future object usage * This invalidate all future object usage
* Ensure your set mod_channel to nil after that to free Lua resources * Ensure your set mod_channel to nil after that to free Lua resources
* `is_writeable()`: returns true if channel is writeable and mod can send over it. * `is_writeable()`: returns true if channel is writable and mod can send over it.
* `send_all(message)`: Send `message` though the mod channel. * `send_all(message)`: Send `message` though the mod channel.
* If mod channel is not writeable or invalid, message will be dropped. * If mod channel is not writable or invalid, message will be dropped.
* Message size is limited to 65535 characters by protocol. * Message size is limited to 65535 characters by protocol.
### Minimap ### Minimap
@ -891,14 +895,14 @@ An interface to manipulate minimap on client UI
* `get_shape()`: Gets the minimap shape. (0 = square, 1 = round) * `get_shape()`: Gets the minimap shape. (0 = square, 1 = round)
### Camera ### Camera
An interface to get or set information about the camera and cameranode. An interface to get or set information about the camera and camera-node.
Please do not try to access the reference until the camera is initialized, otherwise the reference will be nil. Please do not try to access the reference until the camera is initialized, otherwise the reference will be nil.
#### Methods #### Methods
* `set_camera_mode(mode)` * `set_camera_mode(mode)`
* Pass `0` for first-person, `1` for third person, and `2` for third person front * Pass `0` for first-person, `1` for third person, and `2` for third person front
* `get_camera_mode()` * `get_camera_mode()`
* Returns with same syntax as above * Returns 0, 1, or 2 as described above
* `get_fov()` * `get_fov()`
* Returns: * Returns:
@ -925,18 +929,7 @@ Please do not try to access the reference until the camera is initialized, other
* Returns aspect ratio of screen * Returns aspect ratio of screen
### LocalPlayer ### LocalPlayer
An interface to retrieve information about the player. The player is An interface to retrieve information about the player.
not accessible until the client is fully done loading and therefore
not at module init time.
To get the localplayer handle correctly, use `on_connect()` as follows:
```lua
local localplayer
minetest.register_on_connect(function()
localplayer = minetest.localplayer
end)
```
Methods: Methods:
@ -1025,6 +1018,17 @@ Methods:
* returns last look vertical angle * returns last look vertical angle
* `get_key_pressed()`: * `get_key_pressed()`:
* returns last key typed by the player * returns last key typed by the player
* `hud_add(definition)`
* add a HUD element described by HUD def, returns ID number on success and `nil` on failure.
* See [`HUD definition`](#hud-definition-hud_add-hud_get)
* `hud_get(id)`
* returns the [`definition`](#hud-definition-hud_add-hud_get) of the HUD with that ID number or `nil`, if non-existent.
* `hud_remove(id)`
* remove the HUD element of the specified id, returns `true` on success
* `hud_change(id, stat, value)`
* change a value of a previously added HUD element
* element `stat` values: `position`, `name`, `scale`, `text`, `number`, `item`, `dir`
* Returns `true` on success, otherwise returns `nil`
### Settings ### Settings
An interface to read config files in the format of `minetest.conf`. An interface to read config files in the format of `minetest.conf`.
@ -1136,7 +1140,7 @@ Can be obtained via `minetest.get_meta(pos)`.
stack_max = number, -- Number of items stackable together stack_max = number, -- Number of items stackable together
usable = bool, -- Has on_use callback defined usable = bool, -- Has on_use callback defined
liquids_pointable = bool, -- Whether you can point at liquids with the item liquids_pointable = bool, -- Whether you can point at liquids with the item
tool_capabilities = <table>, -- If the item is a tool, tool capabiltites of the item tool_capabilities = <table>, -- If the item is a tool, tool capabilities of the item
groups = table, -- Groups of the item groups = table, -- Groups of the item
sound_place = SimpleSoundSpec, -- Sound played when placed sound_place = SimpleSoundSpec, -- Sound played when placed
sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed
@ -1163,6 +1167,30 @@ Can be obtained via `minetest.get_meta(pos)`.
} }
``` ```
### HUD Definition (`hud_add`, `hud_get`)
```lua
{
hud_elem_type = "image", -- see HUD element types, default "text"
-- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory"
position = {x=0.5, y=0.5},
-- ^ Left corner position of element, default `{x=0,y=0}`.
name = "<name>", -- default ""
scale = {x=2, y=2}, -- default {x=0,y=0}
text = "<text>", -- default ""
number = 2, -- default 0
item = 3, -- default 0
-- ^ Selected item in inventory. 0 for no item selected.
direction = 0, -- default 0
-- ^ Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
alignment = {x=0, y=0}, -- default {x=0, y=0}
-- ^ See "HUD Element Types"
offset = {x=0, y=0}, -- default {x=0, y=0}
-- ^ See "HUD Element Types"
size = { x=100, y=100 }, -- default {x=0, y=0}
-- ^ Size of element in pixels
}
```
Escape sequences Escape sequences
---------------- ----------------
Most text can contain escape sequences, that can for example color the text. Most text can contain escape sequences, that can for example color the text.
@ -1206,3 +1234,134 @@ value must (always) be two hexadecimal digits.
`Color` `Color`
------------- -------------
`{a = alpha, r = red, g = green, b = blue}` defines an ARGB8 color. `{a = alpha, r = red, g = green, b = blue}` defines an ARGB8 color.
HUD element types
-----------------
The position field is used for all element types.
To account for differing resolutions, the position coordinates are the percentage
of the screen, ranging in value from `0` to `1`.
The name field is not yet used, but should contain a description of what the
HUD element represents. The direction field is the direction in which something
is drawn.
`0` draws from left to right, `1` draws from right to left, `2` draws from
top to bottom, and `3` draws from bottom to top.
The `alignment` field specifies how the item will be aligned. It ranges from `-1` to `1`,
with `0` being the center, `-1` is moved to the left/up, and `1` is to the right/down.
Fractional values can be used.
The `offset` field specifies a pixel offset from the position. Contrary to position,
the offset is not scaled to screen size. This allows for some precisely-positioned
items in the HUD.
**Note**: `offset` _will_ adapt to screen DPI as well as user defined scaling factor!
Below are the specific uses for fields in each type; fields not listed for that type are ignored.
**Note**: Future revisions to the HUD API may be incompatible; the HUD API is still
in the experimental stages.
### `image`
Displays an image on the HUD.
* `scale`: The scale of the image, with 1 being the original texture size.
Only the X coordinate scale is used (positive values).
Negative values represent that percentage of the screen it
should take; e.g. `x=-100` means 100% (width).
* `text`: The name of the texture that is displayed.
* `alignment`: The alignment of the image.
* `offset`: offset in pixels from position.
### `text`
Displays text on the HUD.
* `scale`: Defines the bounding rectangle of the text.
A value such as `{x=100, y=100}` should work.
* `text`: The text to be displayed in the HUD element.
* `number`: An integer containing the RGB value of the color used to draw the text.
Specify `0xFFFFFF` for white text, `0xFF0000` for red, and so on.
* `alignment`: The alignment of the text.
* `offset`: offset in pixels from position.
### `statbar`
Displays a horizontal bar made up of half-images.
* `text`: The name of the texture that is used.
* `number`: The number of half-textures that are displayed.
If odd, will end with a vertically center-split texture.
* `direction`
* `offset`: offset in pixels from position.
* `size`: If used, will force full-image size to this value (override texture pack image size)
### `inventory`
* `text`: The name of the inventory list to be displayed.
* `number`: Number of items in the inventory to be displayed.
* `item`: Position of item that is selected.
* `direction`
* `offset`: offset in pixels from position.
### `waypoint`
Displays distance to selected world position.
* `name`: The name of the waypoint.
* `text`: Distance suffix. Can be blank.
* `number:` An integer containing the RGB value of the color used to draw the text.
* `world_pos`: World position of the waypoint.
### Particle definition (`add_particle`)
{
pos = {x=0, y=0, z=0},
velocity = {x=0, y=0, z=0},
acceleration = {x=0, y=0, z=0},
-- ^ Spawn particle at pos with velocity and acceleration
expirationtime = 1,
-- ^ Disappears after expirationtime seconds
size = 1,
collisiondetection = false,
-- ^ collisiondetection: if true collides with physical objects
collision_removal = false,
-- ^ collision_removal: if true then particle is removed when it collides,
-- ^ requires collisiondetection = true to have any effect
vertical = false,
-- ^ vertical: if true faces player using y axis only
texture = "image.png",
-- ^ Uses texture (string)
animation = {Tile Animation definition},
-- ^ optional, specifies how to animate the particle texture
glow = 0
-- ^ optional, specify particle self-luminescence in darkness
}
### `ParticleSpawner` definition (`add_particlespawner`)
{
amount = 1,
time = 1,
-- ^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
minpos = {x=0, y=0, z=0},
maxpos = {x=0, y=0, z=0},
minvel = {x=0, y=0, z=0},
maxvel = {x=0, y=0, z=0},
minacc = {x=0, y=0, z=0},
maxacc = {x=0, y=0, z=0},
minexptime = 1,
maxexptime = 1,
minsize = 1,
maxsize = 1,
-- ^ The particle's properties are random values in between the bounds:
-- ^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
-- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
collisiondetection = false,
-- ^ collisiondetection: if true uses collision detection
collision_removal = false,
-- ^ collision_removal: if true then particle is removed when it collides,
-- ^ requires collisiondetection = true to have any effect
vertical = false,
-- ^ vertical: if true faces player using y axis only
texture = "image.png",
-- ^ Uses texture (string)
}

View File

@ -1,21 +1,21 @@
Minetest Lua Modding API Reference 0.5.0 Minetest Lua Modding API Reference
========================================= ==================================
* More information at <http://www.minetest.net/> * More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/> * Developer Wiki: <http://dev.minetest.net/>
Introduction Introduction
------------ ------------
Content and functionality can be added to Minetest 0.4 by using Lua Content and functionality can be added to Minetest using Lua scripting
scripting in run-time loaded mods. in run-time loaded mods.
A mod is a self-contained bunch of scripts, textures and other related A mod is a self-contained bunch of scripts, textures and other related
things that is loaded by and interfaces with Minetest. things, which is loaded by and interfaces with Minetest.
Mods are contained and ran solely on the server side. Definitions and media Mods are contained and ran solely on the server side. Definitions and media
files are automatically transferred to the client. files are automatically transferred to the client.
If you see a deficiency in the API, feel free to attempt to add the If you see a deficiency in the API, feel free to attempt to add the
functionality in the engine and API. functionality in the engine and API, and to document it here.
Programming in Lua Programming in Lua
------------------ ------------------
@ -47,37 +47,41 @@ Paths
Games Games
----- -----
Games are looked up from: Games are looked up from:
* `$path_share/games/gameid/` * `$path_share/games/gameid/`
* `$path_user/games/gameid/` * `$path_user/games/gameid/`
Where `gameid` is unique to each game.
where `gameid` is unique to each game. The game directory can contain the following files:
* `game.conf`
The game directory contains the file `game.conf`, which contains these fields: Which contains:
* name = <Human-readable full name of the game>
name = <Human-readable full name of the game>
e.g. e.g.
name = Minetest name = Minetest
* Optionally, game.conf can also contain:
The game directory can contain the file minetest.conf, which will be used disallowed_mapgens = <comma-separated mapgens>
to set default settings when running the particular game. e.g.
It can also contain a settingtypes.txt in the same format as the one in builtin. disallowed_mapgens = v5,v6,flat
This settingtypes.txt will be parsed by the menu and the settings will be displayed These mapgens are removed from the list of mapgens for the game.
in the "Games" category in the settings tab. * minetest.conf
Used to set default settings when running this game.
* settingtypes.txt
In the same format as the one in builtin.
This settingtypes.txt will be parsed by the menu and the settings will be
displayed in the "Games" category in the advanced settings tab.
* If the subgame contains a folder called `textures` the server will load it
as a texturepack, overriding mod textures.
Any server texturepack will override mod textures and the game texturepack.
### Menu images ### Menu images
Games can provide custom main menu images. They are put inside a `menu` directory Games can provide custom main menu images. They are put inside a `menu`
inside the game directory. directory inside the game directory.
The images are named `$identifier.png`, where `$identifier` is
one of `overlay,background,footer,header`.
If you want to specify multiple images for one identifier, add additional images named
like `$identifier.$n.png`, with an ascending number $n starting with 1, and a random
image will be chosen from the provided ones.
The images are named `$identifier.png`, where `$identifier` is one of
`overlay`, `background`, `footer`, `header`.
If you want to specify multiple images for one identifier, add additional
images named like `$identifier.$n.png`, with an ascending number $n starting
with 1, and a random image will be chosen from the provided ones.
Mod load path Mod load path
------------- -------------
@ -153,15 +157,15 @@ List of mods that have to be loaded before loading this mod.
A single line contains a single modname. A single line contains a single modname.
Optional dependencies can be defined by appending a question mark Optional dependencies can be defined by appending a question mark
to a single modname. Their meaning is that if the specified mod to a single modname. This means that if the specified mod
is missing, that does not prevent this mod from being loaded. is missing, it does not prevent this mod from being loaded.
### `screenshot.png` ### `screenshot.png`
A screenshot shown in the mod manager within the main menu. It should A screenshot shown in the mod manager within the main menu. It should
have an aspect ratio of 3:2 and a minimum size of 300×200 pixels. have an aspect ratio of 3:2 and a minimum size of 300×200 pixels.
### `description.txt` ### `description.txt`
A File containing description to be shown within mainmenu. A file containing a description to be shown in the Mods tab of the mainmenu.
### `settingtypes.txt` ### `settingtypes.txt`
A file in the same format as the one in builtin. It will be parsed by the A file in the same format as the one in builtin. It will be parsed by the
@ -219,8 +223,7 @@ Aliases
Aliases can be added by using `minetest.register_alias(name, convert_to)` or Aliases can be added by using `minetest.register_alias(name, convert_to)` or
`minetest.register_alias_force(name, convert_to)`. `minetest.register_alias_force(name, convert_to)`.
This will make Minetest to convert things called name to things called This converts anything called `name` to `convert_to`.
`convert_to`.
The only difference between `minetest.register_alias` and The only difference between `minetest.register_alias` and
`minetest.register_alias_force` is that if an item called `name` exists, `minetest.register_alias_force` is that if an item called `name` exists,
@ -229,7 +232,7 @@ The only difference between `minetest.register_alias` and
This can be used for maintaining backwards compatibility. This can be used for maintaining backwards compatibility.
This can be also used for setting quick access names for things, e.g. if This can also set quick access names for things, e.g. if
you have an item called `epiclylongmodname:stuff`, you could do you have an item called `epiclylongmodname:stuff`, you could do
minetest.register_alias("stuff", "epiclylongmodname:stuff") minetest.register_alias("stuff", "epiclylongmodname:stuff")
@ -330,7 +333,7 @@ Example:
default_dirt.png^default_grass_side.png default_dirt.png^default_grass_side.png
`default_grass_side.png` is overlayed over `default_dirt.png`. `default_grass_side.png` is overlaid over `default_dirt.png`.
The texture with the lower resolution will be automatically upscaled to The texture with the lower resolution will be automatically upscaled to
the higher resolution texture. the higher resolution texture.
@ -1023,6 +1026,17 @@ A nodebox is defined as any of:
connect_left = box OR {box1, box2, ...} connect_left = box OR {box1, box2, ...}
connect_back = box OR {box1, box2, ...} connect_back = box OR {box1, box2, ...}
connect_right = box OR {box1, box2, ...} connect_right = box OR {box1, box2, ...}
-- The following `disconnected_*` boxes are the opposites of the
-- `connect_*` ones above, i.e. when a node has no suitable neighbour
-- on the respective side, the corresponding disconnected box is drawn.
disconnected_top = box OR {box1, box2, ...}
disconnected_bottom = box OR {box1, box2, ...}
disconnected_front = box OR {box1, box2, ...}
disconnected_left = box OR {box1, box2, ...}
disconnected_back = box OR {box1, box2, ...}
disconnected_right = box OR {box1, box2, ...}
disconnected = box OR {box1, box2, ...} -- when there is *no* neighbour
disconnected_sides = box OR {box1, box2, ...} -- when there are *no* neighbours to the sides
} }
A `box` is defined as: A `box` is defined as:
@ -1151,7 +1165,7 @@ If `column_height_max` is not specified, this parameter defaults to `clust_size`
for reverse compatibility. New code should prefer `column_height_max`. for reverse compatibility. New code should prefer `column_height_max`.
The `column_midpoint_factor` parameter controls the position of the column at which The `column_midpoint_factor` parameter controls the position of the column at which
ore eminates from. If 1, columns grow upward. If 0, columns grow downward. If 0.5, ore emanates from. If 1, columns grow upward. If 0, columns grow downward. If 0.5,
columns grow equally starting from each direction. `column_midpoint_factor` is a columns grow equally starting from each direction. `column_midpoint_factor` is a
decimal number ranging in value from 0 to 1. If this parameter is not specified, decimal number ranging in value from 0 to 1. If this parameter is not specified,
the default is 0.5. the default is 0.5.
@ -1877,14 +1891,23 @@ examples.
* deprecated: `invsize[<W>,<H>;]` * deprecated: `invsize[<W>,<H>;]`
#### `position[<X>,<Y>]` #### `position[<X>,<Y>]`
* Define the position of the formspec * Must be used after `size` element.
* A value between 0.0 and 1.0 represents a position inside the screen * Defines the position on the game window of the formspec's `anchor` point.
* The default value is the center of the screen (0.5, 0.5) * For X and Y, 0.0 and 1.0 represent opposite edges of the game window, for example:
* [0.0, 0.0] sets the position to the top left corner of the game window.
* [1.0, 1.0] sets the position to the bottom right of the game window.
* Defaults to the center of the game window [0.5, 0.5].
#### `anchor[<X>,<Y>]` #### `anchor[<X>,<Y>]`
* Define the anchor of the formspec * Must be used after both `size` and `position` (if present) elements.
* A value between 0.0 and 1.0 represents an anchor inside the formspec * Defines the location of the anchor point within the formspec.
* The default value is the center of the formspec (0.5, 0.5) * For X and Y, 0.0 and 1.0 represent opposite edges of the formspec, for example:
* [0.0, 1.0] sets the anchor to the bottom left corner of the formspec.
* [1.0, 0.0] sets the anchor to the top right of the formspec.
* Defaults to the center of the formspec [0.5, 0.5].
* `position` and `anchor` elements need suitable values to avoid a formspec
extending off the game window due to particular game window sizes.
#### `container[<X>,<Y>]` #### `container[<X>,<Y>]`
* Start of a container block, moves all physical elements in the container by (X, Y) * Start of a container block, moves all physical elements in the container by (X, Y)
@ -2239,25 +2262,43 @@ The following functions provide escape sequences:
Spatial Vectors Spatial Vectors
--------------- ---------------
* `vector.new(a[, b, c])`: returns a vector: For the following functions, `v`, `v1`, `v2` are vectors, `p1`, `p2` are positions:
* `vector.new(a[, b, c])`:
* Returns a vector.
* A copy of `a` if `a` is a vector. * A copy of `a` if `a` is a vector.
* `{x = a, y = b, z = c}`, if all `a, b, c` are defined * `{x = a, y = b, z = c}`, if all of `a`, `b`, `c` are defined numbers.
* `vector.direction(p1, p2)`: returns a vector * `vector.direction(p1, p2)`:
* `vector.distance(p1, p2)`: returns a number * Returns a vector of length 1 with direction `p1` to `p2`.
* `vector.length(v)`: returns a number * If `p1` and `p2` are identical, returns `{x = 0, y = 0, z = 0}`.
* `vector.normalize(v)`: returns a vector * `vector.distance(p1, p2)`:
* `vector.floor(v)`: returns a vector, each dimension rounded down * Returns zero or a positive number, the distance between `p1` and `p2`.
* `vector.round(v)`: returns a vector, each dimension rounded to nearest int * `vector.length(v)`:
* `vector.apply(v, func)`: returns a vector * Returns zero or a positive number, the length of vector `v`.
* `vector.equals(v1, v2)`: returns a boolean * `vector.normalize(v)`:
* `vector.sort(v1, v2)`: returns minp, maxp vectors of the cuboid defined by v1 and v2 * Returns a vector of length 1 with direction of vector `v`.
* If `v` has zero length, returns `{x = 0, y = 0, z = 0}`.
* `vector.floor(v)`:
* Returns a vector, each dimension rounded down.
* `vector.round(v)`:
* Returns a vector, each dimension rounded to nearest integer.
* `vector.apply(v, func)`:
* Returns a vector where the function `func` has been applied to each component.
* `vector.equals(v1, v2)`:
* Returns a boolean, `true` if the vectors are identical.
* `vector.sort(v1, v2)`:
* Returns in order minp, maxp vectors of the cuboid defined by `v1`, `v2`.
For the following functions `x` can be either a vector or a number: For the following functions `x` can be either a vector or a number:
* `vector.add(v, x)`: returns a vector * `vector.add(v, x)`:
* `vector.subtract(v, x)`: returns a vector * Returns a vector.
* `vector.multiply(v, x)`: returns a scaled vector or Schur product * `vector.subtract(v, x)`:
* `vector.divide(v, x)`: returns a scaled vector or Schur quotient * Returns a vector.
* `vector.multiply(v, x)`:
* Returns a scaled vector or Schur product.
* `vector.divide(v, x)`:
* Returns a scaled vector or Schur quotient.
Helper functions Helper functions
---------------- ----------------
@ -2383,7 +2424,7 @@ Strings that need to be translated can contain several escapes, preceded by `@`.
when translation. Due to how translations are implemented, the original translation string **must** have when translation. Due to how translations are implemented, the original translation string **must** have
its arguments in increasing order, without gaps or repetitions, starting from 1. its arguments in increasing order, without gaps or repetitions, starting from 1.
* `@=` acts as a literal `=`. It is not required in strings given to `minetest.translate`, but is in translation * `@=` acts as a literal `=`. It is not required in strings given to `minetest.translate`, but is in translation
files to avoid begin confused with the `=` separating the original from the translation. files to avoid being confused with the `=` separating the original from the translation.
* `@\n` (where the `\n` is a literal newline) acts as a literal newline. As with `@=`, this escape is not required * `@\n` (where the `\n` is a literal newline) acts as a literal newline. As with `@=`, this escape is not required
in strings given to `minetest.translate`, but is in translation files. in strings given to `minetest.translate`, but is in translation files.
* `@n` acts as a literal newline as well. * `@n` acts as a literal newline as well.
@ -2466,7 +2507,7 @@ Strings that need to be translated can contain several escapes, preceded by `@`.
* `hash`: Full git version (only set if available), eg, "1.2.3-dev-01234567-dirty" * `hash`: Full git version (only set if available), eg, "1.2.3-dev-01234567-dirty"
Use this for informational purposes only. The information in the returned Use this for informational purposes only. The information in the returned
table does not represent the capabilities of the engine, nor is it table does not represent the capabilities of the engine, nor is it
reliable or verifyable. Compatible forks will have a different name and reliable or verifiable. Compatible forks will have a different name and
version entirely. To check for the presence of engine features, test version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example: whether the functions exported by the wanted features exist. For example:
`if minetest.check_for_falling then ... end`. `if minetest.check_for_falling then ... end`.
@ -2644,8 +2685,9 @@ Call these functions only at load time!
* Note that the above two callbacks will be called twice if a player is responsible - * Note that the above two callbacks will be called twice if a player is responsible -
once with the player name, and then with a nil player name. once with the player name, and then with a nil player name.
* Return true in the above callbacks to stop register_on_priv_grant or revoke being called. * Return true in the above callbacks to stop register_on_priv_grant or revoke being called.
* `minetest.register_authentication_handler(handler)` * `minetest.register_authentication_handler(authentication handler definition)`
* See `minetest.builtin_auth_handler` in `builtin.lua` for reference * Registers an auth handler that overrides the builtin one
* This function can be called by a single mod once only.
### Setting-related ### Setting-related
* `minetest.settings`: Settings object containing all of the settings from the * `minetest.settings`: Settings object containing all of the settings from the
@ -2654,37 +2696,44 @@ Call these functions only at load time!
parses it as a position (in the format `(1,2,3)`). Returns a position or nil. parses it as a position (in the format `(1,2,3)`). Returns a position or nil.
### Authentication ### Authentication
* `minetest.notify_authentication_modified(name)`
* Should be called by the authentication handler if privileges changes.
* `name`: string, if omitted, everybody is reported
* `minetest.check_password_entry(name, entry, password)`
* Returns true if the "db entry" for a player with name matches given
* password, false otherwise.
* The "db entry" is the usually player-individual value that is derived
* from the player's chosen password and stored on the server in order to allow
* authentication whenever the player desires to log in.
* Only use this function for making it possible to log in via the password from
* via protocols like IRC, other uses for inside the game are frowned upon.
* `minetest.get_password_hash(name, raw_password)`
* Convert a name-password pair to a password hash that Minetest can use.
* The returned value alone is not a good basis for password checks based
* on comparing the password hash in the database with the password hash
* from the function, with an externally provided password, as the hash
* in the db might use the new SRP verifier format.
* For this purpose, use `minetest.check_password_entry` instead.
* `minetest.string_to_privs(str)`: returns `{priv1=true,...}` * `minetest.string_to_privs(str)`: returns `{priv1=true,...}`
* `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."` * `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."`
* Convert between two privilege representations * 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.get_player_privs(name) -> {priv1=true,...}`
* `minetest.auth_reload()`
* `minetest.check_player_privs(player_or_name, ...)`: returns `bool, missing_privs` * `minetest.check_player_privs(player_or_name, ...)`: returns `bool, missing_privs`
* A quickhand for checking privileges. * A quickhand for checking privileges.
* `player_or_name`: Either a Player object or the name of a player. * `player_or_name`: Either a Player object or the name of a player.
* `...` is either a list of strings, e.g. `"priva", "privb"` or * `...` is either a list of strings, e.g. `"priva", "privb"` or
a table, e.g. `{ priva = true, privb = true }`. a table, e.g. `{ priva = true, privb = true }`.
* `minetest.get_player_ip(name)`: returns an IP address string
* `minetest.check_password_entry(name, entry, password)`
* Returns true if the "password entry" for a player with name matches given
password, false otherwise.
* The "password entry" is the password representation generated by the engine
as returned as part of a `get_auth()` call on the auth handler.
* Only use this function for making it possible to log in via password from
external protocols such as IRC, other uses are frowned upon.
* `minetest.get_password_hash(name, raw_password)`
* Convert a name-password pair to a password hash that Minetest can use.
* The returned value alone is not a good basis for password checks based
on comparing the password hash in the database with the password hash
from the function, with an externally provided password, as the hash
in the db might use the new SRP verifier format.
* For this purpose, use `minetest.check_password_entry` instead.
* `minetest.get_player_ip(name)`: returns an IP address string for the player `name`
* The player needs to be online for this to be successful.
* `minetest.get_auth_handler()`: Return the currently active auth handler
* See the `Authentication handler definition`
* Use this to e.g. get the authentication data for a player:
`local auth_data = minetest.get_auth_handler().get_auth(playername)`
* `minetest.notify_authentication_modified(name)`
* Must be called by the authentication handler for privilege changes.
* `name`: string; if omitted, all auth data should be considered modified
* `minetest.set_player_password(name, password_hash)`: Set password hash of player `name`
* `minetest.set_player_privs(name, {priv1=true,...})`: Set privileges of player `name`
* `minetest.auth_reload()`
* See `reload()` in authentication handler definition
`minetest.set_player_password`, `minetest_set_player_privs`, `minetest_get_player_privs` `minetest.set_player_password`, `minetest_set_player_privs`, `minetest_get_player_privs`
and `minetest.auth_reload` call the authentication handler. and `minetest.auth_reload` call the authentication handler.
@ -2700,6 +2749,15 @@ and `minetest.auth_reload` call the authentication handler.
* `node`: table `{name=string, param1=number, param2=number}` * `node`: table `{name=string, param1=number, param2=number}`
* If param1 or param2 is omitted, it's set to `0`. * If param1 or param2 is omitted, it's set to `0`.
* e.g. `minetest.set_node({x=0, y=10, z=0}, {name="default:wood"})` * e.g. `minetest.set_node({x=0, y=10, z=0}, {name="default:wood"})`
* `minetest.bulk_set_node({pos1, pos2, pos3, ...}, node)`
* Set node on all positions set in the first argument.
* e.g. `minetest.bulk_set_node({{x=0, y=1, z=1}, {x=1, y=2, z=2}}, {name="default:stone"})`
* For node specification or position syntax see `minetest.set_node` call
* Faster than set_node due to single call, but still considerably slower than
Voxel Manipulators (LVM) for large numbers of nodes.
Unlike LVMs, this will call node callbacks. It also allows setting nodes in spread out
positions which would cause LVMs to waste memory.
For setting a cube, this is 1.3x faster than set_node whereas LVM is 20x faster.
* `minetest.swap_node(pos, node)` * `minetest.swap_node(pos, node)`
* Set node at position, but don't remove metadata * Set node at position, but don't remove metadata
* `minetest.remove_node(pos)` * `minetest.remove_node(pos)`
@ -2767,58 +2825,93 @@ and `minetest.auth_reload` call the authentication handler.
* Return voxel manipulator object. * Return voxel manipulator object.
* Loads the manipulator from the map if positions are passed. * Loads the manipulator from the map if positions are passed.
* `minetest.set_gen_notify(flags, {deco_ids})` * `minetest.set_gen_notify(flags, {deco_ids})`
* Set the types of on-generate notifications that should be collected * Set the types of on-generate notifications that should be collected.
* `flags` is a flag field with the available flags: `dungeon`, `temple`, `cave_begin`, * `flags` is a flag field with the available flags:
`cave_end`, `large_cave_begin`, `large_cave_end`, `decoration` * dungeon
* The second parameter is a list of IDS of decorations which notification is requested for * temple
* `get_gen_notify()`: returns a flagstring and a table with the `deco_id`s * cave_begin
* cave_end
* large_cave_begin
* large_cave_end
* decoration
* The second parameter is a list of IDS of decorations which notification
is requested for.
* `get_gen_notify()`
* Returns a flagstring and a table with the `deco_id`s.
* `minetest.get_mapgen_object(objectname)` * `minetest.get_mapgen_object(objectname)`
* Return requested mapgen object if available (see "Mapgen objects") * Return requested mapgen object if available (see "Mapgen objects")
* `minetest.get_heat(pos)`
* Returns the heat at the position, or `nil` on failure.
* `minetest.get_humidity(pos)`
* Returns the humidity at the position, or `nil` on failure.
* `minetest.get_biome_data(pos)`
* Returns a table containing:
* `biome` the biome id of the biome at that position
* `heat` the heat at the position
* `humidity` the humidity at the position
* Or returns `nil` on failure.
* `minetest.get_biome_id(biome_name)` * `minetest.get_biome_id(biome_name)`
* Returns the biome id, as used in the biomemap Mapgen object, for a * Returns the biome id, as used in the biomemap Mapgen object and returned
given biome_name string. by `minetest.get_biome_data(pos)`, for a given biome_name string.
* `minetest.get_mapgen_params()` Returns mapgen parameters, a table containing * `minetest.get_mapgen_params()`
`mgname`, `seed`, `chunksize`, `water_level`, and `flags`. * Deprecated: use `minetest.get_mapgen_setting(name)` instead.
* Deprecated: use `minetest.get_mapgen_setting(name)` instead * Returns a table containing:
* `mgname`
* `seed`
* `chunksize`
* `water_level`
* `flags`
* `minetest.set_mapgen_params(MapgenParams)` * `minetest.set_mapgen_params(MapgenParams)`
* Deprecated: use `minetest.set_mapgen_setting(name, value, override)` instead * Deprecated: use `minetest.set_mapgen_setting(name, value, override)`
* Set map generation parameters instead.
* Function cannot be called after the registration period; only initialization * Set map generation parameters.
and `on_mapgen_init` * Function cannot be called after the registration period; only
* Takes a table as an argument with the fields `mgname`, `seed`, `water_level`, initialization and `on_mapgen_init`.
and `flags`. * Takes a table as an argument with the fields:
* Leave field unset to leave that parameter unchanged * `mgname`
* `flags` contains a comma-delimited string of flags to set, * `seed`
or if the prefix `"no"` is attached, clears instead. * `chunksize`
* `flags` is in the same format and has the same options as `mg_flags` in `minetest.conf` * `water_level`
* `flags`
* Leave field unset to leave that parameter unchanged.
* `flags` contains a comma-delimited string of flags to set, or if the
prefix `"no"` is attached, clears instead.
* `flags` is in the same format and has the same options as `mg_flags` in
`minetest.conf`.
* `minetest.get_mapgen_setting(name)` * `minetest.get_mapgen_setting(name)`
* Gets the *active* mapgen setting (or nil if none exists) in string format with the following * Gets the *active* mapgen setting (or nil if none exists) in string
order of precedence: format with the following order of precedence:
1) Settings loaded from map_meta.txt or overrides set during mod execution 1) Settings loaded from map_meta.txt or overrides set during mod execution
2) Settings set by mods without a metafile override 2) Settings set by mods without a metafile override
3) Settings explicitly set in the user config file, minetest.conf 3) Settings explicitly set in the user config file, minetest.conf
4) Settings set as the user config default 4) Settings set as the user config default
* `minetest.get_mapgen_setting_noiseparams(name)` * `minetest.get_mapgen_setting_noiseparams(name)`
* Same as above, but returns the value as a NoiseParams table if the setting `name` exists * Same as above, but returns the value as a NoiseParams table if the
and is a valid NoiseParams setting `name` exists and is a valid NoiseParams.
* `minetest.set_mapgen_setting(name, value, [override_meta])` * `minetest.set_mapgen_setting(name, value, [override_meta])`
* Sets a mapgen param to `value`, and will take effect if the corresponding mapgen setting * Sets a mapgen param to `value`, and will take effect if the corresponding
is not already present in map_meta.txt. mapgen setting is not already present in map_meta.txt.
* `override_meta` is an optional boolean (default: `false`). If this is set to true, * `override_meta` is an optional boolean (default: `false`). If this is set
the setting will become the active setting regardless of the map metafile contents. to true, the setting will become the active setting regardless of the map
* Note: to set the seed, use `"seed"`, not `"fixed_map_seed"` metafile contents.
* Note: to set the seed, use `"seed"`, not `"fixed_map_seed"`.
* `minetest.set_mapgen_setting_noiseparams(name, value, [override_meta])` * `minetest.set_mapgen_setting_noiseparams(name, value, [override_meta])`
* Same as above, except value is a NoiseParams table. * Same as above, except value is a NoiseParams table.
* `minetest.set_noiseparams(name, noiseparams, set_default)` * `minetest.set_noiseparams(name, noiseparams, set_default)`
* Sets the noiseparams setting of `name` to the noiseparams table specified in `noiseparams`. * Sets the noiseparams setting of `name` to the noiseparams table specified
* `set_default` is an optional boolean (default: `true`) that specifies whether the setting in `noiseparams`.
should be applied to the default config or current active config * `set_default` is an optional boolean (default: `true`) that specifies
* `minetest.get_noiseparams(name)`: returns a table of the noiseparams for name whether the setting should be applied to the default config or current
active config.
* `minetest.get_noiseparams(name)`
* Returns a table of the noiseparams for name.
* `minetest.generate_ores(vm, pos1, pos2)` * `minetest.generate_ores(vm, pos1, pos2)`
* Generate all registered ores within the VoxelManip `vm` and in the area from `pos1` to `pos2`. * Generate all registered ores within the VoxelManip `vm` and in the area
from `pos1` to `pos2`.
* `pos1` and `pos2` are optional and default to mapchunk minp and maxp. * `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
* `minetest.generate_decorations(vm, pos1, pos2)` * `minetest.generate_decorations(vm, pos1, pos2)`
* Generate all registered decorations within the VoxelManip `vm` and in the area from `pos1` to `pos2`. * Generate all registered decorations within the VoxelManip `vm` and in the
area from `pos1` to `pos2`.
* `pos1` and `pos2` are optional and default to mapchunk minp and maxp. * `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
* `minetest.clear_objects([options])` * `minetest.clear_objects([options])`
* Clear all objects in the environment * Clear all objects in the environment
@ -2828,26 +2921,28 @@ and `minetest.auth_reload` call the authentication handler.
clear objects in unloaded mapblocks only when the mapblocks are next activated. clear objects in unloaded mapblocks only when the mapblocks are next activated.
* `minetest.emerge_area(pos1, pos2, [callback], [param])` * `minetest.emerge_area(pos1, pos2, [callback], [param])`
* Queue all blocks in the area from `pos1` to `pos2`, inclusive, to be asynchronously * Queue all blocks in the area from `pos1` to `pos2`, inclusive, to be asynchronously
* fetched from memory, loaded from disk, or if inexistent, generates them. fetched from memory, loaded from disk, or if inexistent, generates them.
* If `callback` is a valid Lua function, this will be called for each block emerged. * If `callback` is a valid Lua function, this will be called for each block emerged.
* The function signature of callback is: * The function signature of callback is:
* `function EmergeAreaCallback(blockpos, action, calls_remaining, param)` * `function EmergeAreaCallback(blockpos, action, calls_remaining, param)`
* - `blockpos` is the *block* coordinates of the block that had been emerged * `blockpos` is the *block* coordinates of the block that had been emerged
* - `action` could be one of the following constant values: * `action` could be one of the following constant values:
* `minetest.EMERGE_CANCELLED`, `minetest.EMERGE_ERRORED`, `minetest.EMERGE_FROM_MEMORY`, * `minetest.EMERGE_CANCELLED`
* `minetest.EMERGE_FROM_DISK`, `minetest.EMERGE_GENERATED` * `minetest.EMERGE_ERRORED`
* - `calls_remaining` is the number of callbacks to be expected after this one * `minetest.EMERGE_FROM_MEMORY`
* - `param` is the user-defined parameter passed to emerge_area (or nil if the * `minetest.EMERGE_FROM_DISK`
* parameter was absent) * `minetest.EMERGE_GENERATED`
* `calls_remaining` is the number of callbacks to be expected after this one
* `param` is the user-defined parameter passed to emerge_area (or nil if the
parameter was absent)
* `minetest.delete_area(pos1, pos2)` * `minetest.delete_area(pos1, pos2)`
* delete all mapblocks in the area from pos1 to pos2, inclusive * delete all mapblocks in the area from pos1 to pos2, inclusive
* `minetest.line_of_sight(pos1, pos2, stepsize)`: returns `boolean, pos` * `minetest.line_of_sight(pos1, pos2)`: returns `boolean, pos`
* Check if there is a direct line of sight between `pos1` and `pos2` * Checks if there is anything other than air between pos1 and pos2.
* Returns false if something is blocking the sight.
* Returns the position of the blocking node when `false` * Returns the position of the blocking node when `false`
* `pos1`: First position * `pos1`: First position
* `pos2`: Second position * `pos2`: Second position
* `stepsize`: smaller gives more accurate results but requires more computing
time. Default is `1`.
* `minetest.raycast(pos1, pos2, objects, liquids)`: returns `Raycast` * `minetest.raycast(pos1, pos2, objects, liquids)`: returns `Raycast`
* Creates a `Raycast` object. * Creates a `Raycast` object.
* `pos1`: start of the ray * `pos1`: start of the ray
@ -3108,17 +3203,20 @@ These functions return the leftover itemstack.
* Optional: Variable number of arguments that are passed to `func` * Optional: Variable number of arguments that are passed to `func`
### Server ### Server
* `minetest.request_shutdown([message],[reconnect],[delay])`: request for server shutdown. Will display `message` to clients, * `minetest.request_shutdown([message],[reconnect],[delay])`: request for server shutdown. Will display `message` to clients.
`reconnect` == true displays a reconnect button, * `reconnect` == true displays a reconnect button
`delay` adds an optional delay (in seconds) before shutdown * `delay` adds an optional delay (in seconds) before shutdown.
negative delay cancels the current active shutdown Negative delay cancels the current active shutdown.
zero delay triggers an immediate shutdown. Zero delay triggers an immediate shutdown.
* `minetest.cancel_shutdown_requests()`: cancel current delayed shutdown * `minetest.cancel_shutdown_requests()`: cancel current delayed shutdown
* `minetest.get_server_status()`: returns server status string * `minetest.get_server_status()`: returns server status string
* `minetest.get_server_uptime()`: returns the server uptime in seconds * `minetest.get_server_uptime()`: returns the server uptime in seconds
* `minetest.remove_player(name)`: remove player from database (if he is not connected). * `minetest.remove_player(name)`: remove player from database (if he is not connected).
* Does not remove player authentication data, minetest.player_exists will continue to return true. * As auth data is not removed, minetest.player_exists will continue to return true.
Call the below method as well if you want to remove auth data too.
* Returns a code (0: successful, 1: no such player, 2: player is connected) * Returns a code (0: successful, 1: no such player, 2: player is connected)
* `minetest.remove_player_auth(name)`: remove player authentication data
* Returns boolean indicating success (false if player nonexistant)
### Bans ### Bans
* `minetest.get_ban_list()`: returns the ban list (same as `minetest.get_ban_description("")`) * `minetest.get_ban_list()`: returns the ban list (same as `minetest.get_ban_description("")`)
@ -3145,8 +3243,7 @@ These functions return the leftover itemstack.
* `minetest.delete_particlespawner(id, player)` * `minetest.delete_particlespawner(id, player)`
* Delete `ParticleSpawner` with `id` (return value from `minetest.add_particlespawner`) * Delete `ParticleSpawner` with `id` (return value from `minetest.add_particlespawner`)
* If playername is specified, only deletes on the player's client, * If playername is specified, only deletes on the player's client, otherwise on all clients
* otherwise on all clients
### Schematics ### Schematics
* `minetest.create_schematic(p1, p2, probability_list, filename, slice_prob_list)` * `minetest.create_schematic(p1, p2, probability_list, filename, slice_prob_list)`
@ -3181,9 +3278,13 @@ These functions return the leftover itemstack.
* `force_placement` is a boolean indicating whether nodes other than `air` and * `force_placement` is a boolean indicating whether nodes other than `air` and
`ignore` are replaced by the schematic `ignore` are replaced by the schematic
* Returns nil if the schematic could not be loaded. * Returns nil if the schematic could not be loaded.
* **Warning**: Once you have loaded a schematic from a file, it will be cached. Future calls
will always use the cached version and the replacement list defined for it,
regardless of whether the file or the replacement list parameter have changed.
The only way to load the file anew is to restart the server.
* `minetest.place_schematic_on_vmanip(vmanip, pos, schematic, rotation, replacement, force_placement)`: * `minetest.place_schematic_on_vmanip(vmanip, pos, schematic, rotation, replacement, force_placement)`:
* This function is analagous to minetest.place_schematic, but places a schematic onto the * This function is analogous to minetest.place_schematic, but places a schematic onto the
specified VoxelManip object `vmanip` instead of the whole map. specified VoxelManip object `vmanip` instead of the whole map.
* Returns false if any part of the schematic was cut-off due to the VoxelManip not * Returns false if any part of the schematic was cut-off due to the VoxelManip not
containing the full area required, and true if the whole schematic was able to fit. containing the full area required, and true if the whole schematic was able to fit.
@ -3228,6 +3329,7 @@ These functions return the leftover itemstack.
### Misc. ### Misc.
* `minetest.get_connected_players()`: returns list of `ObjectRefs` * `minetest.get_connected_players()`: returns list of `ObjectRefs`
* `minetest.is_player(o)`: boolean, whether `o` is a player
* `minetest.player_exists(name)`: boolean, whether player exists (regardless of online status) * `minetest.player_exists(name)`: boolean, whether player exists (regardless of online status)
* `minetest.hud_replace_builtin(name, hud_definition)` * `minetest.hud_replace_builtin(name, hud_definition)`
* Replaces definition of a builtin hud element * Replaces definition of a builtin hud element
@ -3302,7 +3404,7 @@ These functions return the leftover itemstack.
* Decodes a string encoded in base64. * Decodes a string encoded in base64.
* `minetest.is_protected(pos, name)`: returns boolean * `minetest.is_protected(pos, name)`: returns boolean
* Returns true, if player `name` shouldn't be abled to dig at `pos` or do other * Returns true, if player `name` shouldn't be abled to dig at `pos` or do other
actions, defineable by mods, due to some mod-defined ownership-like concept. actions, definable by mods, due to some mod-defined ownership-like concept.
Returns false or nil, if the player is allowed to do such actions. Returns false or nil, if the player is allowed to do such actions.
* `name` will be "" for non-players or unknown players. * `name` will be "" for non-players or unknown players.
* This function should be overridden by protection mods and should be used to * This function should be overridden by protection mods and should be used to
@ -3321,6 +3423,16 @@ These functions return the leftover itemstack.
* `minetest.record_protection_violation(pos, name)` * `minetest.record_protection_violation(pos, name)`
* This function calls functions registered with * This function calls functions registered with
`minetest.register_on_protection_violation`. `minetest.register_on_protection_violation`.
* `minetest.intersects_protection(minp, maxp, player_name, interval)
* Returns a boolean, returns true if the volume defined by `minp` and `maxp`
intersects a protected area not owned by `player_name`.
* Applies `is_protected()` to a 3D lattice of points in the defined volume.
The points are spaced evenly throughout the volume and have a spacing
similar to, but no larger than, `interval`.
* All corners and edges of the defined volume are checked.
* `interval` defaults to 4.
* `interval` should be carefully chosen and maximised to avoid an excessive
number of points being checked.
* `minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags)` * `minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags)`
* Attempt to predict the desired orientation of the facedir-capable node * Attempt to predict the desired orientation of the facedir-capable node
defined by `itemstack`, and place it accordingly (on-wall, on the floor, or defined by `itemstack`, and place it accordingly (on-wall, on the floor, or
@ -3594,7 +3706,7 @@ This is basically a reference to a C++ `ServerActiveObject`
* See Object Properties for more information * See Object Properties for more information
* `set_attribute(attribute, value)`: * `set_attribute(attribute, value)`:
* Sets an extra attribute with value on player. * Sets an extra attribute with value on player.
* `value` must be a string. * `value` must be a string, or a number which will be converted to a string.
* If `value` is `nil`, remove attribute from player. * If `value` is `nil`, remove attribute from player.
* `get_attribute(attribute)`: * `get_attribute(attribute)`:
* Returns value (a string) for extra attribute. * Returns value (a string) for extra attribute.
@ -3672,8 +3784,7 @@ This is basically a reference to a C++ `ServerActiveObject`
* `0`...`1`: Overrides day-night ratio, controlling sunlight to a specific amount * `0`...`1`: Overrides day-night ratio, controlling sunlight to a specific amount
* `nil`: Disables override, defaulting to sunlight based on day-night cycle * `nil`: Disables override, defaulting to sunlight based on day-night cycle
* `get_day_night_ratio()`: returns the ratio or nil if it isn't overridden * `get_day_night_ratio()`: returns the ratio or nil if it isn't overridden
* `set_local_animation(stand/idle, walk, dig, walk+dig, frame_speed=frame_speed)` * `set_local_animation(stand/idle, walk, dig, walk+dig, frame_speed=frame_speed)`:
set animation for player model in third person view set animation for player model in third person view
set_local_animation({x=0, y=79}, -- < stand/idle animation key frames set_local_animation({x=0, y=79}, -- < stand/idle animation key frames
@ -3846,8 +3957,8 @@ Alternatively with `minetest.get_perlin(seeddiff, octaves, persistence, scale)`
or `minetest.get_perlin(noiseparams)`. or `minetest.get_perlin(noiseparams)`.
#### Methods #### Methods
* `get2d(pos)`: returns 2D noise value at `pos={x=,y=}` * `get_2d(pos)`: returns 2D noise value at `pos={x=,y=}`
* `get3d(pos)`: returns 3D noise value at `pos={x=,y=,z=}` * `get_3d(pos)`: returns 3D noise value at `pos={x=,y=,z=}`
### `PerlinNoiseMap` ### `PerlinNoiseMap`
A fast, bulk perlin noise generator. A fast, bulk perlin noise generator.
@ -3864,25 +3975,25 @@ nil, this table will be used to store the result instead of creating a new table
#### Methods #### Methods
* `get2dMap(pos)`: returns a `<size.x>` times `<size.y>` 2D array of 2D noise * `get_2d_map(pos)`: returns a `<size.x>` times `<size.y>` 2D array of 2D noise
with values starting at `pos={x=,y=}` with values starting at `pos={x=,y=}`
* `get3dMap(pos)`: returns a `<size.x>` times `<size.y>` times `<size.z>` 3D array * `get_3d_map(pos)`: returns a `<size.x>` times `<size.y>` times `<size.z>` 3D array
of 3D noise with values starting at `pos={x=,y=,z=}` of 3D noise with values starting at `pos={x=,y=,z=}`
* `get2dMap_flat(pos, buffer)`: returns a flat `<size.x * size.y>` element array of 2D noise * `get_2d_map_flat(pos, buffer)`: returns a flat `<size.x * size.y>` element array of 2D noise
with values starting at `pos={x=,y=}` with values starting at `pos={x=,y=}`
* `get3dMap_flat(pos, buffer)`: Same as `get2dMap_flat`, but 3D noise * `get_3d_map_flat(pos, buffer)`: Same as `get2dMap_flat`, but 3D noise
* `calc2dMap(pos)`: Calculates the 2d noise map starting at `pos`. The result is stored internally. * `calc_2d_map(pos)`: Calculates the 2d noise map starting at `pos`. The result is stored internally.
* `calc3dMap(pos)`: Calculates the 3d noise map starting at `pos`. The result is stored internally. * `calc_3d_map(pos)`: Calculates the 3d noise map starting at `pos`. The result is stored internally.
* `getMapSlice(slice_offset, slice_size, buffer)`: In the form of an array, returns a slice of the * `get_map_slice(slice_offset, slice_size, buffer)`: In the form of an array, returns a slice of the
most recently computed noise results. The result slice begins at coordinates `slice_offset` and most recently computed noise results. The result slice begins at coordinates `slice_offset` and
takes a chunk of `slice_size`. takes a chunk of `slice_size`.
E.g. to grab a 2-slice high horizontal 2d plane of noise starting at buffer offset y = 20: E.g. to grab a 2-slice high horizontal 2d plane of noise starting at buffer offset y = 20:
`noisevals = noise:getMapSlice({y=20}, {y=2})` `noisevals = noise:get_map_slice({y=20}, {y=2})`
It is important to note that `slice_offset` offset coordinates begin at 1, and are relative to It is important to note that `slice_offset` offset coordinates begin at 1, and are relative to
the starting position of the most recently calculated noise. the starting position of the most recently calculated noise.
To grab a single vertical column of noise starting at map coordinates x = 1023, y=1000, z = 1000: To grab a single vertical column of noise starting at map coordinates x = 1023, y=1000, z = 1000:
`noise:calc3dMap({x=1000, y=1000, z=1000})` `noise:calc_3d_map({x=1000, y=1000, z=1000})`
`noisevals = noise:getMapSlice({x=24, z=1}, {x=1, z=1})` `noisevals = noise:get_map_slice({x=24, z=1}, {x=1, z=1})`
### `VoxelManip` ### `VoxelManip`
@ -4112,7 +4223,9 @@ It can be created via `Settings(filename)`.
#### Methods #### Methods
* `get(key)`: returns a value * `get(key)`: returns a value
* `get_bool(key)`: returns a boolean * `get_bool(key, [default])`: returns a boolean
* `default` is the value returned if `key` is not found.
* Returns `nil` if `key` is not found and `default` not specified.
* `get_np_group(key)`: returns a NoiseParams table * `get_np_group(key)`: returns a NoiseParams table
* `set(key, value)` * `set(key, value)`
* Setting names can't contain whitespace or any of `="{}#`. * Setting names can't contain whitespace or any of `="{}#`.
@ -4222,7 +4335,7 @@ Registered entities
* `tool_capabilities`: capability table of used tool (can be `nil`) * `tool_capabilities`: capability table of used tool (can be `nil`)
* `dir`: unit vector of direction of punch. Always defined. Points from * `dir`: unit vector of direction of punch. Always defined. Points from
the puncher to the punched. the puncher to the punched.
`on_death(self, killer)` * `on_death(self, killer)`
* Called when the object dies. * Called when the object dies.
* `killer`: an `ObjectRef` (can be `nil`) * `killer`: an `ObjectRef` (can be `nil`)
* `on_rightclick(self, clicker)` * `on_rightclick(self, clicker)`
@ -4313,8 +4426,13 @@ Definition tables
-- ^ For players: Defaults to `minetest.PLAYER_MAX_HP_DEFAULT` -- ^ For players: Defaults to `minetest.PLAYER_MAX_HP_DEFAULT`
breath_max = 0, breath_max = 0,
-- ^ For players only. Defaults to `minetest.PLAYER_MAX_BREATH_DEFAULT` -- ^ For players only. Defaults to `minetest.PLAYER_MAX_BREATH_DEFAULT`
can_zoom = true, zoom_fov = 0.0,
-- ^ For players only. Enables the zoom feature. Defaults to true -- ^ For players only. Zoom FOV in degrees.
-- Note that zoom loads and/or generates world beyond the server's maximum
-- send and generate distances, so acts like a telescope.
-- Smaller zoomFOV values increase the distance loaded and/or generated.
-- Defaults to 15 in creative mode, 0 in survival mode.
-- zoom_fov = 0 disables zooming for the player.
eye_height = 1.625, eye_height = 1.625,
-- ^ For players only. Camera height above feet position in nodes. Defaults to 1.625 -- ^ For players only. Camera height above feet position in nodes. Defaults to 1.625
physical = true, physical = true,
@ -4322,18 +4440,48 @@ Definition tables
weight = 5, weight = 5,
collisionbox = {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}, collisionbox = {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5},
selectionbox = {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}, selectionbox = {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5},
-- ^ Default, uses collision box dimensions when not set -- ^ Default, uses collision box dimensions when not set.
-- ^ For both boxes: {xmin, ymin, zmin, xmax, ymax, zmax} in nodes from
-- object position.
pointable = true, -- overrides selection box when false pointable = true, -- overrides selection box when false
visual = "cube" / "sprite" / "upright_sprite" / "mesh" / "wielditem", visual = "cube" / "sprite" / "upright_sprite" / "mesh" / "wielditem",
-- ^ "cube" is a node-sized cube.
-- ^ "sprite" is a flat texture always facing the player.
-- ^ "upright_sprite" is a vertical flat texture.
-- ^ "mesh" uses the defined mesh model.
-- ^ "wielditem" is used for dropped items (see builtin/game/item_entity.lua).
-- For this use 'textures = {itemname}'.
-- If the item has a 'wield_image' the object will be an extrusion of that,
-- otherwise:
-- If 'itemname' is a cubic node or nodebox the object will appear identical
-- to 'itemname'.
-- If 'itemname' is a plantlike node the object will be an extrusion of its
-- texture.
-- Otherwise for non-node items, the object will be an extrusion of
-- 'inventory_image'.
visual_size = {x = 1, y = 1}, visual_size = {x = 1, y = 1},
-- ^ `x` multiplies horizontal (X and Z) visual size.
-- ^ `y` multiplies vertical (Y) visual size.
mesh = "model", mesh = "model",
textures = {}, -- number of required textures depends on visual textures = {}, -- number of required textures depends on visual
-- ^ "cube" uses 6 textures in the way a node does.
-- ^ "sprite" uses 1 texture.
-- ^ "upright_sprite" uses 2 textures: {front, back}.
-- ^ "wielditem" expects 'textures = {itemname}' (see 'visual' above).
colors = {}, -- number of required colors depends on visual colors = {}, -- number of required colors depends on visual
spritediv = {x = 1, y = 1}, spritediv = {x = 1, y = 1},
-- ^ Used with spritesheet textures for animation and/or frame selection according
-- to position relative to player.
-- ^ Defines the number of columns and rows in the spritesheet: {columns, rows}.
initial_sprite_basepos = {x = 0, y = 0}, initial_sprite_basepos = {x = 0, y = 0},
-- ^ Used with spritesheet textures.
-- ^ Defines the {column, row} position of the initially used frame in the
-- spritesheet.
is_visible = true, is_visible = true,
makes_footstep_sound = false, makes_footstep_sound = false,
automatic_rotate = false, automatic_rotate = 0,
-- ^ Set constant rotation in radians per second, positive or negative.
-- ^ Set to 0 to disable constant rotation.
stepheight = 0, stepheight = 0,
automatic_face_movement_dir = 0.0, automatic_face_movement_dir = 0.0,
-- ^ Automatically set yaw to movement direction, offset in degrees, -- ^ Automatically set yaw to movement direction, offset in degrees,
@ -4380,19 +4528,30 @@ Definition tables
{ {
label = "Lava cooling", label = "Lava cooling",
-- ^ Descriptive label for profiling purposes (optional). ^ Descriptive label for profiling purposes (optional).
-- Definitions with identical labels will be listed as one. Definitions with identical labels will be listed as one.
-- In the following two fields, also group:groupname will work.
nodenames = {"default:lava_source"}, nodenames = {"default:lava_source"},
neighbors = {"default:water_source", "default:water_flowing"}, -- Any of these --[[ ^ Apply `action` function to these nodes.
^ If left out or empty, any neighbor will do ]] ^ `group:groupname` can also be used here.
interval = 1.0, -- Operation interval in seconds neighbors = {"default:water_source", "default:water_flowing"},
chance = 1, -- Chance of trigger per-node per-interval is 1.0 / this ^ Only apply `action` to nodes that have one of, or any
catch_up = true, -- If true, catch-up behaviour is enabled --[[ combination of, these neighbors.
^ The chance value is temporarily reduced when returning to ^ If left out or empty, any neighbor will do.
an area to simulate time lost by the area being unattended. ^ `group:groupname` can also be used here.
^ Note chance value can often be reduced to 1 ]] interval = 1.0,
action = func(pos, node, active_object_count, active_object_count_wider), ^ Operation interval in seconds.
chance = 1,
^ Chance of triggering `action` per-node per-interval is 1.0 / this value.
catch_up = true,
^ If true, catch-up behaviour is enabled: The `chance` value is temporarily
reduced when returning to an area to simulate time lost by the area being
unattended. Note that the `chance` value can often be reduced to 1.
action = function(pos, node, active_object_count, active_object_count_wider),
^ Function triggered for each qualifying node.
^ `active_object_count` is number of active objects in the node's mapblock.
^ `active_object_count_wider` is number of active objects in the node's
mapblock plus all 26 neighboring mapblocks. If any neighboring mapblocks
are unloaded an estmate is calculated for them based on loaded mapblocks.
} }
### LBM (LoadingBlockModifier) definition (`register_lbm`) ### LBM (LoadingBlockModifier) definition (`register_lbm`)
@ -4696,6 +4855,13 @@ Definition tables
^ interval. Default: nil. ^ interval. Default: nil.
^ Warning: making a liquid node 'floodable' does not work and may cause problems. ]] ^ Warning: making a liquid node 'floodable' does not work and may cause problems. ]]
preserve_metadata = func(pos, oldnode, oldmeta, drops) --[[
^ Called when oldnode is about be converted to an item, but before the
node is deleted from the world or the drops are added. This is generally
the result of either the node being dug or an attached node becoming detached.
^ drops is a table of ItemStacks, so any metadata to be preserved can be
added directly to one or more of the dropped items. See "ItemStackMetaRef".
^ default: nil ]]
after_place_node = func(pos, placer, itemstack, pointed_thing) --[[ after_place_node = func(pos, placer, itemstack, pointed_thing) --[[
^ Called after constructing node when node was placed using ^ Called after constructing node when node was placed using
minetest.item_place_node / minetest.place_node minetest.item_place_node / minetest.place_node
@ -4928,18 +5094,18 @@ Definition tables
y_min = 1, y_min = 1,
y_max = 31000, y_max = 31000,
-- ^ Lower and upper limits for biome. -- ^ Lower and upper limits for biome.
vertical_blend = 8,
-- ^ Vertical distance in nodes above 'y_max' over which the biome will
-- ^ blend with the biome above.
-- ^ Set to 0 for no vertical blend. Defaults to 0.
heat_point = 0, heat_point = 0,
humidity_point = 50, humidity_point = 50,
-- ^ Characteristic average temperature and humidity for the biome. -- ^ Characteristic temperature and humidity for the biome.
-- ^ These values create 'biome points' on a voronoi diagram that has heat -- ^ These values create 'biome points' on a voronoi diagram with heat and
-- ^ and humidity as axes. The resulting voronoi cells determine which -- ^ humidity as axes. The resulting voronoi cells determine the
-- ^ heat/humidity points belong to which biome, and therefore determine -- ^ distribution of the biomes.
-- ^ the area and location of each biome in the world.
-- ^ The biome points need to be carefully and evenly spaced on the voronoi
-- ^ diagram to result in roughly equal size biomes.
-- ^ Heat and humidity have average values of 50, vary mostly between -- ^ Heat and humidity have average values of 50, vary mostly between
-- ^ 0 and 100 but also often exceed these values. -- ^ 0 and 100 but can exceed these values.
-- ^ Heat is not in degrees Celsius, both values are abstract.
} }
### Decoration definition (`register_decoration`) ### Decoration definition (`register_decoration`)
@ -5061,6 +5227,17 @@ Definition tables
-- Returns boolean success and text output. -- Returns boolean success and text output.
} }
Note that in params, use of symbols is as follows:
* `<>` signifies a placeholder to be replaced when the command is used. For example,
when a player name is needed: `<name>`
* `[]` signifies param is optional and not required when the command is used. For
example, if you require param1 but param2 is optional: `<param1> [<param2>]`
* `|` signifies exclusive or. The command requires one param from the options
provided. For example: `<param1> | <param2>`
* `()` signifies grouping. For example, when param1 and param2 are both required,
or only param3 is required: `(<param1> <param2>) | <param3>`
### Detached inventory callbacks ### Detached inventory callbacks
{ {
@ -5169,6 +5346,10 @@ Definition tables
-- ^ Uses texture (string) -- ^ Uses texture (string)
playername = "singleplayer" playername = "singleplayer"
-- ^ Playername is optional, if specified spawns particle only on the player's client -- ^ Playername is optional, if specified spawns particle only on the player's client
animation = {Tile Animation definition},
-- ^ optional, specifies how to animate the particle texture
glow = 0
-- ^ optional, specify particle self-luminescence in darkness
} }
### `HTTPRequest` definition (`HTTPApiTable.fetch_async`, `HTTPApiTable.fetch_async`) ### `HTTPRequest` definition (`HTTPApiTable.fetch_async`, `HTTPApiTable.fetch_async`)
@ -5204,3 +5385,30 @@ Definition tables
-- ^ HTTP status code -- ^ HTTP status code
data = "response" data = "response"
} }
### Authentication handler definition
{
get_auth = func(name),
-- ^ Get authentication data for existing player `name` (`nil` if player doesn't exist)
-- ^ returns following structure `{password=<string>, privileges=<table>, last_login=<number or nil>}`
create_auth = func(name, password),
-- ^ Create new auth data for player `name`
-- ^ Note that `password` is not plain-text but an arbitrary representation decided by the engine
delete_auth = func(name),
-- ^ Delete auth data of player `name`, returns boolean indicating success (false if player nonexistant)
set_password = func(name, password),
-- ^ Set password of player `name` to `password`
Auth data should be created if not present
set_privileges = func(name, privileges),
-- ^ Set privileges of player `name`
-- ^ `privileges` is in table form, auth data should be created if not present
reload = func(),
-- ^ Reload authentication data from the storage location
-- ^ Returns boolean indicating success
record_login = func(name),
-- ^ Called when player joins, used for keeping track of last_login
iterate = func(),
-- ^ Returns an iterator (use with `for` loops) for all player names currently in the auth database
}

View File

@ -682,6 +682,74 @@ minetest.register_chatcommand("test1", {
end, end,
}) })
minetest.register_chatcommand("test_bulk_set_node", {
params = "",
description = "Test 2: bulk set a node",
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then
return
end
local pos_list = {}
local ppos = player:get_pos()
local i = 1
for x=2,10 do
for y=2,10 do
for z=2,10 do
pos_list[i] = {x=ppos.x + x,y = ppos.y + y,z = ppos.z + z}
i = i + 1
end
end
end
minetest.bulk_set_node(pos_list, {name = "default:stone"})
minetest.chat_send_player(name, "Done.");
end,
})
minetest.register_chatcommand("bench_bulk_set_node", {
params = "",
description = "Test 3: bulk set a node (bench)",
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then
return
end
local pos_list = {}
local ppos = player:get_pos()
local i = 1
for x=2,100 do
for y=2,100 do
for z=2,100 do
pos_list[i] = {x=ppos.x + x,y = ppos.y + y,z = ppos.z + z}
i = i + 1
end
end
end
minetest.chat_send_player(name, "Benching bulk set node. Warming up...");
-- warm up with default:stone to prevent having different callbacks
-- due to different node topology
minetest.bulk_set_node(pos_list, {name = "default:stone"})
minetest.chat_send_player(name, "Warming up finished, now benching...");
local start_time = os.clock()
for i=1,#pos_list do
minetest.set_node(pos_list[i], {name = "default:stone"})
end
local middle_time = os.clock()
minetest.bulk_set_node(pos_list, {name = "default:stone"})
local end_time = os.clock()
minetest.chat_send_player(name,
string.format("Bench results: set_node loop[%.2fms], bulk_set_node[%.2fms]",
(middle_time - start_time) * 1000,
(end_time - middle_time) * 1000
)
);
end,
})
minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
experimental.print_to_everything("Inventory fields 1: player="..player:get_player_name()..", fields="..dump(fields)) experimental.print_to_everything("Inventory fields 1: player="..player:get_player_name()..", fields="..dump(fields))
end) end)

View File

@ -701,7 +701,7 @@
# In-game chat console height, between 0.1 (10%) and 1.0 (100%). # In-game chat console height, between 0.1 (10%) and 1.0 (100%).
# type: float min: 0.1 max: 1 # type: float min: 0.1 max: 1
# console_height = 1.0 # console_height = 0.6
# In-game chat console background color (R,G,B). # In-game chat console background color (R,G,B).
# type: string # type: string
@ -994,6 +994,10 @@
# type: int # type: int
# max_out_chat_queue_size = 20 # max_out_chat_queue_size = 20
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is open.
# type: bool
# pause_on_lost_focus = false
## Advanced ## Advanced
# Timeout for client to remove unused map data from memory. # Timeout for client to remove unused map data from memory.
@ -1346,13 +1350,13 @@
# Restricts the access of certain client-side functions on servers # Restricts the access of certain client-side functions on servers
# Combine these byteflags below to restrict more client-side features: # Combine these byteflags below to restrict more client-side features:
# LOOKUP_NODES_LIMIT: 1 (limits get_node call client-side to csm_flavour_noderange_limit) # LOAD_CLIENT_MODS: 1 (disable client mods loading)
# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) # CHAT_MESSAGES: 2 (disable send_chat_message call client-side)
# READ_ITEMDEFS: 4 (disable get_item_def call client-side) # READ_ITEMDEFS: 4 (disable get_item_def call client-side)
# READ_NODEDEFS: 8 (disable get_node_def call client-side) # READ_NODEDEFS: 8 (disable get_node_def call client-side)
# type: int # type: int
# type: int # LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to csm_flavour_noderange_limit)
# csm_flavour_limits = 3 # csm_flavour_limits = 18
# If the CSM flavour for node range is enabled, get_node is limited to # If the CSM flavour for node range is enabled, get_node is limited to
# this many nodes from the player. # this many nodes from the player.

View File

@ -448,6 +448,7 @@ set(common_SRCS
version.cpp version.cpp
voxel.cpp voxel.cpp
voxelalgorithms.cpp voxelalgorithms.cpp
hud.cpp
${common_network_SRCS} ${common_network_SRCS}
${JTHREAD_SRCS} ${JTHREAD_SRCS}
${common_SCRIPT_SRCS} ${common_SCRIPT_SRCS}

View File

@ -1,6 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic BLOT <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -125,11 +126,11 @@ void BanManager::remove(const std::string &ip_or_name)
for (StringMap::iterator it = m_ips.begin(); it != m_ips.end();) { for (StringMap::iterator it = m_ips.begin(); it != m_ips.end();) {
if ((it->first == ip_or_name) || (it->second == ip_or_name)) { if ((it->first == ip_or_name) || (it->second == ip_or_name)) {
m_ips.erase(it++); m_ips.erase(it++);
m_modified = true;
} else { } else {
++it; ++it;
} }
} }
m_modified = true;
} }

View File

@ -72,7 +72,6 @@ Camera::Camera(MapDrawControl &draw_control, Client *client):
m_cache_fall_bobbing_amount = g_settings->getFloat("fall_bobbing_amount"); m_cache_fall_bobbing_amount = g_settings->getFloat("fall_bobbing_amount");
m_cache_view_bobbing_amount = g_settings->getFloat("view_bobbing_amount"); m_cache_view_bobbing_amount = g_settings->getFloat("view_bobbing_amount");
m_cache_fov = g_settings->getFloat("fov"); m_cache_fov = g_settings->getFloat("fov");
m_cache_zoom_fov = g_settings->getFloat("zoom_fov");
m_arm_inertia = g_settings->getBool("arm_inertia"); m_arm_inertia = g_settings->getBool("arm_inertia");
m_nametags.clear(); m_nametags.clear();
} }
@ -453,12 +452,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
// Get FOV // Get FOV
f32 fov_degrees; f32 fov_degrees;
if (player->getPlayerControl().zoom && player->getCanZoom()) { // Disable zoom with zoom FOV = 0
fov_degrees = m_cache_zoom_fov; if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) {
fov_degrees = player->getZoomFOV();
} else { } else {
fov_degrees = m_cache_fov; fov_degrees = m_cache_fov;
} }
fov_degrees = rangelim(fov_degrees, 1.0, 160.0); fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f);
// FOV and aspect ratio // FOV and aspect ratio
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();

View File

@ -225,7 +225,6 @@ private:
f32 m_cache_fall_bobbing_amount; f32 m_cache_fall_bobbing_amount;
f32 m_cache_view_bobbing_amount; f32 m_cache_view_bobbing_amount;
f32 m_cache_fov; f32 m_cache_fov;
f32 m_cache_zoom_fov;
bool m_arm_inertia; bool m_arm_inertia;
std::list<Nametag *> m_nametags; std::list<Nametag *> m_nametags;

View File

@ -18,11 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "chat.h" #include "chat.h"
#include "debug.h"
#include "config.h" #include <algorithm>
#include "util/strfnd.h"
#include <cctype> #include <cctype>
#include <sstream> #include <sstream>
#include "config.h"
#include "debug.h"
#include "util/strfnd.h"
#include "util/string.h" #include "util/string.h"
#include "util/numeric.h" #include "util/numeric.h"
@ -403,8 +406,14 @@ void ChatPrompt::input(const std::wstring &str)
void ChatPrompt::addToHistory(std::wstring line) void ChatPrompt::addToHistory(std::wstring line)
{ {
if (!line.empty()) if (!line.empty() &&
(m_history.size() == 0 || m_history.back() != line)) {
// Remove all duplicates
m_history.erase(std::remove(m_history.begin(), m_history.end(),
line), m_history.end());
// Push unique line
m_history.push_back(line); m_history.push_back(line);
}
if (m_history.size() > m_history_limit) if (m_history.size() > m_history_limit)
m_history.erase(m_history.begin()); m_history.erase(m_history.begin());
m_history_index = m_history.size(); m_history_index = m_history.size();
@ -699,11 +708,10 @@ ChatBuffer& ChatBackend::getRecentBuffer()
return m_recent_buffer; return m_recent_buffer;
} }
EnrichedString ChatBackend::getRecentChat() EnrichedString ChatBackend::getRecentChat() const
{ {
EnrichedString result; EnrichedString result;
for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i) for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i) {
{
const ChatLine& line = m_recent_buffer.getLine(i); const ChatLine& line = m_recent_buffer.getLine(i);
if (i != 0) if (i != 0)
result += L"\n"; result += L"\n";

View File

@ -265,7 +265,7 @@ public:
// Get the recent messages buffer // Get the recent messages buffer
ChatBuffer& getRecentBuffer(); ChatBuffer& getRecentBuffer();
// Concatenate all recent messages // Concatenate all recent messages
EnrichedString getRecentChat(); EnrichedString getRecentChat() const;
// Get the console prompt // Get the console prompt
ChatPrompt& getPrompt(); ChatPrompt& getPrompt();

View File

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "network/networkpacket.h" #include "network/networkpacket.h"
#include "threading/mutex_auto_lock.h" #include "threading/mutex_auto_lock.h"
#include "client/clientevent.h" #include "client/clientevent.h"
#include "client/gameui.h"
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "client/tile.h" #include "client/tile.h"
#include "util/auth.h" #include "util/auth.h"
@ -74,7 +75,7 @@ Client::Client(
ISoundManager *sound, ISoundManager *sound,
MtEventManager *event, MtEventManager *event,
bool ipv6, bool ipv6,
GameUIFlags *game_ui_flags GameUI *game_ui
): ):
m_tsrc(tsrc), m_tsrc(tsrc),
m_shsrc(shsrc), m_shsrc(shsrc),
@ -96,7 +97,7 @@ Client::Client(
m_chosen_auth_mech(AUTH_MECHANISM_NONE), m_chosen_auth_mech(AUTH_MECHANISM_NONE),
m_media_downloader(new ClientMediaDownloader()), m_media_downloader(new ClientMediaDownloader()),
m_state(LC_Created), m_state(LC_Created),
m_game_ui_flags(game_ui_flags), m_game_ui(game_ui),
m_modchannel_mgr(new ModChannelMgr()) m_modchannel_mgr(new ModChannelMgr())
{ {
// Add local player // Add local player
@ -113,18 +114,38 @@ Client::Client(
m_script->setEnv(&m_env); m_script->setEnv(&m_env);
} }
void Client::loadMods() void Client::loadBuiltin()
{ {
// Load builtin // Load builtin
scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath());
// If modding is not enabled, don't load mods, just builtin m_script->loadModFromMemory(BUILTIN_MOD_NAME);
if (!m_modding_enabled) { }
void Client::loadMods()
{
// Don't permit to load mods twice
if (m_mods_loaded) {
return; return;
} }
// If modding is not enabled or flavour disable it, don't load mods, just builtin
if (!m_modding_enabled) {
warningstream << "Client side mods are disabled by configuration." << std::endl;
return;
}
if (checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOAD_CLIENT_MODS)) {
warningstream << "Client side mods are disabled by server." << std::endl;
// If mods loading is disabled and builtin integrity is wrong, disconnect user.
if (!checkBuiltinIntegrity()) {
// @TODO disconnect user
}
return;
}
ClientModConfiguration modconf(getClientModsLuaPath()); ClientModConfiguration modconf(getClientModsLuaPath());
m_mods = modconf.getMods(); m_mods = modconf.getMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies // complain about mods with unsatisfied dependencies
if (!modconf.isConsistent()) { if (!modconf.isConsistent()) {
modconf.printUnsatisfiedModsError(); modconf.printUnsatisfiedModsError();
@ -145,6 +166,18 @@ void Client::loadMods()
} }
scanModIntoMemory(mod.name, mod.path); scanModIntoMemory(mod.name, mod.path);
} }
// Load and run "mod" scripts
for (const ModSpec &mod : m_mods)
m_script->loadModFromMemory(mod.name);
m_mods_loaded = true;
}
bool Client::checkBuiltinIntegrity()
{
// @TODO
return true;
} }
void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path, void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
@ -164,20 +197,6 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo
} }
} }
void Client::initMods()
{
m_script->loadModFromMemory(BUILTIN_MOD_NAME);
// If modding is not enabled, don't load mods, just builtin
if (!m_modding_enabled) {
return;
}
// Load and run "mod" scripts
for (const ModSpec &mod : m_mods)
m_script->loadModFromMemory(mod.name);
}
const std::string &Client::getBuiltinLuaPath() const std::string &Client::getBuiltinLuaPath()
{ {
static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin"; static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin";
@ -267,11 +286,6 @@ void Client::step(float dtime)
if (dtime > 2.0) if (dtime > 2.0)
dtime = 2.0; dtime = 2.0;
if(m_ignore_damage_timer > dtime)
m_ignore_damage_timer -= dtime;
else
m_ignore_damage_timer = 0.0;
m_animation_time += dtime; m_animation_time += dtime;
if(m_animation_time > 60.0) if(m_animation_time > 60.0)
m_animation_time -= 60.0; m_animation_time -= 60.0;
@ -304,6 +318,10 @@ void Client::step(float dtime)
initial_step = false; initial_step = false;
} }
else if(m_state == LC_Created) { else if(m_state == LC_Created) {
if (m_is_registration_confirmation_state) {
// Waiting confirmation
return;
}
float &counter = m_connection_reinit_timer; float &counter = m_connection_reinit_timer;
counter -= dtime; counter -= dtime;
if(counter <= 0.0) { if(counter <= 0.0) {
@ -394,7 +412,6 @@ void Client::step(float dtime)
ClientEnvEvent envEvent = m_env.getClientEnvEvent(); ClientEnvEvent envEvent = m_env.getClientEnvEvent();
if (envEvent.type == CEE_PLAYER_DAMAGE) { if (envEvent.type == CEE_PLAYER_DAMAGE) {
if (m_ignore_damage_timer <= 0) {
u8 damage = envEvent.player_damage.amount; u8 damage = envEvent.player_damage.amount;
if (envEvent.player_damage.send_to_server) if (envEvent.player_damage.send_to_server)
@ -407,7 +424,6 @@ void Client::step(float dtime)
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }
} }
}
/* /*
Print some info Print some info
@ -962,6 +978,18 @@ void Client::sendInit(const std::string &playerName)
Send(&pkt); Send(&pkt);
} }
void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;
m_is_registration_confirmation_state = true;
}
void Client::confirmRegistration()
{
m_is_registration_confirmation_state = false;
startAuth(m_chosen_auth_mech);
}
void Client::startAuth(AuthMechanism chosen_auth_mechanism) void Client::startAuth(AuthMechanism chosen_auth_mechanism)
{ {
m_chosen_auth_mech = chosen_auth_mechanism; m_chosen_auth_mech = chosen_auth_mechanism;
@ -1165,9 +1193,6 @@ void Client::sendChangePassword(const std::string &oldpassword,
void Client::sendDamage(u8 damage) void Client::sendDamage(u8 damage)
{ {
NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
pkt << damage;
Send(&pkt);
} }
void Client::sendRespawn() void Client::sendRespawn()
@ -1670,7 +1695,6 @@ void Client::afterContentReceived()
if (g_settings->getBool("enable_client_modding")) { if (g_settings->getBool("enable_client_modding")) {
m_script->on_client_ready(m_env.getLocalPlayer()); m_script->on_client_ready(m_env.getLocalPlayer());
m_script->on_connect();
} }
text = wgettext("Done!"); text = wgettext("Done!");
@ -1760,34 +1784,9 @@ void Client::pushToEventQueue(ClientEvent *event)
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }
void Client::showGameChat(const bool show)
{
m_game_ui_flags->show_chat = show;
}
void Client::showGameHud(const bool show)
{
m_game_ui_flags->show_hud = show;
}
void Client::showMinimap(const bool show) void Client::showMinimap(const bool show)
{ {
m_game_ui_flags->show_minimap = show; m_game_ui->showMinimap(show);
}
void Client::showProfiler(const bool show)
{
m_game_ui_flags->show_profiler_graph = show;
}
void Client::showGameFog(const bool show)
{
m_game_ui_flags->force_fog_off = !show;
}
void Client::showGameDebug(const bool show)
{
m_game_ui_flags->show_debug = show;
} }
// IGameDef interface // IGameDef interface
@ -1836,7 +1835,7 @@ ParticleManager* Client::getParticleManager()
return &m_particle_manager; return &m_particle_manager;
} }
scene::IAnimatedMesh* Client::getMesh(const std::string &filename) scene::IAnimatedMesh* Client::getMesh(const std::string &filename, bool cache)
{ {
StringMap::const_iterator it = m_mesh_data.find(filename); StringMap::const_iterator it = m_mesh_data.find(filename);
if (it == m_mesh_data.end()) { if (it == m_mesh_data.end()) {
@ -1855,9 +1854,8 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
scene::IAnimatedMesh *mesh = RenderingEngine::get_scene_manager()->getMesh(rfile); scene::IAnimatedMesh *mesh = RenderingEngine::get_scene_manager()->getMesh(rfile);
rfile->drop(); rfile->drop();
// NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
// of uniquely named instances and re-use them
mesh->grab(); mesh->grab();
if (!cache)
RenderingEngine::get_mesh_cache()->removeMesh(mesh); RenderingEngine::get_mesh_cache()->removeMesh(mesh);
return mesh; return mesh;
} }

View File

@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h" #include "gamedef.h"
#include "inventorymanager.h" #include "inventorymanager.h"
#include "localplayer.h" #include "localplayer.h"
#include "hud.h" #include "client/hud.h"
#include "particles.h" #include "particles.h"
#include "mapnode.h" #include "mapnode.h"
#include "tileanimation.h" #include "tileanimation.h"
@ -112,7 +112,7 @@ private:
}; };
class ClientScripting; class ClientScripting;
struct GameUIFlags; class GameUI;
class Client : public con::PeerHandler, public InventoryManager, public IGameDef class Client : public con::PeerHandler, public InventoryManager, public IGameDef
{ {
@ -133,14 +133,14 @@ public:
ISoundManager *sound, ISoundManager *sound,
MtEventManager *event, MtEventManager *event,
bool ipv6, bool ipv6,
GameUIFlags *game_ui_flags GameUI *game_ui
); );
~Client(); ~Client();
DISABLE_CLASS_COPY(Client); DISABLE_CLASS_COPY(Client);
// Load local mods into memory // Load local mods into memory
void loadMods(); void loadBuiltin();
void scanModSubfolder(const std::string &mod_name, const std::string &mod_path, void scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
std::string mod_subpath); std::string mod_subpath);
inline void scanModIntoMemory(const std::string &mod_name, const std::string &mod_path) inline void scanModIntoMemory(const std::string &mod_name, const std::string &mod_path)
@ -148,9 +148,6 @@ public:
scanModSubfolder(mod_name, mod_path, ""); scanModSubfolder(mod_name, mod_path, "");
} }
// Initizle the mods
void initMods();
/* /*
request all threads managed by client to be stopped request all threads managed by client to be stopped
*/ */
@ -301,7 +298,7 @@ public:
u16 getHP(); u16 getHP();
bool checkPrivilege(const std::string &priv) const bool checkPrivilege(const std::string &priv) const
{ return (m_privileges.count(priv) != 0); } { return true; }
const std::unordered_set<std::string> &getPrivilegeList() const const std::unordered_set<std::string> &getPrivilegeList() const
{ return m_privileges; } { return m_privileges; }
@ -348,6 +345,9 @@ public:
{ return m_proto_ver; } { return m_proto_ver; }
bool connectedToServer(); bool connectedToServer();
void confirmRegistration();
bool m_is_registration_confirmation_state = false;
bool m_simple_singleplayer_mode;
float mediaReceiveProgress(); float mediaReceiveProgress();
@ -376,7 +376,7 @@ public:
virtual ParticleManager* getParticleManager(); virtual ParticleManager* getParticleManager();
bool checkLocalPrivilege(const std::string &priv) bool checkLocalPrivilege(const std::string &priv)
{ return checkPrivilege(priv); } { return checkPrivilege(priv); }
virtual scene::IAnimatedMesh* getMesh(const std::string &filename); virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false);
const std::string* getModFile(const std::string &filename); const std::string* getModFile(const std::string &filename);
virtual std::string getModStoragePath() const; virtual std::string getModStoragePath() const;
@ -403,12 +403,7 @@ public:
void pushToEventQueue(ClientEvent *event); void pushToEventQueue(ClientEvent *event);
void showGameChat(bool show = true);
void showGameHud(bool show = true);
void showMinimap(bool show = true); void showMinimap(bool show = true);
void showProfiler(bool show = true);
void showGameFog(bool show = true);
void showGameDebug(bool show = true);
const Address getServerAddress(); const Address getServerAddress();
@ -427,12 +422,19 @@ public:
return m_csm_noderange_limit; return m_csm_noderange_limit;
} }
inline std::unordered_map<u32, u32> &getHUDTranslationMap()
{
return m_hud_server_to_client;
}
bool joinModChannel(const std::string &channel); bool joinModChannel(const std::string &channel);
bool leaveModChannel(const std::string &channel); bool leaveModChannel(const std::string &channel);
bool sendModChannelMessage(const std::string &channel, const std::string &message); bool sendModChannelMessage(const std::string &channel, const std::string &message);
ModChannel *getModChannel(const std::string &channel); ModChannel *getModChannel(const std::string &channel);
private: private:
void loadMods();
bool checkBuiltinIntegrity();
// Virtual methods from con::PeerHandler // Virtual methods from con::PeerHandler
void peerAdded(con::Peer *peer); void peerAdded(con::Peer *peer);
@ -454,6 +456,7 @@ private:
static AuthMechanism choseAuthMech(const u32 mechs); static AuthMechanism choseAuthMech(const u32 mechs);
void sendInit(const std::string &playerName); void sendInit(const std::string &playerName);
void promptConfirmRegistration(AuthMechanism chosen_auth_mechanism);
void startAuth(AuthMechanism chosen_auth_mechanism); void startAuth(AuthMechanism chosen_auth_mechanism);
void sendDeletedBlocks(std::vector<v3s16> &blocks); void sendDeletedBlocks(std::vector<v3s16> &blocks);
void sendGotBlocks(v3s16 block); void sendGotBlocks(v3s16 block);
@ -469,7 +472,6 @@ private:
float m_connection_reinit_timer = 0.1f; float m_connection_reinit_timer = 0.1f;
float m_avg_rtt_timer = 0.0f; float m_avg_rtt_timer = 0.0f;
float m_playerpos_send_timer = 0.0f; float m_playerpos_send_timer = 0.0f;
float m_ignore_damage_timer = 0.0f; // Used after server moves player
IntervalLimiter m_map_timer_and_unload_interval; IntervalLimiter m_map_timer_and_unload_interval;
IWritableTextureSource *m_tsrc; IWritableTextureSource *m_tsrc;
@ -537,6 +539,7 @@ private:
std::queue<ClientEvent *> m_client_event_queue; std::queue<ClientEvent *> m_client_event_queue;
bool m_itemdef_received = false; bool m_itemdef_received = false;
bool m_nodedef_received = false; bool m_nodedef_received = false;
bool m_mods_loaded = false;
ClientMediaDownloader *m_media_downloader; ClientMediaDownloader *m_media_downloader;
// time_of_day speed approximation for old protocol // time_of_day speed approximation for old protocol
@ -556,6 +559,12 @@ private:
// And relations to objects // And relations to objects
std::unordered_map<int, u16> m_sounds_to_objects; std::unordered_map<int, u16> m_sounds_to_objects;
// CSM/client IDs to SSM/server IDs Mapping
// Map server particle spawner IDs to client IDs
std::unordered_map<u32, u32> m_particles_server_to_client;
// Map server hud ids to client hud ids
std::unordered_map<u32, u32> m_hud_server_to_client;
// Privileges // Privileges
std::unordered_set<std::string> m_privileges; std::unordered_set<std::string> m_privileges;
@ -571,6 +580,8 @@ private:
// own state // own state
LocalClientState m_state; LocalClientState m_state;
GameUI *m_game_ui;
// Used for saving server map to disk client-side // Used for saving server map to disk client-side
MapDatabase *m_localdb = nullptr; MapDatabase *m_localdb = nullptr;
IntervalLimiter m_localdb_save_interval; IntervalLimiter m_localdb_save_interval;
@ -581,7 +592,6 @@ private:
std::unordered_map<std::string, ModMetadata *> m_mod_storages; std::unordered_map<std::string, ModMetadata *> m_mod_storages;
float m_mod_storage_save_timer = 10.0f; float m_mod_storage_save_timer = 10.0f;
std::vector<ModSpec> m_mods; std::vector<ModSpec> m_mods;
GameUIFlags *m_game_ui_flags;
bool m_shutdown = false; bool m_shutdown = false;

View File

@ -9,8 +9,10 @@ set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/render/stereo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/render/stereo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/renderingengine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/renderingengine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gameui.cpp
${CMAKE_CURRENT_SOURCE_DIR}/inputhandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/inputhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/hud.cpp
PARENT_SCOPE PARENT_SCOPE
) )

View File

@ -116,7 +116,7 @@ struct ClientEvent
} delete_particlespawner; } delete_particlespawner;
struct struct
{ {
u32 id; u32 server_id;
u8 type; u8 type;
v2f *pos; v2f *pos;
std::string *name; std::string *name;

304
src/client/gameui.cpp Normal file
View File

@ -0,0 +1,304 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gameui.h"
#include <irrlicht_changes/static_text.h>
#include <gettext.h>
#include "gui/mainmenumanager.h"
#include "util/pointedthing.h"
#include "client.h"
#include "clientmap.h"
#include "fontengine.h"
#include "nodedef.h"
#include "profiler.h"
#include "renderingengine.h"
#include "version.h"
inline static const char *yawToDirectionString(int yaw)
{
static const char *direction[4] = {"N +Z", "W -X", "S -Z", "E +X"};
yaw = wrapDegrees_0_360(yaw);
yaw = (yaw + 45) % 360 / 90;
return direction[yaw];
}
GameUI::GameUI()
{
if (guienv && guienv->getSkin())
m_statustext_initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
else
m_statustext_initial_color = video::SColor(255, 0, 0, 0);
}
void GameUI::init()
{
// First line of debug text
m_guitext = gui::StaticText::add(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
// Second line of debug text
m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false,
false, guiroot);
// At the middle of the screen
// Object infos are shown in this
m_guitext_info = gui::StaticText::add(guienv, L"",
core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5)
+ v2s32(100, 200), false, true, guiroot);
// Status text (displays info when showing and hiding GUI stuff, etc.)
m_guitext_status = gui::StaticText::add(guienv, L"<Status>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
m_guitext_status->setVisible(false);
// Chat text
m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
false, true, guiroot);
// Profiler text (size is updated when text is updated)
m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
m_guitext_profiler->setBackgroundColor(video::SColor(120, 0, 0, 0));
m_guitext_profiler->setVisible(false);
m_guitext_profiler->setWordWrap(true);
}
void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_control,
const CameraOrientation &cam, const PointedThing &pointed_old, float dtime)
{
v2u32 screensize = RenderingEngine::get_instance()->getWindowSize();
if (m_flags.show_debug) {
static float drawtime_avg = 0;
drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05;
u16 fps = 1.0 / stats.dtime_jitter.avg;
std::ostringstream os(std::ios_base::binary);
os << std::fixed
<< PROJECT_NAME_C " " << g_version_hash
<< ", FPS: " << fps
<< std::setprecision(0)
<< ", drawtime: " << drawtime_avg << "ms"
<< std::setprecision(1)
<< ", dtime jitter: "
<< (stats.dtime_jitter.max_fraction * 100.0) << "%"
<< std::setprecision(1)
<< ", view range: "
<< (draw_control->range_all ? "All" : itos(draw_control->wanted_range))
<< std::setprecision(3)
<< ", RTT: " << client->getRTT() << "s";
setStaticText(m_guitext, utf8_to_wide(os.str()).c_str());
m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X,
5 + g_fontengine->getTextHeight()));
}
// Finally set the guitext visible depending on the flag
m_guitext->setVisible(m_flags.show_debug);
if (m_flags.show_debug) {
LocalPlayer *player = client->getEnv().getLocalPlayer();
v3f player_position = player->getPosition();
std::ostringstream os(std::ios_base::binary);
os << std::setprecision(1) << std::fixed
<< "pos: (" << (player_position.X / BS)
<< ", " << (player_position.Y / BS)
<< ", " << (player_position.Z / BS)
<< "), yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° "
<< yawToDirectionString(cam.camera_yaw)
<< ", seed: " << ((u64)client->getMapSeed());
if (pointed_old.type == POINTEDTHING_NODE) {
ClientMap &map = client->getEnv().getClientMap();
const INodeDefManager *nodedef = client->getNodeDefManager();
MapNode n = map.getNodeNoEx(pointed_old.node_undersurface);
if (n.getContent() != CONTENT_IGNORE && nodedef->get(n).name != "unknown") {
os << ", pointed: " << nodedef->get(n).name
<< ", param2: " << (u64) n.getParam2();
}
}
setStaticText(m_guitext2, utf8_to_wide(os.str()).c_str());
m_guitext2->setRelativePosition(core::rect<s32>(5,
5 + g_fontengine->getTextHeight(), screensize.X,
5 + g_fontengine->getTextHeight() * 2
));
}
m_guitext2->setVisible(m_flags.show_debug);
setStaticText(m_guitext_info, translate_string(m_infotext).c_str());
m_guitext_info->setVisible(m_flags.show_hud && g_menumgr.menuCount() == 0);
static const float statustext_time_max = 1.5f;
if (!m_statustext.empty()) {
m_statustext_time += dtime;
if (m_statustext_time >= statustext_time_max) {
clearStatusText();
m_statustext_time = 0.0f;
}
}
setStaticText(m_guitext_status, translate_string(m_statustext).c_str());
m_guitext_status->setVisible(!m_statustext.empty());
if (!m_statustext.empty()) {
s32 status_width = m_guitext_status->getTextWidth();
s32 status_height = m_guitext_status->getTextHeight();
s32 status_y = screensize.Y - 150;
s32 status_x = (screensize.X - status_width) / 2;
m_guitext_status->setRelativePosition(core::rect<s32>(status_x ,
status_y - status_height, status_x + status_width, status_y));
// Fade out
video::SColor final_color = m_statustext_initial_color;
final_color.setAlpha(0);
video::SColor fade_color = m_statustext_initial_color.getInterpolated_quadratic(
m_statustext_initial_color, final_color, m_statustext_time / statustext_time_max);
m_guitext_status->setOverrideColor(fade_color);
m_guitext_status->enableOverrideColor(true);
}
}
void GameUI::initFlags()
{
memset(&m_flags, 0, sizeof(GameUI::Flags));
m_flags.show_chat = true;
m_flags.show_hud = true;
m_flags.show_debug = g_settings->getBool("show_debug");
}
void GameUI::showMinimap(bool show)
{
m_flags.show_minimap = show;
}
void GameUI::showTranslatedStatusText(const char *str)
{
const wchar_t *wmsg = wgettext(str);
showStatusText(wmsg);
delete[] wmsg;
}
void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count)
{
setStaticText(m_guitext_chat, chat_text);
// Update gui element size and position
s32 chat_y = 5;
if (m_flags.show_debug)
chat_y += 2 * g_fontengine->getLineHeight();
// first pass to calculate height of text to be set
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
s32 width = std::min(g_fontengine->getTextWidth(chat_text.c_str()) + 10,
window_size.X - 20);
m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width,
chat_y + window_size.Y));
// now use real height of text and adjust rect according to this size
m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width,
chat_y + m_guitext_chat->getTextHeight()));
// Don't show chat if disabled or empty or profiler is enabled
m_guitext_chat->setVisible(m_flags.show_chat &&
recent_chat_count != 0 && m_profiler_current_page == 0);
}
void GameUI::updateProfiler()
{
if (m_profiler_current_page != 0) {
std::ostringstream os(std::ios_base::binary);
g_profiler->printPage(os, m_profiler_current_page, m_profiler_max_page);
std::wstring text = translate_string(utf8_to_wide(os.str()));
setStaticText(m_guitext_profiler, text.c_str());
s32 w = g_fontengine->getTextWidth(text);
if (w < 400)
w = 400;
u32 text_height = g_fontengine->getTextHeight();
core::position2di upper_left, lower_right;
upper_left.X = 6;
upper_left.Y = (text_height + 5) * 2;
lower_right.X = 12 + w;
lower_right.Y = upper_left.Y + (text_height + 1) * MAX_PROFILER_TEXT_ROWS;
s32 screen_height = RenderingEngine::get_video_driver()->getScreenSize().Height;
if (lower_right.Y > screen_height * 2 / 3)
lower_right.Y = screen_height * 2 / 3;
m_guitext_profiler->setRelativePosition(core::rect<s32>(upper_left, lower_right));
}
m_guitext_profiler->setVisible(m_profiler_current_page != 0);
}
void GameUI::toggleChat()
{
m_flags.show_chat = !m_flags.show_chat;
if (m_flags.show_chat)
showTranslatedStatusText("Chat shown");
else
showTranslatedStatusText("Chat hidden");
}
void GameUI::toggleHud()
{
m_flags.show_hud = !m_flags.show_hud;
if (m_flags.show_hud)
showTranslatedStatusText("HUD shown");
else
showTranslatedStatusText("HUD hidden");
}
void GameUI::toggleProfiler()
{
m_profiler_current_page = (m_profiler_current_page + 1) % (m_profiler_max_page + 1);
// FIXME: This updates the profiler with incomplete values
updateProfiler();
if (m_profiler_current_page != 0) {
wchar_t buf[255];
const wchar_t* str = wgettext("Profiler shown (page %d of %d)");
swprintf(buf, sizeof(buf) / sizeof(wchar_t), str,
m_profiler_current_page, m_profiler_max_page);
delete[] str;
showStatusText(buf);
} else {
showTranslatedStatusText("Profiler hidden");
}
}

110
src/client/gameui.h Normal file
View File

@ -0,0 +1,110 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <IGUIEnvironment.h>
#include "util/enriched_string.h"
#include "util/pointedthing.h"
#include "game.h"
using namespace irr;
class Client;
struct MapDrawControl;
/*
* This object intend to contain the core UI elements
* It includes:
* - status texts
* - debug texts
* - chat texts
* - hud flags
*/
class GameUI
{
// Temporary between coding time to move things here
friend class Game;
// Permit unittests to access members directly
friend class TestGameUI;
public:
GameUI();
~GameUI() = default;
// Flags that can, or may, change during main game loop
struct Flags
{
bool show_chat = true;
bool show_hud = true;
bool show_minimap = true;
bool show_debug = true;
bool show_profiler_graph = true;
};
void init();
void update(const RunStats &stats, Client *client, MapDrawControl *draw_control,
const CameraOrientation &cam, const PointedThing &pointed_old,
float dtime);
void initFlags();
const Flags &getFlags() const { return m_flags; }
void showMinimap(bool show);
inline void setInfoText(const std::wstring &str) { m_infotext = str; }
inline void clearInfoText() { m_infotext.clear(); }
inline void showStatusText(const std::wstring &str)
{
m_statustext = str;
m_statustext_time = 0.0f;
}
void showTranslatedStatusText(const char *str);
inline void clearStatusText() { m_statustext.clear(); }
void setChatText(const EnrichedString &chat_text, u32 recent_chat_count);
void updateProfiler();
void toggleChat();
void toggleHud();
void toggleProfiler();
private:
Flags m_flags;
gui::IGUIStaticText *m_guitext = nullptr; // First line of debug text
gui::IGUIStaticText *m_guitext2 = nullptr; // Second line of debug text
gui::IGUIStaticText *m_guitext_info = nullptr; // At the middle of the screen
std::wstring m_infotext;
gui::IGUIStaticText *m_guitext_status = nullptr;
std::wstring m_statustext;
float m_statustext_time = 0.0f;
video::SColor m_statustext_initial_color;
gui::IGUIStaticText *m_guitext_chat = nullptr; // Chat text
gui::IGUIStaticText *m_guitext_profiler = nullptr; // Profiler text
u8 m_profiler_current_page = 0;
const u8 m_profiler_max_page = 3;
};

775
src/client/hud.cpp Normal file
View File

@ -0,0 +1,775 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "client/hud.h"
#include "settings.h"
#include "util/numeric.h"
#include "log.h"
#include "client.h"
#include "inventory.h"
#include "shader.h"
#include "client/tile.h"
#include "localplayer.h"
#include "camera.h"
#include "porting.h"
#include "fontengine.h"
#include "guiscalingfilter.h"
#include "mesh.h"
#include "wieldmesh.h"
#include "client/renderingengine.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "gui/touchscreengui.h"
#endif
Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory)
{
driver = RenderingEngine::get_video_driver();
this->guienv = guienv;
this->client = client;
this->player = player;
this->inventory = inventory;
m_hud_scaling = g_settings->getFloat("hud_scaling");
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5f);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
for (auto &hbar_color : hbar_colors)
hbar_color = video::SColor(255, 255, 255, 255);
tsrc = client->getTextureSource();
v3f crosshair_color = g_settings->getV3F("crosshair_color");
u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
m_selection_boxes.clear();
m_halo_boxes.clear();
std::string mode_setting = g_settings->get("node_highlighting");
if (mode_setting == "halo") {
m_mode = HIGHLIGHT_HALO;
} else if (mode_setting == "none") {
m_mode = HIGHLIGHT_NONE;
} else {
m_mode = HIGHLIGHT_BOX;
}
m_selection_material.Lighting = false;
if (g_settings->getBool("enable_shaders")) {
IShaderSource *shdrsrc = client->getShaderSource();
u16 shader_id = shdrsrc->getShader(
m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
} else {
m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
if (m_mode == HIGHLIGHT_BOX) {
m_selection_material.Thickness =
rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
} else if (m_mode == HIGHLIGHT_HALO) {
m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
} else {
m_selection_material.MaterialType = video::EMT_SOLID;
}
}
Hud::~Hud()
{
if (m_selection_mesh)
m_selection_mesh->drop();
}
void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
bool selected)
{
if (selected) {
/* draw hihlighting around selected item */
if (use_hotbar_selected_image) {
core::rect<s32> imgrect2 = rect;
imgrect2.UpperLeftCorner.X -= (m_padding*2);
imgrect2.UpperLeftCorner.Y -= (m_padding*2);
imgrect2.LowerRightCorner.X += (m_padding*2);
imgrect2.LowerRightCorner.Y += (m_padding*2);
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, imgrect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
} else {
video::SColor c_outside(255,255,0,0);
//video::SColor c_outside(255,0,0,0);
//video::SColor c_inside(255,192,192,192);
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
// Black base borders
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1 - m_padding),
v2s32(x2 + m_padding, y1)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y2),
v2s32(x2 + m_padding, y2 + m_padding)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + m_padding, y2)
), NULL);
/*// Light inside borders
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1 - padding/2),
v2s32(x2 + padding/2, y1)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y2),
v2s32(x2 + padding/2, y2 + padding/2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + padding/2, y2)
), NULL);
*/
}
}
video::SColor bgcolor2(128, 0, 0, 0);
if (!use_hotbar_image)
driver->draw2DRectangle(bgcolor2, rect, NULL);
drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
}
//NOTE: selectitem = 0 -> no selected; selectitem 1-based
void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
{
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui && inv_offset == 0)
g_touchscreengui->resetHud();
#endif
s32 height = m_hotbar_imagesize + m_padding * 2;
s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
s32 tmp = height;
height = width;
width = tmp;
}
// Position of upper left corner of bar
v2s32 pos = screen_offset;
pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos += upperleftpos;
// Store hotbar_image in member variable, used by drawItem()
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
if (!hotbar_image.empty())
use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
else
use_hotbar_image = false;
}
// Store hotbar_selected_image in member variable, used by drawItem()
if (hotbar_selected_image != player->hotbar_selected_image) {
hotbar_selected_image = player->hotbar_selected_image;
if (!hotbar_selected_image.empty())
use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
else
use_hotbar_selected_image = false;
}
// draw customized item background
if (use_hotbar_image) {
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
}
// Draw items
core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
v2s32 steppos;
switch (direction) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
break;
default:
steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
break;
}
drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
#endif
}
}
void Hud::drawLuaElements(const v3s16 &camera_offset)
{
u32 text_height = g_fontengine->getTextHeight();
irr::gui::IGUIFont* font = g_fontengine->getFont();
for (size_t i = 0; i != player->maxHudId(); i++) {
HudElement *e = player->getHud(i);
if (!e)
continue;
v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
floor(e->pos.Y * (float) m_screensize.Y + 0.5));
switch (e->type) {
case HUD_ELEM_IMAGE: {
video::ITexture *texture = tsrc->getTexture(e->text);
if (!texture)
continue;
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
core::dimension2di imgsize(texture->getOriginalSize());
v2s32 dstsize(imgsize.Width * e->scale.X,
imgsize.Height * e->scale.Y);
if (e->scale.X < 0)
dstsize.X = m_screensize.X * (e->scale.X * -0.01);
if (e->scale.Y < 0)
dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
(e->align.Y - 1.0) * dstsize.Y / 2);
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, colors, true);
break; }
case HUD_ELEM_TEXT: {
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
std::wstring text = unescape_translate(utf8_to_wide(e->text));
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
v2s32 offs(e->offset.X, e->offset.Y);
font->draw(text.c_str(), size + pos + offset + offs, color);
break; }
case HUD_ELEM_STATBAR: {
v2s32 offs(e->offset.X, e->offset.Y);
drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
inv, e->item, e->dir);
break; }
case HUD_ELEM_WAYPOINT: {
v3f p_pos = player->getPosition() / BS;
v3f w_pos = e->world_pos * BS;
float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
scene::ICameraSceneNode* camera =
RenderingEngine::get_scene_manager()->getActiveCamera();
w_pos -= intToFloat(camera_offset, BS);
core::matrix4 trans = camera->getProjectionMatrix();
trans *= camera->getViewMatrix();
f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
trans.multiplyWith1x4Matrix(transformed_pos);
if (transformed_pos[3] < 0)
break;
f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
core::reciprocal(transformed_pos[3]);
pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
std::wstring text = unescape_translate(utf8_to_wide(e->name));
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
os << distance << e->text;
text = unescape_translate(utf8_to_wide(os.str()));
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }
default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
" of hud element ID " << i << " due to unrecognized type" << std::endl;
}
}
}
void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size)
{
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
video::ITexture *stat_texture = tsrc->getTexture(texture);
if (!stat_texture)
return;
core::dimension2di srcd(stat_texture->getOriginalSize());
core::dimension2di dstd;
if (size == v2s32()) {
dstd = srcd;
} else {
float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
dstd.Height = size.Y * size_factor;
dstd.Width = size.X * size_factor;
offset.X *= size_factor;
offset.Y *= size_factor;
}
v2s32 p = pos;
if (corner & HUD_CORNER_LOWER)
p -= dstd.Height;
p += offset;
v2s32 steppos;
core::rect<s32> srchalfrect, dsthalfrect;
switch (drawdir) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-1, 0);
srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(0, 1);
srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(0, -1);
srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
break;
default:
steppos = v2s32(1, 0);
srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
}
steppos.X *= dstd.Width;
steppos.Y *= dstd.Height;
for (s32 i = 0; i < count / 2; i++) {
core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
dstrect += p;
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
p += steppos;
}
if (count % 2 == 1) {
dsthalfrect += p;
draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
}
}
void Hud::drawHotbar(u16 playeritem) {
v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
InventoryList *mainlist = inventory->getList("main");
if (mainlist == NULL) {
//silently ignore this we may not be initialized completely
return;
}
s32 hotbar_itemcount = player->hud_hotbar_itemcount;
s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if ( (float) width / (float) window_size.X <=
g_settings->getFloat("hud_hotbar_max_width")) {
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
}
} else {
pos.X += width/4;
v2s32 secondpos = pos;
pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
mainlist, playeritem + 1, 0);
drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
}
}
//////////////////////////// compatibility code to be removed //////////////
// this is ugly as hell but there's no other way to keep compatibility to
// old servers
if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "heart.png",
player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
}
if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
(player->getBreath() < 11)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "bubble.png",
player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
}
////////////////////////////////////////////////////////////////////////////
}
void Hud::drawCrosshair()
{
if (use_crosshair_image) {
video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
v2u32 size = crosshair->getOriginalSize();
v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
m_displaycenter.Y - (size.Y / 2));
driver->draw2DImage(crosshair, lsize,
core::rect<s32>(0, 0, size.X, size.Y),
0, crosshair_argb, true);
} else {
driver->draw2DLine(m_displaycenter - v2s32(10, 0),
m_displaycenter + v2s32(10, 0), crosshair_argb);
driver->draw2DLine(m_displaycenter - v2s32(0, 10),
m_displaycenter + v2s32(0, 10), crosshair_argb);
}
}
void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
m_selection_pos = pos;
m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
}
void Hud::drawSelectionMesh()
{
if (m_mode == HIGHLIGHT_BOX) {
// Draw 3D selection boxes
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
for (std::vector<aabb3f>::const_iterator
i = m_selection_boxes.begin();
i != m_selection_boxes.end(); ++i) {
aabb3f box = aabb3f(
i->MinEdge + m_selection_pos_with_offset,
i->MaxEdge + m_selection_pos_with_offset);
u32 r = (selectionbox_argb.getRed() *
m_selection_mesh_color.getRed() / 255);
u32 g = (selectionbox_argb.getGreen() *
m_selection_mesh_color.getGreen() / 255);
u32 b = (selectionbox_argb.getBlue() *
m_selection_mesh_color.getBlue() / 255);
driver->draw3DBox(box, video::SColor(255, r, g, b));
}
driver->setMaterial(oldmaterial);
} else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
// Draw selection mesh
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
setMeshColor(m_selection_mesh, m_selection_mesh_color);
video::SColor face_color(0,
MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
face_color);
scene::IMesh* mesh = cloneMesh(m_selection_mesh);
translateMesh(mesh, m_selection_pos_with_offset);
u32 mc = m_selection_mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
driver->drawMeshBuffer(buf);
}
mesh->drop();
driver->setMaterial(oldmaterial);
}
}
void Hud::updateSelectionMesh(const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
if (m_mode != HIGHLIGHT_HALO)
return;
if (m_selection_mesh) {
m_selection_mesh->drop();
m_selection_mesh = NULL;
}
if (m_selection_boxes.empty()) {
// No pointed object
return;
}
// New pointed object, create new mesh.
// Texture UV coordinates for selection boxes
static f32 texture_uv[24] = {
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1
};
// Use single halo box instead of multiple overlapping boxes.
// Temporary solution - problem can be solved with multiple
// rendering targets, or some method to remove inner surfaces.
// Thats because of halo transparency.
aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
m_halo_boxes.clear();
for (const auto &selection_box : m_selection_boxes) {
halo_box.addInternalBox(selection_box);
}
m_halo_boxes.push_back(halo_box);
m_selection_mesh = convertNodeboxesToMesh(
m_halo_boxes, texture_uv, 0.5);
}
void Hud::resizeHotbar() {
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if (m_screensize != window_size) {
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
m_screensize = window_size;
m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
}
}
struct MeshTimeInfo {
u64 time;
scene::IMesh *mesh;
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind)
{
static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations");
if (item.empty()) {
if (rotation_kind < IT_ROT_NONE) {
rotation_time_infos[rotation_kind].mesh = NULL;
}
return;
}
const ItemDefinition &def = item.getDefinition(client->idef());
ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
if (imesh && imesh->mesh) {
scene::IMesh *mesh = imesh->mesh;
driver->clearZBuffer();
s32 delta = 0;
if (rotation_kind < IT_ROT_NONE) {
MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
if (mesh != ti.mesh) {
ti.mesh = mesh;
ti.time = porting::getTimeMs();
} else {
delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
}
}
core::rect<s32> oldViewPort = driver->getViewPort();
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
core::matrix4 ProjMatrix;
ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
driver->setTransform(video::ETS_VIEW, ProjMatrix);
core::matrix4 matrix;
matrix.makeIdentity();
if (enable_animations) {
float timer_f = (float) delta / 5000.0;
matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
}
driver->setTransform(video::ETS_WORLD, matrix);
driver->setViewPort(rect);
video::SColor basecolor =
client->idef()->getItemstackColor(item, client);
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; ++j) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
// we can modify vertices relatively fast,
// because these meshes are not buffered.
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) {
ItemPartColor *p = &imesh->buffer_colors[j];
if (p->override_base)
c = p->color;
}
if (imesh->needs_shading)
colorizeMeshBuffer(buf, &c);
else
setMeshBufferColor(buf, c);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
driver->setMaterial(material);
driver->drawMeshBuffer(buf);
}
driver->setTransform(video::ETS_VIEW, oldViewMat);
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort);
}
if(def.type == ITEM_TOOL && item.wear != 0)
{
// Draw a progressbar
float barheight = rect.getHeight()/16;
float barpad_x = rect.getWidth()/16;
float barpad_y = rect.getHeight()/16;
core::rect<s32> progressrect(
rect.UpperLeftCorner.X + barpad_x,
rect.LowerRightCorner.Y - barpad_y - barheight,
rect.LowerRightCorner.X - barpad_x,
rect.LowerRightCorner.Y - barpad_y);
// Shrink progressrect by amount of tool damage
float wear = item.wear / 65535.0;
int progressmid =
wear * progressrect.UpperLeftCorner.X +
(1-wear) * progressrect.LowerRightCorner.X;
// Compute progressbar color
// wear = 0.0: green
// wear = 0.5: yellow
// wear = 1.0: red
video::SColor color(255,255,255,255);
int wear_i = MYMIN(floor(wear * 600), 511);
wear_i = MYMIN(wear_i + 10, 511);
if(wear_i <= 255)
color.set(255, wear_i, 255, 0);
else
color.set(255, 255, 511-wear_i, 0);
core::rect<s32> progressrect2 = progressrect;
progressrect2.LowerRightCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
color = video::SColor(255,0,0,0);
progressrect2 = progressrect;
progressrect2.UpperLeftCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
}
if(font != NULL && item.count >= 2)
{
// Get the item count as a string
std::string text = itos(item.count);
v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
v2s32 sdim(dim.X,dim.Y);
core::rect<s32> rect2(
/*rect.UpperLeftCorner,
core::dimension2d<u32>(rect.getWidth(), 15)*/
rect.LowerRightCorner - sdim,
sdim
);
video::SColor bgcolor(128,0,0,0);
driver->draw2DRectangle(bgcolor, rect2, clip);
video::SColor color(255,255,255,255);
font->draw(text.c_str(), rect2, color, false, false, clip);
}
}

136
src/client/hud.h Normal file
View File

@ -0,0 +1,136 @@
/*
Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2017 red-001 <red-001@outlook.ie>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CLIENT_HUD_HEADER
#define CLIENT_HUD_HEADER
#include <vector>
#include <IGUIFont.h>
#include "irr_aabb3d.h"
#include "../hud.h"
class Client;
class ITextureSource;
class Inventory;
class InventoryList;
class LocalPlayer;
struct ItemStack;
class Hud
{
public:
video::IVideoDriver *driver;
scene::ISceneManager *smgr;
gui::IGUIEnvironment *guienv;
Client *client;
LocalPlayer *player;
Inventory *inventory;
ITextureSource *tsrc;
video::SColor crosshair_argb;
video::SColor selectionbox_argb;
bool use_crosshair_image = false;
std::string hotbar_image = "";
bool use_hotbar_image = false;
std::string hotbar_selected_image = "";
bool use_hotbar_selected_image = false;
Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory);
~Hud();
void drawHotbar(u16 playeritem);
void resizeHotbar();
void drawCrosshair();
void drawSelectionMesh();
void updateSelectionMesh(const v3s16 &camera_offset);
std::vector<aabb3f> *getSelectionBoxes() { return &m_selection_boxes; }
void setSelectionPos(const v3f &pos, const v3s16 &camera_offset);
v3f getSelectionPos() const { return m_selection_pos; }
void setSelectionMeshColor(const video::SColor &color)
{
m_selection_mesh_color = color;
}
void setSelectedFaceNormal(const v3f &face_normal)
{
m_selected_face_normal = face_normal;
}
void drawLuaElements(const v3s16 &camera_offset);
private:
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size = v2s32());
void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem,
u16 direction);
void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected);
float m_hud_scaling; // cached minetest setting
v3s16 m_camera_offset;
v2u32 m_screensize;
v2s32 m_displaycenter;
s32 m_hotbar_imagesize; // Takes hud_scaling into account, updated by resizeHotbar()
s32 m_padding; // Takes hud_scaling into account, updated by resizeHotbar()
video::SColor hbar_colors[4];
std::vector<aabb3f> m_selection_boxes;
std::vector<aabb3f> m_halo_boxes;
v3f m_selection_pos;
v3f m_selection_pos_with_offset;
scene::IMesh *m_selection_mesh = nullptr;
video::SColor m_selection_mesh_color;
v3f m_selected_face_normal;
video::SMaterial m_selection_material;
enum
{
HIGHLIGHT_BOX,
HIGHLIGHT_HALO,
HIGHLIGHT_NONE
} m_mode;
};
enum ItemRotationKind
{
IT_ROT_SELECTED,
IT_ROT_HOVERED,
IT_ROT_DRAGGED,
IT_ROT_NONE, // Must be last, also serves as number
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind);
#endif

View File

@ -22,6 +22,75 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inputhandler.h" #include "inputhandler.h"
#include "gui/mainmenumanager.h" #include "gui/mainmenumanager.h"
void KeyCache::populate_nonchanging()
{
key[KeyType::ESC] = EscapeKey;
}
void KeyCache::populate()
{
key[KeyType::FORWARD] = getKeySetting("keymap_forward");
key[KeyType::BACKWARD] = getKeySetting("keymap_backward");
key[KeyType::LEFT] = getKeySetting("keymap_left");
key[KeyType::RIGHT] = getKeySetting("keymap_right");
key[KeyType::JUMP] = getKeySetting("keymap_jump");
key[KeyType::SPECIAL1] = getKeySetting("keymap_special1");
key[KeyType::SNEAK] = getKeySetting("keymap_sneak");
key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward");
key[KeyType::DROP] = getKeySetting("keymap_drop");
key[KeyType::INVENTORY] = getKeySetting("keymap_inventory");
key[KeyType::CHAT] = getKeySetting("keymap_chat");
key[KeyType::CMD] = getKeySetting("keymap_cmd");
key[KeyType::CMD_LOCAL] = getKeySetting("keymap_cmd_local");
key[KeyType::CONSOLE] = getKeySetting("keymap_console");
key[KeyType::MINIMAP] = getKeySetting("keymap_minimap");
key[KeyType::FREEMOVE] = getKeySetting("keymap_freemove");
key[KeyType::FASTMOVE] = getKeySetting("keymap_fastmove");
key[KeyType::NOCLIP] = getKeySetting("keymap_noclip");
key[KeyType::HOTBAR_PREV] = getKeySetting("keymap_hotbar_previous");
key[KeyType::HOTBAR_NEXT] = getKeySetting("keymap_hotbar_next");
key[KeyType::MUTE] = getKeySetting("keymap_mute");
key[KeyType::INC_VOLUME] = getKeySetting("keymap_increase_volume");
key[KeyType::DEC_VOLUME] = getKeySetting("keymap_decrease_volume");
key[KeyType::CINEMATIC] = getKeySetting("keymap_cinematic");
key[KeyType::SCREENSHOT] = getKeySetting("keymap_screenshot");
key[KeyType::TOGGLE_HUD] = getKeySetting("keymap_toggle_hud");
key[KeyType::TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat");
key[KeyType::TOGGLE_FORCE_FOG_OFF] = getKeySetting("keymap_toggle_force_fog_off");
key[KeyType::TOGGLE_UPDATE_CAMERA] = getKeySetting("keymap_toggle_update_camera");
key[KeyType::TOGGLE_DEBUG] = getKeySetting("keymap_toggle_debug");
key[KeyType::TOGGLE_PROFILER] = getKeySetting("keymap_toggle_profiler");
key[KeyType::CAMERA_MODE] = getKeySetting("keymap_camera_mode");
key[KeyType::INCREASE_VIEWING_RANGE] =
getKeySetting("keymap_increase_viewing_range_min");
key[KeyType::DECREASE_VIEWING_RANGE] =
getKeySetting("keymap_decrease_viewing_range_min");
key[KeyType::RANGESELECT] = getKeySetting("keymap_rangeselect");
key[KeyType::ZOOM] = getKeySetting("keymap_zoom");
key[KeyType::QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next");
key[KeyType::QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev");
key[KeyType::QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc");
key[KeyType::QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec");
for (int i = 0; i < 23; i++) {
std::string slot_key_name = "keymap_slot" + std::to_string(i + 1);
key[KeyType::SLOT_1 + i] = getKeySetting(slot_key_name.c_str());
}
if (handler) {
// First clear all keys, then re-add the ones we listen for
handler->dontListenForKeys();
for (const KeyPress &k : key) {
handler->listenForKey(k);
}
handler->listenForKey(EscapeKey);
handler->listenForKey(CancelKey);
}
}
bool MyEventReceiver::OnEvent(const SEvent &event) bool MyEventReceiver::OnEvent(const SEvent &event)
{ {
/* /*
@ -116,3 +185,72 @@ s32 RandomInputHandler::Rand(s32 min, s32 max)
{ {
return (myrand() % (max - min + 1)) + min; return (myrand() % (max - min + 1)) + min;
} }
void RandomInputHandler::step(float dtime)
{
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_jump"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_special1"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_forward"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_left"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 20);
mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 30);
leftdown = !leftdown;
if (leftdown)
leftclicked = true;
if (!leftdown)
leftreleased = true;
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 15);
rightdown = !rightdown;
if (rightdown)
rightclicked = true;
if (!rightdown)
rightreleased = true;
}
}
mousepos += mousespeed;
}

View File

@ -29,6 +29,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gui/touchscreengui.h" #include "gui/touchscreengui.h"
#endif #endif
class InputHandler;
/****************************************************************************
Fast key cache for main game loop
****************************************************************************/
/* This is faster than using getKeySetting with the tradeoff that functions
* using it must make sure that it's initialised before using it and there is
* no error handling (for example bounds checking). This is really intended for
* use only in the main running loop of the client (the_game()) where the faster
* (up to 10x faster) key lookup is an asset. Other parts of the codebase
* (e.g. formspecs) should continue using getKeySetting().
*/
struct KeyCache
{
KeyCache()
{
handler = NULL;
populate();
populate_nonchanging();
}
void populate();
// Keys that are not settings dependent
void populate_nonchanging();
KeyPress key[KeyType::INTERNAL_ENUM_COUNT];
InputHandler *handler;
};
class KeyList : private std::list<KeyPress> class KeyList : private std::list<KeyPress>
{ {
typedef std::list<KeyPress> super; typedef std::list<KeyPress> super;
@ -179,12 +211,17 @@ private:
class InputHandler class InputHandler
{ {
public: public:
InputHandler() = default; InputHandler()
{
keycache.handler = this;
keycache.populate();
}
virtual ~InputHandler() = default; virtual ~InputHandler() = default;
virtual bool isKeyDown(const KeyPress &keyCode) = 0; virtual bool isKeyDown(GameKeyType k) = 0;
virtual bool wasKeyDown(const KeyPress &keyCode) = 0; virtual bool wasKeyDown(GameKeyType k) = 0;
virtual bool cancelPressed() = 0;
virtual void listenForKey(const KeyPress &keyCode) {} virtual void listenForKey(const KeyPress &keyCode) {}
virtual void dontListenForKeys() {} virtual void dontListenForKeys() {}
@ -212,6 +249,7 @@ public:
virtual void clear() {} virtual void clear() {}
JoystickController joystick; JoystickController joystick;
KeyCache keycache;
}; };
/* /*
Separated input handler Separated input handler
@ -224,13 +262,17 @@ public:
{ {
m_receiver->joystick = &joystick; m_receiver->joystick = &joystick;
} }
virtual bool isKeyDown(const KeyPress &keyCode) virtual bool isKeyDown(GameKeyType k)
{ {
return m_receiver->IsKeyDown(keyCode); return m_receiver->IsKeyDown(keycache.key[k]) || joystick.isKeyDown(k);
} }
virtual bool wasKeyDown(const KeyPress &keyCode) virtual bool wasKeyDown(GameKeyType k)
{ {
return m_receiver->WasKeyDown(keyCode); return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k);
}
virtual bool cancelPressed()
{
return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey);
} }
virtual void listenForKey(const KeyPress &keyCode) virtual void listenForKey(const KeyPress &keyCode)
{ {
@ -259,18 +301,58 @@ public:
} }
} }
virtual bool getLeftState() { return m_receiver->left_active; } virtual bool getLeftState()
virtual bool getRightState() { return m_receiver->right_active; } {
return m_receiver->left_active || joystick.isKeyDown(KeyType::MOUSE_L);
}
virtual bool getRightState()
{
return m_receiver->right_active || joystick.isKeyDown(KeyType::MOUSE_R);
}
virtual bool getLeftClicked() { return m_receiver->leftclicked; } virtual bool getLeftClicked()
virtual bool getRightClicked() { return m_receiver->rightclicked; } {
virtual void resetLeftClicked() { m_receiver->leftclicked = false; } return m_receiver->leftclicked ||
virtual void resetRightClicked() { m_receiver->rightclicked = false; } joystick.getWasKeyDown(KeyType::MOUSE_L);
}
virtual bool getRightClicked()
{
return m_receiver->rightclicked ||
joystick.getWasKeyDown(KeyType::MOUSE_R);
}
virtual bool getLeftReleased() { return m_receiver->leftreleased; } virtual void resetLeftClicked()
virtual bool getRightReleased() { return m_receiver->rightreleased; } {
virtual void resetLeftReleased() { m_receiver->leftreleased = false; } m_receiver->leftclicked = false;
virtual void resetRightReleased() { m_receiver->rightreleased = false; } joystick.clearWasKeyDown(KeyType::MOUSE_L);
}
virtual void resetRightClicked()
{
m_receiver->rightclicked = false;
joystick.clearWasKeyDown(KeyType::MOUSE_R);
}
virtual bool getLeftReleased()
{
return m_receiver->leftreleased ||
joystick.wasKeyReleased(KeyType::MOUSE_L);
}
virtual bool getRightReleased()
{
return m_receiver->rightreleased ||
joystick.wasKeyReleased(KeyType::MOUSE_R);
}
virtual void resetLeftReleased()
{
m_receiver->leftreleased = false;
joystick.clearWasKeyReleased(KeyType::MOUSE_L);
}
virtual void resetRightReleased()
{
m_receiver->rightreleased = false;
joystick.clearWasKeyReleased(KeyType::MOUSE_R);
}
virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); } virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); }
@ -290,8 +372,9 @@ class RandomInputHandler : public InputHandler
public: public:
RandomInputHandler() = default; RandomInputHandler() = default;
virtual bool isKeyDown(const KeyPress &keyCode) { return keydown[keyCode]; } virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; }
virtual bool wasKeyDown(const KeyPress &keyCode) { return false; } virtual bool wasKeyDown(GameKeyType k) { return false; }
virtual bool cancelPressed() { return false; }
virtual v2s32 getMousePos() { return mousepos; } virtual v2s32 getMousePos() { return mousepos; }
virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }
@ -310,74 +393,7 @@ public:
virtual s32 getMouseWheel() { return 0; } virtual s32 getMouseWheel() { return 0; }
virtual void step(float dtime) virtual void step(float dtime);
{
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_jump"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_special1"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_forward"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_left"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 20);
mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 30);
leftdown = !leftdown;
if (leftdown)
leftclicked = true;
if (!leftdown)
leftreleased = true;
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 15);
rightdown = !rightdown;
if (rightdown)
rightclicked = true;
if (!rightdown)
rightreleased = true;
}
}
mousepos += mousespeed;
}
s32 Rand(s32 min, s32 max); s32 Rand(s32 min, s32 max);

View File

@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sidebyside.h" #include "sidebyside.h"
#include <ICameraSceneNode.h> #include <ICameraSceneNode.h>
#include "hud.h" #include "client/hud.h"
RenderingCoreSideBySide::RenderingCoreSideBySide( RenderingCoreSideBySide::RenderingCoreSideBySide(
IrrlichtDevice *_device, Client *_client, Hud *_hud, bool _horizontal) IrrlichtDevice *_device, Client *_client, Hud *_hud, bool _horizontal)

View File

@ -25,8 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clouds.h" #include "clouds.h"
#include "util/numeric.h" #include "util/numeric.h"
#include "guiscalingfilter.h" #include "guiscalingfilter.h"
#include "hud.h"
#include "localplayer.h" #include "localplayer.h"
#include "client/hud.h"
#include "camera.h" #include "camera.h"
#include "minimap.h" #include "minimap.h"
#include "clientmap.h" #include "clientmap.h"
@ -410,6 +410,26 @@ void RenderingEngine::_draw_load_screen(const std::wstring &text,
guitext->remove(); guitext->remove();
} }
/*
Draws the menu scene including (optional) cloud background.
*/
void RenderingEngine::_draw_menu_scene(gui::IGUIEnvironment *guienv,
float dtime, bool clouds)
{
bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
if (cloud_menu_background) {
g_menuclouds->step(dtime * 3);
g_menuclouds->render();
get_video_driver()->beginScene(
true, true, video::SColor(255, 140, 186, 250));
g_menucloudsmgr->drawAll();
} else
get_video_driver()->beginScene(true, true, video::SColor(255, 0, 0, 0));
guienv->drawAll();
get_video_driver()->endScene();
}
std::vector<core::vector3d<u32>> RenderingEngine::getSupportedVideoModes() std::vector<core::vector3d<u32>> RenderingEngine::getSupportedVideoModes()
{ {
IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL); IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);

View File

@ -110,6 +110,12 @@ public:
text, guienv, tsrc, dtime, percent, clouds); text, guienv, tsrc, dtime, percent, clouds);
} }
inline static void draw_menu_scene(
gui::IGUIEnvironment *guienv, float dtime, bool clouds)
{
s_singleton->_draw_menu_scene(guienv, dtime, clouds);
}
inline static void draw_scene(video::SColor skycolor, bool show_hud, inline static void draw_scene(video::SColor skycolor, bool show_hud,
bool show_minimap, bool draw_wield_tool, bool draw_crosshair) bool show_minimap, bool draw_wield_tool, bool draw_crosshair)
{ {
@ -138,6 +144,9 @@ private:
ITextureSource *tsrc, float dtime = 0, int percent = 0, ITextureSource *tsrc, float dtime = 0, int percent = 0,
bool clouds = true); bool clouds = true);
void _draw_menu_scene(gui::IGUIEnvironment *guienv, float dtime = 0,
bool clouds = true);
void _draw_scene(video::SColor skycolor, bool show_hud, bool show_minimap, void _draw_scene(video::SColor skycolor, bool show_hud, bool show_minimap,
bool draw_wield_tool, bool draw_crosshair); bool draw_wield_tool, bool draw_crosshair);

View File

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "raycast.h" #include "raycast.h"
#include "voxelalgorithms.h" #include "voxelalgorithms.h"
#include "settings.h" #include "settings.h"
#include "content_cao.h"
#include <algorithm> #include <algorithm>
#include "client/renderingengine.h" #include "client/renderingengine.h"
@ -207,6 +208,8 @@ void ClientEnvironment::step(float dtime)
//std::cout<<"Looped "<<loopcount<<" times."<<std::endl; //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal();
for (const CollisionInfo &info : player_collisions) { for (const CollisionInfo &info : player_collisions) {
v3f speed_diff = info.new_speed - info.old_speed;; v3f speed_diff = info.new_speed - info.old_speed;;
// Handle only fall damage // Handle only fall damage
@ -227,7 +230,7 @@ void ClientEnvironment::step(float dtime)
pre_factor = 1.0 + (float)addp/100.0; pre_factor = 1.0 + (float)addp/100.0;
} }
float speed = pre_factor * speed_diff.getLength(); float speed = pre_factor * speed_diff.getLength();
if (speed > tolerance) { if (speed > tolerance && !player_immortal) {
f32 damage_f = (speed - tolerance) / BS * post_factor; f32 damage_f = (speed - tolerance) / BS * post_factor;
u8 damage = (u8)MYMIN(damage_f + 0.5, 255); u8 damage = (u8)MYMIN(damage_f + 0.5, 255);
if (damage != 0) { if (damage != 0) {

View File

@ -199,7 +199,7 @@ void RemoteClient::GetNextBlocks (
const s16 full_d_max = std::min(adjustDist(m_max_send_distance, camera_fov), wanted_range); const s16 full_d_max = std::min(adjustDist(m_max_send_distance, camera_fov), wanted_range);
const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, camera_fov), wanted_range); const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, camera_fov), wanted_range);
const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE; const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;
infostream << "Fov from client " << camera_fov << " full_d_max " << full_d_max << std::endl; //infostream << "Fov from client " << camera_fov << " full_d_max " << full_d_max << std::endl;
s16 d_max = full_d_max; s16 d_max = full_d_max;
s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, camera_fov), wanted_range); s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, camera_fov), wanted_range);

View File

@ -360,6 +360,11 @@ v3f GenericCAO::getPosition()
return pos_translator.vect_show; return pos_translator.vect_show;
} }
const bool GenericCAO::isImmortal()
{
return itemgroup_get(getGroups(), "immortal");
}
scene::ISceneNode* GenericCAO::getSceneNode() scene::ISceneNode* GenericCAO::getSceneNode()
{ {
if (m_meshnode) { if (m_meshnode) {
@ -563,7 +568,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
} }
else if(m_prop.visual == "mesh") { else if(m_prop.visual == "mesh") {
infostream<<"GenericCAO::addToScene(): mesh"<<std::endl; infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh); scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh, true);
if(mesh) if(mesh)
{ {
m_animated_meshnode = RenderingEngine::get_scene_manager()-> m_animated_meshnode = RenderingEngine::get_scene_manager()->
@ -575,13 +580,17 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
m_prop.visual_size.Y, m_prop.visual_size.Y,
m_prop.visual_size.X)); m_prop.visual_size.X));
u8 li = m_last_light; u8 li = m_last_light;
// set vertex colors to ensure alpha is set
setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li)); setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
setAnimatedMeshColor(m_animated_meshnode, video::SColor(255,li,li,li));
bool backface_culling = m_prop.backface_culling; bool backface_culling = m_prop.backface_culling;
if (m_is_player) if (m_is_player)
backface_culling = false; backface_culling = false;
m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, false); m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
m_animated_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF); m_animated_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true); m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
@ -669,7 +678,7 @@ void GenericCAO::updateLightNoCheck(u8 light_at_pos)
if (m_meshnode) { if (m_meshnode) {
setMeshColor(m_meshnode->getMesh(), color); setMeshColor(m_meshnode->getMesh(), color);
} else if (m_animated_meshnode) { } else if (m_animated_meshnode) {
setMeshColor(m_animated_meshnode->getMesh(), color); setAnimatedMeshColor(m_animated_meshnode, color);
} else if (m_wield_meshnode) { } else if (m_wield_meshnode) {
m_wield_meshnode->setColor(color); m_wield_meshnode->setColor(color);
} else if (m_spritenode) { } else if (m_spritenode) {
@ -904,19 +913,19 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
} }
if (!getParent() && m_prop.automatic_face_movement_dir && if (!getParent() && m_prop.automatic_face_movement_dir &&
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
{
float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
+ m_prop.automatic_face_movement_dir_offset; + m_prop.automatic_face_movement_dir_offset;
float max_rotation_delta = float max_rotation_delta =
dtime * m_prop.automatic_face_movement_max_rotation_per_sec; dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
float delta = wrapDegrees_0_360(target_yaw - m_yaw);
if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) && if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
(fabs(m_yaw - optimal_yaw) > max_rotation_delta)) { m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
m_yaw = wrapDegrees_0_360(m_yaw);
m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
} else { } else {
m_yaw = optimal_yaw; m_yaw = target_yaw;
} }
updateNodePos(); updateNodePos();
} }
@ -1025,7 +1034,7 @@ void GenericCAO::updateTextures(std::string mod)
// Set material flags and texture // Set material flags and texture
video::SMaterial& material = m_animated_meshnode->getMaterial(i); video::SMaterial& material = m_animated_meshnode->getMaterial(i);
material.TextureLayer[0].Texture = texture; material.TextureLayer[0].Texture = texture;
material.setFlag(video::EMF_LIGHTING, false); material.setFlag(video::EMF_LIGHTING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_BILINEAR_FILTER, false);
// don't filter low-res textures, makes them look blurry // don't filter low-res textures, makes them look blurry
@ -1130,10 +1139,12 @@ void GenericCAO::updateTextures(std::string mod)
buf->getMaterial().AmbientColor = m_prop.colors[1]; buf->getMaterial().AmbientColor = m_prop.colors[1];
buf->getMaterial().DiffuseColor = m_prop.colors[1]; buf->getMaterial().DiffuseColor = m_prop.colors[1];
buf->getMaterial().SpecularColor = m_prop.colors[1]; buf->getMaterial().SpecularColor = m_prop.colors[1];
setMeshColor(mesh, m_prop.colors[1]);
} else if (!m_prop.colors.empty()) { } else if (!m_prop.colors.empty()) {
buf->getMaterial().AmbientColor = m_prop.colors[0]; buf->getMaterial().AmbientColor = m_prop.colors[0];
buf->getMaterial().DiffuseColor = m_prop.colors[0]; buf->getMaterial().DiffuseColor = m_prop.colors[0];
buf->getMaterial().SpecularColor = m_prop.colors[0]; buf->getMaterial().SpecularColor = m_prop.colors[0];
setMeshColor(mesh, m_prop.colors[0]);
} }
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter); buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
@ -1259,8 +1270,8 @@ void GenericCAO::processMessage(const std::string &data)
collision_box.MinEdge *= BS; collision_box.MinEdge *= BS;
collision_box.MaxEdge *= BS; collision_box.MaxEdge *= BS;
player->setCollisionbox(collision_box); player->setCollisionbox(collision_box);
player->setCanZoom(m_prop.can_zoom);
player->setEyeHeight(m_prop.eye_height); player->setEyeHeight(m_prop.eye_height);
player->setZoomFOV(m_prop.zoom_fov);
} }
if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty())

View File

@ -124,7 +124,10 @@ public:
{ {
return ACTIVEOBJECT_TYPE_GENERIC; return ACTIVEOBJECT_TYPE_GENERIC;
} }
inline const ItemGroupList &getGroups() const
{
return m_armor_groups;
}
void initialize(const std::string &data); void initialize(const std::string &data);
void processInitData(const std::string &data); void processInitData(const std::string &data);
@ -143,6 +146,8 @@ public:
return m_yaw; return m_yaw;
} }
const bool isImmortal();
scene::ISceneNode *getSceneNode(); scene::ISceneNode *getSceneNode();
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(); scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode();

View File

@ -385,9 +385,16 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing()
getSpecialTile(1, &tile_liquid); getSpecialTile(1, &tile_liquid);
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z)); MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
c_flowing = nodedef->getId(f->liquid_alternative_flowing); c_flowing = nodedef->getId(f->liquid_alternative_flowing);
c_source = nodedef->getId(f->liquid_alternative_source); c_source = nodedef->getId(f->liquid_alternative_source);
top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source); top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
if (draw_liquid_bottom) {
const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
if (f2.solidness > 1)
draw_liquid_bottom = false;
}
if (data->m_smooth_lighting) if (data->m_smooth_lighting)
return; // don't need to pre-compute anything in this case return; // don't need to pre-compute anything in this case
@ -595,6 +602,24 @@ void MapblockMeshGenerator::drawLiquidTop()
collector->append(tile_liquid_top, vertices, 4, quad_indices, 6); collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
} }
void MapblockMeshGenerator::drawLiquidBottom()
{
video::S3DVertex vertices[4] = {
video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
};
for (int i = 0; i < 4; i++) {
if (data->m_smooth_lighting)
vertices[i].Color = blendLightColor(vertices[i].Pos);
vertices[i].Pos += origin;
}
collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
}
void MapblockMeshGenerator::drawLiquidNode() void MapblockMeshGenerator::drawLiquidNode()
{ {
prepareLiquidNodeDrawing(); prepareLiquidNodeDrawing();
@ -603,6 +628,8 @@ void MapblockMeshGenerator::drawLiquidNode()
drawLiquidSides(); drawLiquidSides();
if (!top_is_same_liquid) if (!top_is_same_liquid)
drawLiquidTop(); drawLiquidTop();
if (draw_liquid_bottom)
drawLiquidBottom();
} }
void MapblockMeshGenerator::drawGlasslikeNode() void MapblockMeshGenerator::drawGlasslikeNode()

View File

@ -92,6 +92,7 @@ public:
// liquid-specific // liquid-specific
bool top_is_same_liquid; bool top_is_same_liquid;
bool draw_liquid_bottom;
TileSpec tile_liquid; TileSpec tile_liquid;
TileSpec tile_liquid_top; TileSpec tile_liquid_top;
content_t c_flowing; content_t c_flowing;
@ -112,6 +113,7 @@ public:
f32 getCornerLevel(int i, int k); f32 getCornerLevel(int i, int k);
void drawLiquidSides(); void drawLiquidSides();
void drawLiquidTop(); void drawLiquidTop();
void drawLiquidBottom();
// raillike-specific // raillike-specific
// name of the group that enables connecting to raillike nodes of different kind // name of the group that enables connecting to raillike nodes of different kind

View File

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h" #include "server.h"
#include "scripting_server.h" #include "scripting_server.h"
#include "genericobject.h" #include "genericobject.h"
#include "settings.h"
#include <algorithm> #include <algorithm>
std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types; std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
@ -372,20 +373,20 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_velocity += dtime * m_acceleration; m_velocity += dtime * m_acceleration;
} }
if((m_prop.automatic_face_movement_dir) && if (m_prop.automatic_face_movement_dir &&
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
{
float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
+ m_prop.automatic_face_movement_dir_offset; + m_prop.automatic_face_movement_dir_offset;
float max_rotation_delta = float max_rotation_delta =
dtime * m_prop.automatic_face_movement_max_rotation_per_sec; dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
float delta = wrapDegrees_0_360(target_yaw - m_yaw);
if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) && if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
(fabs(m_yaw - optimal_yaw) > max_rotation_delta)) { m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
m_yaw = wrapDegrees_0_360(m_yaw);
m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
} else { } else {
m_yaw = optimal_yaw; m_yaw = target_yaw;
} }
} }
} }
@ -801,7 +802,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p
m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
m_prop.pointable = true; m_prop.pointable = true;
// start of default appearance, this should be overwritten by LUA // Start of default appearance, this should be overwritten by Lua
m_prop.visual = "upright_sprite"; m_prop.visual = "upright_sprite";
m_prop.visual_size = v2f(1, 2); m_prop.visual_size = v2f(1, 2);
m_prop.textures.clear(); m_prop.textures.clear();
@ -811,13 +812,14 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p
m_prop.colors.emplace_back(255, 255, 255, 255); m_prop.colors.emplace_back(255, 255, 255, 255);
m_prop.spritediv = v2s16(1,1); m_prop.spritediv = v2s16(1,1);
m_prop.eye_height = 1.625f; m_prop.eye_height = 1.625f;
// end of default appearance // End of default appearance
m_prop.is_visible = true; m_prop.is_visible = true;
m_prop.makes_footstep_sound = true; m_prop.makes_footstep_sound = true;
m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
m_prop.can_zoom = true;
m_hp = m_prop.hp_max; m_hp = m_prop.hp_max;
m_breath = m_prop.breath_max; m_breath = m_prop.breath_max;
// Disable zoom in survival mode using a value of 0
m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
} }
PlayerSAO::~PlayerSAO() PlayerSAO::~PlayerSAO()
@ -1399,26 +1401,38 @@ bool PlayerSAO::checkMovementCheat()
too, and much more lightweight. too, and much more lightweight.
*/ */
float player_max_speed = 0; float player_max_walk = 0; // horizontal movement
float player_max_jump = 0; // vertical upwards movement
if (m_privs.count("fast") != 0) { if (m_privs.count("fast") != 0)
// Fast speed player_max_walk = m_player->movement_speed_fast; // Fast speed
player_max_speed = m_player->movement_speed_fast * m_physics_override_speed; else
} else { player_max_walk = m_player->movement_speed_walk; // Normal speed
// Normal speed player_max_walk *= m_physics_override_speed;
player_max_speed = m_player->movement_speed_walk * m_physics_override_speed; player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
} // FIXME: Bouncy nodes cause practically unbound increase in Y speed,
// Tolerance. The lag pool does this a bit. // until this can be verified correctly, tolerate higher jumping speeds
//player_max_speed *= 2.5; player_max_jump *= 2.0;
// Don't divide by zero!
if (player_max_walk < 0.0001f)
player_max_walk = 0.0001f;
if (player_max_jump < 0.0001f)
player_max_jump = 0.0001f;
v3f diff = (m_base_position - m_last_good_position); v3f diff = (m_base_position - m_last_good_position);
float d_vert = diff.Y; float d_vert = diff.Y;
diff.Y = 0; diff.Y = 0;
float d_horiz = diff.getLength(); float d_horiz = diff.getLength();
float required_time = d_horiz / player_max_speed; float required_time = d_horiz / player_max_walk;
if (d_vert > 0 && d_vert / player_max_speed > required_time) // FIXME: Checking downwards movement is not easily possible currently,
required_time = d_vert / player_max_speed; // Moving upwards // the server could calculate speed differences to examine the gravity
if (d_vert > 0) {
// In certain cases (water, ladders) walking speed is applied vertically
float s = MYMAX(player_max_jump, player_max_walk);
required_time = MYMAX(required_time, d_vert / s);
}
if (m_move_pool.grab(required_time)) { if (m_move_pool.grab(required_time)) {
m_last_good_position = m_base_position; m_last_good_position = m_base_position;

View File

@ -58,6 +58,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_remote_media_server", "true"); settings->setDefault("enable_remote_media_server", "true");
settings->setDefault("enable_client_modding", "false"); settings->setDefault("enable_client_modding", "false");
settings->setDefault("max_out_chat_queue_size", "20"); settings->setDefault("max_out_chat_queue_size", "20");
settings->setDefault("pause_on_lost_focus", "false");
// Keymap // Keymap
settings->setDefault("remote_port", "30000"); settings->setDefault("remote_port", "30000");
@ -147,7 +148,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("3d_paralax_strength", "0.025"); settings->setDefault("3d_paralax_strength", "0.025");
settings->setDefault("tooltip_show_delay", "400"); settings->setDefault("tooltip_show_delay", "400");
settings->setDefault("tooltip_append_itemname", "false"); settings->setDefault("tooltip_append_itemname", "false");
settings->setDefault("zoom_fov", "15");
settings->setDefault("fps_max", "60"); settings->setDefault("fps_max", "60");
settings->setDefault("pause_fps_max", "20"); settings->setDefault("pause_fps_max", "20");
settings->setDefault("viewing_range", "100"); settings->setDefault("viewing_range", "100");
@ -163,8 +163,11 @@ void set_default_settings(Settings *settings)
settings->setDefault("connected_glass", "false"); settings->setDefault("connected_glass", "false");
settings->setDefault("smooth_lighting", "true"); settings->setDefault("smooth_lighting", "true");
settings->setDefault("lighting_alpha", "0.0"); settings->setDefault("lighting_alpha", "0.0");
settings->setDefault("lighting_beta", "0.0"); settings->setDefault("lighting_beta", "1.5");
settings->setDefault("display_gamma", "1.0"); settings->setDefault("display_gamma", "1.0");
settings->setDefault("lighting_boost", "0.2");
settings->setDefault("lighting_boost_center", "0.5");
settings->setDefault("lighting_boost_spread", "0.2");
settings->setDefault("texture_path", ""); settings->setDefault("texture_path", "");
settings->setDefault("shader_path", ""); settings->setDefault("shader_path", "");
settings->setDefault("video_driver", "opengl"); settings->setDefault("video_driver", "opengl");
@ -178,7 +181,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("cloud_radius", "12"); settings->setDefault("cloud_radius", "12");
settings->setDefault("menu_clouds", "true"); settings->setDefault("menu_clouds", "true");
settings->setDefault("opaque_water", "false"); settings->setDefault("opaque_water", "false");
settings->setDefault("console_height", "1.0"); settings->setDefault("console_height", "0.6");
settings->setDefault("console_color", "(0,0,0)"); settings->setDefault("console_color", "(0,0,0)");
settings->setDefault("console_alpha", "200"); settings->setDefault("console_alpha", "200");
settings->setDefault("formspec_fullscreen_bg_color", "(0,0,0)"); settings->setDefault("formspec_fullscreen_bg_color", "(0,0,0)");
@ -327,7 +330,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_block_send_distance", "9"); settings->setDefault("max_block_send_distance", "9");
settings->setDefault("block_send_optimize_distance", "4"); settings->setDefault("block_send_optimize_distance", "4");
settings->setDefault("server_side_occlusion_culling", "true"); settings->setDefault("server_side_occlusion_culling", "true");
settings->setDefault("csm_flavour_limits", "3"); settings->setDefault("csm_flavour_limits", "18");
settings->setDefault("csm_flavour_noderange_limit", "8"); settings->setDefault("csm_flavour_noderange_limit", "8");
settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096"); settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
settings->setDefault("time_speed", "72"); settings->setDefault("time_speed", "72");
@ -347,6 +350,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("ignore_world_load_errors", "false"); settings->setDefault("ignore_world_load_errors", "false");
settings->setDefault("remote_media", ""); settings->setDefault("remote_media", "");
settings->setDefault("debug_log_level", "action"); settings->setDefault("debug_log_level", "action");
settings->setDefault("log_color", "detect");
settings->setDefault("emergequeue_limit_total", "256"); settings->setDefault("emergequeue_limit_total", "256");
settings->setDefault("emergequeue_limit_diskonly", "32"); settings->setDefault("emergequeue_limit_diskonly", "32");
settings->setDefault("emergequeue_limit_generate", "32"); settings->setDefault("emergequeue_limit_generate", "32");

File diff suppressed because it is too large Load Diff

View File

@ -26,16 +26,19 @@ class InputHandler;
class ChatBackend; /* to avoid having to include chat.h */ class ChatBackend; /* to avoid having to include chat.h */
struct SubgameSpec; struct SubgameSpec;
// Flags that can, or may, change during main game loop struct Jitter {
struct GameUIFlags f32 max, min, avg, counter, max_sample, min_sample, max_fraction;
{ };
bool show_chat;
bool show_hud; struct RunStats {
bool show_minimap; u32 drawtime;
bool force_fog_off;
bool show_debug; Jitter dtime_jitter, busy_time_jitter;
bool show_profiler_graph; };
bool disable_camera_update;
struct CameraOrientation {
f32 camera_yaw; // "right/left"
f32 camera_pitch; // "up/down"
}; };
void the_game(bool *kill, void the_game(bool *kill,

View File

@ -1,5 +1,6 @@
set(gui_SRCS set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
@ -9,5 +10,6 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp
${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp
PARENT_SCOPE PARENT_SCOPE
) )

View File

@ -0,0 +1,232 @@
/*
Minetest
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "guiConfirmRegistration.h"
#include "client.h"
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "gettext.h"
// Continuing from guiPasswordChange.cpp
const int ID_confirmPassword = 262;
const int ID_confirm = 263;
const int ID_message = 264;
const int ID_cancel = 265;
GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
const std::string &address, bool *aborted) :
GUIModalMenu(env, parent, id, menumgr),
m_client(client), m_playername(playername), m_password(password),
m_address(address), m_aborted(aborted)
{
}
GUIConfirmRegistration::~GUIConfirmRegistration()
{
removeChildren();
}
void GUIConfirmRegistration::removeChildren()
{
const core::list<gui::IGUIElement *> &children = getChildren();
core::list<gui::IGUIElement *> children_copy;
for (gui::IGUIElement *i : children)
children_copy.push_back(i);
for (gui::IGUIElement *i : children_copy)
i->remove();
}
void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
{
acceptInput();
removeChildren();
/*
Calculate new sizes and positions
*/
core::rect<s32> rect(screensize.X / 2 - 600 / 2, screensize.Y / 2 - 300 / 2,
screensize.X / 2 + 600 / 2, screensize.Y / 2 + 300 / 2);
DesiredRect = rect;
recalculateAbsolutePosition(false);
v2s32 size = rect.getSize();
v2s32 topleft_client(0, 0);
const wchar_t *text;
/*
Add stuff
*/
s32 ypos = 30;
{
std::string address = m_address;
if (address.empty())
address = "localhost";
core::rect<s32> rect(0, 0, 540, 90);
rect += topleft_client + v2s32(30, ypos);
static const std::string info_text_template = strgettext(
"You are about to join the server at %1$s with the "
"name \"%2$s\" for the first time. If you proceed, a "
"new account using your credentials will be created "
"on this server.\n"
"Please retype your password and click Register and "
"Join to confirm account creation or click Cancel to "
"abort.");
char info_text_buf[1024];
snprintf(info_text_buf, sizeof(info_text_buf), info_text_template.c_str(),
address.c_str(), m_playername.c_str());
Environment->addStaticText(narrow_to_wide_c(info_text_buf), rect, false,
true, this, -1);
}
ypos += 120;
{
core::rect<s32> rect(0, 0, 540, 30);
rect += topleft_client + v2s32(30, ypos);
gui::IGUIEditBox *e = Environment->addEditBox(m_pass_confirm.c_str(),
rect, true, this, ID_confirmPassword);
e->setPasswordBox(true);
}
ypos += 90;
{
core::rect<s32> rect(0, 0, 230, 35);
rect = rect + v2s32(size.X / 2 - 220, ypos);
text = wgettext("Register and Join");
Environment->addButton(rect, this, ID_confirm, text);
delete[] text;
}
{
core::rect<s32> rect(0, 0, 120, 35);
rect = rect + v2s32(size.X / 2 + 70, ypos);
text = wgettext("Cancel");
Environment->addButton(rect, this, ID_cancel, text);
delete[] text;
}
{
core::rect<s32> rect(0, 0, 200, 20);
rect += topleft_client + v2s32(30, ypos - 40);
text = wgettext("Passwords do not match!");
IGUIElement *e = Environment->addStaticText(
text, rect, false, true, this, ID_message);
e->setVisible(false);
delete[] text;
}
}
void GUIConfirmRegistration::drawMenu()
{
gui::IGUISkin *skin = Environment->getSkin();
if (!skin)
return;
video::IVideoDriver *driver = Environment->getVideoDriver();
video::SColor bgcolor(140, 0, 0, 0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
gui::IGUIElement::draw();
}
void GUIConfirmRegistration::closeMenu(bool goNext)
{
quitMenu();
if (goNext) {
m_client->confirmRegistration();
} else {
*m_aborted = true;
infostream << "Connect aborted [Escape]" << std::endl;
}
}
void GUIConfirmRegistration::acceptInput()
{
gui::IGUIElement *e;
e = getElementFromId(ID_confirmPassword);
if (e)
m_pass_confirm = e->getText();
}
bool GUIConfirmRegistration::processInput()
{
std::wstring m_password_ws = narrow_to_wide(m_password);
if (m_password_ws != m_pass_confirm) {
gui::IGUIElement *e = getElementFromId(ID_message);
if (e)
e->setVisible(true);
return false;
}
return true;
}
bool GUIConfirmRegistration::OnEvent(const SEvent &event)
{
if (event.EventType == EET_KEY_INPUT_EVENT) {
if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
closeMenu(false);
return true;
}
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
acceptInput();
if (processInput())
closeMenu(true);
return true;
}
}
if (event.EventType != EET_GUI_EVENT)
return Parent ? Parent->OnEvent(event) : false;
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) {
if (!canTakeFocus(event.GUIEvent.Element)) {
dstream << "GUIConfirmRegistration: Not allowing focus "
"change."
<< std::endl;
// Returning true disables focus change
return true;
}
} else if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
switch (event.GUIEvent.Caller->getID()) {
case ID_confirm:
acceptInput();
if (processInput())
closeMenu(true);
return true;
case ID_cancel:
closeMenu(false);
return true;
}
} else if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
switch (event.GUIEvent.Caller->getID()) {
case ID_confirmPassword:
acceptInput();
if (processInput())
closeMenu(true);
return true;
}
}
return false;
}

View File

@ -0,0 +1,61 @@
/*
Minetest
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_extrabloated.h"
#include "modalMenu.h"
#include <string>
class Client;
class GUIConfirmRegistration : public GUIModalMenu
{
public:
GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
const std::string &address, bool *aborted);
~GUIConfirmRegistration();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/
void regenerateGui(v2u32 screensize);
void drawMenu();
void closeMenu(bool goNext);
void acceptInput();
bool processInput();
bool OnEvent(const SEvent &event);
private:
Client *m_client = nullptr;
const std::string &m_playername;
const std::string &m_password;
const std::string &m_address;
bool *m_aborted = nullptr;
std::wstring m_pass_confirm = L"";
};

View File

@ -149,9 +149,8 @@ GUIEngine::GUIEngine(JoystickController *joystick,
g_fontengine->getTextHeight()); g_fontengine->getTextHeight());
rect += v2s32(4, 0); rect += v2s32(4, 0);
m_irr_toplefttext = m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(),
addStaticText(RenderingEngine::get_gui_env(), m_toplefttext, m_toplefttext, rect, false, true, 0, -1);
rect, false, true, 0, -1);
//create formspecsource //create formspecsource
m_formspecgui = new FormspecFormSource(""); m_formspecgui = new FormspecFormSource("");
@ -560,9 +559,8 @@ void GUIEngine::updateTopLeftTextSize()
rect += v2s32(4, 0); rect += v2s32(4, 0);
m_irr_toplefttext->remove(); m_irr_toplefttext->remove();
m_irr_toplefttext = m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(),
addStaticText(RenderingEngine::get_gui_env(), m_toplefttext, m_toplefttext, rect, false, true, 0, -1);
rect, false, true, 0, -1);
} }
/******************************************************************************/ /******************************************************************************/

View File

@ -39,11 +39,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "log.h" #include "log.h"
#include "client/tile.h" // ITextureSource #include "client/tile.h" // ITextureSource
#include "hud.h" // drawItemStack #include "client/hud.h" // drawItemStack
#include "filesys.h" #include "filesys.h"
#include "gettime.h" #include "gettime.h"
#include "gettext.h" #include "gettext.h"
#include "scripting_server.h" #include "scripting_server.h"
#include "mainmenumanager.h"
#include "porting.h" #include "porting.h"
#include "settings.h" #include "settings.h"
#include "client.h" #include "client.h"
@ -57,6 +58,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
#include "intlGUIEditBox.h" #include "intlGUIEditBox.h"
#include "mainmenumanager.h"
#endif #endif
#define MY_CHECKPOS(a,b) \ #define MY_CHECKPOS(a,b) \
@ -129,6 +132,28 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
delete m_text_dst; delete m_text_dst;
} }
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest)
{
if (cur_formspec == nullptr) {
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
client, client->getTextureSource(), fs_src, txt_dest);
cur_formspec->doPause = false;
/*
Caution: do not call (*cur_formspec)->drop() here --
the reference might outlive the menu, so we will
periodically check if *cur_formspec is the only
remaining reference (i.e. the menu was removed)
and delete it in that case.
*/
} else {
cur_formspec->setFormSource(fs_src);
cur_formspec->setTextDest(txt_dest);
}
}
void GUIFormSpecMenu::removeChildren() void GUIFormSpecMenu::removeChildren()
{ {
const core::list<gui::IGUIElement*> &children = getChildren(); const core::list<gui::IGUIElement*> &children = getChildren();
@ -950,12 +975,12 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
Environment->setFocus(e); Environment->setFocus(e);
} }
if (label.length() >= 1) if (label.length() >= 1) {
{
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
this, 0);
} }
e->setPasswordBox(true,L'*'); e->setPasswordBox(true,L'*');
@ -1017,7 +1042,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if (name.empty()) { if (name.empty()) {
// spec field id to 0, this stops submit searching for a value that isn't there // spec field id to 0, this stops submit searching for a value that isn't there
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid); gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, this,
spec.fid);
} else { } else {
spec.send = true; spec.send = true;
gui::IGUIElement *e; gui::IGUIElement *e;
@ -1050,7 +1076,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
this, 0);
} }
} }
@ -1162,7 +1189,8 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>&
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
this, 0);
} }
if (parts.size() >= 6) { if (parts.size() >= 6) {
@ -1237,11 +1265,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
L"", L"",
258+m_fields.size() 258+m_fields.size()
); );
gui::IGUIStaticText *e = gui::IGUIStaticText *e = gui::StaticText::add(Environment,
addStaticText(Environment, spec.flabel.c_str(), spec.flabel.c_str(), rect, false, false, this, spec.fid);
rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
e->setTextAlignment(gui::EGUIA_UPPERLEFT,
gui::EGUIA_CENTER);
m_fields.push_back(spec); m_fields.push_back(spec);
} }
@ -1291,8 +1317,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
L"", L"",
258+m_fields.size() 258+m_fields.size()
); );
gui::IGUIStaticText *t = gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); rect, false, false, this, spec.fid);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
@ -2024,7 +2050,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{ {
assert(!m_tooltip_element); assert(!m_tooltip_element);
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18)); m_tooltip_element = gui::StaticText::add(Environment, L"",
core::rect<s32>(0, 0, 110, 18));
m_tooltip_element->enableOverrideColor(true); m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setDrawBackground(true); m_tooltip_element->setDrawBackground(true);
@ -3668,19 +3695,25 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_i = m_selected_item->i; a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a); m_invmgr->inventoryAction(a);
} else if (craft_amount > 0) { } else if (craft_amount > 0) {
assert(s.isValid());
// if there are no items selected or the selected item
// belongs to craftresult list, proceed with crafting
if (m_selected_item == NULL ||
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
m_selected_content_guess = ItemStack(); // Clear m_selected_content_guess = ItemStack(); // Clear
// Send IAction::Craft
assert(s.isValid());
assert(inv_s); assert(inv_s);
infostream << "Handing IAction::Craft to manager" << std::endl; // Send IACTION_CRAFT
infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction(); ICraftAction *a = new ICraftAction();
a->count = craft_amount; a->count = craft_amount;
a->craft_inv = s.inventoryloc; a->craft_inv = s.inventoryloc;
m_invmgr->inventoryAction(a); m_invmgr->inventoryAction(a);
} }
}
// If m_selected_amount has been decreased to zero, deselect // If m_selected_amount has been decreased to zero, deselect
if (m_selected_amount == 0) { if (m_selected_amount == 0) {

View File

@ -291,6 +291,9 @@ public:
~GUIFormSpecMenu(); ~GUIFormSpecMenu();
static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest);
void setFormSpec(const std::string &formspec_string, void setFormSpec(const std::string &formspec_string,
const InventoryLocation &current_inventory_location) const InventoryLocation &current_inventory_location)
{ {

View File

@ -1430,14 +1430,14 @@ void intlGUIEditBox::calculateScrollPos()
// todo: adjust scrollbar // todo: adjust scrollbar
} }
// vertical scroll position if (!WordWrap && !MultiLine)
if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos) return;
VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos) // vertical scroll position
VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos; if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y)
else VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards
VScrollPos = 0; else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y)
VScrollPos += CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y; // scrolling upwards
// todo: adjust scrollbar // todo: adjust scrollbar
if (m_vscrollbar) if (m_vscrollbar)

166
src/gui/profilergraph.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "profilergraph.h"
#include "util/string.h"
void ProfilerGraph::put(const Profiler::GraphValues &values)
{
m_log.emplace_back(values);
while (m_log.size() > m_log_max_size)
m_log.erase(m_log.begin());
}
void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
gui::IGUIFont *font) const
{
// Do *not* use UNORDERED_MAP here as the order needs
// to be the same for each call to prevent flickering
std::map<std::string, Meta> m_meta;
for (const Piece &piece : m_log) {
for (const auto &i : piece.values) {
const std::string &id = i.first;
const float &value = i.second;
std::map<std::string, Meta>::iterator j = m_meta.find(id);
if (j == m_meta.end()) {
m_meta[id] = Meta(value);
continue;
}
if (value < j->second.min)
j->second.min = value;
if (value > j->second.max)
j->second.max = value;
}
}
// Assign colors
static const video::SColor usable_colors[] = {video::SColor(255, 255, 100, 100),
video::SColor(255, 90, 225, 90),
video::SColor(255, 100, 100, 255),
video::SColor(255, 255, 150, 50),
video::SColor(255, 220, 220, 100)};
static const u32 usable_colors_count =
sizeof(usable_colors) / sizeof(*usable_colors);
u32 next_color_i = 0;
for (auto &i : m_meta) {
Meta &meta = i.second;
video::SColor color(255, 200, 200, 200);
if (next_color_i < usable_colors_count)
color = usable_colors[next_color_i++];
meta.color = color;
}
s32 graphh = 50;
s32 textx = x_left + m_log_max_size + 15;
s32 textx2 = textx + 200 - 15;
s32 meta_i = 0;
for (const auto &p : m_meta) {
const std::string &id = p.first;
const Meta &meta = p.second;
s32 x = x_left;
s32 y = y_bottom - meta_i * 50;
float show_min = meta.min;
float show_max = meta.max;
if (show_min >= -0.0001 && show_max >= -0.0001) {
if (show_min <= show_max * 0.5)
show_min = 0;
}
s32 texth = 15;
char buf[10];
snprintf(buf, 10, "%.3g", show_max);
font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - graphh, textx2,
y - graphh + texth),
meta.color);
snprintf(buf, 10, "%.3g", show_min);
font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - texth, textx2, y), meta.color);
font->draw(utf8_to_wide(id).c_str(),
core::rect<s32>(textx, y - graphh / 2 - texth / 2, textx2,
y - graphh / 2 + texth / 2),
meta.color);
s32 graph1y = y;
s32 graph1h = graphh;
bool relativegraph = (show_min != 0 && show_min != show_max);
float lastscaledvalue = 0.0;
bool lastscaledvalue_exists = false;
for (const Piece &piece : m_log) {
float value = 0;
bool value_exists = false;
Profiler::GraphValues::const_iterator k = piece.values.find(id);
if (k != piece.values.end()) {
value = k->second;
value_exists = true;
}
if (!value_exists) {
x++;
lastscaledvalue_exists = false;
continue;
}
float scaledvalue = 1.0;
if (show_max != show_min)
scaledvalue = (value - show_min) / (show_max - show_min);
if (scaledvalue == 1.0 && value == 0) {
x++;
lastscaledvalue_exists = false;
continue;
}
if (relativegraph) {
if (lastscaledvalue_exists) {
s32 ivalue1 = lastscaledvalue * graph1h;
s32 ivalue2 = scaledvalue * graph1h;
driver->draw2DLine(
v2s32(x - 1, graph1y - ivalue1),
v2s32(x, graph1y - ivalue2),
meta.color);
}
lastscaledvalue = scaledvalue;
lastscaledvalue_exists = true;
} else {
s32 ivalue = scaledvalue * graph1h;
driver->draw2DLine(v2s32(x, graph1y),
v2s32(x, graph1y - ivalue), meta.color);
}
x++;
}
meta_i++;
}
}

61
src/gui/profilergraph.h Normal file
View File

@ -0,0 +1,61 @@
/*
Minetest
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <SColor.h>
#include <deque>
#include <utility>
#include <IGUIFont.h>
#include <IVideoDriver.h>
#include "profiler.h"
/* Profiler display */
class ProfilerGraph
{
private:
struct Piece
{
Piece(Profiler::GraphValues v) : values(std::move(v)) {}
Profiler::GraphValues values;
};
struct Meta
{
float min;
float max;
video::SColor color;
Meta(float initial = 0,
video::SColor color = video::SColor(255, 255, 255, 255)) :
min(initial),
max(initial), color(color)
{
}
};
std::deque<Piece> m_log;
public:
u32 m_log_max_size = 200;
ProfilerGraph() = default;
void put(const Profiler::GraphValues &values);
void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
gui::IGUIFont *font) const;
};

View File

@ -1,775 +1,39 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "hud.h" #include "hud.h"
#include "settings.h"
#include "util/numeric.h"
#include "log.h"
#include "client.h"
#include "inventory.h"
#include "shader.h"
#include "client/tile.h"
#include "localplayer.h"
#include "camera.h"
#include "porting.h"
#include "fontengine.h"
#include "guiscalingfilter.h"
#include "mesh.h"
#include "wieldmesh.h"
#include "client/renderingengine.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "gui/touchscreengui.h"
#endif
Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, const struct EnumString es_HudElementType[] =
Inventory *inventory)
{ {
driver = RenderingEngine::get_video_driver(); {HUD_ELEM_IMAGE, "image"},
this->guienv = guienv; {HUD_ELEM_TEXT, "text"},
this->client = client; {HUD_ELEM_STATBAR, "statbar"},
this->player = player; {HUD_ELEM_INVENTORY, "inventory"},
this->inventory = inventory; {HUD_ELEM_WAYPOINT, "waypoint"},
{0, NULL},
m_hud_scaling = g_settings->getFloat("hud_scaling");
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5f);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
for (auto &hbar_color : hbar_colors)
hbar_color = video::SColor(255, 255, 255, 255);
tsrc = client->getTextureSource();
v3f crosshair_color = g_settings->getV3F("crosshair_color");
u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
m_selection_boxes.clear();
m_halo_boxes.clear();
std::string mode_setting = g_settings->get("node_highlighting");
if (mode_setting == "halo") {
m_mode = HIGHLIGHT_HALO;
} else if (mode_setting == "none") {
m_mode = HIGHLIGHT_NONE;
} else {
m_mode = HIGHLIGHT_BOX;
}
m_selection_material.Lighting = false;
if (g_settings->getBool("enable_shaders")) {
IShaderSource *shdrsrc = client->getShaderSource();
u16 shader_id = shdrsrc->getShader(
m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
} else {
m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
if (m_mode == HIGHLIGHT_BOX) {
m_selection_material.Thickness =
rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
} else if (m_mode == HIGHLIGHT_HALO) {
m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
} else {
m_selection_material.MaterialType = video::EMT_SOLID;
}
}
Hud::~Hud()
{
if (m_selection_mesh)
m_selection_mesh->drop();
}
void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
bool selected)
{
if (selected) {
/* draw hihlighting around selected item */
if (use_hotbar_selected_image) {
core::rect<s32> imgrect2 = rect;
imgrect2.UpperLeftCorner.X -= (m_padding*2);
imgrect2.UpperLeftCorner.Y -= (m_padding*2);
imgrect2.LowerRightCorner.X += (m_padding*2);
imgrect2.LowerRightCorner.Y += (m_padding*2);
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, imgrect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
} else {
video::SColor c_outside(255,255,0,0);
//video::SColor c_outside(255,0,0,0);
//video::SColor c_inside(255,192,192,192);
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
// Black base borders
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1 - m_padding),
v2s32(x2 + m_padding, y1)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y2),
v2s32(x2 + m_padding, y2 + m_padding)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + m_padding, y2)
), NULL);
/*// Light inside borders
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1 - padding/2),
v2s32(x2 + padding/2, y1)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y2),
v2s32(x2 + padding/2, y2 + padding/2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + padding/2, y2)
), NULL);
*/
}
}
video::SColor bgcolor2(128, 0, 0, 0);
if (!use_hotbar_image)
driver->draw2DRectangle(bgcolor2, rect, NULL);
drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
}
//NOTE: selectitem = 0 -> no selected; selectitem 1-based
void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
{
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui && inv_offset == 0)
g_touchscreengui->resetHud();
#endif
s32 height = m_hotbar_imagesize + m_padding * 2;
s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
s32 tmp = height;
height = width;
width = tmp;
}
// Position of upper left corner of bar
v2s32 pos = screen_offset;
pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos += upperleftpos;
// Store hotbar_image in member variable, used by drawItem()
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
if (!hotbar_image.empty())
use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
else
use_hotbar_image = false;
}
// Store hotbar_selected_image in member variable, used by drawItem()
if (hotbar_selected_image != player->hotbar_selected_image) {
hotbar_selected_image = player->hotbar_selected_image;
if (!hotbar_selected_image.empty())
use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
else
use_hotbar_selected_image = false;
}
// draw customized item background
if (use_hotbar_image) {
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
}
// Draw items
core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
v2s32 steppos;
switch (direction) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
break;
default:
steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
break;
}
drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
#endif
}
}
void Hud::drawLuaElements(const v3s16 &camera_offset)
{
u32 text_height = g_fontengine->getTextHeight();
irr::gui::IGUIFont* font = g_fontengine->getFont();
for (size_t i = 0; i != player->maxHudId(); i++) {
HudElement *e = player->getHud(i);
if (!e)
continue;
v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
floor(e->pos.Y * (float) m_screensize.Y + 0.5));
switch (e->type) {
case HUD_ELEM_IMAGE: {
video::ITexture *texture = tsrc->getTexture(e->text);
if (!texture)
continue;
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
core::dimension2di imgsize(texture->getOriginalSize());
v2s32 dstsize(imgsize.Width * e->scale.X,
imgsize.Height * e->scale.Y);
if (e->scale.X < 0)
dstsize.X = m_screensize.X * (e->scale.X * -0.01);
if (e->scale.Y < 0)
dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
(e->align.Y - 1.0) * dstsize.Y / 2);
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, colors, true);
break; }
case HUD_ELEM_TEXT: {
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
std::wstring text = unescape_translate(utf8_to_wide(e->text));
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
v2s32 offs(e->offset.X, e->offset.Y);
font->draw(text.c_str(), size + pos + offset + offs, color);
break; }
case HUD_ELEM_STATBAR: {
v2s32 offs(e->offset.X, e->offset.Y);
drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
inv, e->item, e->dir);
break; }
case HUD_ELEM_WAYPOINT: {
v3f p_pos = player->getPosition() / BS;
v3f w_pos = e->world_pos * BS;
float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
scene::ICameraSceneNode* camera =
RenderingEngine::get_scene_manager()->getActiveCamera();
w_pos -= intToFloat(camera_offset, BS);
core::matrix4 trans = camera->getProjectionMatrix();
trans *= camera->getViewMatrix();
f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
trans.multiplyWith1x4Matrix(transformed_pos);
if (transformed_pos[3] < 0)
break;
f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
core::reciprocal(transformed_pos[3]);
pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
std::wstring text = unescape_translate(utf8_to_wide(e->name));
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
os << distance << e->text;
text = unescape_translate(utf8_to_wide(os.str()));
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }
default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
" of hud element ID " << i << " due to unrecognized type" << std::endl;
}
}
}
void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size)
{
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
video::ITexture *stat_texture = tsrc->getTexture(texture);
if (!stat_texture)
return;
core::dimension2di srcd(stat_texture->getOriginalSize());
core::dimension2di dstd;
if (size == v2s32()) {
dstd = srcd;
} else {
float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
dstd.Height = size.Y * size_factor;
dstd.Width = size.X * size_factor;
offset.X *= size_factor;
offset.Y *= size_factor;
}
v2s32 p = pos;
if (corner & HUD_CORNER_LOWER)
p -= dstd.Height;
p += offset;
v2s32 steppos;
core::rect<s32> srchalfrect, dsthalfrect;
switch (drawdir) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-1, 0);
srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(0, 1);
srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(0, -1);
srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
break;
default:
steppos = v2s32(1, 0);
srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
}
steppos.X *= dstd.Width;
steppos.Y *= dstd.Height;
for (s32 i = 0; i < count / 2; i++) {
core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
dstrect += p;
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
p += steppos;
}
if (count % 2 == 1) {
dsthalfrect += p;
draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
}
}
void Hud::drawHotbar(u16 playeritem) {
v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
InventoryList *mainlist = inventory->getList("main");
if (mainlist == NULL) {
//silently ignore this we may not be initialized completely
return;
}
s32 hotbar_itemcount = player->hud_hotbar_itemcount;
s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if ( (float) width / (float) window_size.X <=
g_settings->getFloat("hud_hotbar_max_width")) {
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
}
} else {
pos.X += width/4;
v2s32 secondpos = pos;
pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
mainlist, playeritem + 1, 0);
drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
}
}
//////////////////////////// compatibility code to be removed //////////////
// this is ugly as hell but there's no other way to keep compatibility to
// old servers
if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "heart.png",
player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
}
if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
(player->getBreath() < 11)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "bubble.png",
player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
}
////////////////////////////////////////////////////////////////////////////
}
void Hud::drawCrosshair()
{
if (use_crosshair_image) {
video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
v2u32 size = crosshair->getOriginalSize();
v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
m_displaycenter.Y - (size.Y / 2));
driver->draw2DImage(crosshair, lsize,
core::rect<s32>(0, 0, size.X, size.Y),
0, crosshair_argb, true);
} else {
driver->draw2DLine(m_displaycenter - v2s32(10, 0),
m_displaycenter + v2s32(10, 0), crosshair_argb);
driver->draw2DLine(m_displaycenter - v2s32(0, 10),
m_displaycenter + v2s32(0, 10), crosshair_argb);
}
}
void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
m_selection_pos = pos;
m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
}
void Hud::drawSelectionMesh()
{
if (m_mode == HIGHLIGHT_BOX) {
// Draw 3D selection boxes
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
for (std::vector<aabb3f>::const_iterator
i = m_selection_boxes.begin();
i != m_selection_boxes.end(); ++i) {
aabb3f box = aabb3f(
i->MinEdge + m_selection_pos_with_offset,
i->MaxEdge + m_selection_pos_with_offset);
u32 r = (selectionbox_argb.getRed() *
m_selection_mesh_color.getRed() / 255);
u32 g = (selectionbox_argb.getGreen() *
m_selection_mesh_color.getGreen() / 255);
u32 b = (selectionbox_argb.getBlue() *
m_selection_mesh_color.getBlue() / 255);
driver->draw3DBox(box, video::SColor(255, r, g, b));
}
driver->setMaterial(oldmaterial);
} else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
// Draw selection mesh
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
setMeshColor(m_selection_mesh, m_selection_mesh_color);
video::SColor face_color(0,
MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
face_color);
scene::IMesh* mesh = cloneMesh(m_selection_mesh);
translateMesh(mesh, m_selection_pos_with_offset);
u32 mc = m_selection_mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
driver->drawMeshBuffer(buf);
}
mesh->drop();
driver->setMaterial(oldmaterial);
}
}
void Hud::updateSelectionMesh(const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
if (m_mode != HIGHLIGHT_HALO)
return;
if (m_selection_mesh) {
m_selection_mesh->drop();
m_selection_mesh = NULL;
}
if (m_selection_boxes.empty()) {
// No pointed object
return;
}
// New pointed object, create new mesh.
// Texture UV coordinates for selection boxes
static f32 texture_uv[24] = {
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1
}; };
// Use single halo box instead of multiple overlapping boxes. const struct EnumString es_HudElementStat[] =
// Temporary solution - problem can be solved with multiple {
// rendering targets, or some method to remove inner surfaces. {HUD_STAT_POS, "position"},
// Thats because of halo transparency. {HUD_STAT_POS, "pos"}, /* Deprecated, only for compatibility's sake */
{HUD_STAT_NAME, "name"},
aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0); {HUD_STAT_SCALE, "scale"},
m_halo_boxes.clear(); {HUD_STAT_TEXT, "text"},
{HUD_STAT_NUMBER, "number"},
for (const auto &selection_box : m_selection_boxes) { {HUD_STAT_ITEM, "item"},
halo_box.addInternalBox(selection_box); {HUD_STAT_DIR, "direction"},
} {HUD_STAT_ALIGN, "alignment"},
{HUD_STAT_OFFSET, "offset"},
m_halo_boxes.push_back(halo_box); {HUD_STAT_WORLD_POS, "world_pos"},
m_selection_mesh = convertNodeboxesToMesh( {0, NULL},
m_halo_boxes, texture_uv, 0.5);
}
void Hud::resizeHotbar() {
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if (m_screensize != window_size) {
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
m_screensize = window_size;
m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
}
}
struct MeshTimeInfo {
u64 time;
scene::IMesh *mesh;
}; };
void drawItemStack(video::IVideoDriver *driver, const struct EnumString es_HudBuiltinElement[] =
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind)
{ {
static MeshTimeInfo rotation_time_infos[IT_ROT_NONE]; {HUD_FLAG_HOTBAR_VISIBLE, "hotbar"},
static thread_local bool enable_animations = {HUD_FLAG_HEALTHBAR_VISIBLE, "healthbar"},
g_settings->getBool("inventory_items_animations"); {HUD_FLAG_CROSSHAIR_VISIBLE, "crosshair"},
{HUD_FLAG_WIELDITEM_VISIBLE, "wielditem"},
if (item.empty()) { {HUD_FLAG_BREATHBAR_VISIBLE, "breathbar"},
if (rotation_kind < IT_ROT_NONE) { {HUD_FLAG_MINIMAP_VISIBLE, "minimap"},
rotation_time_infos[rotation_kind].mesh = NULL; {0, NULL},
} };
return;
}
const ItemDefinition &def = item.getDefinition(client->idef());
ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
if (imesh && imesh->mesh) {
scene::IMesh *mesh = imesh->mesh;
driver->clearZBuffer();
s32 delta = 0;
if (rotation_kind < IT_ROT_NONE) {
MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
if (mesh != ti.mesh) {
ti.mesh = mesh;
ti.time = porting::getTimeMs();
} else {
delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
}
}
core::rect<s32> oldViewPort = driver->getViewPort();
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
core::matrix4 ProjMatrix;
ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
driver->setTransform(video::ETS_VIEW, ProjMatrix);
core::matrix4 matrix;
matrix.makeIdentity();
if (enable_animations) {
float timer_f = (float) delta / 5000.0;
matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
}
driver->setTransform(video::ETS_WORLD, matrix);
driver->setViewPort(rect);
video::SColor basecolor =
client->idef()->getItemstackColor(item, client);
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; ++j) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
// we can modify vertices relatively fast,
// because these meshes are not buffered.
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) {
ItemPartColor *p = &imesh->buffer_colors[j];
if (p->override_base)
c = p->color;
}
if (imesh->needs_shading)
colorizeMeshBuffer(buf, &c);
else
setMeshBufferColor(buf, c);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
driver->setMaterial(material);
driver->drawMeshBuffer(buf);
}
driver->setTransform(video::ETS_VIEW, oldViewMat);
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort);
}
if(def.type == ITEM_TOOL && item.wear != 0)
{
// Draw a progressbar
float barheight = rect.getHeight()/16;
float barpad_x = rect.getWidth()/16;
float barpad_y = rect.getHeight()/16;
core::rect<s32> progressrect(
rect.UpperLeftCorner.X + barpad_x,
rect.LowerRightCorner.Y - barpad_y - barheight,
rect.LowerRightCorner.X - barpad_x,
rect.LowerRightCorner.Y - barpad_y);
// Shrink progressrect by amount of tool damage
float wear = item.wear / 65535.0;
int progressmid =
wear * progressrect.UpperLeftCorner.X +
(1-wear) * progressrect.LowerRightCorner.X;
// Compute progressbar color
// wear = 0.0: green
// wear = 0.5: yellow
// wear = 1.0: red
video::SColor color(255,255,255,255);
int wear_i = MYMIN(floor(wear * 600), 511);
wear_i = MYMIN(wear_i + 10, 511);
if(wear_i <= 255)
color.set(255, wear_i, 255, 0);
else
color.set(255, 255, 511-wear_i, 0);
core::rect<s32> progressrect2 = progressrect;
progressrect2.LowerRightCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
color = video::SColor(255,0,0,0);
progressrect2 = progressrect;
progressrect2.UpperLeftCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
}
if(font != NULL && item.count >= 2)
{
// Get the item count as a string
std::string text = itos(item.count);
v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
v2s32 sdim(dim.X,dim.Y);
core::rect<s32> rect2(
/*rect.UpperLeftCorner,
core::dimension2d<u32>(rect.getWidth(), 15)*/
rect.LowerRightCorner - sdim,
sdim
);
video::SColor bgcolor(128,0,0,0);
driver->draw2DRectangle(bgcolor, rect2, clip);
video::SColor color(255,255,255,255);
font->draw(text.c_str(), rect2, color, false, false, clip);
}
}

114
src/hud.h
View File

@ -1,6 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2017 red-001 <red-001@outlook.ie>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -17,10 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#pragma once #ifndef HUD_HEADER
#define HUD_HEADER
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include <string> #include <string>
#include "common/c_types.h"
#define HUD_DIR_LEFT_RIGHT 0 #define HUD_DIR_LEFT_RIGHT 0
#define HUD_DIR_RIGHT_LEFT 1 #define HUD_DIR_RIGHT_LEFT 1
@ -89,111 +92,8 @@ struct HudElement {
v2s32 size; v2s32 size;
}; };
#ifndef SERVER extern const EnumString es_HudElementType[];
extern const EnumString es_HudElementStat[];
#include <vector> extern const EnumString es_HudBuiltinElement[];
#include <IGUIFont.h>
#include "irr_aabb3d.h"
class Client;
class ITextureSource;
class Inventory;
class InventoryList;
class LocalPlayer;
struct ItemStack;
class Hud {
public:
video::IVideoDriver *driver;
scene::ISceneManager* smgr;
gui::IGUIEnvironment *guienv;
Client *client;
LocalPlayer *player;
Inventory *inventory;
ITextureSource *tsrc;
video::SColor crosshair_argb;
video::SColor selectionbox_argb;
bool use_crosshair_image = false;
std::string hotbar_image = "";
bool use_hotbar_image = false;
std::string hotbar_selected_image = "";
bool use_hotbar_selected_image = false;
Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory);
~Hud();
void drawHotbar(u16 playeritem);
void resizeHotbar();
void drawCrosshair();
void drawSelectionMesh();
void updateSelectionMesh(const v3s16 &camera_offset);
std::vector<aabb3f> *getSelectionBoxes()
{ return &m_selection_boxes; }
void setSelectionPos(const v3f &pos, const v3s16 &camera_offset);
v3f getSelectionPos() const
{ return m_selection_pos; }
void setSelectionMeshColor(const video::SColor &color)
{ m_selection_mesh_color = color; }
void setSelectedFaceNormal(const v3f &face_normal)
{ m_selected_face_normal = face_normal; }
void drawLuaElements(const v3s16 &camera_offset);
private:
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size=v2s32());
void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction);
void drawItem(const ItemStack &item, const core::rect<s32>& rect,
bool selected);
float m_hud_scaling; // cached minetest setting
v3s16 m_camera_offset;
v2u32 m_screensize;
v2s32 m_displaycenter;
s32 m_hotbar_imagesize; // Takes hud_scaling into account, updated by resizeHotbar()
s32 m_padding; // Takes hud_scaling into account, updated by resizeHotbar()
video::SColor hbar_colors[4];
std::vector<aabb3f> m_selection_boxes;
std::vector<aabb3f> m_halo_boxes;
v3f m_selection_pos;
v3f m_selection_pos_with_offset;
scene::IMesh *m_selection_mesh = nullptr;
video::SColor m_selection_mesh_color;
v3f m_selected_face_normal;
video::SMaterial m_selection_material;
enum {
HIGHLIGHT_BOX,
HIGHLIGHT_HALO,
HIGHLIGHT_NONE } m_mode;
};
enum ItemRotationKind {
IT_ROT_SELECTED,
IT_ROT_HOVERED,
IT_ROT_DRAGGED,
IT_ROT_NONE, // Must be last, also serves as number
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind);
#endif #endif

View File

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector3d.h> #include <vector3d.h>
typedef core::vector3df v3f; typedef core::vector3df v3f;
typedef core::vector3d<double> v3d;
typedef core::vector3d<s16> v3s16; typedef core::vector3d<s16> v3s16;
typedef core::vector3d<u16> v3u16; typedef core::vector3d<u16> v3u16;
typedef core::vector3d<s32> v3s32; typedef core::vector3d<s32> v3s32;

View File

@ -42,6 +42,60 @@ namespace gui
//! destructor //! destructor
virtual ~StaticText(); virtual ~StaticText();
static irr::gui::IGUIStaticText *add(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
if (parent == NULL) {
// parent is NULL, so we must find one, or we need not to drop
// result, but then there will be a memory leak.
//
// What Irrlicht does is to use guienv as a parent, but the problem
// is that guienv is here only an IGUIEnvironment, while it is a
// CGUIEnvironment in Irrlicht, which inherits from both IGUIElement
// and IGUIEnvironment.
//
// A solution would be to dynamic_cast guienv to a
// IGUIElement*, but Irrlicht is shipped without rtti support
// in some distributions, causing the dymanic_cast to segfault.
//
// Thus, to find the parent, we create a dummy StaticText and ask
// for its parent, and then remove it.
irr::gui::IGUIStaticText *dummy_text =
guienv->addStaticText(L"", rectangle, border, wordWrap,
parent, id, fillBackground);
parent = dummy_text->getParent();
dummy_text->remove();
}
irr::gui::IGUIStaticText *result = new irr::gui::StaticText(
text, border, guienv, parent,
id, rectangle, fillBackground);
result->setWordWrap(wordWrap);
result->drop();
return result;
}
static irr::gui::IGUIStaticText *add(
irr::gui::IGUIEnvironment *guienv,
const wchar_t *text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
return add(guienv, EnrichedString(text), rectangle, border, wordWrap, parent,
id, fillBackground);
}
//! draws the element and its children //! draws the element and its children
virtual void draw(); virtual void draw();
@ -171,46 +225,6 @@ namespace gui
} // end namespace irr } // end namespace irr
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
if (parent == NULL) {
// parent is NULL, so we must find one, or we need not to drop
// result, but then there will be a memory leak.
//
// What Irrlicht does is to use guienv as a parent, but the problem
// is that guienv is here only an IGUIEnvironment, while it is a
// CGUIEnvironment in Irrlicht, which inherits from both IGUIElement
// and IGUIEnvironment.
//
// A solution would be to dynamic_cast guienv to a
// IGUIElement*, but Irrlicht is shipped without rtti support
// in some distributions, causing the dymanic_cast to segfault.
//
// Thus, to find the parent, we create a dummy StaticText and ask
// for its parent, and then remove it.
irr::gui::IGUIStaticText *dummy_text =
guienv->addStaticText(L"", rectangle, border, wordWrap,
parent, id, fillBackground);
parent = dummy_text->getParent();
dummy_text->remove();
}
irr::gui::IGUIStaticText *result = new irr::gui::StaticText(
text, border, guienv, parent,
id, rectangle, fillBackground);
result->setWordWrap(wordWrap);
result->drop();
return result;
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text) inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{ {
// dynamic_cast not possible due to some distributions shipped // dynamic_cast not possible due to some distributions shipped
@ -225,7 +239,15 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS
#else // USE_FREETYPE #else // USE_FREETYPE
inline irr::gui::IGUIStaticText *addStaticText( namespace irr
{
namespace gui
{
class StaticText
{
public:
static irr::gui::IGUIStaticText *add(
irr::gui::IGUIEnvironment *guienv, irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text, const EnrichedString &text,
const core::rect< s32 > &rectangle, const core::rect< s32 > &rectangle,
@ -237,6 +259,11 @@ inline irr::gui::IGUIStaticText *addStaticText(
{ {
return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground); return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground);
} }
};
} // end namespace gui
} // end namespace irr
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text) inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{ {
@ -245,18 +272,6 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS
#endif #endif
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const wchar_t *text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false) {
return addStaticText(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground);
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text) inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text)
{ {
setStaticText(static_text, EnrichedString(text)); setStaticText(static_text, EnrichedString(text));

View File

@ -24,38 +24,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER #ifndef SERVER
// Length of LIGHT_MAX+1 means LIGHT_MAX is the last value. static u8 light_LUT[LIGHT_SUN + 1];
// LIGHT_SUN is read as LIGHT_MAX from here.
u8 light_LUT[LIGHT_MAX+1]; // The const ref to light_LUT is what is actually used in the code
// the const ref to light_LUT is what is actually used in the code.
const u8 *light_decode_table = light_LUT; const u8 *light_decode_table = light_LUT;
/** Initialize or update the light value tables using the specified \p gamma. // Initialize or update the light value tables using the specified gamma
*/
void set_light_table(float gamma) void set_light_table(float gamma)
{ {
// lighting curve derivatives // Lighting curve derivatives
const float alpha = g_settings->getFloat("lighting_alpha"); const float alpha = g_settings->getFloat("lighting_alpha");
const float beta = g_settings->getFloat("lighting_beta"); const float beta = g_settings->getFloat("lighting_beta");
// lighting curve coefficients // Lighting curve coefficients
const float a = alpha + beta - 2; const float a = alpha + beta - 2.0f;
const float b = 3 - 2 * alpha - beta; const float b = 3.0f - 2.0f * alpha - beta;
const float c = alpha; const float c = alpha;
// gamma correction // Mid boost
gamma = rangelim(gamma, 0.5, 3.0); const float d = g_settings->getFloat("lighting_boost");
const float e = g_settings->getFloat("lighting_boost_center");
const float f = g_settings->getFloat("lighting_boost_spread");
// Gamma correction
gamma = rangelim(gamma, 0.5f, 3.0f);
for (size_t i = 0; i < LIGHT_MAX; i++) { for (size_t i = 0; i < LIGHT_SUN; i++) {
float x = i; float x = i;
x /= LIGHT_MAX; x /= LIGHT_SUN;
float brightness = a * x * x * x + b * x * x + c * x; float brightness = a * x * x * x + b * x * x + c * x;
brightness = powf(brightness, 1.0 / gamma); float boost = d * std::exp(-((x - e) * (x - e)) / (2.0f * f * f));
light_LUT[i] = rangelim((u32)(255 * brightness), 0, 255); brightness = powf(brightness + boost, 1.0f / gamma);
light_LUT[i] = rangelim((u32)(255.0f * brightness), 0, 255);
if (i > 1 && light_LUT[i] <= light_LUT[i - 1]) if (i > 1 && light_LUT[i] <= light_LUT[i - 1])
light_LUT[i] = light_LUT[i - 1] + 1; light_LUT[i] = light_LUT[i - 1] + 1;
} }
light_LUT[LIGHT_MAX] = 255; light_LUT[LIGHT_SUN] = 255;
} }
#endif #endif

View File

@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#pragma once #pragma once
#include <cassert>
#include "irrlichttypes.h" #include "irrlichttypes.h"
/* /*
@ -54,11 +54,12 @@ inline u8 diminish_light(u8 light, u8 distance)
inline u8 undiminish_light(u8 light) inline u8 undiminish_light(u8 light)
{ {
assert(light <= LIGHT_SUN);
// We don't know if light should undiminish from this particular 0. // We don't know if light should undiminish from this particular 0.
// Thus, keep it at 0. // Thus, keep it at 0.
if (light == 0) if (light == 0)
return 0; return 0;
if (light == LIGHT_MAX) if (light >= LIGHT_MAX)
return light; return light;
return light + 1; return light + 1;
@ -84,9 +85,9 @@ extern const u8 *light_decode_table;
// 0 <= return value <= 255 // 0 <= return value <= 255
inline u8 decode_light(u8 light) inline u8 decode_light(u8 light)
{ {
if (light > LIGHT_MAX) // assert(light <= LIGHT_SUN);
light = LIGHT_MAX; if (light > LIGHT_SUN)
light = LIGHT_SUN;
return light_decode_table[light]; return light_decode_table[light];
} }
@ -98,8 +99,8 @@ inline float decode_light_f(float light_f)
if (i <= 0) if (i <= 0)
return (float)light_decode_table[0] / 255.0; return (float)light_decode_table[0] / 255.0;
if (i >= LIGHT_MAX) if (i >= LIGHT_SUN)
return (float)light_decode_table[LIGHT_MAX] / 255.0; return (float)light_decode_table[LIGHT_SUN] / 255.0;
float v1 = (float)light_decode_table[i - 1] / 255.0; float v1 = (float)light_decode_table[i - 1] / 255.0;
float v2 = (float)light_decode_table[i] / 255.0; float v2 = (float)light_decode_table[i] / 255.0;

View File

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h" #include "player.h"
#include "environment.h" #include "environment.h"
#include "constants.h" #include "constants.h"
#include "settings.h"
#include <list> #include <list>
class Client; class Client;
@ -142,8 +143,8 @@ public:
void setCollisionbox(const aabb3f &box) { m_collisionbox = box; } void setCollisionbox(const aabb3f &box) { m_collisionbox = box; }
bool getCanZoom() const { return m_can_zoom; } float getZoomFOV() const { return m_zoom_fov; }
void setCanZoom(bool can_zoom) { m_can_zoom = can_zoom; } void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; }
private: private:
void accelerateHorizontal(const v3f &target_speed, const f32 max_increase); void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
@ -181,8 +182,8 @@ private:
bool camera_barely_in_ceiling = false; bool camera_barely_in_ceiling = false;
aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f, aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f,
BS * 1.75f, BS * 0.30f); BS * 1.75f, BS * 0.30f);
bool m_can_zoom = true;
float m_eye_height = 1.625f; float m_eye_height = 1.625f;
float m_zoom_fov = 0.0f;
GenericCAO *m_cao = nullptr; GenericCAO *m_cao = nullptr;
Client *m_client; Client *m_client;

View File

@ -347,13 +347,10 @@ void StringBuffer::push_back(char c)
flush(std::string(buffer, buffer_index)); flush(std::string(buffer, buffer_index));
buffer_index = 0; buffer_index = 0;
} else { } else {
int index = buffer_index; buffer[buffer_index++] = c;
buffer[index++] = c; if (buffer_index >= BUFFER_LENGTH) {
if (index >= BUFFER_LENGTH) {
flush(std::string(buffer, buffer_index)); flush(std::string(buffer, buffer_index));
buffer_index = 0; buffer_index = 0;
} else {
buffer_index = index;
} }
} }
} }

View File

@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#if !defined(_WIN32) // POSIX
#include <unistd.h>
#endif
#include "settings.h"
#include "irrlichttypes.h" #include "irrlichttypes.h"
class ILogOutput; class ILogOutput;
@ -106,15 +110,51 @@ public:
StreamLogOutput(std::ostream &stream) : StreamLogOutput(std::ostream &stream) :
m_stream(stream) m_stream(stream)
{ {
#if !defined(_WIN32)
is_tty = isatty(fileno(stdout));
#else
is_tty = false;
#endif
} }
void logRaw(LogLevel lev, const std::string &line) void logRaw(LogLevel lev, const std::string &line)
{ {
static const std::string use_logcolor = g_settings->get("log_color");
bool colored = use_logcolor == "detect" ? is_tty : use_logcolor == "yes";
if (colored)
switch (lev) {
case LL_ERROR:
// error is red
m_stream << "\033[91m";
break;
case LL_WARNING:
// warning is yellow
m_stream << "\033[93m";
break;
case LL_INFO:
// info is a bit dark
m_stream << "\033[37m";
break;
case LL_VERBOSE:
// verbose is darker than info
m_stream << "\033[2m";
break;
default:
// action is white
colored = false;
}
m_stream << line << std::endl; m_stream << line << std::endl;
if (colored)
// reset to white color
m_stream << "\033[0m";
} }
private: private:
std::ostream &m_stream; std::ostream &m_stream;
bool is_tty;
}; };
class FileLogOutput : public ICombinedLogOutput { class FileLogOutput : public ICombinedLogOutput {

View File

@ -132,198 +132,6 @@ std::string MapBlock::getModifiedReasonString()
return reason; return reason;
} }
/*
Propagates sunlight down through the block.
Doesn't modify nodes that are not affected by sunlight.
Returns false if sunlight at bottom block is invalid.
Returns true if sunlight at bottom block is valid.
Returns true if bottom block doesn't exist.
If there is a block above, continues from it.
If there is no block above, assumes there is sunlight, unless
is_underground is set or highest node is water.
All sunlighted nodes are added to light_sources.
if remove_light==true, sets non-sunlighted nodes black.
if black_air_left!=NULL, it is set to true if non-sunlighted
air is left in block.
*/
bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
bool remove_light, bool *black_air_left)
{
INodeDefManager *nodemgr = m_gamedef->ndef();
// Whether the sunlight at the top of the bottom block is valid
bool block_below_is_valid = true;
v3s16 pos_relative = getPosRelative();
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
{
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
{
#if 1
bool no_sunlight = false;
//bool no_top_block = false;
// Check if node above block has sunlight
bool is_valid_position;
MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
&is_valid_position);
if (is_valid_position)
{
if(n.getContent() == CONTENT_IGNORE)
{
// Trust heuristics
no_sunlight = is_underground;
}
else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
{
no_sunlight = true;
}
}
else
{
//no_top_block = true;
// NOTE: This makes over-ground roofed places sunlighted
// Assume sunlight, unless is_underground==true
if(is_underground)
{
no_sunlight = true;
}
else
{
MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
if (!m_gamedef->ndef()->get(n).sunlight_propagates) {
no_sunlight = true;
}
}
// NOTE: As of now, this just would make everything dark.
// No sunlight here
//no_sunlight = true;
}
#endif
#if 0 // Doesn't work; nothing gets light.
bool no_sunlight = true;
bool no_top_block = false;
// Check if node above block has sunlight
try{
MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
{
no_sunlight = false;
}
}
catch(InvalidPositionException &e)
{
no_top_block = true;
}
#endif
/*std::cout<<"("<<x<<","<<z<<"): "
<<"no_top_block="<<no_top_block
<<", is_underground="<<is_underground
<<", no_sunlight="<<no_sunlight
<<std::endl;*/
s16 y = MAP_BLOCKSIZE-1;
// This makes difference to diminishing in water.
bool stopped_to_solid_object = false;
u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
for(; y >= 0; y--)
{
v3s16 pos(x, y, z);
MapNode &n = getNodeRef(pos);
if(current_light == 0)
{
// Do nothing
}
else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
{
// Do nothing: Sunlight is continued
} else if (!nodemgr->get(n).light_propagates) {
// A solid object is on the way.
stopped_to_solid_object = true;
// Light stops.
current_light = 0;
}
else
{
// Diminish light
current_light = diminish_light(current_light);
}
u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
if(current_light > old_light || remove_light)
{
n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
}
if(diminish_light(current_light) != 0)
{
light_sources.insert(pos_relative + pos);
}
if(current_light == 0 && stopped_to_solid_object)
{
if(black_air_left)
{
*black_air_left = true;
}
}
}
// Whether or not the block below should see LIGHT_SUN
bool sunlight_should_go_down = (current_light == LIGHT_SUN);
/*
If the block below hasn't already been marked invalid:
Check if the node below the block has proper sunlight at top.
If not, the block below is invalid.
Ignore non-transparent nodes as they always have no light
*/
if(block_below_is_valid)
{
MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
if (is_valid_position) {
if(nodemgr->get(n).light_propagates)
{
if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
&& !sunlight_should_go_down)
block_below_is_valid = false;
else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
&& sunlight_should_go_down)
block_below_is_valid = false;
}
}
else
{
/*std::cout<<"InvalidBlockException for bottom block node"
<<std::endl;*/
// Just no block below, no need to panic.
}
}
}
}
return block_below_is_valid;
}
void MapBlock::copyTo(VoxelManipulator &dst) void MapBlock::copyTo(VoxelManipulator &dst)
{ {
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);

View File

@ -348,10 +348,6 @@ public:
setNode(x0 + x, y0 + y, z0 + z, node); setNode(x0 + x, y0 + y, z0 + z, node);
} }
// See comments in mapblock.cpp
bool propagateSunlight(std::set<v3s16> &light_sources,
bool remove_light=false, bool *black_air_left=NULL);
// Copies data to VoxelManipulator to getPosRelative() // Copies data to VoxelManipulator to getPosRelative()
void copyTo(VoxelManipulator &dst); void copyTo(VoxelManipulator &dst);

View File

@ -196,7 +196,7 @@ u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
Both light banks Both light banks
*/ */
static u16 getSmoothLightCombined(const v3s16 &p, static u16 getSmoothLightCombined(const v3s16 &p,
const std::array<v3s16,8> &dirs, MeshMakeData *data, bool node_solid) const std::array<v3s16,8> &dirs, MeshMakeData *data)
{ {
INodeDefManager *ndef = data->m_client->ndef(); INodeDefManager *ndef = data->m_client->ndef();
@ -206,8 +206,14 @@ static u16 getSmoothLightCombined(const v3s16 &p,
u16 light_day = 0; u16 light_day = 0;
u16 light_night = 0; u16 light_night = 0;
auto add_node = [&] (int i) -> const ContentFeatures& { auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
if (obstructed) {
ambient_occlusion++;
return false;
}
MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]); MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
if (n.getContent() == CONTENT_IGNORE)
return true;
const ContentFeatures &f = ndef->get(n); const ContentFeatures &f = ndef->get(n);
if (f.light_source > light_source_max) if (f.light_source > light_source_max)
light_source_max = f.light_source; light_source_max = f.light_source;
@ -219,37 +225,24 @@ static u16 getSmoothLightCombined(const v3s16 &p,
} else { } else {
ambient_occlusion++; ambient_occlusion++;
} }
return f; return f.light_propagates;
}; };
if (node_solid) {
ambient_occlusion = 3;
bool corner_obstructed = true;
for (int i = 0; i < 2; ++i) {
if (add_node(i).light_propagates)
corner_obstructed = false;
}
add_node(2);
add_node(3);
if (corner_obstructed)
ambient_occlusion++;
else
add_node(4);
} else {
std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }}; std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
add_node(0); add_node(0);
bool opaque1 = !add_node(1).light_propagates; bool opaque1 = !add_node(1);
bool opaque2 = !add_node(2).light_propagates; bool opaque2 = !add_node(2);
bool opaque3 = !add_node(3).light_propagates; bool opaque3 = !add_node(3);
obstructed[0] = opaque1 && opaque2; obstructed[0] = opaque1 && opaque2;
obstructed[1] = opaque1 && opaque3; obstructed[1] = opaque1 && opaque3;
obstructed[2] = opaque2 && opaque3; obstructed[2] = opaque2 && opaque3;
for (int k = 0; k < 4; ++k) { for (u8 k = 0; k < 3; ++k)
if (obstructed[k]) if (add_node(k + 4, obstructed[k]))
ambient_occlusion++;
else if (add_node(k + 4).light_propagates)
obstructed[3] = false; obstructed[3] = false;
} if (add_node(7, obstructed[3])) { // wrap light around nodes
ambient_occlusion -= 3;
for (u8 k = 0; k < 3; ++k)
add_node(k + 4, !obstructed[k]);
} }
if (light_count == 0) { if (light_count == 0) {
@ -277,7 +270,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0); g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
// Table of gamma space multiply factors. // Table of gamma space multiply factors.
static const float light_amount[3] = { static thread_local const float light_amount[3] = {
powf(0.75, 1.0 / ao_gamma), powf(0.75, 1.0 / ao_gamma),
powf(0.5, 1.0 / ao_gamma), powf(0.5, 1.0 / ao_gamma),
powf(0.25, 1.0 / ao_gamma) powf(0.25, 1.0 / ao_gamma)
@ -304,43 +297,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
*/ */
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data) u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
{ {
v3s16 neighbor_offset1, neighbor_offset2; return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
/*
* face_dir, neighbor_offset1 and neighbor_offset2 define an
* orthonormal basis which is used to define the offsets of the 8
* surrounding nodes and to differentiate the "distance" (by going only
* along directly neighboring nodes) relative to the node at p.
* Apart from the node at p, only the 4 nodes which contain face_dir
* can contribute light.
*/
if (face_dir.X != 0) {
neighbor_offset1 = v3s16(0, corner.Y, 0);
neighbor_offset2 = v3s16(0, 0, corner.Z);
} else if (face_dir.Y != 0) {
neighbor_offset1 = v3s16(0, 0, corner.Z);
neighbor_offset2 = v3s16(corner.X, 0, 0);
} else if (face_dir.Z != 0) {
neighbor_offset1 = v3s16(corner.X,0,0);
neighbor_offset2 = v3s16(0,corner.Y,0);
}
const std::array<v3s16,8> dirs = {{
// Always shine light
neighbor_offset1 + face_dir,
neighbor_offset2 + face_dir,
v3s16(0,0,0),
face_dir,
// Can be obstructed
neighbor_offset1 + neighbor_offset2 + face_dir,
// Do not shine light, only for ambient occlusion
neighbor_offset1,
neighbor_offset2,
neighbor_offset1 + neighbor_offset2
}};
return getSmoothLightCombined(p, dirs, data, true);
} }
/* /*
@ -363,7 +320,7 @@ u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData
v3s16(0,corner.Y,corner.Z), v3s16(0,corner.Y,corner.Z),
v3s16(corner.X,corner.Y,corner.Z) v3s16(corner.X,corner.Y,corner.Z)
}}; }};
return getSmoothLightCombined(p, dirs, data, false); return getSmoothLightCombined(p, dirs, data);
} }
void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){ void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){

View File

@ -1,6 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,6 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,6 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,6 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,8 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -301,10 +301,8 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax, void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings) std::vector<s16> &floors, std::vector<s16> &ceilings)
{ {
u16 floor_i = 0;
u16 ceiling_i = 0;
const v3s16 &em = vm->m_area.getExtent(); const v3s16 &em = vm->m_area.getExtent();
bool is_walkable = false; bool is_walkable = false;
@ -318,19 +316,14 @@ void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
is_walkable = ndef->get(mn).walkable; is_walkable = ndef->get(mn).walkable;
if (is_walkable && !walkable_above) { if (is_walkable && !walkable_above) {
floors[floor_i] = y; floors.push_back(y);
floor_i++;
} else if (!is_walkable && walkable_above) { } else if (!is_walkable && walkable_above) {
ceilings[ceiling_i] = y + 1; ceilings.push_back(y + 1);
ceiling_i++;
} }
vm->m_area.add_y(em, vi, -1); vm->m_area.add_y(em, vi, -1);
walkable_above = is_walkable; walkable_above = is_walkable;
} }
*num_floors = floor_i;
*num_ceilings = ceiling_i;
} }

View File

@ -1,8 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -194,7 +194,7 @@ public:
s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax); s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax);
void updateHeightmap(v3s16 nmin, v3s16 nmax); void updateHeightmap(v3s16 nmin, v3s16 nmax);
void getSurfaces(v2s16 p2d, s16 ymin, s16 ymax, void getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings); std::vector<s16> &floors, std::vector<s16> &ceilings);
void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax); void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);

View File

@ -1,8 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2016 paramat, Matt Gregory Copyright (C) 2017-2018 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2010-2018 paramat
Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com> Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,8 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2010-2016 paramat, Matt Gregory Copyright (C) 2017-2018 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2010-2018 paramat
Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com> Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
by Paul Nylander, and from http://www.fractalforums.com, thank you. by Paul Nylander, and from http://www.fractalforums.com, thank you.

View File

@ -1,8 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2013-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,8 +1,8 @@
/* /*
Minetest Minetest
Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2013-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2014-2017 paramat Copyright (C) 2014-2018 paramat
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
Minetest Minetest
Copyright (C) 2014-2017 paramat Copyright (C) 2014-2018 paramat
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

Some files were not shown because too many files have changed in this diff Show More