Compare commits

...

187 Commits

Author SHA1 Message Date
Loïc Blot
426b5d3149 Fix a crash on Android with Align2Npot2 (#8070)
* Fix a crash on Android with Align2Npot2

glGetString can be NULL. If stored in a string it triggers a SIGSEGV.
Instead do a basic strstr and verify the pointer
* Better Align2Npot2 check (+ performance)
2019-01-07 17:05:54 +01:00
Loïc Blot
dda844be16
Drop libgmp on Android and use mini-gmp (#8047) 2019-01-04 16:41:42 +01:00
Loïc Blot
899dcbf1ee Bump android version code for next Play Store version 2019-01-04 14:16:09 +01:00
Loïc Blot
957c59a678 Revert "upright_sprite: Fix texture position for players"
This reverts commit d58801ab3204757e6c49892a05633281144f4f44.
2019-01-03 12:04:51 +01:00
stujones11
3d15a6774d
Android: Fix start-up crashes on version 4.x (#8003)
* Android: Load shared STL library

* Android: Fix exception with window background image
2018-12-22 19:02:27 +01:00
Loic Blot
3c24f941e0
Add missing variables since previous cherry-pick 2018-12-22 17:28:22 +01:00
Maksim
9d64805ec1
Update Android java code (#7820)
Targets SDK 26 as required by the playstore.
Fixes screen auto-rotation closing game.
Hides on-screen navigation bar if present.

Update gradlew.
Fix display aspect on 18+/:9 displays (like a Samsung Galaxy S9).
Remove small app icons, not required.
Fix xml in unpacking activity.
Support Android permission: On Android 6.0+ you need to manually give write
permission (as required by google).
Background during unpacking (just a demo for now).
Material Design: no more Android 2 interface.
Immersive mode (Android 4.4+ - hide NavBar for fullscreen mode).
2018-12-22 17:25:38 +01:00
rubenwardy
ce4497224f
Android: Fix memory leak when displaying images in the mainmenu (#8011) 2018-12-22 17:20:18 +01:00
stujones11
a873a3f4f8
Include alpha channel reference in MaterialTypeParam 2018-12-22 17:15:39 +01:00
Loic Blot
86e29ae586
Update android version code (rebuild) 2018-12-04 19:11:03 +01:00
Loic Blot
dd5f03731a Bump android version to 20 2018-06-28 19:11:01 +02:00
Loic Blot
5e7faf3f50 Update cURL (7.60.0) & SQLite3 (3.24.0) 2018-06-28 19:11:01 +02:00
stujones11
8427ae529a Android: Use correct temporary path (#7463) 2018-06-28 19:11:01 +02:00
sfan5
2e85254e91 Fix MurmurHash implementation to really be unaligned (#7482) 2018-06-28 19:11:01 +02:00
red-001
971dea7efd Fix crash caused by Lua error during startup (#7473) 2018-06-28 19:11:01 +02:00
red-001
85ce23b165 Fix buffer overrun in SRP (#7484)
The old code got a pointer to the array instead of the first element, this resulted in a buffer overflow when the function was used more than once.
2018-06-28 19:11:01 +02:00
red-001
e66d5e500c Fix small memory leaks in client. (#7492) 2018-06-28 19:11:01 +02:00
Loic Blot
ea86ecf015
Android: gradle 3.1.3 is not available on mavenCentral
Also update wrapper
2018-06-11 20:37:17 +02:00
Loic Blot
4a48cd57e8
Fix android tools version used to build MT 2018-06-11 19:14:10 +02:00
Loic Blot
289c8dfde5
Android: fix clean_assets target 2018-06-11 19:13:56 +02:00
Loic Blot
9dfe9be1f3
Bump android version for next release 2018-06-10 18:28:41 +02:00
Loic Blot
f5c9c760e3
Android: use c++_shared library instead of c++_static
MT doesn't launch without that
2018-06-10 17:48:48 +02:00
SmallJoker
6dc7177a5d Bump version to 0.4.17.1 2018-06-10 17:18:11 +02:00
number Zero
24a2fd4dc4 Fix narrow/utf8 difference in incoming/outcoming messages 2018-06-10 17:18:11 +02:00
SmallJoker
2515207606 Fix crash in log_deprecated when triggered from no function
Based on commit a1598e1b
2018-06-10 17:18:11 +02:00
Loic Blot
119aa5c919 Fix crash due to missing pointer validation
Based on commit 014a1a0
2018-06-10 17:18:11 +02:00
Loic Blot
fb4bfc60de
Android: fix another build typo 2018-06-10 17:11:57 +02:00
Loic Blot
9bbe99b4f8
Android: fix RTTI issue 2018-06-10 17:07:32 +02:00
Loic Blot
0ef9c53a8c
Fix many Android build issues
It remains one issue with MT itself and rtti
2018-06-10 17:01:47 +02:00
stujones11
b5350e27ad
Android: Update build system for ndk-r15x
Add workarounds for ndk-r16.
2018-06-03 23:14:26 +02:00
SmallJoker
ecbb9310de Bump version to 0.4.17 2018-06-03 17:35:20 +02:00
Loic Blot
aac35a6e7c Backport buildbot win32 scripts too 2018-06-03 17:32:00 +02:00
Loic Blot
04fe8cc0a2 Update macosx build from master 2018-06-03 17:32:00 +02:00
Loic Blot
24abdf45b9 Fix windows toolchain due to missing packages, upgrade to more recent toolchain 2018-06-03 17:32:00 +02:00
SmallJoker
263400b3d8 C++03 oldify in various source files 2018-06-03 17:32:00 +02:00
Loic Blot
695d02e6bd More C++03 fixes 2018-06-03 17:32:00 +02:00
rubenwardy
e2815d27f1 Fix luajit include not being found 2018-06-03 17:32:00 +02:00
Paramat
ebfdb21624 Dungeons: Mostly fix missing stair nodes 2018-06-03 17:32:00 +02:00
mazocomp
0088fa6db4 Fix i386 bit build at OpenBSD (#7259) 2018-06-03 17:32:00 +02:00
Loic Blot
e660b05523 Fix C++03 compiling due to C++11 initialization issues in backport 2018-06-03 17:32:00 +02:00
SmallJoker
14d20f5827 Builtin auth handler: Speed up file writing (#7252) 2018-06-03 17:32:00 +02:00
Paramat
0414322d23 Cavegen: Fix variable typo that broke mgvalleys large cave distribution (#7249)
Fix elusive 5 year old bug that caused mgvalleys large caves to be flat and
limited to mapchunk borders.
Error was fixed 2 years ago in 'CavesV6' but not in 'CavesRandomWalk'.
2018-06-03 17:32:00 +02:00
sfan5
a6cfe73cb0 Fix segfault caused by wrong wgettext() 2018-06-03 17:32:00 +02:00
minduser00
1d06a8ef6c Fix for translating empty strings
Fix for incorrect translation of empty strings

In the key change menu, when a button key not have name an empty string is passed to gettext.
The empty string is reserved for gettext to return de header of the .po file an this is shoved in the button
2018-06-03 17:32:00 +02:00
SmallJoker
875972ffa6 upright_sprite: Fix texture position for players Fixes #6471 2018-06-03 17:32:00 +02:00
Loïc Blot
396daf1be1 Huge LBM lookup performance improvement on mapblock loading (#7195)
* Huge LBM lookup performance improvement on mapblock loading
2018-06-03 17:32:00 +02:00
SmallJoker
fe41725e50 core.rotate_node: Do not trigger after_place_node (#6900) 2018-06-03 17:32:00 +02:00
D Tim Cummings
5624cf750f macOS: don't require X11 libraries during compilation (#7149)
The xxf86vm needs to be removed from Apple builds to avoid CMake Error XXF86VM_LIBRARY is NOTFOUND
2018-06-03 17:32:00 +02:00
paramat
1d7fbd035d Minetest ASCII art: Move from actionstream to rawstream 2018-06-03 17:32:00 +02:00
paramat
a6b9acb7af Generate Notifier: Clear events once after all 'on generated' functions 2018-06-03 17:32:00 +02:00
red-001
c40f535df8 Fix liquid post effect colour behaviour in third person view 2018-06-03 17:32:00 +02:00
paramat
79fde0dd52 CollisionMoveSimple: Collide with 'ignore' nodes 2018-06-03 17:32:00 +02:00
paramat
8aaf526730 SAO limits: Allow SAOs to exist outside the set 'mapgen limit' 2018-06-03 17:32:00 +02:00
paramat
c683e050d4 Find nodes in area (under air): Raise volume limit and document it 2018-06-03 17:32:00 +02:00
Muhammad Rifqi Priyo Susanto
c2e39b9363 Delete world dialog: Move buttons to avoid double click deletion
Move confirmation delete button to never overlap initial delete button,
to avoid world deletion by accidental double click.
2018-06-03 17:32:00 +02:00
dopik
f8cc92c190 /shutdown can't do countdown when using reconnect and/or shutdown message (#7055)
Delay was converted from the param string and not the delay value, thus never using the actual given delay value when used in combination with other string values in the param, in this case reconnect and the shutdown messsage.
2018-06-03 17:32:00 +02:00
sfan5
858c41b842 Check argument types inside MetaDataRef Lua API (#7045) 2018-06-03 17:32:00 +02:00
you
2a4fbbbff8 Fix "Ignoring CONTENT_IGNORE redefinition" warning (#4393)
minetest.override_item still passes to core
2018-06-03 17:32:00 +02:00
paramat
6d346a817b Item entity: Delete in 'ignore' nodes 2018-06-03 17:32:00 +02:00
paramat
529f00a240 Falling.lua: Delete falling node entities on contact with 'ignore'
Prevents falling node entities entering the ignore at a world edge and
resting on unloaded nodes 16 nodes below, unreachable, undiggable and
still being processed by 'on step' because they don't revert to nodes.
2018-06-03 17:32:00 +02:00
you
7725030067 Allow dumping userdata (#7012) 2018-06-03 17:32:00 +02:00
red-001
bb28afcfc3 Move setlocale from Lua to C++. 2018-06-03 17:32:00 +02:00
you
880a25c921 Add minetest.is_player (#7013)
* Add minetest.is_player

* First use for is_player
2018-06-03 17:32:00 +02:00
mazocomp
0268c9d7c9 "static constexpr v3s16 light_dirs[8]" fails to compile, sync it with master! (#7261) 2018-06-03 17:32:00 +02:00
mazocomp
f72490950a Fix C++11 feature detection for undefined _MSC_VER (#7255) 2018-06-03 17:32:00 +02:00
sfan5
9d3fa874be Refine movement anticheat again (#7004)
* Account for walking speed in vertical dir
* Avoid undefined behaviour due to division-by-zero
2018-06-03 17:32:00 +02:00
sfan5
842eccee19 Apply physics overrides correctly during anticheat calculations (#6970) 2018-06-03 17:32:00 +02:00
Pedro Gimeno
127b1fa6f8 Fix off-by-one in log output line length (#6896) 2018-06-03 17:32:00 +02:00
Pedro Gimeno
6b5e2618fb Fix buffer parameter not working in LuaPerlinNoiseMap::l_getMapSlice() 2018-06-03 17:32:00 +02:00
you
b2099d4277 Fix Wstringop-overflow warning from util/srp.cpp (#6855)
* Fix Wstringop-overflow warning from util/srp.cpp
2018-06-03 17:32:00 +02:00
rubenwardy
df0a8574dc Fix rounding error in g/set_node caused by truncation to float 2018-06-03 17:32:00 +02:00
number Zero
6698067256 Fix dancing text 2018-06-03 17:32:00 +02:00
nOOb3167
bb219e1059 Fix undefined behaviour on getting pointer to data in empty vector
`&vector[0]` is undefined if vector.empty(), causing build failure on MSVC
2018-06-03 17:32:00 +02:00
Vitaliy
b3167d4e57 Fix wrong scrolling (#6809) 2018-06-03 17:32:00 +02:00
SmallJoker
8a849e893a Builtin: Fix handle_node_drops crash with nil digger 2018-06-03 17:32:00 +02:00
SmallJoker
f9738c1909 Damage: Remove damage ignore timer 2018-06-03 17:32:00 +02:00
Luis Cáceres
6f80e302fd Ensure no item stack is being held before crafting (#4779) 2018-06-03 17:32:00 +02:00
raymoo
48ebbf0fc6 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.
2018-06-03 17:32:00 +02:00
sfan5
7d9dbbbf3c 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.
2018-06-03 17:32:00 +02:00
sfan5
7cc1a36b3c Hint at problematic code when logging deprecated calls 2018-06-03 17:32:00 +02:00
Ezhh
3f1094475f Improve documentation for player:set_attribute() 2018-06-03 17:32:00 +02:00
SmallJoker
f72ac42ca0 Inventory: Restrict access from too far away 2018-06-03 17:32:00 +02:00
SmallJoker
8fba3c93d5 core.rotate_node: Run callbacks like with any regular placed node (#6648) 2018-06-03 17:32:00 +02:00
paramat
a90c314c80 Biome dust node: Only place on 'walkable' cubic non-liquid drawtypes
No longer decide placement on 'buildable_to' parameter.
Dust nodes only look acceptable placed on cubic nodes.
Modders may not want to make their plantlike decorations 'buildable_to'.
2018-06-03 17:32:00 +02:00
ezhh
222fab3d20 Improve Settings tab button alignments 2018-06-03 17:32:00 +02:00
paramat
1cf32b22be Lua_api.txt: Add documentation of required mapgen aliases 2018-06-03 17:32:00 +02:00
Ezhh
6f803b9c89 Remove incorrect entry from settingtypes 2018-06-03 17:32:00 +02:00
sfan5
0fe3e7574d Make use of safe file writing in auth handler (fixes #6576) 2018-06-03 17:32:00 +02:00
sfan5
b816c63196 Add minetest.safe_write_file() to script API 2018-06-03 17:32:00 +02:00
Muhammad Rifqi Priyo Susanto
313ca53b36 Fix issue Minetest crash when custom font path is not exist
We try to use default fallback for both mono and main font when custom font path is not exist. This way, if Minetest is not corrupted, we could avoid crash.
2018-06-03 17:32:00 +02:00
Ezhh
a65a46b889 Fix Settings tab formspec alignment (#6585) 2018-06-03 17:32:00 +02:00
Lars Hofhansl
0cfe3a810a Do not scale texture unless necessary.
This avoids scaling textures to 'texture_min_size' unless it is actually
required (because either auto-scaling or bi/trilinear filtering is enabled)
2018-06-03 17:32:00 +02:00
sfan5
6808a3d144 httpfetch: Enable gzip support 2018-06-03 17:32:00 +02:00
Rob Blanckaert
4d9bf75d3a Add sha1 to lua utils. (#6563) 2018-06-03 17:32:00 +02:00
rubenwardy
416c4535c7 Fix day_night_ratio_do_override not being initialised server-side
Causes get_day_night_ratio() to return unpredictable results.
2018-06-03 17:32:00 +02:00
lhofhansl
73baeb82ef Avoid filtering low-res textures for animated meshes (incl. players) (#6562) 2018-06-03 17:32:00 +02:00
raymoo
7b8288d605 Fix default item callbacks to work with nil users (#5819)
* Fix default item callbacks to work with nil users

* item.lua: Handle node drops for invalid players

The if-condition for the dropping loop is the same as `inv`, which means that the 2nd possible definition of `give_item` is never used.
Remove redundant `local _, dropped_item`
2018-06-03 17:32:00 +02:00
Auke Kok
2f969196b8 Correct prot_vers in lua_api.txt.
We should avoid providing incorrect struct members in documentation
since people will be coding based on them.
2018-06-03 17:32:00 +02:00
Esteban I. RM
0041bcc73e Don't try to craft a non-existent item 2018-06-03 17:32:00 +02:00
rubenwardy
cc48c95ca7 Profiler: Fix var args not being passed to callback register function
Fixes #6517
2018-06-03 17:31:59 +02:00
Loïc Blot
0129c9a9dd Thread: fix a crash on Windows due to data race condition on Thread::m_start_finished_mutex (#6515) 2018-06-03 17:31:59 +02:00
Loic Blot
9dc1f2d638 NetworkPacket::putRawPacket: resize m_data to datasize + memcpy
In some cases NetworkPacket was created using default constructor and m_data is not properly sized.
This fixed out of bounds memory copy
Also use memcpy instead of std::vector affectation to enhance packet creation
2018-06-03 17:31:59 +02:00
adrido
d215198fe8 Replace deprecated WINAPI GetVersionInfoEx (#6496)
* Replace deprecated WINAPI GetVersionInfoEx
2018-06-03 17:31:59 +02:00
Rob Blanckaert
c56c3d8d6f Add setting for near plane distance. (#6395)
* Allow setting the near plane

* - Add near_plane limit of 0.5 to prevent x-ray.
- Add more details to near_plane setting.
2018-06-03 17:31:59 +02:00
SmallJoker
0034abb560 Unkown nodes: Provide position on interact (#6505)
* Unkown nodes: Provide position on interact
2018-06-03 17:31:59 +02:00
SmallJoker
200e9cc4a2 ParticleSpawner::step cleanup and rotation fix (#6486)
* Particles: Move spawner code to a separate fucntion
2018-06-03 17:31:59 +02:00
paramat
6b0fb94d60 CAO footstep sounds: Reduce gain to balance volume 2018-06-03 17:31:59 +02:00
raymoo
017815161b Fix attached particle spawners far from spawn (#6479)
* Fix attached particle spawners far from spawn

When far from spawn, attached particle spawners
did not spawn particles.
2018-06-03 17:31:59 +02:00
SmallJoker
ab72100a2c Localplayer: Fix disable_jump effect and getStandingNodePos()
Leave the old move code untouched.
2018-06-03 17:31:59 +02:00
raymoo
9927076b47 Document orientation parameter of set_attach (#6473) 2018-06-03 17:31:59 +02:00
Paramat
bd8d6f8f2f Fix recent commit: std::max -> std::fmax for floats (#6469)
Fixes commit a455297d297c0819a7eff89e51e5f01a5ac731c3
<cmath> header was already present in commit.
2018-06-03 17:31:59 +02:00
paramat
3a9d500396 Positional sound: Limit volume when closer than 1 node
Change OpenAL distance model from AL_INVERSE_DISTANCE to
AL_INVERSE_DISTANCE_CLAMPED to avoid excessive volume when very close
to the sound location, for example MTG doors, and MTG fire sounds which
are combined at an average position and often located in air nodes.

Because AL_REFERENCE_DISTANCE has been reduced to 1 node (the distance
under which gain is clamped), multiply volume by the same factor to keep
sound gains the same as before, since the gain is calculated as:

gain = (AL_REFERENCE_DISTANCE / distance)
2018-06-03 17:31:59 +02:00
sfan5
01b9da870b Fix blocks written by vmanip not being marked as modified
This bug can be triggered by e.g. calling minetest.place_schematic()
and stopping the server immediately afterwards.
2018-06-03 17:31:59 +02:00
DTA7
e8286e8894 Set placer to nil instead of a non-functional one in item_OnPlace (#6449)
* Set placer to nil instead of a non-functional one

This requires nil checks in core.rotate_node and core.rotate_and_place.
2018-06-03 17:31:59 +02:00
paramat
b8f473be21 Leveled nodebox: Change levels from 1/63rds to 1/64ths
Add missing documentation of leveled nodebox to lua_api.txt, plus
a little cleaning up nearby.
2018-06-03 17:31:59 +02:00
tenplus1
b1fae4c7be Fix Rotate Node Placement (#6424)
This properly checks for creative mode or privilege when using fixed rotate_node() function.
2018-06-03 17:31:59 +02:00
sfan5
c2a0333901 ServerEnv: Clean up object lifecycle handling (#6414)
* ServerEnv: Clean up object lifecycle handling
2018-06-03 17:31:59 +02:00
sfan5
5b2461c713 Fix core.wrap_text and make its behaviour consistent with the docs
Code based on initial implementation by @dsohler.
2018-06-03 17:31:59 +02:00
rubenwardy
e9087d1be7 Fix empty legacy meta being persisted 2018-06-03 17:31:59 +02:00
Nathanaël Courant
bb4ef52954 Statbars: fix incorrect half-images in non-standard orientations (fixes #6198) 2018-06-03 17:31:59 +02:00
Paramat
888b99e1b6 Android stepheight: Only increase if 'touching ground' (#6313) 2018-06-03 17:31:59 +02:00
Dániel Juhász
2c450ed93f Fix Android node selection distance (#6187) 2018-06-03 17:31:59 +02:00
Loïc Blot
d9c7af109a serialize: use a temporary for SerializeException Exception must always use temporary instead of global copied exception instances, it's not recommended and should have undefined issues 2018-06-03 17:31:59 +02:00
Loic Blot
12562be393 Typo fix in compat code from commit 1d8d01074fdb52946f81110bebf1d001185b394b 2018-06-03 17:31:59 +02:00
Loïc Blot
151c19a6be ClientInterface: add a function to verify (correctly) if user limit was reached (#6258)
* ClientInterface: add a function to verify (correctly) if user limit was reached

CS_HelloSent is a better indicator of active slots than CS_Created, which are session objects created after init packet reception

Switch existing checks to ClientInterface::isUserLimitReached()

Use range-based for loop for getClientIds() used function too

This will fix #6254 (not the memory overhead if init is flooded)
2018-06-03 17:31:59 +02:00
Dániel Juhász
9d40d89d27 Make dropped items colorable 2018-06-03 17:31:59 +02:00
SmallJoker
037b01eac7 Trigger on_rightclick regardless on the formspec meta field
Document behaviour for older clients.
2018-06-03 17:31:59 +02:00
Fixer
c789c532bb Full viewing range key message clarified
To make it sound less confusing to players
2018-06-03 17:31:59 +02:00
Jens Rottmann
b3ffe675c3 Add tiny Y offset in collisionMoveSimple() to tweak performance
Another small general problem: the player is always standing exactly on the
bondary between 2 nodes e.g. Y=1.5 is exactly between nodes Y=1 and Y=2.
floatToInt() and myround() will round +/-n.5 always 'outwards' to +/-(n+1),
which means they behave differently depending on where you are: they round
upwards above sea level and downwards when underground. This inconsistency
comes from the way the coordinates are calculated, independent of the
specific C++ code.

The result is a tiny bit of lost performance when moving underground,
because 1 node level more than necessary is checked for collisions. This can
be amended by adding a tiny offset to minpos_f.Y, like @paramat suggested.
This is not an elegant solution, but still better than wasting CPU.
2018-06-03 17:31:59 +02:00
Jens Rottmann
90a9e4e69f Fix player coordinate rounding in collisionMoveSimple() (#6197)
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
2018-06-03 17:31:59 +02:00
Juozas Pocius
e5311a4d56 Fix crash when using --go in command line 2018-06-03 17:31:59 +02:00
SmallJoker
4be7d8b43a Noise: Prevent unittest crash caused by division by zero 2018-06-03 17:31:59 +02:00
SmallJoker
070ab6654a Sneak: Stripped down version
Fix taking damage caused by sneaking over a nodebox gap.
Fix strange behaviour on stair nodeboxes.
Enable jumping from node edges while sneaking.
Enable movement around corners while sneaking on a 1-node-high groove in a wall.
2018-06-03 17:31:59 +02:00
stujones11
ebf9dda2e6 Include TILE_MATERIAL_OPAQUE in shaders header (#6086) 2018-06-03 17:31:59 +02:00
stujones11
c352ff71e8 Tile material: Add 'TILE_MATERIAL_OPAQUE', use for drawtype 'NDT_NORMAL'
Prevents normal drawtype nodes having transparency.
Avoids clients cheating by using 'x-ray' texture packs with transparent textures.
2018-06-03 17:31:59 +02:00
paramat
26d0753d87 Mgv7: Fix undefined 'float_mount_height'
Commit cad10ce3b747b721fd63784915e05f12bc488128 altered the parameter
'float_mount_height' but was missing the necessary line in the constructor
to get the altered value from 'params'.

Fixes 3D floatland terrain generating everywhere.
2018-06-03 17:31:59 +02:00
Jesse McDonald
46ff2e2cef Fix for empty key/value when reading item string with wear but no metadata (#6058) 2018-06-03 17:31:59 +02:00
paramat
a08a93bc9c Mgv7: Clean up divide-by-zero fix 2018-06-03 17:31:59 +02:00
paramat
6e0557e20c Mgv7: Avoid divide-by-zero errors
Some settings of paramters can cause mgv7 variables to be -inf, nan or -nan.
This can cause massive vertical columns of water to appear above sea level.
2018-06-03 17:31:59 +02:00
Dániel Juhász
0c91c65a11 Fix render order of overlays (#6008)
* Fix render order of overlays

* Use C++11 loops

* Fix time_t
2018-06-03 17:31:59 +02:00
Ezhh
849fe19f8c Fix console resize issue when maximising game window (#6023) 2018-06-03 17:31:59 +02:00
Zeno-
cfa6216694 Fix console not being properly resized after window size changed (#6020) 2018-06-03 17:31:59 +02:00
Dániel Juhász
322e5aaf92 Automatic item and node colorization (#5640)
* Automatic item and node colorization

Now nodes with a palette yield colored item stacks, and colored items
place colored nodes by default. The client predicts the colorization.

* Backwards compatibility

* Use nil

* Style fixes

* Fix code style

* Document changes
2018-06-03 17:31:59 +02:00
SmallJoker
03bc584f57 find_nodes_in_area: Extend maximal count to U32_MAX (#5277)
Extend documentation, limit area volume
Remove u16 count limitation

* Prevent integer overflow, replace minp/maxp with pos1/pos2
2018-06-03 17:31:59 +02:00
Loïc Blot
5f796f7a04 Verify HudSetParams input when hotbar textures are set (#6013)
* Verify HudSetParams input when hotbar textures are set

This fix #6011
2018-06-03 17:31:59 +02:00
paramat
7aa52fe4e1 (Re)spawn players within 'mapgen_limit'
Previously, findSpawnPos() did not take the 'mapgen_limit' setting into account,
a small limit often resulted in a spawn out in the void.
Use the recently added 'calcMapgenEdges()' to get max spawn range through a new
mapgenParams function 'getSpawnRangeMax()'.

Previously, when a player respawned into a world, 'objectpos_over_limit()' was
used as a check, which was inaccurate.
Use the recently added 'saoPosOverLimit()' to get exact mapgen edges.

Also fix default value of 'm_sao_limit_min'.
2018-06-03 17:31:59 +02:00
red-001
0664b5f772 Add a server-sided way to remove color codes from incoming chat messages (#5948)
These code be generated by CSM, a modded client or just copy and pasted by the player.

Changes
- Update configuration example and setting translation file.
- Remove colour codes before logging chat.
- Add setting to remove colour codes before processing the chat.
2018-06-03 17:31:59 +02:00
DS
14b039f0b4 fix an example in lua_api (#5604) 2018-06-03 17:31:59 +02:00
red-001
c399f5a541 Fix sending color codes to clients that don't support them. (#5950)
Also remove `disable_escape_sequences` since it's not needed anymore.
2018-06-03 17:31:59 +02:00
DS
f736226c1a make ret variable in /builtin/mainmenu/tab_credits.lua local (#5942) 2018-06-03 17:31:59 +02:00
red-001
e665e75e77 Fix typos/mistakes in the documentation for colour related functions. (#5936) 2018-06-03 17:31:59 +02:00
ShadowNinja
cba783f7fa Fix segmentation fault with tool capabilities (#5899) 2018-06-03 17:31:59 +02:00
sfan5
87b9451820 Revert version scheme changes
This reverts commit 41b7823057bdaddd760f932dce802719301c3a0f.
This reverts commit 7968f1ddaa67432719d5becdda5ca8bec58faa47.
2017-11-20 19:28:58 +01:00
rubenwardy
41b7823057 Fix branch being labelled as 0.4.17 instead of 0.4.17-dev
You should use tags instead of this branch to track the latest release
2017-09-09 20:42:23 +01:00
Loïc Blot
7968f1ddaa
New version scheme (#6292)
* Version changes: current dev version is now 0.4.17

* This change permit to have multi branches with various versions
* Dev version is 0.4.17-dev and next release will be 0.4.17
2017-08-20 17:25:07 +02:00
ShadowNinja
caecdb681c Merge 0.4.16 into stable-0.4 2017-06-03 14:55:10 -04:00
est31
81d56b9491 Merge 0.4.15 changes into stable-0.4
0.4.15 release!
2016-12-22 23:16:00 +01:00
est31
8077612dcb Merge 0.4.14 changes into stable-0.4
0.4.14 release!
2016-05-15 15:20:25 +02:00
est31
07fddf1f6c Merge 0.4.13 changes into stable-0.4
0.4.13 release!
2015-08-20 21:02:42 +02:00
Loic Blot
315b00d150 Bump android version code 2015-03-16 20:37:07 +01:00
est31
0c0248a19c Android: Fix auto-entry of server address and port in mainmenu
Fixes #2497.
2015-03-16 19:46:54 +01:00
Loic Blot
0429ec4cfd Android: update makefile and backport language fix from master 2015-03-15 11:01:39 +01:00
Craig Robbins
a740a48f62 Fix narrow_to_wide_c (ANDROID)
* Ensure converted string is NUL terminated
* Restore logic to that used prior to 9e2a9b5
2015-03-15 10:17:09 +01:00
Loic Blot
b12f569fc6 Releasing android 2015-03-14 19:57:21 +01:00
Perttu Ahola
7993a403f2 Bump version to 0.4.12 2015-02-18 19:50:37 +02:00
Novatux
b0df67d9c0 Add modname convention checking
Fixes #2037
2015-02-18 17:06:09 +01:00
Loic Blot
45ff8569d7 Fix serverlist include 2015-02-18 16:26:23 +01:00
est31
82bfa2ee7b Server: announce MIN/MAX protocol version supported to serverlist. Client: check serverlist
Client now informs about incompatible servers from the list, this permits to prevent the protocol movements.
Server announces its supported protocol versions to master server
2015-02-18 16:17:03 +01:00
fz72
9ef2e5000f Fix map_seed not changed when creating a new world after login to another 2015-02-18 14:02:30 +02:00
BlockMen
678546308e Increase default font_size 2015-02-18 12:59:16 +01:00
BlockMen
6f688c50ee Fix font_size under windows 2015-02-18 12:59:07 +01:00
Perttu Ahola
bb603ff18e Use fixed size for builtin menus on non-android platforms 2015-02-18 13:57:54 +02:00
Loic Blot
4208fdfd22 Fix unused (and so, broken) enable_rollback_recording. This option must be reloaded at server loop but loaded when server starts, for data consistency (not a hot load variable) 2015-02-18 09:36:42 +01:00
Kahrl
4875213168 Grab GUIChatConsole::m_font, fixes segfault when changing font_size 2015-02-18 00:40:11 +10:00
Loic Blot
f92540e8ad Add german and french translation for minetest.desktop
This fixes #1573
2015-02-14 13:12:08 +01:00
Jakub Vaněk
ec0bf899ed Update czech translation 2015-02-14 13:11:50 +01:00
ngosang
2b635a892c Minor fixes in translations 2015-02-14 13:11:33 +01:00
Loic Blot
7f6fc148bd Fix issue #2279. ok @zeno- 2015-02-14 21:02:19 +10:00
ngosang
878e9f7594 Fix .zip extraction (mod store) 2015-02-14 21:00:13 +10:00
Rui
c7249f5983 Fix store.lua bug: default screenshot 2015-02-14 21:00:06 +10:00
Rui
7f07858209 Fix tab_mods.lua: default screenshot patch
https://forum.minetest.net/viewtopic.php?f=6&t=11201
Fixed this bug.
2015-02-14 21:00:01 +10:00
ShadowNinja
9e9688fc61 Fix Android build of narrow_to_wide 2015-02-14 20:59:45 +10:00
Markus Koschany
93e5ab367a Fix FTBFS on GNU/Hurd platform
Minetest fails to build on GNU/Hurd due to a name clash with OSX/Apple,
both are defining the __MACH__ keyword. This commit fixes the issue.
2015-02-14 20:59:35 +10:00
est31
60fa5807b9 README.txt: Simplify initial build steps by using git to fetch sources
Also simplify wget steps and apt-get install zlib1g-dev libjsoncpp-dev
2015-02-14 20:59:00 +10:00
ngosang
9dbca41385 Fix Exit to OS button focus in Pause Menu 2015-02-14 20:58:39 +10:00
est31
61588a43dd Fix crash on passing false as value in table to table.copy(t)
Fixes #2293.
2015-02-14 15:24:09 +10:00
BlockMen
e62927ed71 Fix gettext on MSVC 2015-02-13 01:23:30 +10:00
153 changed files with 2880 additions and 2148 deletions

3
.gitignore vendored

@ -77,6 +77,7 @@ src/cmake_config_githash.h
src/lua/build/
locale/
.directory
.gradle/
*.cbp
*.layout
*.o
@ -84,6 +85,8 @@ locale/
*.ninja
.ninja*
*.gch
*.iml
test_config.h
cmake-build-debug/
cmake-build-release/

@ -13,13 +13,14 @@ set(PROJECT_NAME_CAPITALIZED "Minetest")
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 16)
set(VERSION_PATCH 17)
set(VERSION_TWEAK 1)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Change to false for releases
set(DEVELOPMENT_BUILD FALSE)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}")
if(VERSION_EXTRA)
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
elseif(DEVELOPMENT_BUILD)

@ -6,7 +6,8 @@ OS := $(shell uname)
# GPROF = 1
# build for build platform
APP_PLATFORM = android-9
API = 14
APP_PLATFORM = android-$(API)
ANDR_ROOT = $(shell pwd)
PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..)
@ -30,7 +31,7 @@ TARGET_HOST = arm-linux
TARGET_ABI = armeabi-v7a
TARGET_LIBDIR = armeabi-v7a
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_ARCH = armv7
CROSS_PREFIX = arm-linux-androideabi-
@ -82,7 +83,7 @@ OGG_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so
VORBIS_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so
OGG_TIMESTAMP = $(OGG_DIR)timestamp
OGG_TIMESTAMP_INT = $(ANDR_ROOT)/deps/ogg_timestamp
OGG_URL_GIT = https://github.com/vincentjames501/libvorbis-libogg-android
OGG_URL_GIT = https://gitlab.com/minetest/libvorbis-libogg-android
IRRLICHT_REVISION = 5145
IRRLICHT_DIR = $(ANDR_ROOT)/deps/irrlicht/
@ -99,20 +100,13 @@ OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp
OPENSSL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openssl_timestamp
OPENSSL_URL = https://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz
CURL_VERSION = 7.54.0
CURL_VERSION = 7.60.0
CURL_DIR = $(ANDR_ROOT)/deps/curl-$(CURL_VERSION)
CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a
CURL_TIMESTAMP = $(CURL_DIR)/timestamp
CURL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/curl_timestamp
CURL_URL_HTTP = https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2
GMP_VERSION = 6.1.2
GMP_DIR = $(ANDR_ROOT)/deps/gmp-$(GMP_VERSION)
GMP_LIB = $(GMP_DIR)/usr/lib/libgmp.so
GMP_TIMESTAMP = $(GMP_DIR)/timestamp
GMP_TIMESTAMP_INT = $(ANDR_ROOT)/deps/gmp_timestamp
GMP_URL_HTTP = https://gmplib.org/download/gmp/gmp-$(GMP_VERSION).tar.bz2
FREETYPE_DIR = $(ANDR_ROOT)/deps/freetype2-android/
FREETYPE_LIB = $(FREETYPE_DIR)/Android/obj/local/$(TARGET_ABI)/libfreetype2-static.a
FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp
@ -126,12 +120,11 @@ ICONV_TIMESTAMP = $(ICONV_DIR)timestamp
ICONV_TIMESTAMP_INT = $(ANDR_ROOT)/deps/iconv_timestamp
ICONV_URL_HTTP = https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz
SQLITE3_FOLDER = sqlite-amalgamation-3180000
SQLITE3_URL = https://www.sqlite.org/2017/$(SQLITE3_FOLDER).zip
SQLITE3_FOLDER = sqlite-amalgamation-3240000
SQLITE3_URL = https://www.sqlite.org/2018/$(SQLITE3_FOLDER).zip
ANDROID_SDK = $(shell grep '^sdk\.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
ifeq ($(HAVE_LEVELDB),1)
@ -217,14 +210,10 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP)
if [ $$REFRESH -ne 0 ] ; then \
echo "changed timestamp for openal detected building..."; \
cd ${OPENAL_DIR}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} APP_ABI=${TARGET_ABI} \
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; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
touch ${OPENAL_TIMESTAMP}; \
touch ${OPENAL_TIMESTAMP_INT}; \
else \
@ -248,7 +237,6 @@ ogg_download :
git clone ${OGG_URL_GIT}|| exit 1; \
cd libvorbis-libogg-android ; \
patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \
sed -i 's-:-?-' jni/Application.mk; \
fi
ogg : $(OGG_LIB)
@ -265,14 +253,12 @@ $(OGG_LIB): $(OGG_TIMESTAMP)
if [ $$REFRESH -ne 0 ] ; then \
echo "changed timestamp for ogg detected building..."; \
cd ${OGG_DIR}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \
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; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
touch ${OGG_TIMESTAMP}; \
touch ${OGG_TIMESTAMP_INT}; \
else \
@ -301,7 +287,7 @@ openssl_download :
openssl : $(OPENSSL_LIB)
$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) $(GMP_LIB)
$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP)
@REFRESH=0; \
if [ ! -e ${OPENSSL_TIMESTAMP_INT} ] ; then \
echo "${OPENSSL_TIMESTAMP_INT} doesn't exist"; \
@ -317,10 +303,10 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) $(GMP_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
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 -DL_ENDIAN android-${TARGET_ARCH};\
CC=${CROSS_PREFIX}gcc ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make depend; \
CC=${CROSS_PREFIX}gcc ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make build_libs; \
touch ${OPENSSL_TIMESTAMP}; \
@ -368,7 +354,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
@ -420,14 +406,10 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
mkdir -p ${FREETYPE_DIR}; \
echo "changed timestamp for freetype detected building..."; \
cd ${FREETYPE_DIR}/Android/jni; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \
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; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
touch ${FREETYPE_TIMESTAMP}; \
touch ${FREETYPE_TIMESTAMP_INT}; \
else \
@ -478,9 +460,10 @@ $(ICONV_LIB) : $(ICONV_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \
export CC=${CROSS_PREFIX}gcc; \
export CXX=${CROSS_PREFIX}g++; \
export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
@ -513,6 +496,7 @@ irrlicht_download :
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-texturehack.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-native_activity.patch || exit 1; \
fi
$(IRRLICHT_TIMESTAMP) : irrlicht_download
@ -538,14 +522,10 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
mkdir -p ${IRRLICHT_DIR}; \
echo "changed timestamp for irrlicht detected building..."; \
cd deps/irrlicht/source/Irrlicht/Android; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \
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; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Irrlicht.mk || exit 1; \
touch ${IRRLICHT_TIMESTAMP}; \
touch ${IRRLICHT_TIMESTAMP_INT}; \
else \
@ -593,7 +573,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
@ -615,64 +595,6 @@ clean_curl :
$(RM) -rf deps/curl-${CURL_VERSION} \
$(RM) -f deps/curl
$(GMP_TIMESTAMP) : gmp_download
@LAST_MODIF=$$(find ${GMP_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
touch ${GMP_TIMESTAMP}; \
fi
gmp_download :
@if [ ! -d "${GMP_DIR}" ] ; then \
echo "gmp sources missing, downloading..."; \
mkdir -p ${ANDR_ROOT}/deps; \
cd deps; \
wget ${GMP_URL_HTTP} || exit 1; \
tar -xjf gmp-${GMP_VERSION}.tar.bz2 || exit 1; \
rm gmp-${GMP_VERSION}.tar.bz2; \
ln -s gmp-${GMP_VERSION} gmp; \
fi
gmp : $(GMP_LIB)
$(GMP_LIB): $(GMP_TIMESTAMP)
@REFRESH=0; \
if [ ! -e ${GMP_TIMESTAMP_INT} ] ; then \
REFRESH=1; \
fi; \
if [ ! -e ${GMP_LIB} ] ; then \
REFRESH=1; \
fi; \
if [ ${GMP_TIMESTAMP} -nt ${GMP_TIMESTAMP_INT} ] ; then \
REFRESH=1; \
fi; \
if [ $$REFRESH -ne 0 ] ; then \
mkdir -p ${GMP_DIR}; \
echo "changed timestamp for gmp detected building..."; \
cd deps/gmp-${GMP_VERSION}; \
export CROSS_PREFIX=${CROSS_PREFIX}; \
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
export CXX=${CROSS_PREFIX}g++; \
export LIBGMP_LDFLAGS="-avoid-version"; \
export LIBGMPXX_LDFLAGS="-avoid-version"; \
./configure --disable-static --host=${TARGET_HOST} --prefix=/usr; \
$(MAKE) install DESTDIR=/${GMP_DIR} || exit 1; \
touch ${GMP_TIMESTAMP}; \
touch ${GMP_TIMESTAMP_INT}; \
$(RM) -rf $${TOOLCHAIN}; \
else \
echo "nothing to be done for gmp"; \
fi
clean_gmp:
$(RM) -rf deps/gmp-${GMP_VERSION} \
$(RM) -f deps/gmp
sqlite3_download: deps/${SQLITE3_FOLDER}/sqlite3.c
deps/${SQLITE3_FOLDER}/sqlite3.c :
@ -762,20 +684,16 @@ assets : $(ASSETS_TIMESTAMP)
fi
clean_assets :
@$(RM) -r assets
@$(RM) -r ${APP_ROOT}/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) $(LEVELDB_TARGET) \
$(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \
$(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download
+ @${ANDROID_NDK}/ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \
GPROF=${GPROF} APP_ABI=${TARGET_ABI} HAVE_LEVELDB=${HAVE_LEVELDB} \
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_LIBDIR=${TARGET_LIBDIR} \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ @export TARGET_LIBDIR=${TARGET_LIBDIR}; \
export HAVE_LEVELDB=${HAVE_LEVELDB}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build || exit 1; \
if [ ! -e ${APP_ROOT}/jniLibs ]; then \
ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \
fi; \
@ -807,7 +725,7 @@ clean_apk :
clean_all :
@$(MAKE) clean_apk; \
$(MAKE) clean_assets clean_iconv clean_irrlicht clean_leveldb clean_curl \
clean_openssl clean_openal clean_ogg clean_gmp; \
clean_openssl clean_openal clean_ogg; \
sleep 1; \
$(RM) -r gen libs obj deps bin Debug and_env

@ -1,25 +1,46 @@
buildscript {
repositories {
mavenCentral()
maven { url 'https://maven.google.com' }
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:1.5.0"
classpath 'com.android.tools.build:gradle:3.1.3'
}
}
allprojects {
repositories {
maven { url 'https://maven.google.com' }
jcenter()
}
}
def curl_version = "7.60.0"
def gmp_version = "6.1.2"
def irrlicht_revision = "5150"
def openal_version = "1.18.2"
def openssl_version = "1.0.2n"
def sqlite3_version = "3260000"
apply plugin: "com.android.application"
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
versionCode 17
versionCode 22
versionName "${System.env.VERSION_STR}.${versionCode}"
minSdkVersion 9
targetSdkVersion 9
minSdkVersion 14
targetSdkVersion 28
applicationId "net.minetest.minetest"
manifestPlaceholders = [ package: "net.minetest.minetest", project: project.name ]
manifestPlaceholders = [package: "net.minetest.minetest", project: project.name]
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64',
'arm64-v8a'
}
}
lintOptions {
@ -46,3 +67,68 @@ android {
}
}
}
task cleanAssets(type: Delete) {
delete 'src/main/assets'
}
task cleanIconv(type: Delete) {
delete 'deps/libiconv'
}
task cleanIrrlicht(type: Delete) {
delete 'deps/irrlicht'
}
task cleanLevelDB(type: Delete) {
delete 'deps/leveldb'
}
task cleanCURL(type: Delete) {
delete 'deps/curl'
delete 'deps/curl-' + curl_version
}
task cleanOpenSSL(type: Delete) {
delete 'deps/openssl'
delete 'deps/openssl-' + openssl_version
delete 'deps/openssl-' + openssl_version + '.tar.gz'
}
task cleanOpenAL(type: Delete) {
delete 'deps/openal-soft'
}
task cleanFreetype(type: Delete) {
delete 'deps/freetype2-android'
}
task cleanOgg(type: Delete) {
delete 'deps/libvorbis-libogg-android'
}
task cleanSQLite3(type: Delete) {
delete 'deps/sqlite-amalgamation-' + sqlite3_version
delete 'deps/sqlite-amalgamation-' + sqlite3_version + '.zip'
}
task cleanGMP(type: Delete) {
delete 'deps/gmp'
delete 'deps/gmp-' + gmp_version
}
task cleanAll(type: Delete, dependsOn: [clean, cleanAssets, cleanIconv,
cleanFreetype, cleanIrrlicht, cleanLevelDB, cleanSQLite3, cleanCURL,
cleanOpenSSL, cleanOpenAL, cleanOgg, cleanGMP]) {
delete 'deps'
delete 'gen'
delete 'libs'
delete 'obj'
delete 'bin'
delete 'Debug'
delete 'and_env'
}
dependencies {
implementation 'com.android.support:support-v4:28.0.0'
}

Binary file not shown.

@ -1,6 +1,6 @@
#Sat Aug 27 20:10:09 CEST 2016
#Mon Oct 15 00:47:03 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

110
build/android/gradlew vendored

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@ -6,47 +6,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

@ -44,11 +44,6 @@ LOCAL_MODULE := vorbis
LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libvorbis.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := gmp
LOCAL_SRC_FILES := deps/gmp/usr/lib/libgmp.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ssl
LOCAL_SRC_FILES := deps/openssl/libssl.a
@ -98,6 +93,7 @@ endif
LOCAL_C_INCLUDES := \
jni/src \
jni/src/script \
jni/lib/gmp \
jni/lib/lua/src \
jni/lib/jsoncpp \
jni/src/cguittfont \
@ -107,7 +103,6 @@ LOCAL_C_INCLUDES := \
deps/curl/include \
deps/openal-soft/jni/OpenAL/include \
deps/libvorbis-libogg-android/jni/include \
deps/gmp/usr/include \
deps/leveldb/include \
deps/sqlite/
@ -339,6 +334,9 @@ LOCAL_SRC_FILES += \
#freetype2 support
LOCAL_SRC_FILES += jni/src/cguittfont/xCGUITTFont.cpp
# GMP
LOCAL_SRC_FILES += jni/lib/gmp/mini-gmp.c
# Lua
LOCAL_SRC_FILES += \
jni/lib/lua/src/lapi.c \
@ -385,7 +383,7 @@ LOCAL_SRC_FILES += \
# JSONCPP
LOCAL_SRC_FILES += jni/lib/jsoncpp/jsoncpp.cpp
LOCAL_SHARED_LIBRARIES := iconv openal ogg vorbis gmp
LOCAL_SHARED_LIBRARIES := iconv openal ogg vorbis
LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS)
ifeq ($(HAVE_LEVELDB), 1)

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

@ -0,0 +1,7 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := c++_shared
APP_DEPRECATED_HEADERS := true
APP_CFLAGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3
APP_CPPFLAGS += -fexceptions

@ -0,0 +1,8 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := c++_static
APP_DEPRECATED_HEADERS := true
APP_MODULES := Irrlicht
APP_CFLAGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3
APP_CPPFLAGS += -fexceptions

@ -0,0 +1,13 @@
--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2018-06-10 16:58:11.357709173 +0200
+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2018-06-10 16:58:25.100709843 +0200
@@ -9,6 +9,10 @@
#include "irrString.h"
#include "os.h"
+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
+#include <android/native_activity.h>
+#endif
+
namespace irr
{
namespace video

@ -1,2 +1 @@
rootProject.name = "Minetest"

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.SET_DEBUG_APP" />
</manifest>

@ -2,33 +2,58 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.minetest.minetest"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00010000" android:required="true"/>
<uses-feature
android:glEsVersion="0x00010000"
android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:icon="@drawable/irr_icon"
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="${project}"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:allowBackup="true">
<activity android:name=".MtNativeActivity"
android:resizeableActivity="false">
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:label="${project}"
android:launchMode="singleTask"
android:configChanges="orientation|keyboard|keyboardHidden|navigation"
android:screenOrientation="sensorLandscape"
android:clearTaskOnLaunch="true">
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="minetest" />
</activity>
<activity android:name=".MinetestTextEntry"
android:theme="@style/Theme.Transparent"
android:excludeFromRecents="true">
</activity>
<activity android:name=".MinetestAssetCopy"
android:theme="@style/Theme.Transparent"
android:excludeFromRecents="true">
<activity
android:name=".MtNativeActivity"
android:configChanges="orientation|keyboard|keyboardHidden|navigation|screenSize|smallestScreenSize"
android:hardwareAccelerated="true"
android:launchMode="singleTask"
android:screenOrientation="sensorLandscape"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="minetest" />
</activity>
<activity
android:name=".MinetestTextEntry"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/Theme.Dialog"
android:windowSoftInputMode="stateAlwaysHidden"/>
<activity
android:name=".MinetestAssetCopy"
android:screenOrientation="sensorLandscape"
android:theme="@style/AppTheme"/>
</application>
</manifest>

@ -0,0 +1,79 @@
package net.minetest.minetest;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends Activity {
private final static int PERMISSIONS = 1;
private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkPermission();
} else {
next();
}
}
protected void checkPermission() {
final List<String> missingPermissions = new ArrayList<String>();
// check required permission
for (final String permission : REQUIRED_SDK_PERMISSIONS) {
final int result = ContextCompat.checkSelfPermission(this, permission);
if (result != PackageManager.PERMISSION_GRANTED) {
missingPermissions.add(permission);
}
}
if (!missingPermissions.isEmpty()) {
// request permission
final String[] permissions = missingPermissions
.toArray(new String[missingPermissions.size()]);
ActivityCompat.requestPermissions(this, permissions, PERMISSIONS);
} else {
final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length];
Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS,
grantResults);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
@NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS:
for (int index = 0; index < permissions.length; index++) {
if (grantResults[index] != PackageManager.PERMISSION_GRANTED) {
// permission not granted - toast and exit
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
finish();
return;
}
}
// permission were granted - run
next();
break;
}
}
public void next() {
Intent intent = new Intent(this, MtNativeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}

@ -1,5 +1,17 @@
package net.minetest.minetest;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
@ -7,36 +19,20 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Vector;
import java.util.Iterator;
import java.lang.Object;
import java.util.Vector;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
public class MinetestAssetCopy extends Activity {
ProgressBar m_ProgressBar;
TextView m_Filename;
copyAssetTask m_AssetCopy;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.graphics.Rect;
import android.graphics.Paint;
import android.text.TextPaint;
public class MinetestAssetCopy extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.assetcopy);
m_ProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
m_Filename = (TextView) findViewById(R.id.textView1);
m_ProgressBar = findViewById(R.id.progressBar1);
m_Filename = findViewById(R.id.textView1);
Display display = getWindowManager().getDefaultDisplay();
m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8);
m_ProgressBar.invalidate();
@ -44,53 +40,69 @@ public class MinetestAssetCopy extends Activity
/* check if there's already a copy in progress and reuse in case it is*/
MinetestAssetCopy prevActivity =
(MinetestAssetCopy) getLastNonConfigurationInstance();
if(prevActivity!= null) {
if (prevActivity != null) {
m_AssetCopy = prevActivity.m_AssetCopy;
}
else {
} else {
m_AssetCopy = new copyAssetTask();
m_AssetCopy.execute();
}
}
@Override
protected void onResume() {
super.onResume();
makeFullScreen();
}
public void makeFullScreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
makeFullScreen();
}
}
/* preserve asset copy background task to prevent restart of copying */
/* this way of doing it is not recommended for latest android version */
/* but the recommended way isn't available on android 2.x */
public Object onRetainNonConfigurationInstance()
{
public Object onRetainNonConfigurationInstance() {
return this;
}
ProgressBar m_ProgressBar;
TextView m_Filename;
private class copyAssetTask extends AsyncTask<String, Integer, String> {
boolean m_copy_started = false;
String m_Foldername = "media";
Vector<String> m_foldernames;
Vector<String> m_filenames;
Vector<String> m_tocopy;
Vector<String> m_asset_size_unknown;
copyAssetTask m_AssetCopy;
private class copyAssetTask extends AsyncTask<String, Integer, String>
{
private long getFullSize(String filename)
{
private long getFullSize(String filename) {
long size = 0;
try {
InputStream src = getAssets().open(filename);
byte[] buf = new byte[4096];
int len = 0;
while ((len = src.read(buf)) > 0)
{
while ((len = src.read(buf)) > 0) {
size += len;
}
}
catch (IOException e)
{
} catch (IOException e) {
e.printStackTrace();
}
return size;
}
@Override
protected String doInBackground(String... files)
{
protected String doInBackground(String... files) {
m_foldernames = new Vector<String>();
m_filenames = new Vector<String>();
m_tocopy = new Vector<String>();
@ -103,16 +115,13 @@ public class MinetestAssetCopy extends Activity
// prepare temp folder
File TempFolder = new File(baseDir + "Minetest/tmp/");
if (!TempFolder.exists())
{
if (!TempFolder.exists()) {
TempFolder.mkdir();
}
else {
} else {
File[] todel = TempFolder.listFiles();
for(int i=0; i < todel.length; i++)
{
Log.v("MinetestAssetCopy","deleting: " + todel[i].getAbsolutePath());
for (int i = 0; i < todel.length; i++) {
Log.v("MinetestAssetCopy", "deleting: " + todel[i].getAbsolutePath());
todel[i].delete();
}
}
@ -122,7 +131,7 @@ public class MinetestAssetCopy extends Activity
OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia");
dst.close();
} catch (IOException e) {
Log.e("MinetestAssetCopy","Failed to create .nomedia file");
Log.e("MinetestAssetCopy", "Failed to create .nomedia file");
e.printStackTrace();
}
@ -138,74 +147,63 @@ public class MinetestAssetCopy extends Activity
m_copy_started = true;
m_ProgressBar.setMax(m_tocopy.size());
for (int i = 0; i < m_tocopy.size(); i++)
{
try
{
for (int i = 0; i < m_tocopy.size(); i++) {
try {
String filename = m_tocopy.get(i);
publishProgress(i);
boolean asset_size_unknown = false;
long filesize = -1;
if (m_asset_size_unknown.contains(filename))
{
if (m_asset_size_unknown.contains(filename)) {
File testme = new File(baseDir + "/" + filename);
if(testme.exists())
{
if (testme.exists()) {
filesize = testme.length();
}
asset_size_unknown = true;
}
InputStream src;
try
{
try {
src = getAssets().open(filename);
} catch (IOException e) {
Log.e("MinetestAssetCopy","Copying file: " + filename + " FAILED (not in assets)");
Log.e("MinetestAssetCopy", "Copying file: " + filename + " FAILED (not in assets)");
e.printStackTrace();
continue;
}
// Transfer bytes from in to out
byte[] buf = new byte[1*1024];
byte[] buf = new byte[1024];
int len = src.read(buf, 0, 1024);
/* following handling is crazy but we need to deal with */
/* compressed assets.Flash chips limited livetime due to */
/* write operations, we can't allow large files to destroy */
/* users flash. */
if (asset_size_unknown)
{
if ( (len > 0) && (len < buf.length) && (len == filesize))
{
if (asset_size_unknown) {
if ((len > 0) && (len < buf.length) && (len == filesize)) {
src.close();
continue;
}
if (len == buf.length)
{
if (len == buf.length) {
src.close();
long size = getFullSize(filename);
if ( size == filesize)
{
if (size == filesize) {
continue;
}
src = getAssets().open(filename);
len = src.read(buf, 0, 1024);
}
}
if (len > 0)
{
if (len > 0) {
int total_filesize = 0;
OutputStream dst;
try
{
try {
dst = new FileOutputStream(baseDir + "/" + filename);
} catch (IOException e) {
Log.e("MinetestAssetCopy","Copying file: " + baseDir +
Log.e("MinetestAssetCopy", "Copying file: " + baseDir +
"/" + filename + " FAILED (couldn't open output file)");
e.printStackTrace();
src.close();
@ -214,27 +212,22 @@ public class MinetestAssetCopy extends Activity
dst.write(buf, 0, len);
total_filesize += len;
while ((len = src.read(buf)) > 0)
{
while ((len = src.read(buf)) > 0) {
dst.write(buf, 0, len);
total_filesize += len;
}
dst.close();
Log.v("MinetestAssetCopy","Copied file: " +
Log.v("MinetestAssetCopy", "Copied file: " +
m_tocopy.get(i) + " (" + total_filesize +
" bytes)");
}
else if (len < 0)
{
Log.e("MinetestAssetCopy","Copying file: " +
} else if (len < 0) {
Log.e("MinetestAssetCopy", "Copying file: " +
m_tocopy.get(i) + " failed, size < 0");
}
src.close();
}
catch (IOException e)
{
Log.e("MinetestAssetCopy","Copying file: " +
} catch (IOException e) {
Log.e("MinetestAssetCopy", "Copying file: " +
m_tocopy.get(i) + " failed");
e.printStackTrace();
}
@ -242,22 +235,17 @@ public class MinetestAssetCopy extends Activity
return "";
}
/**
* update progress bar
*/
protected void onProgressUpdate(Integer... progress)
{
protected void onProgressUpdate(Integer... progress) {
if (m_copy_started)
{
if (m_copy_started) {
boolean shortened = false;
String todisplay = m_tocopy.get(progress[0]);
m_ProgressBar.setProgress(progress[0]);
m_Filename.setText(todisplay);
}
else
{
} else {
boolean shortened = false;
String todisplay = m_Foldername;
String full_text = "scanning " + todisplay + " ...";
@ -266,38 +254,31 @@ public class MinetestAssetCopy extends Activity
}
/**
* check al files and folders in filelist
* check all files and folders in filelist
*/
protected void ProcessFileList()
{
protected void ProcessFileList() {
String FlashBaseDir =
Environment.getExternalStorageDirectory().getAbsolutePath();
Iterator itr = m_filenames.iterator();
while (itr.hasNext())
{
while (itr.hasNext()) {
String current_path = (String) itr.next();
String FlashPath = FlashBaseDir + "/" + current_path;
if (isAssetFolder(current_path))
{
if (isAssetFolder(current_path)) {
/* store information and update gui */
m_Foldername = current_path;
publishProgress(0);
/* open file in order to check if it's a folder */
File current_folder = new File(FlashPath);
if (!current_folder.exists())
{
if (!current_folder.mkdirs())
{
Log.e("MinetestAssetCopy","\t failed create folder: " +
if (!current_folder.exists()) {
if (!current_folder.mkdirs()) {
Log.e("MinetestAssetCopy", "\t failed create folder: " +
FlashPath);
}
else
{
Log.v("MinetestAssetCopy","\t created folder: " +
} else {
Log.v("MinetestAssetCopy", "\t created folder: " +
FlashPath);
}
}
@ -313,33 +294,27 @@ public class MinetestAssetCopy extends Activity
long asset_filesize = -1;
long stored_filesize = -1;
if (testme.exists())
{
try
{
if (testme.exists()) {
try {
AssetFileDescriptor fd = getAssets().openFd(current_path);
asset_filesize = fd.getLength();
fd.close();
}
catch (IOException e)
{
} catch (IOException e) {
refresh = true;
m_asset_size_unknown.add(current_path);
Log.e("MinetestAssetCopy","Failed to open asset file \"" +
Log.e("MinetestAssetCopy", "Failed to open asset file \"" +
FlashPath + "\" for size check");
}
stored_filesize = testme.length();
if (asset_filesize == stored_filesize)
{
if (asset_filesize == stored_filesize) {
refresh = false;
}
}
if (refresh)
{
if (refresh) {
m_tocopy.add(current_path);
}
}
@ -348,23 +323,19 @@ public class MinetestAssetCopy extends Activity
/**
* read list of folders prepared on package build
*/
protected void BuildFolderList()
{
try
{
protected void BuildFolderList() {
try {
InputStream is = getAssets().open("index.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = reader.readLine();
while (line != null)
{
while (line != null) {
m_foldernames.add(line);
line = reader.readLine();
}
is.close();
} catch (IOException e1)
{
Log.e("MinetestAssetCopy","Error on processing index.txt");
} catch (IOException e1) {
Log.e("MinetestAssetCopy", "Error on processing index.txt");
e1.printStackTrace();
}
}
@ -372,45 +343,31 @@ public class MinetestAssetCopy extends Activity
/**
* read list of asset files prepared on package build
*/
protected void BuildFileList()
{
protected void BuildFileList() {
long entrycount = 0;
try
{
try {
InputStream is = getAssets().open("filelist.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = reader.readLine();
while (line != null)
{
while (line != null) {
m_filenames.add(line);
line = reader.readLine();
entrycount ++;
entrycount++;
}
is.close();
}
catch (IOException e1)
{
Log.e("MinetestAssetCopy","Error on processing filelist.txt");
} catch (IOException e1) {
Log.e("MinetestAssetCopy", "Error on processing filelist.txt");
e1.printStackTrace();
}
}
protected void onPostExecute (String result)
{
protected void onPostExecute(String result) {
finish();
}
protected boolean isAssetFolder(String path)
{
protected boolean isAssetFolder(String path) {
return m_foldernames.contains(path);
}
boolean m_copy_started = false;
String m_Foldername = "media";
Vector<String> m_foldernames;
Vector<String> m_filenames;
Vector<String> m_tocopy;
Vector<String> m_asset_size_unknown;
}
}

@ -6,24 +6,21 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
public class MinetestTextEntry extends Activity {
public AlertDialog mTextInputDialog;
public EditText mTextInputWidget;
private final int MultiLineTextInput = 1;
private final int SingleLineTextInput = 2;
private final int SingleLinePasswordInput = 3;
public AlertDialog mTextInputDialog;
public EditText mTextInputWidget;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle b = getIntent().getExtras();
String acceptButton = b.getString("EnterButton");
String hint = b.getString("hint");
@ -38,18 +35,17 @@ public class MinetestTextEntry extends Activity {
if (editType == SingleLinePasswordInput) {
mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
else {
} else {
mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT);
}
builder.setView(mTextInputWidget);
if (editType == MultiLineTextInput) {
builder.setPositiveButton(acceptButton, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton)
{ pushResult(mTextInputWidget.getText().toString()); }
public void onClick(DialogInterface dialog, int whichButton) {
pushResult(mTextInputWidget.getText().toString());
}
});
}
@ -62,7 +58,7 @@ public class MinetestTextEntry extends Activity {
mTextInputWidget.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View view, int KeyCode, KeyEvent event) {
if ( KeyCode == KeyEvent.KEYCODE_ENTER){
if (KeyCode == KeyEvent.KEYCODE_ENTER) {
pushResult(mTextInputWidget.getText().toString());
return true;
@ -78,7 +74,7 @@ public class MinetestTextEntry extends Activity {
public void pushResult(String text) {
Intent resultData = new Intent();
resultData.putExtra("text", text);
setResult(Activity.RESULT_OK,resultData);
setResult(Activity.RESULT_OK, resultData);
mTextInputDialog.dismiss();
finish();
}

@ -2,22 +2,54 @@ package net.minetest.minetest;
import android.app.NativeActivity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
public class MtNativeActivity extends NativeActivity {
static {
System.loadLibrary("c++_shared");
System.loadLibrary("openal");
System.loadLibrary("ogg");
System.loadLibrary("vorbis");
System.loadLibrary("iconv");
System.loadLibrary("minetest");
}
private int m_MessagReturnCode;
private String m_MessageReturnValue;
public static native void putMessageBoxResult(String text);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_MessagReturnCode = -1;
m_MessageReturnValue = "";
}
@Override
public void onDestroy() {
super.onDestroy();
protected void onResume() {
super.onResume();
makeFullScreen();
}
public void makeFullScreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
makeFullScreen();
}
}
public void copyAssets() {
@ -40,8 +72,6 @@ public class MtNativeActivity extends NativeActivity {
m_MessagReturnCode = -1;
}
public static native void putMessageBoxResult(String text);
/* ugly code to workaround putMessageBoxResult not beeing found */
public int getDialogState() {
return m_MessagReturnCode;
@ -72,28 +102,9 @@ public class MtNativeActivity extends NativeActivity {
String text = data.getStringExtra("text");
m_MessagReturnCode = 0;
m_MessageReturnValue = text;
}
else {
} else {
m_MessagReturnCode = 1;
}
}
}
static {
System.loadLibrary("openal");
System.loadLibrary("ogg");
System.loadLibrary("vorbis");
System.loadLibrary("ssl");
System.loadLibrary("crypto");
System.loadLibrary("gmp");
System.loadLibrary("iconv");
// We don't have to load libminetest.so ourselves,
// but if we do, we get nicer logcat errors when
// loading fails.
System.loadLibrary("minetest");
}
private int m_MessagReturnCode;
private String m_MessageReturnValue;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/background"
android:tileMode="repeat" />

@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
android:layout_height="30dp"
android:layout_centerInParent="true"
android:layout_marginLeft="90dp"
android:layout_marginRight="90dp" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:singleLine="true"
android:layout_gravity="center_horizontal"
android:text="@string/preparing_media"
android:textAppearance="?android:attr/textAppearanceSmall" />
android:layout_below="@+id/progressBar1"
android:layout_centerInParent="true"
android:text="@string/preparing_media" />
</LinearLayout>
</RelativeLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/android:Theme.Material.Light.NoActionBar.Fullscreen">
<item name="android:windowNoTitle">true</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowBackground">@drawable/bg</item>
</style>
<style name="Theme.Dialog" parent="@android:style/Theme.Material.Light.Dialog.NoActionBar"/>
</resources>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="preparing_media">Preparing media...</string>
<string name="preparing_media">Preparing media&#8230;</string>
<string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string>
</resources>

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Transparent" parent="android:Theme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<style name="AppTheme" parent="@android:style/android:Theme.Holo.Light.NoActionBar.Fullscreen">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowBackground">@drawable/bg</item>
</style>
<style name="Theme.Dialog" parent="@android:style/android:Theme.Holo.Light.Dialog.NoActionBar"/>
</resources>

@ -120,7 +120,12 @@ end
-- The dumped and level arguments are internal-only.
function dump(o, indent, nested, level)
if type(o) ~= "table" then
local t = type(o)
if not level and t == "userdata" then
-- when userdata (e.g. player) is passed directly, print its metatable:
return "userdata metatable: " .. dump(getmetatable(o))
end
if t ~= "table" then
return basic_dump(o)
end
-- Contains table -> true/nil of currently nested tables
@ -308,59 +313,25 @@ function core.formspec_escape(text)
end
function core.wrap_text(text, charlimit)
local retval = {}
local current_idx = 1
local start,stop = string_find(text, " ", current_idx)
local nl_start,nl_stop = string_find(text, "\n", current_idx)
local gotnewline = false
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
stop = nl_stop
gotnewline = true
end
local last_line = ""
while start ~= nil do
if string.len(last_line) + (stop-start) > charlimit then
retval[#retval + 1] = last_line
last_line = ""
function core.wrap_text(text, max_length, as_table)
local result = {}
local line = {}
if #text <= max_length then
return as_table and {text} or text
end
if last_line ~= "" then
last_line = last_line .. " "
for word in text:gmatch('%S+') do
local cur_length = #table.concat(line, ' ')
if cur_length > 0 and cur_length + #word + 1 >= max_length then
-- word wouldn't fit on current line, move to next line
table.insert(result, table.concat(line, ' '))
line = {}
end
table.insert(line, word)
end
last_line = last_line .. string_sub(text, current_idx, stop - 1)
if gotnewline then
retval[#retval + 1] = last_line
last_line = ""
gotnewline = false
end
current_idx = stop+1
start,stop = string_find(text, " ", current_idx)
nl_start,nl_stop = string_find(text, "\n", current_idx)
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
stop = nl_stop
gotnewline = true
end
end
--add last part of text
if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
retval[#retval + 1] = last_line
retval[#retval + 1] = string_sub(text, current_idx)
else
last_line = last_line .. " " .. string_sub(text, current_idx)
retval[#retval + 1] = last_line
end
return retval
table.insert(result, table.concat(line, ' '))
return as_table and result or table.concat(result, '\n')
end
--------------------------------------------------------------------------------
@ -370,7 +341,7 @@ if INIT == "game" then
local dirs2 = {20, 23, 22, 21}
function core.rotate_and_place(itemstack, placer, pointed_thing,
infinitestacks, orient_flags)
infinitestacks, orient_flags, prevent_after_place)
orient_flags = orient_flags or {}
local unode = core.get_node_or_nil(pointed_thing.under)
@ -379,41 +350,20 @@ if INIT == "game" then
end
local undef = core.registered_nodes[unode.name]
if undef and undef.on_rightclick then
undef.on_rightclick(pointed_thing.under, unode, placer,
return undef.on_rightclick(pointed_thing.under, unode, placer,
itemstack, pointed_thing)
return
end
local fdir = core.dir_to_facedir(placer:get_look_dir())
local wield_name = itemstack:get_name()
local fdir = placer and core.dir_to_facedir(placer:get_look_dir()) or 0
local above = pointed_thing.above
local under = pointed_thing.under
local iswall = (above.y == under.y)
local isceiling = not iswall and (above.y < under.y)
local anode = core.get_node_or_nil(above)
if not anode then
return
end
local pos = pointed_thing.above
local node = anode
if undef and undef.buildable_to then
pos = pointed_thing.under
node = unode
iswall = false
end
if core.is_protected(pos, placer:get_player_name()) then
core.record_protection_violation(pos,
placer:get_player_name())
return
end
local ndef = core.registered_nodes[node.name]
if not ndef or not ndef.buildable_to then
return
end
if orient_flags.force_floor then
iswall = false
isceiling = false
@ -427,31 +377,26 @@ if INIT == "game" then
iswall = not iswall
end
local param2 = fdir
if iswall then
core.set_node(pos, {name = wield_name,
param2 = dirs1[fdir + 1]})
param2 = dirs1[fdir + 1]
elseif isceiling then
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 20})
cparam2 = 20
else
core.set_node(pos, {name = wield_name,
param2 = dirs2[fdir + 1]})
param2 = dirs2[fdir + 1]
end
else -- place right side up
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 0})
else
core.set_node(pos, {name = wield_name,
param2 = fdir})
param2 = 0
end
end
if not infinitestacks then
itemstack:take_item()
return itemstack
end
local old_itemstack = ItemStack(itemstack)
local new_itemstack, removed = core.item_place_node(
itemstack, placer, pointed_thing, param2, prevent_after_place
)
return infinitestacks and old_itemstack or new_itemstack
end
@ -459,12 +404,18 @@ if INIT == "game" then
--Wrapper for rotate_and_place() to check for sneak and assume Creative mode
--implies infinite stacks when performing a 6d rotation.
--------------------------------------------------------------------------------
local creative_mode_cache = core.settings:get_bool("creative_mode")
local function is_creative(name)
return creative_mode_cache or
core.check_player_privs(name, {creative = true})
end
core.rotate_node = function(itemstack, placer, pointed_thing)
local name = placer and placer:get_player_name() or ""
local invert_wall = placer and placer:get_player_control().sneak or false
core.rotate_and_place(itemstack, placer, pointed_thing,
core.settings:get_bool("creative_mode"),
{invert_wall = placer:get_player_control().sneak})
is_creative(name),
{invert_wall = invert_wall}, true)
return itemstack
end
end
@ -642,32 +593,15 @@ end
local ESCAPE_CHAR = string.char(0x1b)
-- Client-side mods don't have access to settings
if core.settings and core.settings:get_bool("disable_escape_sequences") then
function core.get_color_escape_sequence(color)
return ""
end
function core.get_background_escape_sequence(color)
return ""
end
function core.colorize(color, message)
return message
end
else
function core.get_color_escape_sequence(color)
function core.get_color_escape_sequence(color)
return ESCAPE_CHAR .. "(c@" .. color .. ")"
end
end
function core.get_background_escape_sequence(color)
function core.get_background_escape_sequence(color)
return ESCAPE_CHAR .. "(b@" .. color .. ")"
end
end
function core.colorize(color, message)
function core.colorize(color, message)
local lines = tostring(message):split("\n", true)
local color_code = core.get_color_escape_sequence(color)
@ -676,10 +610,9 @@ else
end
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
end
end
function core.strip_foreground_colors(str)
return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", ""))
end

@ -67,16 +67,15 @@ local function save_auth_file()
assert(type(stuff.privileges) == "table")
assert(stuff.last_login == nil or type(stuff.last_login) == "number")
end
local file, errmsg = io.open(core.auth_file_path, 'w+b')
if not file then
error(core.auth_file_path.." could not be opened for writing: "..errmsg)
end
local content = {}
for name, stuff in pairs(core.auth_table) do
local priv_string = core.privs_to_string(stuff.privileges)
local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
file:write(table.concat(parts, ":").."\n")
content[#content + 1] = table.concat(parts, ":")
end
if not core.safe_file_write(core.auth_file_path, table.concat(content, "\n")) then
error(core.auth_file_path.." could not be written to")
end
io.close(file)
end
read_auth_file()

@ -653,8 +653,8 @@ core.register_chatcommand("pulverize", {
core.rollback_punch_callbacks = {}
core.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
if core.rollback_punch_callbacks[name] then
local name = puncher and puncher:get_player_name()
if name and core.rollback_punch_callbacks[name] then
core.rollback_punch_callbacks[name](pos, node, puncher)
core.rollback_punch_callbacks[name] = nil
end
@ -814,7 +814,7 @@ core.register_chatcommand("shutdown", {
message = message or ""
if delay ~= "" then
delay = tonumber(param) or 0
delay = tonumber(delay) or 0
else
delay = 0
core.log("action", name .. " shuts down server")

@ -60,8 +60,13 @@ core.register_entity(":__builtin:falling_node", {
local pos = self.object:getpos()
-- Position of bottom center point
local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z}
-- Avoid bugs caused by an unloaded node below
-- 'bcn' is nil for unloaded nodes
local bcn = core.get_node_or_nil(bcp)
-- Delete on contact with ignore at world edges
if bcn and bcn.name == "ignore" then
self.object:remove()
return
end
local bcd = bcn and core.registered_nodes[bcn.name]
if bcn and
(not bcd or bcd.walkable or
@ -93,7 +98,7 @@ core.register_entity(":__builtin:falling_node", {
core.remove_node(np)
if nd and nd.buildable_to == false then
-- Add dropped items
local drops = core.get_node_drops(n2.name, "")
local drops = core.get_node_drops(n2, "")
for _, dropped_item in pairs(drops) do
core.add_item(np, dropped_item)
end
@ -145,9 +150,9 @@ function core.spawn_falling_node(pos)
end
local function drop_attached_node(p)
local nn = core.get_node(p).name
local n = core.get_node(p)
core.remove_node(p)
for _, item in pairs(core.get_node_drops(nn, "")) do
for _, item in pairs(core.get_node_drops(n, "")) do
local pos = {
x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25,

@ -155,11 +155,45 @@ function core.yaw_to_dir(yaw)
return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
end
function core.get_node_drops(nodename, toolname)
function core.is_colored_paramtype(ptype)
return (ptype == "color") or (ptype == "colorfacedir") or
(ptype == "colorwallmounted")
end
function core.strip_param2_color(param2, paramtype2)
if not core.is_colored_paramtype(paramtype2) then
return nil
end
if paramtype2 == "colorfacedir" then
param2 = math.floor(param2 / 32) * 32
elseif paramtype2 == "colorwallmounted" then
param2 = math.floor(param2 / 8) * 8
end
-- paramtype2 == "color" requires no modification.
return param2
end
function core.get_node_drops(node, toolname)
-- Compatibility, if node is string
local nodename = node
local param2 = 0
-- New format, if node is table
if (type(node) == "table") then
nodename = node.name
param2 = node.param2
end
local def = core.registered_nodes[nodename]
local drop = def and def.drop
local ptype = def and def.paramtype2
-- get color, if there is color (otherwise nil)
local palette_index = core.strip_param2_color(param2, ptype)
if drop == nil then
-- default drop
if palette_index then
local stack = ItemStack(nodename)
stack:get_meta():set_int("palette_index", palette_index)
return {stack:to_string()}
end
return {nodename}
elseif type(drop) == "string" then
-- itemstring drop
@ -181,6 +215,8 @@ function core.get_node_drops(nodename, toolname)
end
if item.tools ~= nil then
good_tool = false
end
if item.tools ~= nil and toolname then
for _, tool in ipairs(item.tools) do
if tool:sub(1, 1) == '~' then
good_tool = toolname:find(tool:sub(2)) ~= nil
@ -195,6 +231,12 @@ function core.get_node_drops(nodename, toolname)
if good_rarity and good_tool then
got_count = got_count + 1
for _, add_item in ipairs(item.items) do
-- add color, if necessary
if item.inherit_color and palette_index then
local stack = ItemStack(add_item)
stack:get_meta():set_int("palette_index", palette_index)
add_item = stack:to_string()
end
got_items[#got_items+1] = add_item
end
if drop.max_items ~= nil and got_count == drop.max_items then
@ -205,7 +247,22 @@ function core.get_node_drops(nodename, toolname)
return got_items
end
function core.item_place_node(itemstack, placer, pointed_thing, param2)
local function user_name(user)
return user and user:get_player_name() or ""
end
local function is_protected(pos, name)
return core.is_protected(pos, name) and
not minetest.check_player_privs(name, "protection_bypass")
end
-- Returns a logging function. For empty names, does not log.
local function make_log(name)
return name ~= "" and core.log or function() end
end
function core.item_place_node(itemstack, placer, pointed_thing, param2,
prevent_after_place)
local def = itemstack:get_definition()
if def.type ~= "node" or pointed_thing.type ~= "node" then
return itemstack, false
@ -215,10 +272,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local oldnode_under = core.get_node_or_nil(under)
local above = pointed_thing.above
local oldnode_above = core.get_node_or_nil(above)
local playername = placer:get_player_name()
local playername = user_name(placer)
local log = make_log(playername)
if not oldnode_under or not oldnode_above then
core.log("info", playername .. " tried to place"
log("info", playername .. " tried to place"
.. " node in unloaded position " .. core.pos_to_string(above))
return itemstack, false
end
@ -229,7 +287,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
olddef_above = olddef_above or core.nodedef_default
if not olddef_above.buildable_to and not olddef_under.buildable_to then
core.log("info", playername .. " tried to place"
log("info", playername .. " tried to place"
.. " node in invalid position " .. core.pos_to_string(above)
.. ", replacing " .. oldnode_above.name)
return itemstack, false
@ -240,13 +298,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- If node under is buildable_to, place into it instead (eg. snow)
if olddef_under.buildable_to then
core.log("info", "node under is buildable to")
log("info", "node under is buildable to")
place_to = {x = under.x, y = under.y, z = under.z}
end
if core.is_protected(place_to, playername) and
not minetest.check_player_privs(placer, "protection_bypass") then
core.log("action", playername
if is_protected(place_to, playername) then
log("action", playername
.. " tried to place " .. def.name
.. " at protected position "
.. core.pos_to_string(place_to))
@ -254,11 +311,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
return itemstack
end
core.log("action", playername .. " places node "
log("action", playername .. " places node "
.. def.name .. " at " .. core.pos_to_string(place_to))
local oldnode = core.get_node(place_to)
local newnode = {name = def.name, param1 = 0, param2 = param2}
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
-- Calculate direction for wall mounted stuff like torches and signs
if def.place_param2 ~= nil then
@ -274,7 +331,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir") and not param2 then
local placer_pos = placer:getpos()
local placer_pos = placer and placer:getpos()
if placer_pos then
local dir = {
x = above.x - placer_pos.x,
@ -282,14 +339,33 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
z = above.z - placer_pos.z
}
newnode.param2 = core.dir_to_facedir(dir)
core.log("action", "facedir: " .. newnode.param2)
log("action", "facedir: " .. newnode.param2)
end
end
local metatable = itemstack:get_meta():to_table().fields
-- Transfer color information
if metatable.palette_index and not def.place_param2 then
local color_divisor = nil
if def.paramtype2 == "color" then
color_divisor = 1
elseif def.paramtype2 == "colorwallmounted" then
color_divisor = 8
elseif def.paramtype2 == "colorfacedir" then
color_divisor = 32
end
if color_divisor then
local color = math.floor(metatable.palette_index / color_divisor)
local other = newnode.param2 % color_divisor
newnode.param2 = color * color_divisor + other
end
end
-- Check if the node is attached and if it can be placed there
if core.get_item_group(def.name, "attached_node") ~= 0 and
not builtin_shared.check_attached_node(place_to, newnode) then
core.log("action", "attached node " .. def.name ..
log("action", "attached node " .. def.name ..
" can not be placed at " .. core.pos_to_string(place_to))
return itemstack, false
end
@ -300,7 +376,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local take_item = true
-- Run callback
if def.after_place_node then
if def.after_place_node and not prevent_after_place then
-- Deepcopy place_to and pointed_thing because callback can modify it
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
@ -360,29 +436,28 @@ function core.item_secondary_use(itemstack, placer)
end
function core.item_drop(itemstack, dropper, pos)
if dropper and dropper:is_player() then
local v = dropper:get_look_dir()
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
local cs = itemstack:get_count()
local dropper_is_player = dropper and dropper:is_player()
local p = table.copy(pos)
local cnt = itemstack:get_count()
if dropper_is_player then
p.y = p.y + 1.2
if dropper:get_player_control().sneak then
cs = 1
cnt = 1
end
local item = itemstack:take_item(cs)
end
local item = itemstack:take_item(cnt)
local obj = core.add_item(p, item)
if obj then
v.x = v.x*2
v.y = v.y*2 + 2
v.z = v.z*2
obj:setvelocity(v)
if dropper_is_player then
local dir = dropper:get_look_dir()
dir.x = dir.x * 2.9
dir.y = dir.y * 2.9 + 2
dir.z = dir.z * 2.9
obj:set_velocity(dir)
obj:get_luaentity().dropped_by = dropper:get_player_name()
return itemstack
end
else
if core.add_item(pos, itemstack) then
return itemstack
end
end
-- If we reach this, adding the object to the
-- environment failed
end
@ -402,7 +477,8 @@ function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed
itemstack:add_item(replace_with_item)
else
local inv = user:get_inventory()
if inv:room_for_item("main", {name=replace_with_item}) then
-- Check if inv is null, since non-players don't have one
if inv and inv:room_for_item("main", {name=replace_with_item}) then
inv:add_item("main", replace_with_item)
else
local pos = user:getpos()
@ -417,8 +493,10 @@ end
function core.item_eat(hp_change, replace_with_item)
return function(itemstack, user, pointed_thing) -- closure
if user then
return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
end
end
end
function core.node_punch(pos, node, puncher, pointed_thing)
@ -434,10 +512,21 @@ end
function core.handle_node_drops(pos, drops, digger)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
local left = digger:get_inventory():add_item("main", dropped_item)
local inv = digger and digger:get_inventory()
local give_item
if inv then
give_item = function(item)
return inv:add_item("main", item)
end
else
give_item = function(item)
-- itemstring to ItemStack for left:is_empty()
return ItemStack(item)
end
end
for _, dropped_item in pairs(drops) do
local left = give_item(dropped_item)
if not left:is_empty() then
local p = {
x = pos.x + math.random()/2-0.25,
@ -447,35 +536,36 @@ function core.handle_node_drops(pos, drops, digger)
core.add_item(p, left)
end
end
end
end
function core.node_dig(pos, node, digger)
local diggername = user_name(digger)
local log = make_log(diggername)
local def = core.registered_nodes[node.name]
if def and (not def.diggable or
(def.can_dig and not def.can_dig(pos, digger))) then
core.log("info", digger:get_player_name() .. " tried to dig "
log("info", diggername .. " tried to dig "
.. node.name .. " which is not diggable "
.. core.pos_to_string(pos))
return
end
if core.is_protected(pos, digger:get_player_name()) and
not minetest.check_player_privs(digger, "protection_bypass") then
core.log("action", digger:get_player_name()
if is_protected(pos, diggername) then
log("action", diggername
.. " tried to dig " .. node.name
.. " at protected position "
.. core.pos_to_string(pos))
core.record_protection_violation(pos, digger:get_player_name())
core.record_protection_violation(pos, diggername)
return
end
core.log('action', digger:get_player_name() .. " digs "
log('action', diggername .. " digs "
.. node.name .. " at " .. core.pos_to_string(pos))
local wielded = digger:get_wielded_item()
local drops = core.get_node_drops(node.name, wielded:get_name())
local wielded = digger and digger:get_wielded_item()
local drops = core.get_node_drops(node, wielded and wielded:get_name())
if wielded then
local wdef = wielded:get_definition()
local tp = wielded:get_tool_capabilities()
local dp = core.get_dig_params(def and def.groups, tp)
@ -491,6 +581,7 @@ function core.node_dig(pos, node, digger)
end
end
digger:set_wielded_item(wielded)
end
-- Handle drops
core.handle_node_drops(pos, drops, digger)

@ -174,19 +174,18 @@ core.register_entity(":__builtin:item", {
local p = self.object:getpos()
p.y = p.y - 0.5
local node = core.get_node_or_nil(p)
local in_unloaded = (node == nil)
if in_unloaded then
-- Don't infinetly fall into unloaded map
self.object:setvelocity({x = 0, y = 0, z = 0})
self.object:setacceleration({x = 0, y = 0, z = 0})
self.physical_state = false
self.object:set_properties({physical = false})
-- Delete in 'ignore' nodes
if node and node.name == "ignore" then
self.itemstring = ""
self.object:remove()
return
end
local nn = node.name
-- If node is not registered or node is walkably solid and resting on nodebox
-- If node is nil (unloaded area), or node is not registered, or node is
-- walkably solid and item is resting on nodebox
local v = self.object:getvelocity()
if not core.registered_nodes[nn] or core.registered_nodes[nn].walkable and v.y == 0 then
if not node or not core.registered_nodes[node.name] or
core.registered_nodes[node.name].walkable and v.y == 0 then
if self.physical_state then
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
-- Merge with close entities of the same item

@ -5,12 +5,11 @@
--
function core.check_player_privs(name, ...)
local arg_type = type(name)
if (arg_type == "userdata" or arg_type == "table") and
name.get_player_name then -- If it quacks like a Player...
if core.is_player(name) then
name = name:get_player_name()
elseif arg_type ~= "string" then
error("Invalid core.check_player_privs argument type: " .. arg_type, 2)
elseif type(name) ~= "string" then
error("core.check_player_privs expects a player or playername as " ..
"argument.", 2)
end
local requested_privs = {...}
@ -70,6 +69,16 @@ function core.get_connected_players()
return temp_table
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)
return minetest.get_auth_handler().get_auth(name) ~= nil
end

@ -116,6 +116,8 @@ function core.register_item(name, itemdef)
end
itemdef.name = name
local is_overriding = core.registered_items[name]
-- Apply defaults and add to registered_* table
if itemdef.type == "node" then
-- Use the nodebox as selection box if it's not set manually
@ -177,7 +179,13 @@ function core.register_item(name, itemdef)
--core.log("Registering item: " .. itemdef.name)
core.registered_items[itemdef.name] = itemdef
core.registered_aliases[itemdef.name] = nil
-- Used to allow builtin to register ignore to registered_items
if name ~= "ignore" then
register_item_raw(itemdef)
elseif is_overriding then
core.log("warning", "Attempted redefinition of \"ignore\"")
end
end
function core.unregister_item(name)

@ -21,7 +21,6 @@ if core.print then
core.print = nil -- don't pollute our namespace
end
math.randomseed(os.time())
os.setlocale("C", "numeric")
minetest = core
-- Load other files
@ -47,7 +46,6 @@ elseif INIT == "mainmenu" then
elseif INIT == "async" then
dofile(asyncpath .. "init.lua")
elseif INIT == "client" then
os.setlocale = nil
dofile(clientpath .. "init.lua")
else
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))

@ -250,7 +250,7 @@ end
--------------------------------------------------------------------------------
function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency)
local textlines = core.wrap_text(text, textlen)
local textlines = core.wrap_text(text, textlen, true)
local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width ..
"," .. height .. ";" .. tl_name .. ";"

@ -17,19 +17,17 @@
local function delete_world_formspec(dialogdata)
local retval =
"size[11.5,4.5,true]" ..
"label[2,2;" ..
"size[10,2.5,true]" ..
"label[0.5,0.5;" ..
fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" ..
"button[3.25,3.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" ..
"button[5.75,3.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]"
"button[0.5,1.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" ..
"button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]"
return retval
end
local function delete_world_buttonhandler(this, fields)
if fields["world_delete_confirm"] then
if this.data.delete_index > 0 and
this.data.delete_index <= #menudata.worldlist:get_raw_list() then
core.delete_world(this.data.delete_index)
@ -48,8 +46,7 @@ local function delete_world_buttonhandler(this, fields)
end
function create_delete_world_dlg(name_to_del,index_to_del)
function create_delete_world_dlg(name_to_del, index_to_del)
assert(name_to_del ~= nil and type(name_to_del) == "string" and name_to_del ~= "")
assert(index_to_del ~= nil and type(index_to_del) == "number")

@ -74,7 +74,7 @@ local previous_contributors = {
}
local function buildCreditList(source)
ret = {}
local ret = {}
for i = 1, #source do
ret[i] = core.formspec_escape(source[i])
end

@ -75,7 +75,7 @@ local function get_formspec(tabview, name, tabdata)
if error == nil then
local descriptiontext = descriptionfile:read("*all")
descriptionlines = core.wrap_text(descriptiontext, 42)
descriptionlines = core.wrap_text(descriptiontext, 42, true)
descriptionfile:close()
else
descriptionlines = {}

@ -176,7 +176,7 @@ end
local function formspec(tabview, name, tabdata)
local tab_string =
"box[0,0;3.5,4.5;#999999]" ..
"box[0,0;3.75,4.5;#999999]" ..
"checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";"
.. dump(core.settings:get_bool("smooth_lighting")) .. "]" ..
"checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";"
@ -187,38 +187,38 @@ local function formspec(tabview, name, tabdata)
.. dump(core.settings:get_bool("opaque_water")) .. "]" ..
"checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";"
.. dump(core.settings:get_bool("connected_glass")) .. "]" ..
"dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
"dropdown[0.25,2.8;3.5;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
.. getSettingIndex.NodeHighlighting() .. "]" ..
"dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
"dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
.. getSettingIndex.Leaves() .. "]" ..
"box[3.75,0;3.75,4.45;#999999]" ..
"label[3.85,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";"
"box[4,0;3.75,4.5;#999999]" ..
"label[4.25,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";"
.. getSettingIndex.Filter() .. "]" ..
"dropdown[3.85,1.35;3.85;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
"dropdown[4.25,1.35;3.5;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
.. getSettingIndex.Mipmap() .. "]" ..
"label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[3.85,2.6;3.85;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
"label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
.. getSettingIndex.Antialiasing() .. "]" ..
"label[3.85,3.45;" .. fgettext("Screen:") .. "]" ..
"checkbox[3.85,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";"
"label[4.25,3.45;" .. fgettext("Screen:") .. "]" ..
"checkbox[4.25,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";"
.. dump(core.settings:get_bool("autosave_screensize")) .. "]" ..
"box[7.75,0;4,4.4;#999999]" ..
"checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";"
"box[8,0;3.75,4.5;#999999]" ..
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";"
.. dump(core.settings:get_bool("enable_shaders")) .. "]"
if PLATFORM == "Android" then
tab_string = tab_string ..
"button[8,4.75;3.75,0.5;btn_reset_singleplayer;"
"button[8,4.75;4.1,1;btn_reset_singleplayer;"
.. fgettext("Reset singleplayer world") .. "]"
else
tab_string = tab_string ..
"button[8,4.85;3.75,0.5;btn_change_keys;"
"button[8,4.75;4,1;btn_change_keys;"
.. fgettext("Change keys") .. "]"
end
tab_string = tab_string ..
"button[0,4.85;3.75,0.5;btn_advanced_settings;"
"button[0,4.75;4,1;btn_advanced_settings;"
.. fgettext("Advanced Settings") .. "]"
@ -231,19 +231,19 @@ local function formspec(tabview, name, tabdata)
if core.settings:get_bool("enable_shaders") then
tab_string = tab_string ..
"checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";"
"checkbox[8.25,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";"
.. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" ..
"checkbox[8,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
"checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
"checkbox[8,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";"
"checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";"
.. dump(core.settings:get_bool("generate_normalmaps")) .. "]" ..
"checkbox[8,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
"checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
.. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" ..
"checkbox[8,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";"
"checkbox[8.25,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";"
.. dump(core.settings:get_bool("enable_waving_water")) .. "]" ..
"checkbox[8,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
"checkbox[8.25,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
"checkbox[8,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
"checkbox[8.25,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"
else
tab_string = tab_string ..

@ -133,7 +133,7 @@ local function instrument_register(func, func_name)
return func(instrument {
func = callback,
func_name = register_name
}), ...
}, ...)
end
end

@ -166,10 +166,6 @@ keymap_cmd (Command key) key /
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_cmd_local (Command key) key .
# Key for opening the chat console.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keyman_console (Console key) key KEY_F10
# Key for toggling unlimited view range.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_rangeselect (Range select key) key KEY_KEY_R
@ -476,6 +472,12 @@ pause_fps_max (FPS in pause menu) int 20
# View distance in nodes.
viewing_range (Viewing range) int 100 20 4000
# Camera near plane distance in nodes, between 0 and 0.5
# Most users will not need to change this.
# Increasing can reduce artifacting on weaker GPUs.
# 0.1 = Default, 0.25 = Good value for weaker tablets.
near_plane (Near plane) float 0.1 0 0.5
# Width component of the initial window size.
screenW (Screen width) int 800
@ -721,10 +723,9 @@ server_announce (Announce server) bool false
# If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net.
serverlist_url (Serverlist URL) string servers.minetest.net
# Disable escape sequences, e.g. chat coloring.
# Use this if you want to run a server with pre-0.4.14 clients and you want to disable
# the escape sequences generated by mods.
disable_escape_sequences (Disable escape sequences) bool false
# Remove color codes from incoming chat messages
# Use this to stop players from being able to use color in their messages
strip_color_codes (Strip color codes) bool false
[*Network]

@ -9,7 +9,7 @@
FIND_PATH(LUA_INCLUDE_DIR luajit.h
HINTS
$ENV{LUA_DIR}
PATH_SUFFIXES include/luajit-2.0 include/luajit-5_1-2.0 include
PATH_SUFFIXES include/luajit-2.1 include/luajit-2.0 include/luajit-5_1-2.1 include/luajit-5_1-2.0 include
PATHS
~/Library/Frameworks
/Library/Frameworks

@ -628,6 +628,9 @@ Minetest namespace reference
version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example:
`if minetest.nodeupdate then ... end`.
* `minetest.sha1(data, [raw])`: returns the sha1 hash of data
* `data`: string of data to hash
* `raw`: return raw bytes instead of hex digits, default: false
### Logging
* `minetest.debug(...)`
@ -1117,15 +1120,15 @@ The following functions provide escape sequences:
`minetest.get_color_escape_sequence(color) ..
message ..
minetest.get_color_escape_sequence("#ffffff")`
* `color.get_background_escape_sequence(color)`
* `minetest.get_background_escape_sequence(color)`
* `color` is a [ColorString](#colorstring)
* The escape sequence sets the background of the whole text element to
`color`. Only defined for item descriptions and tooltips.
* `color.strip_foreground_colors(str)`
* `minetest.strip_foreground_colors(str)`
* Removes foreground colors added by `get_color_escape_sequence`.
* `color.strip_background_colors(str)`
* `minetest.strip_background_colors(str)`
* Removes background colors added by `get_background_escape_sequence`.
* `color.strip_colors(str)`
* `minetest.strip_colors(str)`
* Removes all color escape sequences.
`ColorString`

@ -1,4 +1,4 @@
Minetest Lua Modding API Reference 0.4.16
Minetest Lua Modding API Reference 0.4.17
=========================================
* More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/>
@ -211,7 +211,8 @@ when registering it.
The `:` prefix can also be used for maintaining backwards compatibility.
### Aliases
Aliases
-------
Aliases can be added by using `minetest.register_alias(name, convert_to)` or
`minetest.register_alias_force(name, convert_to)`.
@ -232,6 +233,75 @@ you have an item called `epiclylongmodname:stuff`, you could do
and be able to use `/giveme stuff`.
Mapgen aliases
--------------
In a game, a certain number of these must be set to tell core mapgens which
of the game's nodes are to be used by the core mapgens. For example:
minetest.register_alias("mapgen_stone", "default:stone")
### Aliases needed for all mapgens except Mapgen v6
Base terrain:
"mapgen_stone"
"mapgen_water_source"
"mapgen_river_water_source"
Caves:
"mapgen_lava_source"
Dungeons:
Only needed for registered biomes where 'node_stone' is stone:
"mapgen_cobble"
"mapgen_stair_cobble"
"mapgen_mossycobble"
Only needed for registered biomes where 'node_stone' is desert stone:
"mapgen_desert_stone"
"mapgen_stair_desert_stone"
Only needed for registered biomes where 'node_stone' is sandstone:
"mapgen_sandstone"
"mapgen_sandstonebrick"
"mapgen_stair_sandstone_block"
### Aliases needed for Mapgen v6
Terrain and biomes:
"mapgen_stone"
"mapgen_water_source"
"mapgen_lava_source"
"mapgen_dirt"
"mapgen_dirt_with_grass"
"mapgen_sand"
"mapgen_gravel"
"mapgen_desert_stone"
"mapgen_desert_sand"
"mapgen_dirt_with_snow"
"mapgen_snowblock"
"mapgen_snow"
"mapgen_ice"
Flora:
"mapgen_tree"
"mapgen_leaves"
"mapgen_apple"
"mapgen_jungletree"
"mapgen_jungleleaves"
"mapgen_junglegrass"
"mapgen_pine_tree"
"mapgen_pine_needles"
Dungeons:
"mapgen_cobble"
"mapgen_stair_cobble"
"mapgen_mossycobble"
"mapgen_stair_desert_stone"
Textures
--------
Mods should generally prefix their textures with `modname_`, e.g. given
@ -531,9 +601,26 @@ for conversion.
If the `ItemStack`'s metadata contains the `color` field, it will be
lost on placement, because nodes on the map can only use palettes.
If the `ItemStack`'s metadata contains the `palette_index` field, you
currently must manually convert between it and the node's `param2` with
custom `on_place` and `on_dig` callbacks.
If the `ItemStack`'s metadata contains the `palette_index` field, it is
automatically transferred between node and item forms by the engine,
when a player digs or places a colored node.
You can disable this feature by setting the `drop` field of the node
to itself (without metadata).
To transfer the color to a special drop, you need a drop table.
Example:
minetest.register_node("mod:stone", {
description = "Stone",
tiles = {"default_stone.png"},
paramtype2 = "color",
palette = "palette.png",
drop = {
items = {
-- assume that mod:cobblestone also has the same palette
{items = {"mod:cobblestone"}, inherit_color = true },
}
}
})
### Colored items in craft recipes
Craft recipes only support item strings, but fortunately item strings
@ -792,6 +879,11 @@ node definition:
0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y-
facedir modulo 4 = rotation around that axis
paramtype2 == "leveled"
^ Only valid for "nodebox" with type = "leveled".
The level of the top face of the nodebox is stored in param2.
The other faces are defined by 'fixed = {}' like 'type = "fixed"' nodeboxes.
The nodebox height is param2 / 64 nodes.
The maximum accepted value of param2 is 127.
paramtype2 == "degrotate"
^ The rotation of this node is stored in param2. Plants are rotated this way.
Values range 0 - 179. The value stored in param2 is multiplied by two to
@ -2060,15 +2152,15 @@ The following functions provide escape sequences:
`minetest.get_color_escape_sequence(color) ..
message ..
minetest.get_color_escape_sequence("#ffffff")`
* `color.get_background_escape_sequence(color)`
* `minetest.get_background_escape_sequence(color)`
* `color` is a ColorString
* The escape sequence sets the background of the whole text element to
`color`. Only defined for item descriptions and tooltips.
* `color.strip_foreground_colors(str)`
* `minetest.strip_foreground_colors(str)`
* Removes foreground colors added by `get_color_escape_sequence`.
* `color.strip_background_colors(str)`
* `minetest.strip_background_colors(str)`
* Removes background colors added by `get_background_escape_sequence`.
* `color.strip_colors(str)`
* `minetest.strip_colors(str)`
* Removes all color escape sequences.
Spatial Vectors
@ -2111,9 +2203,11 @@ Helper functions
* e.g. `string:split("a,b", ",") == {"a","b"}`
* `string:trim()`
* e.g. `string.trim("\n \t\tfoo bar\t ") == "foo bar"`
* `minetest.wrap_text(str, limit)`: returns a string
* Adds new lines to the string to keep it within the specified character limit
* `minetest.wrap_text(str, limit, [as_table])`: returns a string or table
* Adds newlines to the string to keep it within the specified character limit
Note that returned lines may be longer than the limit since it only splits at word borders.
* limit: Maximal amount of characters in one line
* as_table: optional, if true return table of lines instead of string
* `minetest.pos_to_string({x=X,y=Y,z=Z}, decimal_places))`: returns string `"(X,Y,Z)"`
* Convert position to a printable string
Optional: 'decimal_places' will round the x, y and z of the pos to the given decimal place.
@ -2181,7 +2275,7 @@ Helper functions
max_jitter = 0.5, -- maximum packet time jitter
avg_jitter = 0.03, -- average packet time jitter
connection_uptime = 200, -- seconds since client connected
prot_vers = 31, -- protocol version used by client
protocol_version = 32, -- protocol version used by client
-- following information is available on debug build only!!!
-- DO NOT USE IN MODS
--ser_vers = 26, -- serialization version used by client
@ -2199,6 +2293,10 @@ Helper functions
* nil: return all entries,
* true: return only subdirectory names, or
* false: return only file names.
* `minetest.safe_file_write(path, content)`: returns boolean indicating success
* Replaces contents of file at path with new contents in a safe (atomic) way.
Use this instead of below code when writing e.g. database files:
`local f = io.open(path, "wb"); f:write(content); f:close()`
* `minetest.get_version()`: returns a table containing components of the
engine version. Components:
* `project`: Name of the project, eg, "Minetest"
@ -2210,6 +2308,9 @@ Helper functions
version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example:
`if minetest.nodeupdate then ... end`.
* `minetest.sha1(data, [raw])`: returns the sha1 hash of data
* `data`: string of data to hash
* `raw`: return raw bytes instead of hex digits, default: false
### Logging
* `minetest.debug(...)`
@ -2229,6 +2330,8 @@ Call these functions only at load time!
* `minetest.register_craftitem(name, item definition)`
* `minetest.unregister_item(name)`
* `minetest.register_alias(name, convert_to)`
* Also use this to set the 'mapgen aliases' needed in a game for the core
* mapgens. See 'Mapgen aliases' section above.
* `minetest.register_alias_force(name, convert_to)`
* `minetest.register_craft(recipe)`
* Check recipe table syntax for different types below.
@ -2263,6 +2366,7 @@ Call these functions only at load time!
* `minetest.register_on_placenode(func(pos, newnode, placer, oldnode, itemstack, pointed_thing))`
* Called when a node has been placed
* If return `true` no item is taken from `itemstack`
* `placer` may be any valid ObjectRef or nil.
* **Not recommended**; use `on_construct` or `after_place_node` in node definition
whenever possible
* `minetest.register_on_dignode(func(pos, oldnode, digger))`
@ -2356,8 +2460,9 @@ Call these functions only at load time!
* `definition`: `{ description = "description text", give_to_singleplayer = boolean}`
the default of `give_to_singleplayer` is true
* To allow players with `basic_privs` to grant, see `basic_privs` minetest.conf setting.
* `minetest.register_authentication_handler(handler)`
* See `minetest.builtin_auth_handler` in `builtin.lua` for reference
* `minetest.register_authentication_handler(authentication handler definition)`
* Registers an auth handler that overrides the builtin one
* This function can be called by a single mod once only.
### Setting-related
* `minetest.settings`: Settings object containing all of the settings from the
@ -2366,37 +2471,44 @@ Call these functions only at load time!
parses it as a position (in the format `(1,2,3)`). Returns a position or nil.
### Authentication
* `minetest.notify_authentication_modified(name)`
* Should be called by the authentication handler if privileges changes.
* To report everybody, set `name=nil`.
* `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.privs_to_string(privs)`: returns `"priv1,priv2,..."`
* Convert between two privilege representations
* `minetest.set_player_password(name, password_hash)`
* `minetest.set_player_privs(name, {priv1=true,...})`
* `minetest.get_player_privs(name) -> {priv1=true,...}`
* `minetest.auth_reload()`
* `minetest.check_player_privs(player_or_name, ...)`: returns `bool, missing_privs`
* A quickhand for checking privileges.
* `player_or_name`: Either a Player object or the name of a player.
* `...` is either a list of strings, e.g. `"priva", "privb"` or
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`
and `minetest.auth_reload` call the authetification handler.
@ -2462,12 +2574,15 @@ and `minetest.auth_reload` call the authetification handler.
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `search_center` is an optional boolean (default: `false`)
If true `pos` is also checked for the nodes
* `minetest.find_nodes_in_area(minp, maxp, nodenames)`: returns a list of positions
* returns as second value a table with the count of the individual nodes found
* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of positions
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `minetest.find_nodes_in_area_under_air(minp, maxp, nodenames)`: returns a list of positions
* returned positions are nodes with a node air above
* First return value: Table with all node positions
* Second return value: Table with the count of each node with the node name as index
* Area volume is limited to 4,096,000 nodes
* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* Return value: Table with all node positions with a node air above
* Area volume is limited to 4,096,000 nodes
* `minetest.get_perlin(noiseparams)`
* `minetest.get_perlin(seeddiff, octaves, persistence, scale)`
* Return world-specific perlin noise (`int(worldseed)+seeddiff`)
@ -2676,6 +2791,13 @@ and `minetest.auth_reload` call the authetification handler.
* Convert a vector into a yaw (angle)
* `minetest.yaw_to_dir(yaw)`
* Convert yaw (angle) to a vector
* `minetest.is_colored_paramtype(ptype)`
* Returns a boolean. Returns `true` if the given `paramtype2` contains color
information (`color`, `colorwallmounted` or `colorfacedir`).
* `minetest.strip_param2_color(param2, paramtype2)`
* Removes everything but the color information from the
given `param2` value.
* Returns `nil` if the given `paramtype2` does not contain color information
* `minetest.get_node_drops(nodename, toolname)`
* Returns list of item names.
* **Note**: This will be removed or modified in a future version.
@ -2711,9 +2833,9 @@ and `minetest.auth_reload` call the authetification handler.
* Example query for `"default:gold_ingot"` will return table:
{
[1]={type = "cooking", width = 3, output = "default:gold_ingot",
[1]={method = "cooking", width = 3, output = "default:gold_ingot",
items = {1 = "default:gold_lump"}},
[2]={type = "normal", width = 1, output = "default:gold_ingot 9",
[2]={method = "normal", width = 1, output = "default:gold_ingot 9",
items = {1 = "default:goldblock"}}
}
* `minetest.handle_node_drops(pos, drops, digger)`
@ -2735,9 +2857,11 @@ and `minetest.auth_reload` call the authetification handler.
### Defaults for the `on_*` item definition functions
These functions return the leftover itemstack.
* `minetest.item_place_node(itemstack, placer, pointed_thing, param2)`
* `minetest.item_place_node(itemstack, placer, pointed_thing[, param2, prevent_after_place])`
* Place item as a node
* `param2` overrides `facedir` and wallmounted `param2`
* `prevent_after_place`: if set to `true`, `after_place_node` is not called
for the newly placed node to prevent a callback and placement loop
* returns `itemstack, success`
* `minetest.item_place_object(itemstack, placer, pointed_thing)`
* Place item as-is
@ -2893,6 +3017,7 @@ These functions return the leftover itemstack.
### Misc.
* `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.hud_replace_builtin(name, hud_definition)`
* Replaces definition of a builtin hud element
@ -2960,6 +3085,7 @@ These functions return the leftover itemstack.
* 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.
Returns false or nil, if the player is allowed to do such actions.
* `name` will be "" for non-players or unknown players.
* This function should be overridden by protection mods and should be used to
check if a player can interact at a position.
* This function should call the old version of itself if the position is not
@ -2976,25 +3102,29 @@ These functions return the leftover itemstack.
* `minetest.record_protection_violation(pos, name)`
* This function calls functions registered with
`minetest.register_on_protection_violation`.
* `minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags)`
* `minetest.rotate_and_place(itemstack, placer, pointed_thing[, infinitestacks,
orient_flags, prevent_after_place])`
* Attempt to predict the desired orientation of the facedir-capable node
defined by `itemstack`, and place it accordingly (on-wall, on the floor, or
hanging from the ceiling). Stacks are handled normally if the `infinitestacks`
field is false or omitted (else, the itemstack is not changed). `orient_flags`
is an optional table containing extra tweaks to the placement code:
* `invert_wall`: if `true`, place wall-orientation on the ground and ground-
orientation on the wall.
defined by `itemstack`, and place it accordingly (on-wall, on the floor,
or hanging from the ceiling).
* `infinitestacks`: if `true`, the itemstack is not changed. Otherwise the
stacks are handled normally.
* `orient_flags`: Optional table containing extra tweaks to the placement code:
* `invert_wall`: if `true`, place wall-orientation on the ground and
ground-orientation on the wall.
* `force_wall` : if `true`, always place the node in wall orientation.
* `force_ceiling`: if `true`, always place on the ceiling.
* `force_floor`: if `true`, always place the node on the floor.
* `force_facedir`: if `true`, forcefully reset the facedir to north when placing on
the floor or ceiling
* The first four options are mutually-exclusive; the last in the list takes
precedence over the first.
* `force_facedir`: if `true`, forcefully reset the facedir to north
when placing on the floor or ceiling.
* The first four options are mutually-exclusive; the last in the list
takes precedence over the first.
* `prevent_after_place` is directly passed to `minetest.item_place_node`
* Returns the new itemstack after placement
* `minetest.rotate_node(itemstack, placer, pointed_thing)`
* calls `rotate_and_place()` with infinitestacks set according to the state of
the creative mode setting, and checks for "sneak" to set the `invert_wall`
parameter.
* calls `rotate_and_place()` with `infinitestacks` set according to the state
of the creative mode setting, checks for "sneak" to set the `invert_wall`
parameter and `prevent_after_place` set to `true`.
* `minetest.forceload_block(pos[, transient])`
* forceloads the position `pos`.
@ -3150,7 +3280,7 @@ This is basically a reference to a C++ `ServerActiveObject`
* `set_attach(parent, bone, position, rotation)`
* `bone`: string
* `position`: `{x=num, y=num, z=num}` (relative)
* `rotation`: `{x=num, y=num, z=num}`
* `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees
* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't attached
* `set_detach()`
* `set_bone_position(bone, position, rotation)`
@ -3218,7 +3348,7 @@ This is basically a reference to a C++ `ServerActiveObject`
* `11`: bubbles bar is not shown
* `set_attribute(attribute, value)`:
* 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.
* `get_attribute(attribute)`:
* Returns value (a string) for extra attribute.
@ -3322,8 +3452,9 @@ An `InvRef` is a reference to an inventory.
* `add_item(listname, stack)`: add item somewhere in list, returns leftover `ItemStack`
* `room_for_item(listname, stack):` returns `true` if the stack of items
can be fully added to the list
* `contains_item(listname, stack)`: returns `true` if the stack of items
can be fully taken from the list
* `contains_item(listname, stack, [match_meta])`: returns `true` if
the stack of items can be fully taken from the list.
If `match_meta` is false, only the items' names are compared (default: `false`).
* `remove_item(listname, stack)`: take as many items as specified from the list,
returns the items that were actually removed (as an `ItemStack`) -- note that
any item metadata is ignored, so attempting to remove a specific unique
@ -4212,6 +4343,7 @@ Definition tables
{
items = {"foo:bar", "baz:frob"}, -- Items to drop.
rarity = 1, -- Probability of dropping is 1 / rarity.
inherit_color = true, -- To inherit palette color from the node
},
},
},
@ -4242,6 +4374,7 @@ Definition tables
^ Called after constructing node when node was placed using
minetest.item_place_node / minetest.place_node
^ If return true no item is taken from itemstack
^ `placer` may be any valid ObjectRef or nil
^ default: nil ]]
after_dig_node = func(pos, oldnode, oldmetadata, digger), --[[
^ oldmetadata is in table format
@ -4257,9 +4390,11 @@ Definition tables
^ By default: Calls minetest.register_on_punchnode callbacks ]]
on_rightclick = func(pos, node, clicker, itemstack, pointed_thing), --[[
^ default: nil
^ if defined, itemstack will hold clicker's wielded item
^ itemstack will hold clicker's wielded item
^ Shall return the leftover itemstack
^ Note: pointed_thing can be nil, if a mod calls this function ]]
^ Note: pointed_thing can be nil, if a mod calls this function
This function does not get triggered by clients <=0.4.16 if the
"formspec" node metadata field is set ]]
on_dig = func(pos, node, digger), --[[
^ default: minetest.node_dig
@ -4674,3 +4809,26 @@ The Biome API is still in an experimental phase and subject to change.
-- ^ HTTP status code
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
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
}

@ -1,4 +1,4 @@
Minetest Lua Mainmenu API Reference 0.4.16
Minetest Lua Mainmenu API Reference 0.4.17
========================================
Introduction

@ -1270,7 +1270,9 @@ minetest.register_node("default:chest_locked", {
sounds = default.node_sound_wood_defaults(),
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name() or "")
local pname =
placer and placer:get_player_name() or ""
meta:set_string("owner", pname)
meta:set_string("infotext", "Locked Chest (owned by "..
meta:get_string("owner")..")")
end,

@ -161,11 +161,6 @@
# type: key
# keymap_cmd_local = .
# Key for opening the chat console.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
# type: key
# keyman_console = KEY_F10
# Key for toggling unlimited view range.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
# type: key
@ -344,8 +339,8 @@
# serverlist_file = favoriteservers.txt
# Maximum size of the out chat queue. 0 to disable queueing and -1 to make the queue size unlimited
# type: int min: -1
max_out_chat_queue_size = 20
# type: int
# max_out_chat_queue_size = 20
## Graphics
@ -555,7 +550,7 @@ max_out_chat_queue_size = 20
# type: int
# screenH = 600
# Save the window size automatically when modified.
# Save window size automatically when modified.
# type: bool
# autosave_screensize = true
@ -867,11 +862,10 @@ max_out_chat_queue_size = 20
# type: string
# serverlist_url = servers.minetest.net
# Disable escape sequences, e.g. chat coloring.
# Use this if you want to run a server with pre-0.4.14 clients and you want to disable
# the escape sequences generated by mods.
# Remove color codes from incoming chat messages
# Use this to stop players from being able to use color in their messages
# type: bool
# disable_escape_sequences = false
# strip_color_codes = false
## Network
@ -1841,3 +1835,4 @@ max_out_chat_queue_size = 20
# Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers.
# type: int
# profiler_print_interval = 0

@ -268,7 +268,7 @@ if(WIN32)
else() # Probably MinGW = GCC
set(PLATFORM_LIBS "")
endif()
set(PLATFORM_LIBS ws2_32.lib shlwapi.lib ${PLATFORM_LIBS})
set(PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib ${PLATFORM_LIBS})
# Zlib stuff
set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../../zlib/zlib-1.2.5"
@ -325,10 +325,12 @@ else()
endif(HAVE_LIBRT)
endif(APPLE)
if(NOT APPLE)
# This way Xxf86vm is found on OpenBSD too
find_library(XXF86VM_LIBRARY Xxf86vm)
mark_as_advanced(XXF86VM_LIBRARY)
set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
endif(NOT APPLE)
# Prefer local iconv if installed
find_library(ICONV_LIBRARY iconv)

@ -386,8 +386,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
// *100.0 helps in large map coordinates
m_cameranode->setTarget(my_cp-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction);
// update the camera position in front-view mode to render blocks behind player
if (m_camera_mode == CAMERA_MODE_THIRD_FRONT)
// update the camera position in third-person mode to render blocks behind player
// and correctly apply liquid post FX.
if (m_camera_mode != CAMERA_MODE_FIRST)
m_camera_position = my_cp;
// Get FOV
@ -487,7 +488,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
void Camera::updateViewingRange()
{
f32 viewing_range = g_settings->getFloat("viewing_range");
f32 near_plane = g_settings->getFloat("near_plane");
m_draw_control.wanted_range = viewing_range;
m_cameranode->setNearValue(rangelim(near_plane, 0.0f, 0.5f) * BS);
if (m_draw_control.range_all) {
m_cameranode->setFarValue(100000.0);
return;

@ -332,7 +332,7 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
route_y_min = 0;
// Allow half a diameter + 7 over stone surface
route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
// Limit maximum to area
route_y_max = rangelim(route_y_max, 0, ar.Y - 1);

@ -134,7 +134,6 @@ public:
bool large_cave_is_flat;
bool flooded;
s16 max_stone_y;
v3s16 node_min;
v3s16 node_max;

@ -31,7 +31,7 @@
#ifndef __IRR_USTRING_H_INCLUDED__
#define __IRR_USTRING_H_INCLUDED__
#if (__cplusplus > 199711L) || (_MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__)
#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define USTRING_CPP0X
# if defined(__GXX_EXPERIMENTAL_CXX0X__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)))
# define USTRING_CPP0X_NEWLITERALS

@ -73,7 +73,6 @@ Client::Client(
m_connection_reinit_timer(0.1),
m_avg_rtt_timer(0.0),
m_playerpos_send_timer(0.0),
m_ignore_damage_timer(0.0),
m_tsrc(tsrc),
m_shsrc(shsrc),
m_itemdef(itemdef),
@ -231,6 +230,8 @@ Client::~Client()
m_shutdown = true;
m_con.Disconnect();
deleteAuthData();
m_mesh_update_thread.stop();
m_mesh_update_thread.wait();
while (!m_mesh_update_thread.m_queue_out.empty()) {
@ -258,6 +259,7 @@ Client::~Client()
}
delete m_minimap;
delete m_media_downloader;
}
void Client::connect(Address address, bool is_local_server)
@ -275,14 +277,9 @@ void Client::step(float dtime)
DSTACK(FUNCTION_NAME);
// Limit a bit
if(dtime > 2.0)
if (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;
if(m_animation_time > 60.0)
m_animation_time -= 60.0;
@ -429,7 +426,6 @@ void Client::step(float dtime)
ClientEnvEvent envEvent = m_env.getClientEnvEvent();
if (envEvent.type == CEE_PLAYER_DAMAGE) {
if (m_ignore_damage_timer <= 0) {
u8 damage = envEvent.player_damage.amount;
if (envEvent.player_damage.send_to_server)
@ -441,7 +437,6 @@ void Client::step(float dtime)
event.player_damage.amount = damage;
m_client_event_queue.push(event);
}
}
// Protocol v29 or greater obsoleted this event
else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
u16 breath = envEvent.player_breath.amount;

@ -574,7 +574,6 @@ private:
float m_connection_reinit_timer;
float m_avg_rtt_timer;
float m_playerpos_send_timer;
float m_ignore_damage_timer; // Used after server moves player
IntervalLimiter m_map_timer_and_unload_interval;
IWritableTextureSource *m_tsrc;

@ -60,6 +60,8 @@ struct JoystickButtonCmb : public JoystickCombination {
this->key = key;
}
virtual ~JoystickButtonCmb() {}
virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
u32 filter_mask;
@ -77,6 +79,8 @@ struct JoystickAxisCmb : public JoystickCombination {
this->key = key;
}
virtual ~JoystickAxisCmb() {}
virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
u16 axis_to_compare;

@ -1138,13 +1138,14 @@ video::IImage * Align2Npot2(video::IImage * image,
core::dimension2d<u32> dim = image->getDimension();
std::string extensions = (char*) glGetString(GL_EXTENSIONS);
// Only GLES2 is trusted to correctly report npot support
if (get_GL_major_version() > 1 &&
extensions.find("GL_OES_texture_npot") != std::string::npos) {
// Note: we cache the boolean result. GL context will never change on Android.
static const bool hasNPotSupport = get_GL_major_version() > 1 &&
glGetString(GL_EXTENSIONS) &&
strstr(glGetString(GL_EXTENSIONS), "GL_OES_texture_npot");
if (hasNPotSupport)
return image;
}
unsigned int height = npot2(dim.Height);
unsigned int width = npot2(dim.Width);
@ -1805,7 +1806,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
* mix high- and low-res textures, or for mods with least-common-denominator
* textures that don't have the resources to offer high-res alternatives.
*/
s32 scaleto = g_settings->getS32("texture_min_size");
const bool filter = m_setting_trilinear_filter || m_setting_bilinear_filter;
const s32 scaleto = filter ? g_settings->getS32("texture_min_size") : 1;
if (scaleto > 1) {
const core::dimension2d<u32> dim = baseimg->getDimension();

@ -159,7 +159,8 @@ enum MaterialType{
TILE_MATERIAL_LIQUID_TRANSPARENT,
TILE_MATERIAL_LIQUID_OPAQUE,
TILE_MATERIAL_WAVING_LEAVES,
TILE_MATERIAL_WAVING_PLANTS
TILE_MATERIAL_WAVING_PLANTS,
TILE_MATERIAL_OPAQUE
};
// Material flags
@ -243,18 +244,20 @@ struct TileLayer
void applyMaterialOptions(video::SMaterial &material) const
{
switch (material_type) {
case TILE_MATERIAL_OPAQUE:
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialTypeParam = 0.5;
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;

@ -633,6 +633,16 @@ std::vector<u16> ClientInterface::getClientIDs(ClientState min_state)
return reply;
}
/**
* Verify if user limit was reached.
* User limit count all clients from HelloSent state (MT protocol user) to Active state
* @return true if user limit was reached
*/
bool ClientInterface::isUserLimitReached()
{
return getClientIDs(CS_HelloSent).size() >= g_settings->getU16("max_users");
}
void ClientInterface::step(float dtime)
{
m_print_info_timer += dtime;

@ -449,6 +449,9 @@ public:
/* get list of active client id's */
std::vector<u16> getClientIDs(ClientState min_state=CS_Active);
/* verify is server user limit was reached */
bool isUserLimitReached();
/* get list of client player names */
const std::vector<std::string> &getPlayerNames() const { return m_clients_names; }
@ -493,7 +496,6 @@ public:
}
static std::string state2Name(ClientState state);
protected:
//TODO find way to avoid this functions
void lock() { m_clients_mutex.lock(); }

@ -290,49 +290,46 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
struct MeshBufList
{
/*!
* Specifies in which layer the list is.
* All lists which are in a lower layer are rendered before this list.
*/
u8 layer;
video::SMaterial m;
std::vector<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
std::vector<MeshBufList> lists;
/*!
* Stores the mesh buffers of the world.
* The array index is the material's layer.
* The vector part groups vertices by material.
*/
std::vector<MeshBufList> lists[MAX_TILE_LAYERS];
void clear()
{
lists.clear();
for (int l = 0; l < MAX_TILE_LAYERS; l++)
lists[l].clear();
}
void add(scene::IMeshBuffer *buf, u8 layer)
{
// Append to the correct layer
std::vector<MeshBufList> &list = lists[layer];
const video::SMaterial &m = buf->getMaterial();
for(std::vector<MeshBufList>::iterator i = lists.begin();
i != lists.end(); ++i){
MeshBufList &l = *i;
for (std::vector<MeshBufList>::iterator it = list.begin(); it != list.end();
++it) {
// comparing a full material is quite expensive so we don't do it if
// not even first texture is equal
if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
if ((*it).m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
continue;
if(l.layer != layer)
continue;
if (l.m == m) {
l.bufs.push_back(buf);
if ((*it).m == m) {
(*it).bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.layer = layer;
l.m = m;
l.bufs.push_back(buf);
lists.push_back(l);
list.push_back(l);
}
};
@ -360,7 +357,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
time_t time1 = time(0);
/*
Get animation parameters
@ -476,15 +473,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
}
}
std::vector<MeshBufList> &lists = drawbufs.lists;
// Render all layers in order
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
std::vector<MeshBufList> &lists = drawbufs.lists[layer];
int timecheck_counter = 0;
for (std::vector<MeshBufList>::iterator i = lists.begin();
i != lists.end(); ++i) {
for (std::vector<MeshBufList>::iterator it = lists.begin(); it != lists.end();
++it) {
timecheck_counter++;
if (timecheck_counter > 50) {
timecheck_counter = 0;
int time2 = time(0);
time_t time2 = time(0);
if (time2 > time1 + 4) {
infostream << "ClientMap::renderMap(): "
"Rendering takes ages, returning."
@ -493,18 +492,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
}
}
MeshBufList &list = *i;
driver->setMaterial((*it).m);
driver->setMaterial(list.m);
for (std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
j != list.bufs.end(); ++j) {
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
for (std::vector<scene::IMeshBuffer*>::iterator it2 = (*it).bufs.begin();
it2 != (*it).bufs.end(); ++it2) {
driver->drawMeshBuffer(*it2);
vertex_count += (*it2)->getVertexCount();
meshbuffer_count++;
}
}
}
} // ScopeProfiler

@ -8,6 +8,7 @@
#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@
#define VERSION_PATCH @VERSION_PATCH@
#define VERSION_TWEAK @VERSION_TWEAK@
#define VERSION_EXTRA "@VERSION_EXTRA@"
#define VERSION_STRING "@VERSION_STRING@"
#define PRODUCT_VERSION_STRING "@VERSION_MAJOR@.@VERSION_MINOR@"

@ -258,27 +258,32 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
//TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
v3s16 oldpos_i = floatToInt(*pos_f, BS);
v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS);
s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
v3f newpos_f = *pos_f + *speed_f * dtime;
v3f minpos_f(
MYMIN(pos_f->X, newpos_f.X),
MYMIN(pos_f->Y, newpos_f.Y) + 0.01 * BS, // bias rounding, player often at +/-n.5
MYMIN(pos_f->Z, newpos_f.Z)
);
v3f maxpos_f(
MYMAX(pos_f->X, newpos_f.X),
MYMAX(pos_f->Y, newpos_f.Y),
MYMAX(pos_f->Z, newpos_f.Z)
);
v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);
bool any_position_valid = false;
for(s16 x = min_x; x <= max_x; x++)
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
for(s16 x = min.X; x <= max.X; x++)
for(s16 y = min.Y; y <= max.Y; y++)
for(s16 z = min.Z; z <= max.Z; z++)
{
v3s16 p(x,y,z);
bool is_position_valid;
MapNode n = map->getNodeNoEx(p, &is_position_valid);
if (is_position_valid) {
if (is_position_valid && n.getContent() != CONTENT_IGNORE) {
// Object collides into walkable nodes
any_position_valid = true;
@ -328,7 +333,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
false, n_bouncy_value, p, box));
}
} else {
// Collide with unloaded nodes
// Collide with unloaded nodes (position invalid) and loaded
// CONTENT_IGNORE nodes (position valid)
aabb3f box = getNodeBox(p, BS);
cinfo.push_back(NearbyCollisionInfo(true, false, 0, p, box));
}
@ -336,6 +342,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
// Do not move if world has not loaded yet, since custom node boxes
// are not available for collision detection.
// This also intentionally occurs in the case of the object being positioned
// solely on loaded CONTENT_IGNORE nodes, no matter where they come from.
if (!any_position_valid) {
*speed_f = v3f(0, 0, 0);
return result;

@ -1184,16 +1184,17 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
m_step_distance_counter += moved;
if(m_step_distance_counter > 1.5*BS)
{
m_step_distance_counter = 0;
if(!m_is_local_player && m_prop.makes_footstep_sound)
{
if (m_step_distance_counter > 1.5f * BS) {
m_step_distance_counter = 0.0f;
if (!m_is_local_player && m_prop.makes_footstep_sound) {
INodeDefManager *ndef = m_client->ndef();
v3s16 p = floatToInt(getPosition() + v3f(0,
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
v3s16 p = floatToInt(getPosition() +
v3f(0.0f, (m_prop.collisionbox.MinEdge.Y - 0.5f) * BS, 0.0f), BS);
MapNode n = m_env->getMap().getNodeNoEx(p);
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
// Reduce footstep gain, as non-local-player footsteps are
// somehow louder.
spec.gain *= 0.6f;
m_client->sound()->playSoundAt(spec, false, getPosition());
}
}
@ -1354,6 +1355,13 @@ void GenericCAO::updateTextures(std::string mod)
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
// don't filter low-res textures, makes them look blurry
// player models have a res of 64
const core::dimension2d<u32> &size = texture->getOriginalSize();
const u32 res = std::min(size.Height, size.Width);
use_trilinear_filter &= res > 64;
use_bilinear_filter &= res > 64;
m_animated_meshnode->getMaterial(i)
.setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_animated_meshnode->getMaterial(i)

@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Corresponding offsets are listed in g_27dirs
#define FRAMED_NEIGHBOR_COUNT 18
static constexpr v3s16 light_dirs[8] = {
static const v3s16 light_dirs[8] = {
v3s16(-1, -1, -1),
v3s16(-1, -1, 1),
v3s16(-1, 1, -1),

@ -59,7 +59,7 @@ public:
m_age += dtime;
if(m_age > 10)
{
m_removed = true;
m_pending_removal = true;
return;
}
@ -406,20 +406,6 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_env->getScriptIface()->luaentity_Step(m_id, dtime);
}
// Remove LuaEntity beyond terrain edges
{
ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
assert(map);
if (!m_pending_deactivation &&
map->saoPositionOverLimit(m_base_position)) {
infostream << "Remove SAO " << m_id << "(" << m_init_name
<< "), outside of limits" << std::endl;
m_pending_deactivation = true;
m_removed = true;
return;
}
}
if(send_recommended == false)
return;
@ -555,9 +541,9 @@ int LuaEntitySAO::punch(v3f dir,
ServerActiveObject *puncher,
float time_from_last_punch)
{
if (!m_registered){
if (!m_registered) {
// Delete unknown LuaEntities when punched
m_removed = true;
m_pending_removal = true;
return 0;
}
@ -601,7 +587,7 @@ int LuaEntitySAO::punch(v3f dir,
}
if (getHP() == 0)
m_removed = true;
m_pending_removal = true;
@ -1361,11 +1347,10 @@ void PlayerSAO::setWieldIndex(int i)
}
}
// Erase the peer id and make the object for removal
void PlayerSAO::disconnected()
{
m_peer_id = 0;
m_removed = true;
m_pending_removal = true;
}
void PlayerSAO::unlinkPlayerSessionAndSave()
@ -1402,26 +1387,38 @@ bool PlayerSAO::checkMovementCheat()
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) {
// Fast speed
player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
} else {
// Normal speed
player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
}
// Tolerance. The lag pool does this a bit.
//player_max_speed *= 2.5;
if (m_privs.count("fast") != 0)
player_max_walk = m_player->movement_speed_fast; // Fast speed
else
player_max_walk = m_player->movement_speed_walk; // Normal speed
player_max_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,
// until this can be verified correctly, tolerate higher jumping speeds
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);
float d_vert = diff.Y;
diff.Y = 0;
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)
required_time = d_vert / player_max_speed; // Moving upwards
// FIXME: Checking downwards movement is not easily possible currently,
// 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)) {
m_last_good_position = m_base_position;

@ -923,8 +923,19 @@ public:
<< " against " << def->dump() << std::endl;*/
if (def->check(input, gamedef)) {
// Check if the crafted node/item exists
CraftOutput out = def->getOutput(input, gamedef);
ItemStack is;
is.deSerialize(out.item, gamedef->idef());
if (!is.isKnown(gamedef->idef())) {
infostream << "trying to craft non-existent "
<< out.item << ", ignoring recipe" << std::endl;
continue;
}
// Get output, then decrement input (if requested)
output = def->getOutput(input, gamedef);
output = out;
if (decrementInput)
def->decrementInput(input, output_replacement, gamedef);
/*errorstream << "Check RETURNS TRUE" << std::endl;*/

@ -125,6 +125,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("fps_max", "60");
settings->setDefault("pause_fps_max", "20");
settings->setDefault("viewing_range", "100");
settings->setDefault("near_plane", "0.1");
settings->setDefault("screenW", "800");
settings->setDefault("screenH", "600");
settings->setDefault("autosave_screensize", "true");
@ -252,6 +253,7 @@ void set_default_settings(Settings *settings)
// Server
settings->setDefault("disable_escape_sequences", "false");
settings->setDefault("strip_color_codes", "false");
// Network
settings->setDefault("enable_ipv6", "true");

@ -431,8 +431,10 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(dp.c_wall),
0);
makeHole(p);
makeHole(p - dir);
makeFill(p, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
makeFill(p - dir, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
// TODO: fix stairs code so it works 100%
// (quite difficult)
@ -451,16 +453,21 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);
for (u16 st = 0; st < stair_width; st++) {
if (make_stairs == -1) {
u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
vm->m_data[vi].getContent() == dp.c_wall)
vm->m_data[vi].getContent() == dp.c_wall) {
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
if (vm->m_area.contains(ps) &&
vm->m_data[vi].getContent() == dp.c_wall)
}
} else if (make_stairs == 1) {
u32 vi = vm->m_area.index(ps.X, ps.Y - 1, ps.Z);
if (vm->m_area.contains(ps + v3s16(0, -1, 0)) &&
vm->m_data[vi].getContent() == dp.c_wall) {
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
}
}
ps += swv;
}
}

@ -146,6 +146,9 @@ EmergeManager::~EmergeManager()
}
delete thread;
// Mapgen init might not be finished if there is an error during startup.
if (m_mapgens.size() > i)
delete m_mapgens[i];
}
@ -570,6 +573,12 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what()));
}
/*
Clear generate notifier events
*/
Mapgen *mg = m_emerge->getCurrentMapgen();
mg->gennotify.clearEvents();
EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
/*

@ -27,6 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "config.h"
#include "porting.h"
#ifdef __ANDROID__
#include "settings.h" // For g_settings
#endif
namespace fs
{
@ -374,7 +377,7 @@ std::string TempPath()
configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
*/
#ifdef __ANDROID__
return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp";
return g_settings->get("TMPFolder");
#else
return DIR_DELIM "tmp";
#endif

@ -341,14 +341,33 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
font_path.c_str(), size, true, true, font_shadow,
font_shadow_alpha);
if (font != NULL) {
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
if (font_config_prefix == "mono_") {
const std::string &mono_font_path = m_settings->getDefault("mono_font_path");
if (font_path != mono_font_path) {
// try original mono font
errorstream << "FontEngine: failed to load custom mono "
"font: " << font_path << ", trying to fall back to "
"original mono font" << std::endl;
font = gui::CGUITTFont::createTTFont(m_env,
mono_font_path.c_str(), size, true, true,
font_shadow, font_shadow_alpha);
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
}
} else {
// try fallback font
errorstream << "FontEngine: failed to load: " << font_path << ", trying to fall back "
"to fallback font" << std::endl;
errorstream << "FontEngine: failed to load: " << font_path <<
", trying to fall back to fallback font" << std::endl;
font_path = g_settings->get(font_config_prefix + "fallback_font_path");
@ -356,17 +375,36 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
font_path.c_str(), size, true, true, font_shadow,
font_shadow_alpha);
if (font != NULL) {
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
const std::string &fallback_font_path = m_settings->getDefault("fallback_font_path");
if (font_path != fallback_font_path) {
// try original fallback font
errorstream << "FontEngine: failed to load custom fallback "
"font: " << font_path << ", trying to fall back to "
"original fallback font" << std::endl;
font = gui::CGUITTFont::createTTFont(m_env,
fallback_font_path.c_str(), size, true, true,
font_shadow, font_shadow_alpha);
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
}
}
// give up
errorstream << "FontEngine: failed to load freetype font: "
<< font_path << std::endl;
errorstream << "minetest can not continue without a valid font. Please correct "
"the 'font_path' setting or install the font file in the proper "
"location" << std::endl;
errorstream << "minetest can not continue without a valid font. "
"Please correct the 'font_path' setting or install the font "
"file in the proper location" << std::endl;
abort();
}
#endif
@ -468,7 +506,7 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
}
}
if (font != NULL) {
if (font) {
font->grab();
m_font_cache[mode][basesize] = font;
}

@ -170,6 +170,7 @@ struct LocalFormspecHandler : public TextDest
}
// Don't disable this part when modding is disabled, it's used in builtin
if (m_client && m_client->getScript())
m_client->getScript()->on_formspec_input(m_formname, fields);
}
@ -774,8 +775,8 @@ public:
};
bool nodePlacementPrediction(Client &client,
const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
bool nodePlacementPrediction(Client &client, const ItemDefinition &playeritem_def,
const ItemStack &playeritem, v3s16 nodepos, v3s16 neighbourpos)
{
std::string prediction = playeritem_def.node_placement_prediction;
INodeDefManager *nodedef = client.ndef();
@ -818,11 +819,13 @@ bool nodePlacementPrediction(Client &client,
return false;
}
const ContentFeatures &predicted_f = nodedef->get(id);
// Predict param2 for facedir and wallmounted nodes
u8 param2 = 0;
if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) {
if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
v3s16 dir = nodepos - neighbourpos;
if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
@ -834,8 +837,8 @@ bool nodePlacementPrediction(Client &client,
}
}
if (nodedef->get(id).param_type_2 == CPT2_FACEDIR ||
nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) {
if (predicted_f.param_type_2 == CPT2_FACEDIR ||
predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
if (abs(dir.X) > abs(dir.Z)) {
@ -848,7 +851,7 @@ bool nodePlacementPrediction(Client &client,
assert(param2 <= 5);
//Check attachment if node is in group attached_node
if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) {
if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) {
static v3s16 wallmounted_dirs[8] = {
v3s16(0, 1, 0),
v3s16(0, -1, 0),
@ -859,8 +862,8 @@ bool nodePlacementPrediction(Client &client,
};
v3s16 pp;
if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED)
if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
pp = p + wallmounted_dirs[param2];
else
pp = p + v3s16(0, -1, 0);
@ -869,6 +872,28 @@ bool nodePlacementPrediction(Client &client,
return false;
}
// Apply color
if ((predicted_f.param_type_2 == CPT2_COLOR
|| predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
|| predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
const std::string &indexstr = playeritem.metadata.getString(
"palette_index", 0);
if (!indexstr.empty()) {
s32 index = mystoi(indexstr);
if (predicted_f.param_type_2 == CPT2_COLOR) {
param2 = index;
} else if (predicted_f.param_type_2
== CPT2_COLORED_WALLMOUNTED) {
// param2 = pure palette index + other
param2 = (index & 0xf8) | (param2 & 0x07);
} else if (predicted_f.param_type_2
== CPT2_COLORED_FACEDIR) {
// param2 = pure palette index + other
param2 = (index & 0xe0) | (param2 & 0x1f);
}
}
}
// Add node to client map
MapNode n(id, 0, param2);
@ -1277,7 +1302,8 @@ protected:
const core::line3d<f32> &shootline, bool liquids_pointable,
bool look_for_object, const v3s16 &camera_offset);
void handlePointingAtNothing(const ItemStack &playerItem);
void handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
void handlePointingAtNode(const PointedThing &pointed,
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
const ToolCapabilities &playeritem_toolcap, f32 dtime);
void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
const v3f &player_position, bool show_debug);
@ -2949,8 +2975,8 @@ void Game::decreaseViewRange()
void Game::toggleFullViewRange()
{
static const wchar_t *msg[] = {
L"Disabled full viewing range",
L"Enabled full viewing range"
L"Normal view range",
L"Infinite view range"
};
draw_control->range_all = !draw_control->range_all;
@ -3525,6 +3551,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
shootline = g_touchscreengui->getShootline();
// Scale shootline to the acual distance the player can reach
shootline.end = shootline.start
+ shootline.getVector().normalize() * BS * d;
shootline.start += intToFloat(camera_offset, BS);
shootline.end += intToFloat(camera_offset, BS);
}
@ -3597,10 +3626,11 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
} else if (pointed.type == POINTEDTHING_NODE) {
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(itemdef_manager);
if (playeritem.name.empty()) {
if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) {
playeritem_toolcap = *hand_def.tool_capabilities;
}
handlePointingAtNode(pointed, playeritem_def, playeritem_toolcap, dtime);
handlePointingAtNode(pointed, playeritem_def, playeritem,
playeritem_toolcap, dtime);
} else if (pointed.type == POINTEDTHING_OBJECT) {
handlePointingAtObject(pointed, playeritem, player_position, show_debug);
} else if (isLeftPressed()) {
@ -3735,7 +3765,8 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem)
}
void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
void Game::handlePointingAtNode(const PointedThing &pointed,
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
const ToolCapabilities &playeritem_toolcap, f32 dtime)
{
v3s16 nodepos = pointed.node_undersurface;
@ -3774,6 +3805,11 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
if (meta && meta->getString("formspec") != "" && !random_input
&& !isKeyDown(KeyType::SNEAK)) {
// Report right click to server
if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) {
client->interact(3, pointed);
}
infostream << "Launching custom inventory view" << std::endl;
InventoryLocation inventoryloc;
@ -3796,7 +3832,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
// If the wielded item has node placement prediction,
// make that happen
bool placed = nodePlacementPrediction(*client,
playeritem_def,
playeritem_def, playeritem,
nodepos, neighbourpos);
if (placed) {

@ -51,12 +51,13 @@ extern wchar_t *utf8_to_wide_c(const char *str);
// The returned string is allocated using new
inline const wchar_t *wgettext(const char *str)
{
return utf8_to_wide_c(gettext(str));
// We must check here that is not an empty string to avoid trying to translate it
return str[0] ? utf8_to_wide_c(gettext(str)) : utf8_to_wide_c("");
}
inline std::string strgettext(const std::string &text)
{
return gettext(text.c_str());
return text.empty() ? "" : gettext(text.c_str());
}
#endif

@ -204,8 +204,8 @@ void GUIChatConsole::draw()
// scale current console height to new window size
if (m_screensize.Y != 0)
m_height = m_height * screensize.Y / m_screensize.Y;
m_desired_height = m_desired_height_fraction * m_screensize.Y;
m_screensize = screensize;
m_desired_height = m_desired_height_fraction * m_screensize.Y;
reformatConsole();
}
@ -231,6 +231,7 @@ void GUIChatConsole::reformatConsole()
s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt
if (cols <= 0 || rows <= 0)
cols = rows = 0;
recalculateConsolePosition();
m_chat_backend->reformat(cols, rows);
}

@ -87,22 +87,30 @@ MenuTextureSource::~MenuTextureSource()
/******************************************************************************/
video::ITexture* MenuTextureSource::getTexture(const std::string &name, u32 *id)
{
if(id)
if (id)
*id = 0;
if(name.empty())
if (name.empty())
return NULL;
m_to_delete.insert(name);
#ifdef __ANDROID__
video::ITexture *retval = m_driver->findTexture(name.c_str());
if (retval)
return retval;
video::IImage *image = m_driver->createImageFromFile(name.c_str());
if (image) {
if (!image)
return NULL;
image = Align2Npot2(image, m_driver);
video::ITexture* retval = m_driver->addTexture(name.c_str(), image);
retval = m_driver->addTexture(name.c_str(), image);
image->drop();
return retval;
}
#endif
#else
return m_driver->getTexture(name.c_str());
#endif
}
/******************************************************************************/

@ -3681,19 +3681,25 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
} 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
// Send IACTION_CRAFT
assert(s.isValid());
assert(inv_s);
// Send IACTION_CRAFT
infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
m_invmgr->inventoryAction(a);
}
}
// If m_selected_amount has been decreased to zero, deselect
if (m_selected_amount == 0) {

@ -248,6 +248,7 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip");
std::string bind_address = g_settings->get("bind_address");
if (!bind_address.empty()) {

@ -407,24 +407,32 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
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++)
{
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);
@ -433,13 +441,9 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
p += steppos;
}
if (count % 2 == 1)
{
core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
dstrect += p;
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
if (count % 2 == 1) {
dsthalfrect += p;
draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
}
}

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

@ -659,7 +659,7 @@ bool InventoryList::roomForItem(const ItemStack &item_) const
return false;
}
bool InventoryList::containsItem(const ItemStack &item) const
bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
{
u32 count = item.count;
if(count == 0)
@ -670,9 +670,9 @@ bool InventoryList::containsItem(const ItemStack &item) const
{
if(count == 0)
break;
if(i->name == item.name)
{
if(i->count >= count)
if (i->name == item.name
&& (!match_meta || (i->metadata == item.metadata))) {
if (i->count >= count)
return true;
else
count -= i->count;

@ -223,9 +223,10 @@ public:
// Checks whether there is room for a given item
bool roomForItem(const ItemStack &item) const;
// Checks whether the given count of the given item name
// Checks whether the given count of the given item
// exists in this inventory list.
bool containsItem(const ItemStack &item) const;
// If match_meta is false, only the items' names are compared.
bool containsItem(const ItemStack &item, bool match_meta) const;
// Removes the given count of the given item name from
// this inventory list. Walks the list in reverse order.

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

@ -13,11 +13,11 @@ void ItemStackMetadata::serialize(std::ostream &os) const
{
std::ostringstream os2;
os2 << DESERIALIZE_START;
for (StringMap::const_iterator
it = m_stringvars.begin();
it != m_stringvars.end(); ++it) {
os2 << it->first << DESERIALIZE_KV_DELIM
<< it->second << DESERIALIZE_PAIR_DELIM;
for (StringMap::const_iterator it = m_stringvars.begin(); it != m_stringvars.end();
++it) {
if (!(*it).first.empty() || !(*it).second.empty())
os2 << (*it).first << DESERIALIZE_KV_DELIM
<< (*it).second << DESERIALIZE_PAIR_DELIM;
}
os << serializeJsonStringIfNeeded(os2.str());
}
@ -28,7 +28,8 @@ void ItemStackMetadata::deSerialize(std::istream &is)
m_stringvars.clear();
if (!in.empty() && in[0] == DESERIALIZE_START) {
if (!in.empty()) {
if (in[0] == DESERIALIZE_START) {
Strfnd fnd(in);
fnd.to(1);
while (!fnd.at_end()) {
@ -40,4 +41,5 @@ void ItemStackMetadata::deSerialize(std::istream &is)
// BACKWARDS COMPATIBILITY
m_stringvars[""] = in;
}
}
}

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
LocalPlayer::LocalPlayer(Client *client, const char *name):
Player(name, client->idef()),
parent(0),
parent(NULL),
hp(PLAYER_MAX_HP),
isAttached(false),
touching_ground(false),
@ -53,8 +53,8 @@ LocalPlayer::LocalPlayer(Client *client, const char *name):
overridePosition(v3f(0,0,0)),
last_position(v3f(0,0,0)),
last_speed(v3f(0,0,0)),
last_pitch(0),
last_yaw(0),
last_pitch(0.0f),
last_yaw(0.0f),
last_keyPressed(0),
last_camera_fov(0),
last_wanted_range(0),
@ -67,13 +67,12 @@ LocalPlayer::LocalPlayer(Client *client, const char *name):
hurt_tilt_timer(0.0f),
hurt_tilt_strength(0.0f),
m_position(0,0,0),
m_sneak_node(32767,32767,32767),
m_sneak_node_bb_ymax(0), // To support temporary option for old move code
m_sneak_node_bb_top(0,0,0,0,0,0),
m_sneak_node(32767, 32767, 32767),
m_sneak_node_bb_top(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f),
m_sneak_node_exists(false),
m_need_to_get_new_sneak_node(true),
m_sneak_ladder_detected(false),
m_ledge_detected(false),
m_sneak_node_bb_ymax(0.0f),
m_need_to_get_new_sneak_node(true),
m_old_node_below(32767,32767,32767),
m_old_node_below_type("air"),
m_can_jump(false),
@ -96,91 +95,142 @@ LocalPlayer::~LocalPlayer()
{
}
static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
{
if (nodeboxes.size() == 0)
return aabb3f(0, 0, 0, 0, 0, 0);
aabb3f b_max;
b_max.reset(-BS, -BS, -BS);
for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
it != nodeboxes.end(); ++it) {
aabb3f box = *it;
if (box.MaxEdge.Y > b_max.MaxEdge.Y)
b_max = box;
else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
b_max.addInternalBox(box);
}
return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
b_max = aabb3f(it->MinEdge, it->MaxEdge);
++it;
for (; it != nodeboxes.end(); ++it)
b_max.addInternalBox(*it);
return b_max;
}
#define GETNODE(map, p3, v2, y, valid) \
(map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
// pos is the node the player is standing inside(!)
static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
const v3f &sneak_max)
{
// Detects a structure known as "sneak ladder" or "sneak elevator"
// that relies on bugs to provide a fast means of vertical transportation,
// the bugs have since been fixed but this function remains to keep it working.
// NOTE: This is just entirely a huge hack and causes way too many problems.
bool is_valid_position;
static const v3s16 dir9_center[9] = {
v3s16( 0, 0, 0),
v3s16( 1, 0, 0),
v3s16(-1, 0, 0),
v3s16( 0, 0, 1),
v3s16( 0, 0, -1),
v3s16( 1, 0, 1),
v3s16(-1, 0, 1),
v3s16( 1, 0, -1),
v3s16(-1, 0, -1)
};
INodeDefManager *nodemgr = m_client->ndef();
MapNode node;
// X/Z vectors for 4 neighboring nodes
static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
bool is_valid_position;
bool new_sneak_node_exists = m_sneak_node_exists;
for (u16 i = 0; i < ARRLEN(vecs); i++) {
const v2s16 vec = vecs[i];
// We want the top of the sneak node to be below the players feet
f32 position_y_mod = 0.05 * BS;
if (m_sneak_node_exists)
position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
// walkability of bottom & top node should differ
node = GETNODE(map, pos, vec, 0, &is_valid_position);
if (!is_valid_position)
continue;
bool w = nodemgr->get(node).walkable;
node = GETNODE(map, pos, vec, 1, &is_valid_position);
if (!is_valid_position || w == nodemgr->get(node).walkable)
continue;
// Get position of current standing node
const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
// check one more node above OR below with corresponding walkability
node = GETNODE(map, pos, vec, -1, &is_valid_position);
bool ok = is_valid_position && w != nodemgr->get(node).walkable;
if (!ok) {
node = GETNODE(map, pos, vec, 2, &is_valid_position);
ok = is_valid_position && w == nodemgr->get(node).walkable;
if (current_node != m_sneak_node) {
new_sneak_node_exists = false;
} else {
node = map->getNodeNoEx(current_node, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
new_sneak_node_exists = false;
}
if (ok)
// Keep old sneak node
if (new_sneak_node_exists)
return true;
// Get new sneak node
m_sneak_ladder_detected = false;
f32 min_distance_f = 100000.0 * BS;
for (s16 d = 0; d < 9; d++) {
const v3s16 p = current_node + dir9_center[d];
const v3f pf = intToFloat(p, BS);
const v2f diff(position.X - pf.X, position.Z - pf.Z);
f32 distance_f = diff.getLength();
if (distance_f > min_distance_f ||
fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
continue;
// The node to be sneaked on has to be walkable
node = map->getNodeNoEx(p, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
continue;
// And the node(s) above have to be nonwalkable
bool ok = true;
if (!physics_override_sneak_glitch) {
u16 height = ceilf(
(m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
);
for (u16 y = 1; y <= height; y++) {
node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
if (!is_valid_position || nodemgr->get(node).walkable) {
ok = false;
break;
}
}
} else {
// legacy behaviour: check just one node
node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
ok = is_valid_position && !nodemgr->get(node).walkable;
}
if (!ok)
continue;
min_distance_f = distance_f;
m_sneak_node = p;
new_sneak_node_exists = true;
}
if (!new_sneak_node_exists)
return false;
}
static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos)
{
bool is_valid_position;
MapNode node;
// X/Z vectors for 4 neighboring nodes
static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)};
// Update saved top bounding box of sneak node
node = map->getNodeNoEx(m_sneak_node);
std::vector<aabb3f> nodeboxes;
node.getCollisionBoxes(nodemgr, &nodeboxes);
m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
for (u16 i = 0; i < ARRLEN(vecs); i++) {
const v2s16 vec = vecs[i];
node = GETNODE(map, pos, vec, 1, &is_valid_position);
if (physics_override_sneak_glitch) {
// Detect sneak ladder:
// Node two meters above sneak node must be solid
node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
&is_valid_position);
if (is_valid_position && nodemgr->get(node).walkable) {
// Ledge exists
node = GETNODE(map, pos, vec, 2, &is_valid_position);
if (is_valid_position && !nodemgr->get(node).walkable)
// Space above ledge exists
// Node three meters above: must be non-solid
node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
&is_valid_position);
m_sneak_ladder_detected = is_valid_position &&
!nodemgr->get(node).walkable;
}
}
return true;
}
}
return false;
}
#undef GETNODE
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
std::vector<CollisionInfo> *collision_info)
{
if (!collision_info || collision_info->empty()) {
// Node below the feet, update each ClientEnvironment::step()
m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);
}
// Temporary option for old move code
if (!physics_override_new_move) {
old_move(dtime, env, pos_max_d, collision_info);
@ -193,10 +243,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
v3f position = getPosition();
// Copy parent position if local player is attached
if(isAttached)
{
if (isAttached) {
setPosition(overridePosition);
m_sneak_node_exists = false;
return;
}
@ -204,11 +252,11 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
bool fly_allowed = m_client->checkLocalPrivilege("fly");
bool noclip = m_client->checkLocalPrivilege("noclip") &&
g_settings->getBool("noclip");
bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
if (free_move) {
bool free_move = g_settings->getBool("free_move") && fly_allowed;
if (noclip && free_move) {
position += m_speed * dtime;
setPosition(position);
m_sneak_node_exists = false;
return;
}
@ -279,7 +327,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|| nodemgr->get(node2.getContent()).climbable) && !free_move;
}
/*
Collision uncertainty radius
Make it a bit larger than the maximum distance of movement
@ -291,28 +338,85 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
// This should always apply, otherwise there are glitches
sanity_check(d > pos_max_d);
// TODO: this shouldn't be hardcoded but transmitted from server
float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f);
#ifdef __ANDROID__
if (touching_ground)
player_stepheight += (0.6f * BS);
#endif
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(env, m_client,
pos_max_d, m_collisionbox, player_stepheight, dtime,
&position, &m_speed, accel_f);
bool could_sneak = control.sneak && !free_move && !in_liquid &&
!is_climbing && physics_override_sneak;
// Add new collisions to the vector
if (collision_info && !free_move) {
v3f diff = intToFloat(m_standing_node, BS) - position;
f32 distance = diff.getLength();
// Force update each ClientEnvironment::step()
bool is_first = collision_info->empty();
for (std::vector<CollisionInfo>::const_iterator
colinfo = result.collisions.begin();
colinfo != result.collisions.end(); ++colinfo) {
collision_info->push_back(*colinfo);
if (colinfo->type != COLLISION_NODE ||
colinfo->new_speed.Y != 0 ||
(could_sneak && m_sneak_node_exists))
continue;
diff = intToFloat(colinfo->node_p, BS) - position;
// Find nearest colliding node
f32 len = diff.getLength();
if (is_first || len < distance) {
m_standing_node = colinfo->node_p;
distance = len;
}
}
}
/*
If the player's feet touch the topside of any node, this is
set to true.
Player is allowed to jump when this is true.
*/
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
bool sneak_can_jump = false;
// Max. distance (X, Z) over border for sneaking determined by collision box
// * 0.49 to keep the center just barely on the node
v3f sneak_max = m_collisionbox.getExtent() * 0.49;
if (m_sneak_ladder_detected) {
// restore legacy behaviour (this makes the m_speed.Y hack necessary)
sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
}
/*
If sneaking, keep in range from the last walked node and don't
fall off from it
If sneaking, keep on top of last walked node and don't fall off
*/
if (control.sneak && m_sneak_node_exists &&
!(fly_allowed && g_settings->getBool("free_move")) &&
!in_liquid && !is_climbing &&
physics_override_sneak) {
if (could_sneak && m_sneak_node_exists) {
const v3f sn_f = intToFloat(m_sneak_node, BS);
const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
const v3f old_pos = position;
const v3f old_speed = m_speed;
f32 y_diff = bmax.Y - position.Y;
m_standing_node = m_sneak_node;
// (BS * 0.6f) is the basic stepheight while standing on ground
if (y_diff < BS * 0.6f) {
// Only center player when they're on the node
position.X = rangelim(position.X,
bmin.X - sneak_max.X, bmax.X + sneak_max.X);
position.Z = rangelim(position.Z,
@ -322,18 +426,21 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
m_speed.X = 0;
if (position.Z != old_pos.Z)
m_speed.Z = 0;
// Because we keep the player collision box on the node, limiting
// position.Y is not necessary but useful to prevent players from
// being inside a node if sneaking on e.g. the lower part of a stair
if (!m_sneak_ladder_detected) {
position.Y = MYMAX(position.Y, bmax.Y);
} else {
// legacy behaviour that sometimes causes some weird slow sinking
m_speed.Y = MYMAX(m_speed.Y, 0);
}
if (collision_info != NULL &&
if (y_diff > 0 && m_speed.Y < 0 &&
(physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
// Move player to the maximal height when falling or when
// the ledge is climbed on the next step.
position.Y = bmax.Y;
m_speed.Y = 0;
}
// Allow jumping on node edges while sneaking
if (m_speed.Y == 0 || m_sneak_ladder_detected)
sneak_can_jump = true;
if (collision_info &&
m_speed.Y - old_speed.Y > BS) {
// Collide with sneak node, report fall damage
CollisionInfo sn_info;
@ -344,144 +451,24 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
// TODO: this shouldn't be hardcoded but transmitted from server
float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f);
#ifdef __ANDROID__
player_stepheight += (0.6f * BS);
#endif
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(env, m_client,
pos_max_d, m_collisionbox, player_stepheight, dtime,
&position, &m_speed, accel_f);
/*
If the player's feet touch the topside of any node, this is
set to true.
Player is allowed to jump when this is true.
Find the next sneak node if necessary
*/
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
bool new_sneak_node_exists = false;
// We want the top of the sneak node to be below the players feet
f32 position_y_mod;
if (m_sneak_node_exists)
position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
else
position_y_mod = (0.5 - 0.05) * BS;
v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
/*
Check the nodes under the player to see from which node the
player is sneaking from, if any. If the node from under
the player has been removed, the player falls.
*/
if (m_sneak_node_exists &&
nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
m_old_node_below_type != "air") {
// Old node appears to have been removed; that is,
// it wasn't air before but now it is
m_need_to_get_new_sneak_node = false;
m_sneak_node_exists = false;
} else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
// We are on something, so make sure to recalculate the sneak
// node.
m_need_to_get_new_sneak_node = true;
}
if (m_need_to_get_new_sneak_node && physics_override_sneak) {
v2f player_p2df(position.X, position.Z);
f32 min_distance_f = 100000.0 * BS;
v3s16 new_sneak_node = m_sneak_node;
for(s16 x=-1; x<=1; x++)
for(s16 z=-1; z<=1; z++)
{
v3s16 p = current_node + v3s16(x,0,z);
v3f pf = intToFloat(p, BS);
v2f node_p2df(pf.X, pf.Z);
f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
if (distance_f > min_distance_f ||
fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
continue;
// The node to be sneaked on has to be walkable
node = map->getNodeNoEx(p, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
continue;
// And the node(s) above have to be nonwalkable
bool ok = true;
if (!physics_override_sneak_glitch) {
u16 height = ceilf(
(m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
);
for (u16 y = 1; y <= height; y++) {
node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
if (!is_valid_position || nodemgr->get(node).walkable) {
ok = false;
break;
}
}
} else {
// legacy behaviour: check just one node
node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
ok = is_valid_position && !nodemgr->get(node).walkable;
}
if (!ok)
continue;
min_distance_f = distance_f;
new_sneak_node = p;
}
bool sneak_node_found = (min_distance_f < 100000.0 * BS);
m_sneak_node = new_sneak_node;
m_sneak_node_exists = sneak_node_found;
if (sneak_node_found) {
// Update saved top bounding box of sneak node
MapNode n = map->getNodeNoEx(m_sneak_node);
std::vector<aabb3f> nodeboxes;
n.getCollisionBoxes(nodemgr, &nodeboxes);
m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
m_sneak_ladder_detected = physics_override_sneak_glitch &&
detectSneakLadder(map, nodemgr, floatToInt(position, BS));
} else {
m_sneak_ladder_detected = false;
}
}
/*
If 'sneak glitch' enabled detect ledge for old sneak-jump
behaviour of climbing onto a ledge 2 nodes up.
*/
if (physics_override_sneak_glitch && control.sneak && control.jump)
m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
if (could_sneak)
new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
/*
Set new position but keep sneak node set
*/
bool sneak_node_exists = m_sneak_node_exists;
setPosition(position);
m_sneak_node_exists = sneak_node_exists;
m_sneak_node_exists = new_sneak_node_exists;
/*
Report collisions
*/
// Dont report if flying
if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
for(size_t i=0; i<result.collisions.size(); i++) {
const CollisionInfo &info = result.collisions[i];
collision_info->push_back(info);
}
}
if(!result.standing_on_object && !touching_ground_was && touching_ground) {
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_client->event()->put(e);
@ -501,22 +488,15 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
/*
Update the node last under the player
*/
m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
/*
Check properties of the node on which the player is standing
*/
const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
// Determine if jumping is possible
m_can_jump = touching_ground && !in_liquid;
m_can_jump = (touching_ground && !in_liquid && !is_climbing)
|| sneak_can_jump;
if (itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
m_can_jump = true;
// Jump key pressed while jumping off from a bouncy block
if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
@ -705,17 +685,8 @@ void LocalPlayer::applyControl(float dtime)
*/
v3f speedJ = getSpeed();
if(speedJ.Y >= -0.5 * BS) {
if (m_ledge_detected) {
// Limit jump speed to a minimum that allows
// jumping up onto a ledge 2 nodes up.
speedJ.Y = movement_speed_jump *
MYMAX(physics_override_jump, 1.3f);
setSpeed(speedJ);
m_ledge_detected = false;
} else {
speedJ.Y = movement_speed_jump * physics_override_jump;
setSpeed(speedJ);
}
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_client->event()->put(e);
@ -772,7 +743,7 @@ v3s16 LocalPlayer::getStandingNodePos()
{
if(m_sneak_node_exists)
return m_sneak_node;
return floatToInt(getPosition() - v3f(0, BS, 0), BS);
return m_standing_node;
}
v3s16 LocalPlayer::getFootstepNodePos()

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
#include "environment.h"
#include "constants.h"
#include <list>
class Client;
@ -46,6 +47,8 @@ public:
ClientActiveObject *parent;
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
u16 hp;
bool isAttached;
bool touching_ground;
@ -108,7 +111,7 @@ public:
void setCAO(GenericCAO *toset)
{
assert(m_cao == NULL); // Pre-condition
assert(!m_cao); // Pre-condition
m_cao = toset;
}
@ -140,30 +143,31 @@ public:
private:
void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
void accelerateVertical(const v3f &target_speed, const f32 max_increase);
bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max);
v3f m_position;
v3s16 m_standing_node;
v3s16 m_sneak_node;
// Stores the max player uplift by m_sneak_node
// To support temporary option for old move code
f32 m_sneak_node_bb_ymax;
// Stores the top bounding box of m_sneak_node
aabb3f m_sneak_node_bb_top;
// Whether the player is allowed to sneak
bool m_sneak_node_exists;
// Whether recalculation of m_sneak_node and its top bbox is needed
bool m_need_to_get_new_sneak_node;
// Whether a "sneak ladder" structure is detected at the players pos
// see detectSneakLadder() in the .cpp for more info (always false if disabled)
bool m_sneak_ladder_detected;
// Whether a 2-node-up ledge is detected at the players pos,
// see detectLedge() in the .cpp for more info (always false if disabled).
bool m_ledge_detected;
// ***** Variables for temporary option of the old move code *****
// Stores the max player uplift by m_sneak_node
f32 m_sneak_node_bb_ymax;
// Whether recalculation of m_sneak_node and its top bbox is needed
bool m_need_to_get_new_sneak_node;
// Node below player, used to determine whether it has been removed,
// and its old type
v3s16 m_old_node_below;
std::string m_old_node_below_type;
// ***** End of variables for temporary option *****
bool m_can_jump;
u16 m_breath;
f32 m_yaw;

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

@ -1378,11 +1378,6 @@ s16 ServerMap::getWaterLevel()
return getMapgenParams()->water_level;
}
bool ServerMap::saoPositionOverLimit(const v3f &p)
{
return getMapgenParams()->saoPosOverLimit(p);
}
bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
{
const s16 mapgen_limit_bp = rangelim(
@ -2737,6 +2732,7 @@ void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
continue;
block->copyFrom(*this);
block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
if(modified_blocks)
(*modified_blocks)[p] = block;

@ -377,8 +377,6 @@ public:
*/
ServerMapSector *createSector(v2s16 p);
bool saoPositionOverLimit(const v3f &p);
/*
Blocks are generated by using these and makeBlock().
*/

@ -122,7 +122,8 @@ public:
#define MOD_REASON_STATIC_DATA_REMOVED (1 << 16)
#define MOD_REASON_STATIC_DATA_CHANGED (1 << 17)
#define MOD_REASON_EXPIRE_DAYNIGHTDIFF (1 << 18)
#define MOD_REASON_UNKNOWN (1 << 19)
#define MOD_REASON_VMANIP (1 << 19)
#define MOD_REASON_UNKNOWN (1 << 20)
////
//// MapBlock itself

@ -809,7 +809,16 @@ void MapgenBasic::dustTopNodes()
}
content_t c = vm->m_data[vi].getContent();
if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
NodeDrawType dtype = ndef->get(c).drawtype;
// Only place on walkable cubic non-liquid nodes
// Dust check needed due to vertical overgeneration
if ((dtype == NDT_NORMAL ||
dtype == NDT_ALLFACES_OPTIONAL ||
dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL ||
dtype == NDT_GLASSLIKE ||
dtype == NDT_GLASSLIKE_FRAMED ||
dtype == NDT_ALLFACES) &&
ndef->get(c).walkable && c != biome->c_dust) {
vm->m_area.add_y(em, vi, 1);
vm->m_data[vi] = MapNode(biome->c_dust);
}
@ -973,8 +982,7 @@ bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
void GenerateNotifier::getEvents(
std::map<std::string, std::vector<v3s16> > &event_map,
bool peek_events)
std::map<std::string, std::vector<v3s16> > &event_map)
{
std::list<GenNotifyEvent>::iterator it;
@ -986,8 +994,11 @@ void GenerateNotifier::getEvents(
event_map[name].push_back(gn.pos);
}
}
if (!peek_events)
void GenerateNotifier::clearEvents()
{
m_notify_events.clear();
}
@ -1049,16 +1060,15 @@ void MapgenParams::writeParams(Settings *settings) const
bparams->writeParams(settings);
}
// Calculate edges of outermost generated mapchunks (less than
// 'mapgen_limit'), and corresponding exact limits for SAO entities.
// Calculate exact edges of the outermost mapchunks that are within the
// set 'mapgen_limit'.
void MapgenParams::calcMapgenEdges()
{
// Central chunk offset, in blocks
s16 ccoff_b = -chunksize / 2;
// Chunksize, in nodes
s32 csize_n = chunksize * MAP_BLOCKSIZE;
// Minp/maxp of central chunk, in nodes
s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
s16 ccmax = ccmin + csize_n - 1;
@ -1077,25 +1087,17 @@ void MapgenParams::calcMapgenEdges()
s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
// Mapgen edges, in nodes
// These values may be useful later as additional class members
s16 mapgen_edge_min = ccmin - numcmin * csize_n;
s16 mapgen_edge_max = ccmax + numcmax * csize_n;
// SAO position limits, in Irrlicht units
m_sao_limit_min = mapgen_edge_min * BS - 3.0f;
m_sao_limit_max = mapgen_edge_max * BS + 3.0f;
mapgen_edge_min = ccmin - numcmin * csize_n;
mapgen_edge_max = ccmax + numcmax * csize_n;
m_mapgen_edges_calculated = true;
}
bool MapgenParams::saoPosOverLimit(const v3f &p)
s32 MapgenParams::getSpawnRangeMax()
{
if (!m_sao_limit_calculated) {
if (!m_mapgen_edges_calculated)
calcMapgenEdges();
m_sao_limit_calculated = true;
}
return p.X < m_sao_limit_min ||
p.X > m_sao_limit_max ||
p.Y < m_sao_limit_min ||
p.Y > m_sao_limit_max ||
p.Z < m_sao_limit_min ||
p.Z > m_sao_limit_max;
return MYMIN(-mapgen_edge_min, mapgen_edge_max);
}

@ -101,8 +101,8 @@ public:
void setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids);
bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0);
void getEvents(std::map<std::string, std::vector<v3s16> > &event_map,
bool peek_events=false);
void getEvents(std::map<std::string, std::vector<v3s16> > &event_map);
void clearEvents();
private:
u32 m_notify_on;
@ -131,6 +131,9 @@ struct MapgenParams {
BiomeParams *bparams;
s16 mapgen_edge_min;
s16 mapgen_edge_max;
MapgenParams() :
mgtype(MAPGEN_DEFAULT),
chunksize(5),
@ -139,9 +142,10 @@ struct MapgenParams {
mapgen_limit(MAX_MAP_GENERATION_LIMIT),
flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS),
bparams(NULL),
m_sao_limit_min(MAX_MAP_GENERATION_LIMIT * BS),
m_sao_limit_max(MAX_MAP_GENERATION_LIMIT * BS),
m_sao_limit_calculated(false)
mapgen_edge_min(-MAX_MAP_GENERATION_LIMIT),
mapgen_edge_max(MAX_MAP_GENERATION_LIMIT),
m_mapgen_edges_calculated(false)
{
}
@ -150,13 +154,12 @@ struct MapgenParams {
virtual void readParams(const Settings *settings);
virtual void writeParams(Settings *settings) const;
bool saoPosOverLimit(const v3f &p);
s32 getSpawnRangeMax();
private:
void calcMapgenEdges();
float m_sao_limit_min;
float m_sao_limit_max;
bool m_sao_limit_calculated;
bool m_mapgen_edges_calculated;
};

@ -57,13 +57,17 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge)
this->spflags = params->spflags;
this->cave_width = params->cave_width;
this->float_mount_density = params->float_mount_density;
this->float_mount_height = params->float_mount_height;
this->floatland_level = params->floatland_level;
this->shadow_limit = params->shadow_limit;
this->cavern_limit = params->cavern_limit;
this->cavern_taper = params->cavern_taper;
this->cavern_threshold = params->cavern_threshold;
// This is to avoid a divide-by-zero.
// Parameter will be saved to map_meta.txt in limited form.
params->float_mount_height = MYMAX(params->float_mount_height, 1.0f);
this->float_mount_height = params->float_mount_height;
// 2D noise
noise_terrain_base = new Noise(&params->np_terrain_base, seed, csize.X, csize.Z);
noise_terrain_alt = new Noise(&params->np_terrain_alt, seed, csize.X, csize.Z);
@ -376,7 +380,8 @@ float MapgenV7::baseTerrainLevelFromMap(int index)
bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
{
float mnt_h_n = NoisePerlin2D(&noise_mount_height->np, x, z, seed);
float mnt_h_n =
MYMAX(NoisePerlin2D(&noise_mount_height->np, x, z, seed), 1.0f);
float density_gradient = -((float)y / mnt_h_n);
float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed);
@ -386,7 +391,7 @@ bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
{
float mounthn = noise_mount_height->result[idx_xz];
float mounthn = MYMAX(noise_mount_height->result[idx_xz], 1.0f);
float density_gradient = -((float)y / mounthn);
float mountn = noise_mountain->result[idx_xyz];
@ -415,7 +420,8 @@ void MapgenV7::floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max,
float n_base = noise_floatland_base->result[idx_xz];
if (n_base > 0.0f) {
float n_base_height = noise_float_base_height->result[idx_xz];
float n_base_height =
MYMAX(noise_float_base_height->result[idx_xz], 1.0f);
float amp = n_base * n_base_height;
float ridge = n_base_height / 3.0f;
base_min = floatland_level - amp / 1.5f;

@ -249,18 +249,15 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
int facedir = n.getFaceDir(nodemgr);
u8 axisdir = facedir>>2;
facedir&=0x03;
for(std::vector<aabb3f>::const_iterator
for (std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); ++i)
{
i != fixed.end(); ++i) {
aabb3f box = *i;
if (nodebox.type == NODEBOX_LEVELED) {
box.MaxEdge.Y = -BS/2 + BS*((float)1/LEVELED_MAX) * n.getLevel(nodemgr);
}
if (nodebox.type == NODEBOX_LEVELED)
box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
switch (axisdir)
{
switch (axisdir) {
case 0:
if(facedir == 1)
{

@ -103,8 +103,8 @@ enum Rotation {
#define LIQUID_INFINITY_MASK 0x80 //0b10000000
// mask for param2, now as for liquid
#define LEVELED_MASK 0x3F
// mask for leveled nodebox param2
#define LEVELED_MASK 0x7F
#define LEVELED_MAX LEVELED_MASK

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