Compare commits

..

119 Commits
0.4.3 ... 0.4.4

Author SHA1 Message Date
Perttu Ahola
ab06fca4be Bump version to 0.4.4 2012-12-06 21:21:10 +02:00
Perttu Ahola
fd9e226b79 Update GUI texts 2012-12-06 21:02:02 +02:00
Jürgen Doser
7d2f0d7b1b draw gui later. this fixes the hotbar disappearing while rendering to texture. 2012-12-06 20:43:17 +02:00
Jürgen Doser
870f7bcc93 fix screen flickering black when rendering to texture 2012-12-06 19:56:49 +02:00
Perttu Ahola
543fc6702f Set proper field background colors in Irrlicht 1.8 2012-12-06 19:35:40 +02:00
Perttu Ahola
5080b63435 Raise default maximum render distance to 240 2012-12-06 15:49:20 +02:00
Perttu Ahola
8bb36f4650 Fix automagic render distance tuner 2012-12-06 15:49:20 +02:00
Matthew I
a47b829040 Add #include guards to base64.h 2012-12-03 19:44:54 +02:00
Perttu Ahola
07632b7d69 Try to add a bit of topside brightness when not using shaders 2012-12-02 23:48:11 +02:00
Perttu Ahola
ec54e35757 Tweak shader randomly a bit 2012-12-02 23:47:53 +02:00
Perttu Ahola
68e36226b3 Default to preload_item_visuals=true 2012-12-02 20:43:55 +02:00
MirceaKitsune
15b86a6b9b Don't send player position from client to server if the player hasn't moved 2012-12-02 20:22:02 +02:00
Ilya Zhuravlev
eeb0900291 Fix minetest.get_node_drops(). It should always return list of item names, not ItemStack(s). 2012-12-02 21:09:30 +04:00
Perttu Ahola
804a7875a2 Smooth day-night transitions 2012-12-02 18:02:45 +02:00
Perttu Ahola
041585a78e Const-correct util/serialize.h 2012-12-02 14:36:12 +02:00
Perttu Ahola
cd1f604ffe Handle day-night transition in shader and make light sources brighter when shaders are used 2012-12-02 14:24:58 +02:00
Perttu Ahola
566f7f638a Get rid of jordan4ibanez's insane digging animation speed 2012-12-02 03:10:23 +02:00
Perttu Ahola
3e07c5bf82 Fix tile MaterialType to make sense and make lava surface be shader'd lower like water 2012-12-02 02:59:15 +02:00
Perttu Ahola
ca16119adb Install shaders properly 2012-12-02 01:45:31 +02:00
Perttu Ahola
d0b9b10ee0 Remove accidental vim swap file 2012-12-02 01:42:03 +02:00
Perttu Ahola
98fd364672 Add enable_shaders to minetest.conf.example 2012-12-02 00:54:44 +02:00
obneq
151fad1f2b Add checkboxes for shaders and on-demand item visual generation 2012-12-02 00:52:35 +02:00
Perttu Ahola
27373919f4 Implement a global shader parameter passing system and useful shaders 2012-12-02 00:46:18 +02:00
Kahrl
22e6fb7056 ShaderSource and silly example shaders 2012-12-02 00:46:18 +02:00
Perttu Ahola
e3258b78e2 Add setting preload_item_visuals 2012-12-02 00:46:10 +02:00
Perttu Ahola
bee170570d On-demand item meshes and textures 2012-12-02 00:38:26 +02:00
Perttu Ahola
ba73e75ef9 Modify some client log message levels 2012-12-02 00:24:11 +02:00
RealBadAngel
f4b7e1a570 New elements in formspec, item_image and item_image_button.
Fixed also game bug drawing dragged item behind fields, buttons etc.
2012-12-01 20:39:41 +02:00
PilzAdam
fa50341a71 Only check attachment for nodes with group attached_node 2012-12-01 15:20:53 +01:00
PilzAdam
fa76057e7f Add the group attached_node
Nodes in this group will be dropped as items if the node under them or the node in the wallmounted direction is not walkable.
2012-12-01 13:32:32 +01:00
PilzAdam
bb454b1846 Merge support for anaglyph stereo 2012-12-01 12:41:05 +01:00
Perttu Ahola
778d9b0cd6 Add the ability for client to check if image exists 2012-11-30 22:19:54 +02:00
DannyDark
84ace27872 Log /grant and /revoke command usage 2012-11-30 22:12:21 +02:00
Jürgen Doser
c1d8eeb70a Make dragged itemstack following the mouse cursor much smoother
by using the cursor coordinates directly, instead of updating them only when a mouse event is seen.
2012-11-30 22:07:30 +02:00
Matthew I
6cf87e1d23 Ignore directories beginning with a "." when searching for mods
This is not a problem on POSIX systems (these directories are
ignored by the POSIX implementation of fs::GetDirListing() in
filesys.cpp), but these directories still are reported on Windows
systems.  This becomes a problem when mod authors use version
control systems that create directories like ".git" or ".svn"
and collectMods() picks up on them.  It has also been suggested
that ignoring such directories would allow for the easily disabling
mods by inserting a "." in front of their name.

This quick fix simply makes collectMods() ignore directories
beginning with a ".".
2012-11-30 21:41:50 +02:00
Perttu Ahola
8a93581c8a Run scriptapi_on_shutdown() thread-safely and remove some old crap 2012-11-30 19:41:13 +02:00
Matthew I
6c8fa83ecd Add shutdown hook interface to Lua API
Scripts can call minetest.register_on_shutdown() to register a
shutdown hook.

Document that minetest.register_on_shutdown() callbacks may not be run

If the server crashes, it is unlikely that callbacks registered using
minetest.register_on_shutdown() will be called.
2012-11-30 19:26:51 +02:00
Perttu Ahola
ab45133ab4 Add congestion control settings to minetest.conf 2012-11-29 22:13:22 +02:00
Perttu Ahola
7b6d642300 Full protocol 13 compatibility on server side 2012-11-29 22:08:25 +02:00
Perttu Ahola
96a286dcf5 Make strict and non-strict protocol version checking to work more like expected 2012-11-29 22:08:25 +02:00
Perttu Ahola
420de7ad5e Allow server to accept protocol 13 clients altough they don't work too well 2012-11-29 22:08:25 +02:00
Perttu Ahola
8ccdd3bdb4 Ranged support of protocol version on server side 2012-11-29 22:08:25 +02:00
Perttu Ahola
23913f26cd Support serialization of protocol 13 ContentFeatures 2012-11-29 22:08:25 +02:00
Perttu Ahola
9714612999 unit test: TestNodedefSerialization 2012-11-29 22:08:25 +02:00
Perttu Ahola
69cdcea9fc Modify new ObjectProperties format to such that 0.4.3 will eat it 2012-11-29 22:08:25 +02:00
Perttu Ahola
30ec69c7d3 Fix Taoki's messed up generic object command ids 2012-11-29 22:08:25 +02:00
Perttu Ahola
f720c67f3e Change version to 0.4.4-dev because... ehm... why it wasn't that already? 2012-11-29 22:08:25 +02:00
Calinou
655bb526f4 Fix head shifting downards in glass, nodeboxes... 2012-11-28 16:14:52 +04:00
Perttu Ahola
5c26972cdd Fix mesh glitches on MapBlock boundaries 2012-11-27 19:00:09 +02:00
Perttu Ahola
117f208826 Revert "Use wielditem drawtype for all nodes in item_entity"
This reverts commit ffad18e424.
2012-11-27 17:32:58 +02:00
MirceaKitsune
5f798d944e Fix forgotten material properties for meshes (also seems to have been forgotten for cubes previously). This allows transparent png images to work properly 2012-11-27 08:06:13 +02:00
Perttu Ahola
6b927229f5 Default server step to 0.1s and sync object/player update intervals to it 2012-11-26 22:34:59 +02:00
PilzAdam
ffad18e424 Use wielditem drawtype for all nodes in item_entity 2012-11-26 18:30:07 +01:00
Perttu Ahola
e0eaed5fdb Remove unnecessary util/serialize.h from content_mapblock.cpp 2012-11-26 11:56:18 +02:00
Perttu Ahola
5bd638d4a5 Const-correct SharedBuffer::SharedBuffer(const T *t, unsigned int size) 2012-11-26 11:39:52 +02:00
Perttu Ahola
eca1c96412 Move util/serialize.h out from staticobject.h for smaller header dependencies 2012-11-26 11:18:34 +02:00
Perttu Ahola
2f5686ca48 Move a function from clientserver.h to clientserver.cpp 2012-11-26 10:31:24 +02:00
Perttu Ahola
4fa4340b95 Proper versioning of new network-serialized stuff 2012-11-26 09:49:31 +02:00
Perttu Ahola
197542c7ec New PROTOCOL_VERSION scheme (allow client to support a range of versions) 2012-11-26 09:49:07 +02:00
Perttu Ahola
ab507f83e2 Fix LuaEntitySAO::getClientInitializationData() and bump the related version 2012-11-26 09:10:27 +02:00
Perttu Ahola
d67004a3be Bump version to 0.4.4 as this isn't compatible with 0.4.3 2012-11-26 00:56:47 +02:00
Perttu Ahola
c8acc373c1 Update ContentFeatures serialization format now as PROTOCOL_VERSION was changed 2012-11-25 21:11:45 +02:00
MirceaKitsune
3d1c481f0b RealBadAngel's patch which allows the lua api to read pressed player keys. This should make it possible to change the player's animation based on what he is doing
Correct lua api version number

Always update animations and attachments after the entity is added to scene client side. Fixes animations not being applied in client initialization for some reason. Attachments should be re-tested now just to be safe.

Fix a segmentation fault caused by reaching materials that didn't exist in a loop for setting texture
2012-11-25 19:14:24 +02:00
MirceaKitsune
756db8174a A bunch of fixes
No longer hide players who are dead. With models, a death animation should be used instead

Some changes requested by celeron55

Rename a lot of things in the code, and use better lua api function names

Minor code corrections

Bump protocol version up, since the models / animations / attachments code creates new client<->server messages
2012-11-25 19:14:24 +02:00
MirceaKitsune
fa67b46c04 Allow Vanessa's texture filtering / mipmap patch to work on models, as well as all other node types
Initialize m_bone_posrot too

Update pos_translator for attached players, which should fix a bug noticed with carts where a detached player would travel from the detachment spot to where it was detached

Don't make attachments possible to right-click either

Properly apply object and text visibility for local attachments, and fix them showing when they shouldn't
2012-11-25 19:14:24 +02:00
MirceaKitsune
4d656963e4 Fix more things I forgot for attached players. Such players will now properly see themselves moving when attached, and the server will read their position accordingly
Fix attached players being able to bob their view and generate foostep sounds by pressing a movement key (running in place)
2012-11-25 19:14:24 +02:00
MirceaKitsune
9259d028ac Update attachments at the ending of the addToScene function for parents. And with this... *drum roll* Client-side attachments are at last functional and stick visibly.
Fix the last segmentation fault (apparently). So far attachments seem to be fully functional, although removing the parent causes children to go to origin 0,0,0 and possibly still cause such a fault (though this should already be addressed)

Fix a bug in falling code where entities get stuck

Also check if the parent has been removed server-side, and detach the child if so. Fixes children going to origin 0,0,0 when their parent is removed.

Unset all attachment properties when permanently detaching (on both the client and server). Also store less data we don't need

Create a separate function for detaching, and also update lua api documentation

When a child is detached, update its position from the server to clients. This WILL cause it to get positioned slightly differently client side, as the server attachment system only copies parent origin and knows not about mesh / bone transformation. This prevents different clients seeing the object detached in different spots which is most correct

Update the position of attached players to clients. An attached player will see himself move, but this is currently VERY ugly and laggy as it is done by the server (it probably must stay this way too)

Use a different approach for locally attached players. This allows for smooth positio transitions to work, as well at the player turning around freely. Still buggy however
2012-11-25 19:14:24 +02:00
MirceaKitsune
52fcb0b4b9 Send animations, bone overrides and attachments in entity initialization. Clients no longer have to be near an object when an animation or attachment is set to see the changes, and newly connected clients (or a client that simply renders the object for the first time) will get all of those settings. Therefore, the lua script no longer needs to run every X seconds either, just once per entity.
Finish fixing the material color code. But it won't work until MineTest has dynamic lighting... another day another feature.

Extra checks for the bone positioning / rotation code

Many checks and consistency improvements to the client attachment code

Make a separate function for checking if a client object is attached. A more in-depth change will be needed here to fix reading of invalid pointers

Use a different method of fetching the parent. Fixes the mass segmentation faults when rendering an attachment (some still happen though)

Major change to how attachments are handled. Fix the last segmentaton fault, which was due to the parent becoming invalid while being refreshed / removed which would bause the child to remain attached to nothing. Parents remove their children when being deleted themselves and add them back when re-added. Attachments are stored inside a 2D a vector which easily allows both a child to find their parent and a parent to find its children.

Remove attachment list entry when an object is being permanently removed. Also avoid duplicate entries in this list when re-attaching the same object

The "big code comments" can now go away. Client attachments almost work properly, and I know what else needs to be done
2012-11-25 19:14:23 +02:00
MirceaKitsune
d7d759b43f Enable client-side attachments, add detachment code 2012-11-25 19:14:23 +02:00
MirceaKitsune
ba3fd63e29 Fix some remaining issues with attachments, now they work. Tested with object->player and player->player attachments 2012-11-25 19:14:22 +02:00
MirceaKitsune
948b5a8be7 Complete the attachment framework.
The child ID can now be checked against the parent ID in content_cao.cpp so the parent can be detected. Actual attachment code to come

Divide attachment system between server attachments and client attachments, neither coded right now. As explained in the code comment:

// Attachments need to be handled on both the server and client.
// If we attach only on the server, models (which are client-side)
// can't be read so we don't know the origin and orientation of bones.
// If we attach only on the client, the real position of attachments is
// not updated and you can't click them for example.
2012-11-25 18:14:15 +02:00
MirceaKitsune
e42eeec8f6 Framework for the attachment system, new object property which allows changing the color and alpha of mesh materials
New object property which allows changing the color and alpha of mesh materials. Due to the current lighting systems it doesn't work yet, but the full implementation is there

Framework for the attachment system, with no actual functionality yet

Send bone and player object to the setAttachment function in content_sao.cpp, but we need a way to translate it there and send it to the client

I will also want position and rotation offsets to be possible to apply to attachments

Network object ID from server to client. This will be used to identify the parent client-side and know what to attach to
2012-11-25 18:14:15 +02:00
MirceaKitsune
118285e6ba Get the new animation framework properly working
Store start and end frames as v2f

Also move bone animations to their own function instead of object properties
2012-11-25 18:14:15 +02:00
MirceaKitsune
ba4d93027f Joint positioning and rotation code, and fix a problem related to their lua API
Attempt to move the animation system to a more correct implementation, rather than using object properties. Incomplete and breaks functionality
2012-11-25 18:14:15 +02:00
MirceaKitsune
fb0c431864 Allow the LUA API to set animations to meshes as well as the animation speed. Also update animations only when needed.
Support for animation blending, though for some reason it doesn't work.

Don't enable animation transitions by default for many reaosons

Object property to store individual bone positions. LUA can specify a bone name followed by a bone position. No functionality yet. Bone rotation to be added in the following commit

Same system for bone rotation, plus a few other things I missed
2012-11-25 18:14:15 +02:00
MirceaKitsune
9c8ba42750 Get the mesh working through the memory cache properly. Most credit goes to celeron55 for the help on this code
Get the texture from memory as well

Add .x to the list of supported formats

Update LUA API documentation
2012-11-25 18:14:15 +02:00
MirceaKitsune
f9675bd2b4 Add a subfolder for models and transfer models from server to client
(obj, md2 and md3 are currently allowed)

Get rid of the texture string and use the existing textures array. Segmented meshes have multiple materials, and this will allow us to texture each. Do not switch to this commit yet!

If a texture string is left empty in LUA, don't modify that material. Useful so a script can change specific textures without affecting others
2012-11-25 18:14:15 +02:00
MirceaKitsune
cb40b3517a Fix material properties and allow lighting. Models should now be affected by light instead of being black 2012-11-25 18:14:15 +02:00
MirceaKitsune
0a020dbcc8 Properly read the mesh from LUA.
Players can now be set to meshes using the following test script:

function switch_player_visual()
		prop = {
			mesh="player.obj",
			texture="player.png",
			visual="mesh",
		}
	for _, obj in pairs(minetest.get_connected_players()) do
		obj:set_properties(prop)
	end
	minetest.after(1.0, switch_player_visual)
end
minetest.after(1.0, switch_player_visual)
2012-11-25 18:14:14 +02:00
MirceaKitsune
ac97a7f70e 3D model support for players using Irrlicht. Also ready the basis for mesh support on nodes / items via LUA (to be done). Supports any mesh format compatible with Irrlicht, but animations are not set up yet. 2012-11-25 18:14:14 +02:00
Perttu Ahola
e02b95741b Revert "Don't leak textures all over the place"
This causes textures to be deleted before they are no longer accessed,
resulting in a segmentation fault. This needs to be rewritten in such a way
that textures are deleted when the client disconnects from a server.

This reverts commit 41c00e87d4.
2012-11-25 16:39:07 +02:00
Ilya Zhuravlev
bbd990f2cc Fix trees growing into any type of node 2012-11-23 00:36:52 +04:00
xyzz
a0b5605db8 Merge pull request #268 from khonkhortisan/image_button
Allow transparent image_buttons
2012-11-22 09:13:18 -08:00
Anthony
3ba9b26310 Fix server crash on /clearpassword
According to #253, using `/clearpassword` without an argument causes the server to crash from an assertion failure. I've resubmitted matttpt's patch as a pull request to aid in merging.
2012-11-20 16:45:30 +01:00
MirceaKitsune
6c43ec2d2b Add anaglyph settings to minetest.conf.example 2012-11-13 19:21:18 +02:00
MirceaKitsune
9b9eb23b6d Default anaglyph to 0.1 2012-11-13 19:20:57 +02:00
MirceaKitsune
8b16e5f27d Anaglyph stereo support, created by xyz 2012-11-13 19:20:35 +02:00
xyzz
5292476841 Merge pull request #284 from RealBadAngel/master
Adding backgrounds to formspec
2012-11-12 11:50:58 -08:00
PilzAdam
1db0334787 Merge pull request #286 from PilzAdam/digging_animation
Add jordan4ibanez' better digging animation
2012-11-10 10:33:16 -08:00
PilzAdam
6e2ba111af Add jordan4ibanez' better digging animation 2012-11-10 17:36:56 +01:00
Perttu Ahola
5f1d3ad754 Swap out pixel-perfect nyan cat by request of Chris Torres 2012-11-09 20:14:04 +02:00
Vanessa Ezekowitz
46f471c89f added GUI checkboxes in Settings tab for the aforementioned video settings. 2012-11-09 19:12:49 +02:00
Vanessa Ezekowitz
0dd9c14ff8 Added video settings filter options via config file - second try :-)
Set one or more of these in the config to turn the related option on:

mip_map = 1
anisotropic_filter = 1
bilinear_filter = 1
trilinear_filter = 1
2012-11-09 19:12:44 +02:00
Perttu Ahola
0709918bd8 Fix unnecessary network protocol incompatibility in ContentFeatures 2012-11-08 20:43:32 +02:00
RealBadAngel
8e5167d726 Adding background to Formspec 2012-11-08 19:22:37 +01:00
PilzAdam
8c3ffa3907 Merge pull request #279 from RealBadAngel/patch-2
Formspec: Draw images before inventories. Allows to use image as backgro...
2012-11-05 08:12:12 -08:00
RealBadAngel
8540871ebc Formspec: Draw images before inventories. Allows to use image as background. 2012-11-04 21:18:28 +01:00
PilzAdam
23837e4352 Merge pull request #274 from PilzAdam/falling
Fix a bug in falling code where entities get stuck
2012-11-03 15:14:14 -07:00
PilzAdam
3cab24fbcf Merge pull request #243 from xyzz/liquid_renewable
Add liquid_renewable property, allow non-renewable liquids.
2012-11-03 12:34:44 -07:00
PilzAdam
755df5f659 Merge pull request #273 from PilzAdam/air
Make air and ignore drop nothing
2012-11-03 10:36:46 -07:00
PilzAdam
936411e4d1 Make air and ignore drop nothing 2012-11-03 18:29:01 +01:00
PilzAdam
21317be493 Fix a bug in falling code where entities get stuck 2012-11-03 18:07:49 +01:00
Kahrl
41c00e87d4 Don't leak textures all over the place 2012-11-02 18:45:39 +02:00
Perttu Ahola
2863a3291a Fix flowing liquid animation direction calculation 2012-11-02 18:05:42 +02:00
PilzAdam
8adad36a66 Merge pull request #272 from PilzAdam/pow
Replace pow() with multiplikation to improve speed
2012-11-02 08:35:30 -07:00
PilzAdam
b701f8a878 Replace pow() with multiplikation to improve speed 2012-11-02 16:24:57 +01:00
PilzAdam
63e73f29cc Merge pull request #270 from PilzAdam/master
Move falling to builtin
2012-11-01 11:00:57 -07:00
PilzAdam
7fcf153ada Add functions to the default mod of minimal game to support old code 2012-11-01 18:49:12 +01:00
PilzAdam
0443d36abf Add falling_node to special groups in lua-api.txt 2012-11-01 12:33:24 +01:00
PilzAdam
df2ad89518 Remove a useless comment 2012-10-31 19:08:00 +01:00
PilzAdam
10dc2b30fb Move falling to builtin 2012-10-31 19:06:11 +01:00
Kyle
f9087d14a3 Allow transparent image_buttons 2012-10-18 12:06:47 -07:00
Perttu Ahola
25cf3757b2 Add nodedef.on_blast() to lua_api.txt in order to support chained explosions of any explosives 2012-09-09 23:24:48 +03:00
Perttu Ahola
72ef6f637e Statically store always_collect field of __builtin:item 2012-09-09 23:14:41 +03:00
Perttu Ahola
9cadaf824b Add dtime_s to entity activation 2012-09-09 17:12:29 +03:00
Perttu Ahola
1cc1b93e65 Fix ServerActiveObject stuff 2012-09-09 16:11:05 +03:00
Perttu Ahola
c4f400acd0 Fix wielditem entity drawtype brightness control 2012-09-09 13:26:52 +03:00
Perttu Ahola
ac628c9b0a Fix ObjectRef:punch() 2012-09-09 12:05:38 +03:00
Ilya Zhuravlev
926830e0df Add liquid_renewable property. 2012-09-07 20:48:12 +04:00
84 changed files with 4524 additions and 925 deletions

View File

@@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 3)
set(VERSION_PATCH 4)
if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif()
@@ -131,6 +131,7 @@ if(NOT CUSTOM_LOCALEDIR STREQUAL "")
endif()
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client" DESTINATION "${SHAREDIR}")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minimal" DESTINATION "${SHAREDIR}/games")
set(MINETEST_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game")
if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE})

View File

@@ -22,4 +22,5 @@ dofile(minetest.get_modpath("__builtin").."/auth.lua")
dofile(minetest.get_modpath("__builtin").."/chatcommands.lua")
dofile(minetest.get_modpath("__builtin").."/static_spawn.lua")
dofile(minetest.get_modpath("__builtin").."/detached_inventory.lua")
dofile(minetest.get_modpath("__builtin").."/falling.lua")

View File

@@ -138,6 +138,7 @@ minetest.register_chatcommand("grant", {
return
end
minetest.set_player_privs(grantname, privs)
minetest.log(name..' granted ('..minetest.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
minetest.chat_send_player(name, "Privileges of "..grantname..": "..minetest.privs_to_string(minetest.get_player_privs(grantname), ' '))
if grantname ~= name then
minetest.chat_send_player(grantname, name.." granted you privileges: "..minetest.privs_to_string(grantprivs, ' '))
@@ -175,6 +176,7 @@ minetest.register_chatcommand("revoke", {
end
end
minetest.set_player_privs(revokename, privs)
minetest.log(name..' revoked ('..minetest.privs_to_string(revokeprivs, ', ')..') privileges from '..revokename)
minetest.chat_send_player(name, "Privileges of "..revokename..": "..minetest.privs_to_string(minetest.get_player_privs(revokename), ' '))
if revokename ~= name then
minetest.chat_send_player(revokename, name.." revoked privileges from you: "..minetest.privs_to_string(revokeprivs, ' '))
@@ -215,8 +217,8 @@ minetest.register_chatcommand("clearpassword", {
privs = {password=true},
func = function(name, param)
toname = param
if not toname then
minetest.chat_send_player(toname, "Name field required")
if toname == "" then
minetest.chat_send_player(name, "Name field required")
return
end
minetest.set_player_password(toname, '')

190
builtin/falling.lua Normal file
View File

@@ -0,0 +1,190 @@
-- Minetest: builtin/item.lua
--
-- Falling stuff
--
minetest.register_entity("__builtin:falling_node", {
initial_properties = {
physical = true,
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
visual = "wielditem",
textures = {},
visual_size = {x=0.667, y=0.667},
},
nodename = "",
set_node = function(self, nodename)
self.nodename = nodename
local stack = ItemStack(nodename)
local itemtable = stack:to_table()
local itemname = nil
if itemtable then
itemname = stack:to_table().name
end
local item_texture = nil
local item_type = ""
if minetest.registered_items[itemname] then
item_texture = minetest.registered_items[itemname].inventory_image
item_type = minetest.registered_items[itemname].type
end
prop = {
is_visible = true,
textures = {nodename},
}
self.object:set_properties(prop)
end,
get_staticdata = function(self)
return self.nodename
end,
on_activate = function(self, staticdata)
self.nodename = staticdata
self.object:set_armor_groups({immortal=1})
--self.object:setacceleration({x=0, y=-10, z=0})
self:set_node(self.nodename)
end,
on_step = function(self, dtime)
-- Set gravity
self.object:setacceleration({x=0, y=-10, z=0})
-- Turn to actual sand when collides to ground or just move
local pos = self.object:getpos()
local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point
local bcn = minetest.env:get_node(bcp)
-- Note: walkable is in the node definition, not in item groups
if minetest.registered_nodes[bcn.name] and
minetest.registered_nodes[bcn.name].walkable then
local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
-- Check what's here
local n2 = minetest.env:get_node(np)
-- If it's not air or liquid, remove node and replace it with
-- it's drops
if n2.name ~= "air" and (not minetest.registered_nodes[n2.name] or
minetest.registered_nodes[n2.name].liquidtype == "none") then
local drops = minetest.get_node_drops(n2.name, "")
minetest.env:remove_node(np)
-- Add dropped items
local _, dropped_item
for _, dropped_item in ipairs(drops) do
minetest.env:add_item(np, dropped_item)
end
-- Run script hook
local _, callback
for _, callback in ipairs(minetest.registered_on_dignodes) do
callback(np, n2, nil)
end
end
-- Create node and remove entity
minetest.env:add_node(np, {name=self.nodename})
self.object:remove()
else
-- Do nothing
end
end
})
function spawn_falling_node(p, nodename)
obj = minetest.env:add_entity(p, "__builtin:falling_node")
obj:get_luaentity():set_node(nodename)
end
function drop_attached_node(p)
local nn = minetest.env:get_node(p).name
minetest.env:remove_node(p)
for _,item in ipairs(minetest.get_node_drops(nn, "")) do
local pos = {
x = p.x + math.random(60)/60-0.3,
y = p.y + math.random(60)/60-0.3,
z = p.z + math.random(60)/60-0.3,
}
minetest.env:add_item(pos, item)
end
end
function check_attached_node(p, n)
local def = minetest.registered_nodes[n.name]
local d = {x=0, y=0, z=0}
if def.paramtype2 == "wallmounted" then
if n.param2 == 0 then
d.y = 1
elseif n.param2 == 1 then
d.y = -1
elseif n.param2 == 2 then
d.x = 1
elseif n.param2 == 3 then
d.x = -1
elseif n.param2 == 4 then
d.z = 1
elseif n.param2 == 5 then
d.z = -1
end
else
d.y = -1
end
local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z}
local nn = minetest.env:get_node(p2).name
local def2 = minetest.registered_nodes[nn]
if def2 and not def2.walkable then
return false
end
return true
end
--
-- Some common functions
--
function nodeupdate_single(p)
n = minetest.env:get_node(p)
if minetest.get_node_group(n.name, "falling_node") ~= 0 then
p_bottom = {x=p.x, y=p.y-1, z=p.z}
n_bottom = minetest.env:get_node(p_bottom)
-- Note: walkable is in the node definition, not in item groups
if minetest.registered_nodes[n_bottom.name] and
not minetest.registered_nodes[n_bottom.name].walkable then
minetest.env:remove_node(p)
spawn_falling_node(p, n.name)
nodeupdate(p)
end
end
if minetest.get_node_group(n.name, "attached_node") ~= 0 then
if not check_attached_node(p, n) then
drop_attached_node(p)
nodeupdate(p)
end
end
end
function nodeupdate(p)
-- Round p to prevent falling entities to get stuck
p.x = math.floor(p.x+0.5)
p.y = math.floor(p.y+0.5)
p.z = math.floor(p.z+0.5)
for x = -1,1 do
for y = -1,1 do
for z = -1,1 do
p2 = {x=p.x+x, y=p.y+y, z=p.z+z}
nodeupdate_single(p2)
end
end
end
end
--
-- Global callbacks
--
function on_placenode(p, node)
nodeupdate(p)
end
minetest.register_on_placenode(on_placenode)
function on_dignode(p, node)
nodeupdate(p)
end
minetest.register_on_dignode(on_dignode)

View File

@@ -76,10 +76,10 @@ function minetest.get_node_drops(nodename, toolname)
local drop = ItemStack({name=nodename}):get_definition().drop
if drop == nil then
-- default drop
return {ItemStack({name=nodename})}
return {nodename}
elseif type(drop) == "string" then
-- itemstring drop
return {ItemStack(drop)}
return {drop}
elseif drop.items == nil then
-- drop = {} to disable default drop
return {}
@@ -181,6 +181,14 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
end
end
-- Check if the node is attached and if it can be placed there
if minetest.get_item_group(def.name, "attached_node") ~= 0 and
not check_attached_node(place_to, newnode) then
minetest.log("action", "attached node " .. def.name ..
" can not be placed at " .. minetest.pos_to_string(place_to))
return
end
-- Add node and update
minetest.env:add_node(place_to, newnode)

View File

@@ -57,11 +57,23 @@ minetest.register_entity("__builtin:item", {
end,
get_staticdata = function(self)
return self.itemstring
--return self.itemstring
return minetest.serialize({
itemstring = self.itemstring,
always_collect = self.always_collect,
})
end,
on_activate = function(self, staticdata)
self.itemstring = staticdata
if string.sub(staticdata, 1, string.len("return")) == "return" then
local data = minetest.deserialize(staticdata)
if data and type(data) == "table" then
self.itemstring = data.itemstring
self.always_collect = data.always_collect
end
else
self.itemstring = staticdata
end
self.object:set_armor_groups({immortal=1})
self.object:setvelocity({x=0, y=2, z=0})
self.object:setacceleration({x=0, y=-10, z=0})

View File

@@ -259,6 +259,7 @@ minetest.register_node(":air", {
diggable = false,
buildable_to = true,
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1},
})
@@ -274,6 +275,7 @@ minetest.register_node(":ignore", {
diggable = false,
buildable_to = true, -- A way to remove accidentally placed ignores
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1},
})
@@ -301,6 +303,7 @@ end
minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
minetest.registered_on_shutdown, minetest.register_on_shutdown = make_registration()
minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()

View File

@@ -0,0 +1 @@
trans_alphach_ref

View File

@@ -0,0 +1,25 @@
uniform sampler2D myTexture;
uniform vec4 skyBgColor;
uniform float fogDistance;
varying vec3 vPosition;
void main (void)
{
//vec4 col = vec4(1.0, 0.0, 0.0, 1.0);
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
float a = col.a;
col *= gl_Color;
col = col * col; // SRGB -> Linear
col *= 1.8;
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
col = sqrt(col); // Linear -> SRGB
if(fogDistance != 0.0){
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
col = mix(col, skyBgColor, d);
}
gl_FragColor = vec4(col.r, col.g, col.b, a);
}

View File

@@ -0,0 +1,58 @@
uniform mat4 mWorldViewProj;
uniform mat4 mInvWorld;
uniform mat4 mTransWorld;
uniform float dayNightRatio;
varying vec3 vPosition;
void main(void)
{
gl_Position = mWorldViewProj * gl_Vertex;
vPosition = (mWorldViewProj * gl_Vertex).xyz;
vec4 color;
//color = vec4(1.0, 1.0, 1.0, 1.0);
float day = gl_Color.r;
float night = gl_Color.g;
float light_source = gl_Color.b;
/*color.r = mix(night, day, dayNightRatio);
color.g = color.r;
color.b = color.r;*/
float rg = mix(night, day, dayNightRatio);
rg += light_source * 1.0; // Make light sources brighter
float b = rg;
// Moonlight is blue
b += (day - night) / 13.0;
rg -= (day - night) / 13.0;
// Emphase blue a bit in darker places
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
// Artificial light is yellow-ish
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
color.r = rg;
color.g = rg;
color.b = b;
// Make sides and bottom darker than the top
color = color * color; // SRGB -> Linear
if(gl_Normal.y <= 0.5)
color *= 0.6;
//color *= 0.7;
color = sqrt(color); // Linear -> SRGB
color.a = gl_Color.a;
gl_FrontColor = gl_BackColor = color;
gl_TexCoord[0] = gl_MultiTexCoord0;
}

View File

@@ -0,0 +1 @@
trans_alphach

View File

@@ -0,0 +1,23 @@
uniform sampler2D myTexture;
uniform float fogDistance;
varying vec3 vPosition;
void main (void)
{
vec4 col = texture2D(myTexture, vec2(gl_TexCoord[0]));
col *= gl_Color;
float a = gl_Color.a;
col = col * col; // SRGB -> Linear
col *= 1.8;
col.r = 1.0 - exp(1.0 - col.r) / exp(1.0);
col.g = 1.0 - exp(1.0 - col.g) / exp(1.0);
col.b = 1.0 - exp(1.0 - col.b) / exp(1.0);
col = sqrt(col); // Linear -> SRGB
if(fogDistance != 0.0){
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
a = mix(a, 0.0, d);
}
gl_FragColor = vec4(col.r, col.g, col.b, a);
}

View File

@@ -0,0 +1,53 @@
uniform mat4 mWorldViewProj;
uniform mat4 mInvWorld;
uniform mat4 mTransWorld;
uniform float dayNightRatio;
varying vec3 vPosition;
void main(void)
{
vec4 pos = gl_Vertex;
pos.y -= 2.0;
gl_Position = mWorldViewProj * pos;
vPosition = (mWorldViewProj * gl_Vertex).xyz;
vec4 color;
//color = vec4(1.0, 1.0, 1.0, 1.0);
float day = gl_Color.r;
float night = gl_Color.g;
float light_source = gl_Color.b;
/*color.r = mix(night, day, dayNightRatio);
color.g = color.r;
color.b = color.r;*/
float rg = mix(night, day, dayNightRatio);
rg += light_source * 1.0; // Make light sources brighter
float b = rg;
// Moonlight is blue
b += (day - night) / 13.0;
rg -= (day - night) / 13.0;
// Emphase blue a bit in darker places
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
// Artificial light is yellow-ish
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
color.r = rg;
color.g = rg;
color.b = b;
color.a = gl_Color.a;
gl_FrontColor = gl_BackColor = color;
gl_TexCoord[0] = gl_MultiTexCoord0;
}

View File

@@ -1,4 +1,4 @@
Minetest Lua Modding API Reference 0.4.0
Minetest Lua Modding API Reference 0.4.4
==========================================
More information at http://c55.me/minetest/
@@ -456,6 +456,10 @@ Special groups
- disable_jump: Player (and possibly other things) cannot jump from node
- fall_damage_add_percent: damage speed = speed * (1 + value/100)
- bouncy: value is bounce speed in percent
- falling_node: if there is no walkable block under the node it will fall
- attached_node: if the node under it is not a walkable block the node will be
dropped as an item. If the node is wallmounted the
wallmounted direction is checked.
Known damage and digging time defining groups
----------------------------------------------
@@ -693,6 +697,15 @@ image[<X>,<Y>;<W>,<H>;<texture name>]
^ Show an image
^ Position and size units are inventory slots
item_image[<X>,<Y>;<W>,<H>;<item name>]
^ Show an inventory image of registered item/node
^ Position and size units are inventory slots
background[<X>,<Y>;<W>,<H>;<texture name>]
^ Use a background. Inventory rectangles are not drawn then.
^ Position and size units are inventory slots
^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
^ Textual field; will be sent to server when a button is clicked
^ x and y position the field relative to the top left of the menu
@@ -729,6 +742,12 @@ image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ image is the filename of an image
^ Position and size units are inventory slots
item_image_button[<X>,<Y>;<W>,<H>;<item name>;<name>;<label>]
^ x, y, w, h, name and label work as per button
^ item name is the registered name of an item/node,
tooltip will be made out of its descritption
^ Position and size units are inventory slots
button_exit[<X>,<Y>;<W>,<H>;<name>;<label>]
^ When clicked, fields will be sent and the form will quit.
@@ -786,6 +805,11 @@ minetest.register_craft(recipe)
Global callback registration functions: (Call these only at load time)
minetest.register_globalstep(func(dtime))
^ Called every server step, usually interval of 0.05s
minetest.register_on_shutdown(func())
^ Called before server shutdown
^ WARNING: If the server terminates abnormally (i.e. crashes), the registered
callbacks WILL LIKELY NOT BE RUN. Data should be saved at
semi-frequent intervals as well as on server shutdown.
minetest.register_on_placenode(func(pos, newnode, placer, oldnode))
^ Called when a node has been placed
^ Deprecated: Use on_construct or after_place_node in node definition instead
@@ -1087,6 +1111,7 @@ methods:
- punch(puncher, time_from_last_punch, tool_capabilities, direction)
^ puncher = an another ObjectRef,
^ time_from_last_punch = time since last punch action of the puncher
^ direction: can be nil
- right_click(clicker); clicker = an another ObjectRef
- get_hp(): returns number of hitpoints (2 * number of hearts)
- set_hp(hp): set number of hitpoints (2 * number of hearts)
@@ -1096,6 +1121,10 @@ methods:
- get_wielded_item() -> ItemStack
- set_wielded_item(item): replaces the wielded item, returns true if successful
- set_armor_groups({group1=rating, group2=rating, ...})
- set_animation({x=1,y=1}, frame_speed=15, frame_blend=0)
- set_attach(parent, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
- set_detach()
- set_bone_position("", {x=0,y=0,z=0}, {x=0,y=0,z=0})
- set_properties(object property table)
LuaEntitySAO-only: (no-op for other objects)
- setvelocity({x=num, y=num, z=num})
@@ -1121,7 +1150,11 @@ Player-only: (no-op for other objects)
^ Redefine player's inventory form
^ Should usually be called in on_joinplayer
- get_inventory_formspec() -> formspec string
- get_player_control(): returns table with player pressed keys
{jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool}
- get_player_control_bits(): returns integer with bit packed player pressed keys
bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB
InvRef: Reference to an inventory
methods:
- is_empty(listname): return true if list is empty
@@ -1220,9 +1253,11 @@ Object Properties
physical = true,
weight = 5,
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
visual = "cube"/"sprite"/"upright_sprite",
visual = "cube"/"sprite"/"upright_sprite"/"mesh",
visual_size = {x=1, y=1},
mesh = "model",
textures = {}, -- number of required textures depends on visual
colors = {}, -- number of required colors depends on visual
spritediv = {x=1, y=1},
initial_sprite_basepos = {x=0, y=0},
is_visible = true,
@@ -1236,7 +1271,7 @@ Entity definition (register_entity)
initial_properties = <initial object properties>,
on_activate = function(self, staticdata),
on_activate = function(self, staticdata, dtime_s),
on_step = function(self, dtime),
on_punch = function(self, hitter),
on_rightclick = function(self, clicker),
@@ -1345,6 +1380,8 @@ Node definition (register_node)
liquid_alternative_flowing = "", -- Flowing version of source liquid
liquid_alternative_source = "", -- Source version of flowing liquid
liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7)
liquid_renewable = true, -- Can new liquid source be created by placing
two or more sources nearly?
light_source = 0, -- Amount of light emitted by node
damage_per_second = 0, -- If player is inside node, this damage is caused
node_box = {type="regular"}, -- See "Node boxes"
@@ -1420,6 +1457,11 @@ Node definition (register_node)
on_metadata_inventory_take = func(pos, listname, index, stack, player),
^ Called after the actual action has happened, according to what was allowed.
^ No return value
on_blast = func(pos, intensity),
^ intensity: 1.0 = mid range of regular TNT
^ If defined, called when an explosion touches the node, instead of
removing the node
}
Recipe for register_craft: (shaped)

View File

@@ -769,7 +769,7 @@ minetest.register_node("default:sand", {
description = "Sand",
tiles ={"default_sand.png"},
is_ground_content = true,
groups = {crumbly=3},
groups = {crumbly=3, falling_node=1},
sounds = default.node_sound_sand_defaults(),
})
@@ -777,7 +777,7 @@ minetest.register_node("default:gravel", {
description = "Gravel",
tiles ={"default_gravel.png"},
is_ground_content = true,
groups = {crumbly=2},
groups = {crumbly=2, falling_node=1},
sounds = default.node_sound_dirt_defaults({
footstep = {name="default_gravel_footstep", gain=0.45},
}),
@@ -837,7 +837,7 @@ minetest.register_node("default:junglegrass", {
wield_image = "default_junglegrass.png",
paramtype = "light",
walkable = false,
groups = {snappy=3},
groups = {snappy=3,attached_node=1},
sounds = default.node_sound_leaves_defaults(),
})
@@ -1106,7 +1106,7 @@ minetest.register_node("default:torch", {
wall_bottom = {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
wall_side = {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
},
groups = {choppy=2,dig_immediate=3},
groups = {choppy=2,dig_immediate=3,attached_node=1},
legacy_wallmounted = true,
sounds = default.node_sound_defaults(),
})
@@ -1127,7 +1127,7 @@ minetest.register_node("default:sign_wall", {
--wall_bottom = <default>
--wall_side = <default>
},
groups = {choppy=2,dig_immediate=2},
groups = {choppy=2,dig_immediate=2,attached_node=1},
legacy_wallmounted = true,
sounds = default.node_sound_defaults(),
on_construct = function(pos)
@@ -1495,7 +1495,7 @@ minetest.register_node("default:sapling", {
wield_image = "default_sapling.png",
paramtype = "light",
walkable = false,
groups = {snappy=2,dig_immediate=3},
groups = {snappy=2,dig_immediate=3,attached_node=1},
sounds = default.node_sound_defaults(),
})
@@ -1586,73 +1586,22 @@ minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal")
minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron")
minetest.register_alias("mapgen_mese", "default:mese")
--
-- Some common functions
--
default.falling_node_names = {}
function nodeupdate_single(p)
n = minetest.env:get_node(p)
if default.falling_node_names[n.name] ~= nil then
p_bottom = {x=p.x, y=p.y-1, z=p.z}
n_bottom = minetest.env:get_node(p_bottom)
if n_bottom.name == "air" then
minetest.env:remove_node(p)
minetest.env:add_entity(p, "default:falling_"..n.name)
nodeupdate(p)
end
end
-- Support old code
function default.spawn_falling_node(p, nodename)
spawn_falling_node(p, nodename)
end
function nodeupdate(p)
for x = -1,1 do
for y = -1,1 do
for z = -1,1 do
p2 = {x=p.x+x, y=p.y+y, z=p.z+z}
nodeupdate_single(p2)
end
end
end
end
--
-- Falling stuff
--
-- Horrible crap to support old code
-- Don't use this and never do what this does, it's completely wrong!
-- (More specifically, the client and the C++ code doesn't get the group)
function default.register_falling_node(nodename, texture)
default.falling_node_names[nodename] = true
-- Override naming conventions for stuff like :default:falling_default:sand
minetest.register_entity(":default:falling_"..nodename, {
-- Static definition
physical = true,
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
visual = "cube",
textures = {texture,texture,texture,texture,texture,texture},
-- State
-- Methods
on_step = function(self, dtime)
-- Set gravity
self.object:setacceleration({x=0, y=-10, z=0})
-- Turn to actual sand when collides to ground or just move
local pos = self.object:getpos()
local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point
local bcn = minetest.env:get_node(bcp)
if bcn.name ~= "air" then
-- Turn to a sand node
local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
minetest.env:add_node(np, {name=nodename})
self.object:remove()
else
-- Do nothing
end
end
})
minetest.log("error", debug.traceback())
minetest.log('error', "WARNING: default.register_falling_node is deprecated")
if minetest.registered_nodes[nodename] then
minetest.registered_nodes[nodename].groups.falling_node = 1
end
end
default.register_falling_node("default:sand", "default_sand.png")
default.register_falling_node("default:gravel", "default_gravel.png")
--
-- Global callbacks
--
@@ -1665,13 +1614,11 @@ minetest.register_globalstep(on_step)
function on_placenode(p, node)
--print("on_placenode")
nodeupdate(p)
end
minetest.register_on_placenode(on_placenode)
function on_dignode(p, node)
--print("on_dignode")
nodeupdate(p)
end
minetest.register_on_dignode(on_dignode)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 231 B

View File

@@ -117,6 +117,9 @@
#screenshot_path = .
# Amount of view bobbing (0 = no view bobbing, 1.0 = normal, 2.0 = double)
#view_bobbing_amount = 1.0
# Anaglyph stereo
#anaglyph = false
#anaglyph_strength = 0.1
# In-game chat console background color (R,G,B)
#console_color = (0,0,0)
# In-game chat console background alpha (opaqueness, between 0 and 255)
@@ -126,6 +129,17 @@
#sound_volume = 0.7
# Whether node texture animations should be desynchronized per MapBlock
#desynchronize_mapblock_texture_animation = true
# Texture filtering settings
#mip_map = false
#anisotropic_filter = false
#bilinear_filter = false
#trilinear_filter = false
# Set to true to pre-generate all item visuals
#preload_item_visuals = true
# 0: disable shaders
# (1: low level shaders; not implemented)
# 2: enable high level shaders
#enable_shaders = 2
#
# Server stuff
@@ -139,8 +153,8 @@
#motd = Welcome to this awesome Minetest server!
# Maximum number of players connected simultaneously
#max_users = 100
# Set to false to allow old clients to connect
#strict_protocol_version_checking = true
# Set to true to disallow old clients from connecting
#strict_protocol_version_checking = false
# Set to true to enable creative mode (unlimited inventory)
#creative_mode = false
# Enable players getting damage and dying
@@ -194,7 +208,12 @@
# To reduce lag, block transfers are slowed down when a player is building something.
# This determines how long they are slowed down after placing or removing a node.
#full_block_send_enable_min_time_from_building = 2.0
# Length of a server tick in dedicated server
#dedicated_server_step = 0.05
# Length of a server tick and the interval at which objects are generally updated over network
#dedicated_server_step = 0.1
# Can be set to true to disable shutting down on invalid world data
#ignore_world_load_errors = false
# Congestion control parameters
# time in seconds, rate in ~500B packets
#congestion_control_aim_rtt = 0.2
#congestion_control_max_rate = 400
#congestion_control_min_rate = 10

View File

@@ -202,6 +202,8 @@ set(common_SRCS
sha1.cpp
base64.cpp
ban.cpp
clientserver.cpp
staticobject.cpp
util/serialize.cpp
util/directiontables.cpp
util/numeric.cpp
@@ -260,6 +262,7 @@ set(minetest_SRCS
client.cpp
filecache.cpp
tile.cpp
shader.cpp
game.cpp
main.cpp
)

View File

@@ -1,5 +1,10 @@
#ifndef BASE64_HEADER
#define BASE64_HEADER
#include <string>
bool base64_is_valid(std::string const& s);
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
#endif // BASE64_HEADER

View File

@@ -336,13 +336,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
if (m_digging_button != -1)
{
f32 digfrac = m_digging_anim;
wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * M_PI);
wield_position.Y += 15 * sin(digfrac * 2 * M_PI);
wield_position.Z += 5 * digfrac;
wield_position.X -= 50 * sin(pow(digfrac, 0.8f) * M_PI);
wield_position.Y += 24 * sin(digfrac * 1.8 * M_PI);
wield_position.Z += 25 * 0.5;
// Euler angles are PURE EVIL, so why not use quaternions?
core::quaternion quat_begin(wield_rotation * core::DEGTORAD);
core::quaternion quat_end(v3f(90, -10, -130) * core::DEGTORAD);
core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD);
core::quaternion quat_slerp;
quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI));
quat_slerp.toEuler(wield_rotation);
@@ -529,7 +529,7 @@ void Camera::setDigging(s32 button)
void Camera::wield(const ItemStack &item)
{
IItemDefManager *idef = m_gamedef->idef();
scene::IMesh *wield_mesh = item.getDefinition(idef).wield_mesh;
scene::IMesh *wield_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef);
if(wield_mesh)
{
m_wieldnode->setMesh(wield_mesh);
@@ -544,7 +544,7 @@ void Camera::wield(const ItemStack &item)
void Camera::drawWieldedTool()
{
// Set vertex colors of wield mesh according to light level
u8 li = decode_light(m_wieldlight);
u8 li = m_wieldlight;
video::SColor color(255,li,li,li);
setMeshColor(m_wieldnode->getMesh(), color);

View File

@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodemetadata.h"
#include "nodedef.h"
#include "itemdef.h"
#include "shader.h"
#include <IFileSystem.h>
#include "sha1.h"
#include "base64.h"
@@ -41,6 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sound.h"
#include "util/string.h"
#include "hex.h"
#include "IMeshCache.h"
#include "util/serialize.h"
static std::string getMediaCacheDir()
{
@@ -226,12 +229,14 @@ Client::Client(
std::string password,
MapDrawControl &control,
IWritableTextureSource *tsrc,
IWritableShaderSource *shsrc,
IWritableItemDefManager *itemdef,
IWritableNodeDefManager *nodedef,
ISoundManager *sound,
MtEventManager *event
):
m_tsrc(tsrc),
m_shsrc(shsrc),
m_itemdef(itemdef),
m_nodedef(nodedef),
m_sound(sound),
@@ -265,6 +270,7 @@ Client::Client(
m_time_of_day_set(false),
m_last_time_of_day_f(-1),
m_time_of_day_update_timer(0),
m_recommended_send_interval(0.1),
m_removed_sounds_check_timer(0)
{
m_packetcounter_timer = 0.0;
@@ -498,8 +504,9 @@ void Client::step(float dtime)
// [2] u8 SER_FMT_VER_HIGHEST
// [3] u8[20] player_name
// [23] u8[28] password (new in some version)
// [51] u16 client network protocol version (new in some version)
SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
// [51] u16 minimum supported network protocol version (added sometime)
// [53] u16 maximum supported network protocol version (added later than the previous one)
SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
writeU16(&data[0], TOSERVER_INIT);
writeU8(&data[2], SER_FMT_VER_HIGHEST);
@@ -512,8 +519,8 @@ void Client::step(float dtime)
memset((char*)&data[23], 0, PASSWORD_SIZE);
snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
// This should be incremented in each version
writeU16(&data[51], PROTOCOL_VERSION);
writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
// Send as unreliable
Send(0, data, false);
@@ -655,7 +662,7 @@ void Client::step(float dtime)
{
float &counter = m_playerpos_send_timer;
counter += dtime;
if(counter >= 0.2)
if(counter >= m_recommended_send_interval)
{
counter = 0.0;
sendPlayerPos();
@@ -821,7 +828,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
if(name != "")
{
verbosestream<<"Client: Attempting to load image "
<<"file \""<<filename<<"\""<<std::endl;
<<"file \""<<filename<<"\""<<std::endl;
io::IFileSystem *irrfs = m_device->getFileSystem();
video::IVideoDriver *vdrv = m_device->getVideoDriver();
@@ -855,11 +862,33 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
if(name != "")
{
verbosestream<<"Client: Attempting to load sound "
<<"file \""<<filename<<"\""<<std::endl;
<<"file \""<<filename<<"\""<<std::endl;
m_sound->loadSoundData(name, data);
return true;
}
const char *model_ext[] = {
".x", ".b3d", ".md2", ".obj",
NULL
};
name = removeStringEnd(filename, model_ext);
if(name != "")
{
verbosestream<<"Client: Storing model into Irrlicht: "
<<"\""<<filename<<"\""<<std::endl;
io::IFileSystem *irrfs = m_device->getFileSystem();
io::IReadFile *rfile = irrfs->createMemoryReadFile(
*data_rw, data_rw.getSize(), filename.c_str());
assert(rfile);
scene::ISceneManager *smgr = m_device->getSceneManager();
scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
return true;
}
errorstream<<"Client: Don't know how to load file \""
<<filename<<"\""<<std::endl;
return false;
@@ -997,6 +1026,14 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
m_map_seed = readU64(&data[2+1+6]);
infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
}
if(datasize >= 2+1+6+8+4)
{
// Get map seed
m_recommended_send_interval = readF1000(&data[2+1+6+8]);
infostream<<"Client: received recommended send interval "
<<m_recommended_send_interval<<std::endl;
}
// Reply to server
u32 replysize = 2;
@@ -1434,8 +1471,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
int num_files = readU16(is);
verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
<<num_files<<" files)"<<std::endl;
infostream<<"Client: Received media announcement: packet size: "
<<datasize<<std::endl;
core::list<MediaRequest> file_requests;
@@ -1939,10 +1976,24 @@ void Client::sendPlayerPos()
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
Player *myplayer = m_env.getLocalPlayer();
LocalPlayer *myplayer = m_env.getLocalPlayer();
if(myplayer == NULL)
return;
// Save bandwidth by only updating position when something changed
if(myplayer->last_position == myplayer->getPosition() &&
myplayer->last_speed == myplayer->getSpeed() &&
myplayer->last_pitch == myplayer->getPitch() &&
myplayer->last_yaw == myplayer->getYaw() &&
myplayer->last_keyPressed == myplayer->keyPressed)
return;
myplayer->last_position = myplayer->getPosition();
myplayer->last_speed = myplayer->getSpeed();
myplayer->last_pitch = myplayer->getPitch();
myplayer->last_yaw = myplayer->getYaw();
myplayer->last_keyPressed = myplayer->keyPressed;
u16 our_peer_id;
{
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
@@ -1961,7 +2012,7 @@ void Client::sendPlayerPos()
v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
s32 pitch = myplayer->getPitch() * 100;
s32 yaw = myplayer->getYaw() * 100;
u32 keyPressed=myplayer->keyPressed;
/*
Format:
[0] u16 command
@@ -1969,15 +2020,15 @@ void Client::sendPlayerPos()
[2+12] v3s32 speed*100
[2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
*/
SharedBuffer<u8> data(2+12+12+4+4);
SharedBuffer<u8> data(2+12+12+4+4+4);
writeU16(&data[0], TOSERVER_PLAYERPOS);
writeV3S32(&data[2], position);
writeV3S32(&data[2+12], speed);
writeS32(&data[2+12+12], pitch);
writeS32(&data[2+12+12+4], yaw);
writeS32(&data[2+12+12+4], yaw);
writeU32(&data[2+12+12+4+4], keyPressed);
// Send as unreliable
Send(0, data, false);
}
@@ -2404,7 +2455,7 @@ ClientEvent Client::getClientEvent()
void Client::afterContentReceived()
{
verbosestream<<"Client::afterContentReceived() started"<<std::endl;
infostream<<"Client::afterContentReceived() started"<<std::endl;
assert(m_itemdef_received);
assert(m_nodedef_received);
assert(m_media_received);
@@ -2414,31 +2465,43 @@ void Client::afterContentReceived()
m_media_name_sha1_map.clear();
// Rebuild inherited images and recreate textures
verbosestream<<"Rebuilding images and textures"<<std::endl;
infostream<<"- Rebuilding images and textures"<<std::endl;
m_tsrc->rebuildImagesAndTextures();
// Update texture atlas
verbosestream<<"Updating texture atlas"<<std::endl;
infostream<<"- Updating texture atlas"<<std::endl;
if(g_settings->getBool("enable_texture_atlas"))
m_tsrc->buildMainAtlas(this);
// Rebuild shaders
m_shsrc->rebuildShaders();
// Update node aliases
verbosestream<<"Updating node aliases"<<std::endl;
infostream<<"- Updating node aliases"<<std::endl;
m_nodedef->updateAliases(m_itemdef);
// Update node textures
verbosestream<<"Updating node textures"<<std::endl;
infostream<<"- Updating node textures"<<std::endl;
m_nodedef->updateTextures(m_tsrc);
// Update item textures and meshes
verbosestream<<"Updating item textures and meshes"<<std::endl;
m_itemdef->updateTexturesAndMeshes(this);
// Preload item textures and meshes if configured to
if(g_settings->getBool("preload_item_visuals"))
{
verbosestream<<"Updating item textures and meshes"<<std::endl;
std::set<std::string> names = m_itemdef->getAll();
for(std::set<std::string>::const_iterator
i = names.begin(); i != names.end(); ++i){
// Asking for these caches the result
m_itemdef->getInventoryTexture(*i, this);
m_itemdef->getWieldMesh(*i, this);
}
}
// Start mesh update thread after setting up content definitions
verbosestream<<"Starting mesh update thread"<<std::endl;
infostream<<"- Starting mesh update thread"<<std::endl;
m_mesh_update_thread.Start();
verbosestream<<"Client::afterContentReceived() done"<<std::endl;
infostream<<"Client::afterContentReceived() done"<<std::endl;
}
float Client::getRTT(void)
@@ -2469,6 +2532,10 @@ ITextureSource* Client::getTextureSource()
{
return m_tsrc;
}
IShaderSource* Client::getShaderSource()
{
return m_shsrc;
}
u16 Client::allocateUnknownNodeId(const std::string &name)
{
errorstream<<"Client::allocateUnknownNodeId(): "

View File

@@ -39,6 +39,7 @@ struct MeshMakeData;
class MapBlockMesh;
class IGameDef;
class IWritableTextureSource;
class IWritableShaderSource;
class IWritableItemDefManager;
class IWritableNodeDefManager;
//class IWritableCraftDefManager;
@@ -174,6 +175,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
std::string password,
MapDrawControl &control,
IWritableTextureSource *tsrc,
IWritableShaderSource *shsrc,
IWritableItemDefManager *itemdef,
IWritableNodeDefManager *nodedef,
ISoundManager *sound,
@@ -305,6 +307,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
virtual INodeDefManager* getNodeDefManager();
virtual ICraftDefManager* getCraftDefManager();
virtual ITextureSource* getTextureSource();
virtual IShaderSource* getShaderSource();
virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager();
virtual MtEventManager* getEventManager();
@@ -337,6 +340,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
IntervalLimiter m_map_timer_and_unload_interval;
IWritableTextureSource *m_tsrc;
IWritableShaderSource *m_shsrc;
IWritableItemDefManager *m_itemdef;
IWritableNodeDefManager *m_nodedef;
ISoundManager *m_sound;
@@ -382,6 +386,9 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
float m_last_time_of_day_f;
float m_time_of_day_update_timer;
// An interval for generally sending object positions and stuff
float m_recommended_send_interval;
// Sounds
float m_removed_sounds_check_timer;
// Mapping from server sound ids to our sound ids

View File

@@ -352,13 +352,17 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
if(sector_blocks_drawn != 0)
m_last_drawn_sectors[sp] = true;
}
m_control.blocks_would_have_drawn = blocks_would_have_drawn;
m_control.blocks_drawn = blocks_drawn;
g_profiler->avg("CM: blocks in range", blocks_in_range);
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
if(blocks_in_range != 0)
g_profiler->avg("CM: blocks in range without mesh (frac)",
(float)blocks_in_range_without_mesh/blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks);
}
struct MeshBufList
@@ -413,6 +417,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
m_last_drawn_sectors.clear();
}
bool use_trilinear_filter = g_settings->getBool("trilinear_filter");
bool use_bilinear_filter = g_settings->getBool("bilinear_filter");
bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter");
/*
Get time for measuring timeout.
@@ -463,9 +471,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Blocks that had mesh that would have been drawn according to
// rendering range (if max blocks limit didn't kick in)
u32 blocks_would_have_drawn = 0;
// Blocks that were drawn and had a mesh
u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass
@@ -544,6 +549,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
for(u32 i=0; i<c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
const video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
@@ -656,9 +666,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
g_profiler->avg(prefix+"empty blocks (frac)",
(float)blocks_without_stuff / blocks_drawn);
m_control.blocks_drawn = blocks_drawn;
m_control.blocks_would_have_drawn = blocks_would_have_drawn;
/*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
<<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
}

View File

@@ -36,7 +36,7 @@ ClientActiveObject::ClientActiveObject(u16 id, IGameDef *gamedef,
ClientActiveObject::~ClientActiveObject()
{
removeFromScene();
removeFromScene(true);
}
ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef,

View File

@@ -49,13 +49,19 @@ class ClientActiveObject : public ActiveObject
virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
IrrlichtDevice *irr){}
virtual void removeFromScene(){}
virtual void removeFromScene(bool permanent){}
// 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
virtual core::aabbox3d<f32>* getCollisionBox(){return NULL;}
virtual v3f getPosition(){return v3f(0,0,0);}
virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;}
virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;}
virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;}
virtual bool isPlayer(){return false;}
virtual bool isLocalPlayer(){return false;}
virtual void setAttachments(){}
virtual bool doShowSelectionBox(){return true;}
// Step object in time

31
src/clientserver.cpp Normal file
View File

@@ -0,0 +1,31 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "clientserver.h"
#include "util/serialize.h"
SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed)
{
SharedBuffer<u8> data(2+2+4);
writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
writeU16(&data[2], time);
writeF1000(&data[4], time_speed);
return data;
}

View File

@@ -1,6 +1,6 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef CLIENTSERVER_HEADER
#define CLIENTSERVER_HEADER
#include "util/serialize.h"
#include "util/pointer.h"
SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
/*
changes by PROTOCOL_VERSION:
@@ -67,10 +69,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
TOCLIENT_DETACHED_INVENTORY
PROTOCOL_VERSION 13:
InventoryList field "Width" (deserialization fails with old versions)
PROTOCOL_VERSION 14:
Added transfer of player pressed keys to the server
Added new messages for mesh and bone animation, as well as attachments
GENERIC_CMD_SET_ANIMATION
GENERIC_CMD_SET_BONE_POSITION
GENERIC_CMD_SET_ATTACHMENT
PROTOCOL_VERSION 15:
Serialization format changes
*/
#define PROTOCOL_VERSION 13
#define LATEST_PROTOCOL_VERSION 15
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13
#define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
// Client's supported network protocol range
#define CLIENT_PROTOCOL_VERSION_MIN 13
#define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
// Constant that differentiates the protocol from random data and other protocols
#define PROTOCOL_ID 0x4f457403
#define PASSWORD_SIZE 28 // Maximum password length. Allows for
@@ -89,6 +108,7 @@ enum ToClientCommand
[2] u8 deployed version
[3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
[12] u64 map seed (new as of 2011-02-27)
[20] f1000 recommended send interval (in seconds) (new as of 14)
NOTE: The position in here is deprecated; position is
explicitly sent afterwards
@@ -344,7 +364,8 @@ enum ToServerCommand
[2] u8 SER_FMT_VER_HIGHEST
[3] u8[20] player_name
[23] u8[28] password (new in some version)
[51] u16 client network protocol version (new in some version)
[51] u16 minimum supported network protocol version (added sometime)
[53] u16 maximum supported network protocol version (added later than the previous one)
*/
TOSERVER_INIT2 = 0x11,
@@ -366,6 +387,7 @@ enum ToServerCommand
[2+12] v3s32 speed*100
[2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
*/
TOSERVER_GOTBLOCKS = 0x24,
@@ -551,14 +573,5 @@ enum ToServerCommand
};
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed)
{
SharedBuffer<u8> data(2+2+4);
writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
writeU16(&data[2], time);
writeF1000(&data[4], time_speed);
return data;
}
#endif

View File

@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#include "util/numeric.h"
#include "util/string.h"
#include "settings.h"
namespace con
{
@@ -466,7 +467,10 @@ Peer::Peer(u16 a_id, Address a_address):
m_sendtime_accu(0),
m_max_packets_per_second(10),
m_num_sent(0),
m_max_num_sent(0)
m_max_num_sent(0),
congestion_control_aim_rtt(0.2),
congestion_control_max_rate(400),
congestion_control_min_rate(10)
{
}
Peer::~Peer()
@@ -477,15 +481,15 @@ void Peer::reportRTT(float rtt)
{
if(rtt >= 0.0){
if(rtt < 0.01){
if(m_max_packets_per_second < 400)
if(m_max_packets_per_second < congestion_control_max_rate)
m_max_packets_per_second += 10;
} else if(rtt < 0.2){
if(m_max_packets_per_second < 100)
} else if(rtt < congestion_control_aim_rtt){
if(m_max_packets_per_second < congestion_control_max_rate)
m_max_packets_per_second += 2;
} else {
m_max_packets_per_second *= 0.8;
if(m_max_packets_per_second < 10)
m_max_packets_per_second = 10;
if(m_max_packets_per_second < congestion_control_min_rate)
m_max_packets_per_second = congestion_control_min_rate;
}
}
@@ -891,12 +895,24 @@ void Connection::receive()
void Connection::runTimeouts(float dtime)
{
float congestion_control_aim_rtt
= g_settings->getFloat("congestion_control_aim_rtt");
float congestion_control_max_rate
= g_settings->getFloat("congestion_control_max_rate");
float congestion_control_min_rate
= g_settings->getFloat("congestion_control_min_rate");
core::list<u16> timeouted_peers;
core::map<u16, Peer*>::Iterator j;
j = m_peers.getIterator();
for(; j.atEnd() == false; j++)
{
Peer *peer = j.getNode()->getValue();
// Update congestion control values
peer->congestion_control_aim_rtt = congestion_control_aim_rtt;
peer->congestion_control_max_rate = congestion_control_max_rate;
peer->congestion_control_min_rate = congestion_control_min_rate;
/*
Check peer timeout

View File

@@ -394,7 +394,11 @@ class Peer
float m_max_packets_per_second;
int m_num_sent;
int m_max_num_sent;
// Updated from configuration by Connection
float congestion_control_aim_rtt;
float congestion_control_max_rate;
float congestion_control_min_rate;
private:
};

View File

@@ -40,6 +40,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#include "util/mathconstants.h"
#include "map.h"
#include "main.h" // g_settings
#include <IMeshManipulator.h>
#include <IAnimatedMeshSceneNode.h>
#include <IBoneSceneNode.h>
class Settings;
struct ToolCapabilities;
@@ -468,7 +472,7 @@ void ItemCAO::updateTexture()
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
item.deSerialize(is, idef);
texture = item.getDefinition(idef).inventory_texture;
texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
}
catch(SerializationError &e)
{
@@ -551,7 +555,8 @@ class GenericCAO : public ClientActiveObject
// Only set at initialization
std::string m_name;
bool m_is_player;
bool m_is_local_player; // determined locally
bool m_is_local_player;
int m_id;
// Property-ish things
ObjectProperties m_prop;
//
@@ -559,6 +564,7 @@ class GenericCAO : public ClientActiveObject
IrrlichtDevice *m_irr;
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_meshnode;
scene::IAnimatedMeshSceneNode *m_animated_meshnode;
scene::IBillboardSceneNode *m_spritenode;
scene::ITextSceneNode* m_textnode;
v3f m_position;
@@ -572,6 +578,14 @@ class GenericCAO : public ClientActiveObject
v2s16 m_tx_basepos;
bool m_initial_tx_basepos_set;
bool m_tx_select_horiz_by_yawpitch;
v2f m_animation_range;
int m_animation_speed;
int m_animation_blend;
std::map<std::string, core::vector2d<v3f> > m_bone_position; // stores position and rotation for each bone name
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attached_to_local;
int m_anim_frame;
int m_anim_num_frames;
float m_anim_framelength;
@@ -581,6 +595,7 @@ class GenericCAO : public ClientActiveObject
bool m_visuals_expired;
float m_step_distance_counter;
u8 m_last_light;
bool m_is_visible;
public:
GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
@@ -588,11 +603,13 @@ class GenericCAO : public ClientActiveObject
//
m_is_player(false),
m_is_local_player(false),
m_id(0),
//
m_smgr(NULL),
m_irr(NULL),
m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
m_meshnode(NULL),
m_animated_meshnode(NULL),
m_spritenode(NULL),
m_textnode(NULL),
m_position(v3f(0,10*BS,0)),
@@ -604,6 +621,14 @@ class GenericCAO : public ClientActiveObject
m_tx_basepos(0,0),
m_initial_tx_basepos_set(false),
m_tx_select_horiz_by_yawpitch(false),
m_animation_range(v2f(0,0)),
m_animation_speed(15),
m_animation_blend(0),
m_bone_position(std::map<std::string, core::vector2d<v3f> >()),
m_attachment_bone(""),
m_attachment_position(v3f(0,0,0)),
m_attachment_rotation(v3f(0,0,0)),
m_attached_to_local(false),
m_anim_frame(0),
m_anim_num_frames(1),
m_anim_framelength(0.2),
@@ -611,7 +636,8 @@ class GenericCAO : public ClientActiveObject
m_reset_textures_timer(-1),
m_visuals_expired(false),
m_step_distance_counter(0),
m_last_light(255)
m_last_light(255),
m_is_visible(false)
{
if(gamedef == NULL)
ClientActiveObject::registerType(getType(), create);
@@ -621,21 +647,36 @@ class GenericCAO : public ClientActiveObject
{
infostream<<"GenericCAO: Got init data"<<std::endl;
std::istringstream is(data, std::ios::binary);
int num_messages = 0;
// version
u8 version = readU8(is);
// check version
if(version != 0){
if(version == 1) // In PROTOCOL_VERSION 14
{
m_name = deSerializeString(is);
m_is_player = readU8(is);
m_id = readS16(is);
m_position = readV3F1000(is);
m_yaw = readF1000(is);
m_hp = readS16(is);
num_messages = readU8(is);
}
else if(version == 0) // In PROTOCOL_VERSION 13
{
m_name = deSerializeString(is);
m_is_player = readU8(is);
m_position = readV3F1000(is);
m_yaw = readF1000(is);
m_hp = readS16(is);
num_messages = readU8(is);
}
else
{
errorstream<<"GenericCAO: Unsupported init data version"
<<std::endl;
return;
}
m_name = deSerializeString(is);
m_is_player = readU8(is);
m_position = readV3F1000(is);
m_yaw = readF1000(is);
m_hp = readS16(is);
int num_messages = readU8(is);
for(int i=0; i<num_messages; i++){
std::string message = deSerializeLongString(is);
processMessage(message);
@@ -667,21 +708,113 @@ class GenericCAO : public ClientActiveObject
}
core::aabbox3d<f32>* getSelectionBox()
{
if(!m_prop.is_visible || m_is_local_player)
if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
return NULL;
return &m_selection_box;
}
v3f getPosition()
{
if(getParent() != NULL){
if(m_meshnode)
return m_meshnode->getAbsolutePosition();
if(m_animated_meshnode)
return m_animated_meshnode->getAbsolutePosition();
if(m_spritenode)
return m_spritenode->getAbsolutePosition();
return m_position;
}
return pos_translator.vect_show;
}
void removeFromScene()
scene::IMeshSceneNode *getMeshSceneNode()
{
if(m_meshnode)
return m_meshnode;
return NULL;
}
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode()
{
if(m_animated_meshnode)
return m_animated_meshnode;
return NULL;
}
scene::IBillboardSceneNode *getSpriteSceneNode()
{
if(m_spritenode)
return m_spritenode;
return NULL;
}
bool isPlayer()
{
return m_is_player;
}
bool isLocalPlayer()
{
return m_is_local_player;
}
void setAttachments()
{
updateAttachments();
}
ClientActiveObject *getParent()
{
ClientActiveObject *obj = NULL;
for(std::vector<core::vector2d<int> >::const_iterator cii = m_env->attachment_list.begin(); cii != m_env->attachment_list.end(); cii++)
{
if(cii->X == getId()){ // This ID is our child
if(cii->Y > 0){ // A parent ID exists for our child
if(cii->X != cii->Y){ // The parent and child ID are not the same
obj = m_env->getActiveObject(cii->Y);
}
}
break;
}
}
if(obj)
return obj;
return NULL;
}
void removeFromScene(bool permanent)
{
if(permanent) // Should be true when removing the object permanently and false when refreshing (eg: updating visuals)
{
// Detach this object's children
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->Y == getId()) // Is a child of our object
{
ii->Y = 0;
ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
if(obj)
obj->setAttachments();
}
}
// Delete this object from the attachments list
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->X == getId()) // Is our object
{
m_env->attachment_list.erase(ii);
break;
}
}
}
if(m_meshnode){
m_meshnode->remove();
m_meshnode = NULL;
}
if(m_animated_meshnode){
m_animated_meshnode->remove();
m_animated_meshnode = NULL;
}
if(m_spritenode){
m_spritenode->remove();
m_spritenode = NULL;
@@ -694,7 +827,7 @@ class GenericCAO : public ClientActiveObject
m_smgr = smgr;
m_irr = irr;
if(m_meshnode != NULL || m_spritenode != NULL)
if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
return;
m_visuals_expired = false;
@@ -790,15 +923,49 @@ class GenericCAO : public ClientActiveObject
m_prop.visual_size.X));
u8 li = m_last_light;
setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
} else if(m_prop.visual == "wielditem"){
m_meshnode->setMaterialFlag(video::EMF_LIGHTING, false);
m_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
m_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
m_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
}
else if(m_prop.visual == "mesh"){
infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
if(mesh)
{
m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
m_animated_meshnode->animateJoints(); // Needed for some animations
m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
m_prop.visual_size.Y,
m_prop.visual_size.X));
u8 li = m_last_light;
setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, false);
m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
m_animated_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
}
else
errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
}
else if(m_prop.visual == "wielditem"){
infostream<<"GenericCAO::addToScene(): node"<<std::endl;
infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
if(m_prop.textures.size() >= 1){
infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
IItemDefManager *idef = m_gamedef->idef();
ItemStack item(m_prop.textures[0], 1, 0, "", idef);
scene::IMesh *mesh = item.getDefinition(idef).wield_mesh;
scene::IMesh *item_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef);
// Copy mesh to be able to set unique vertex colors
scene::IMeshManipulator *manip =
irr->getVideoDriver()->getMeshManipulator();
scene::IMesh *mesh = manip->createMeshUniquePrimitives(item_mesh);
m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
m_prop.visual_size.Y/2,
@@ -815,6 +982,8 @@ class GenericCAO : public ClientActiveObject
scene::ISceneNode *node = NULL;
if(m_spritenode)
node = m_spritenode;
else if(m_animated_meshnode)
node = m_animated_meshnode;
else if(m_meshnode)
node = m_meshnode;
if(node && m_is_player && !m_is_local_player){
@@ -825,8 +994,11 @@ class GenericCAO : public ClientActiveObject
wname.c_str(), video::SColor(255,255,255,255), node);
m_textnode->setPosition(v3f(0, BS*1.1, 0));
}
updateNodePos();
updateAnimation();
updateBonePosition();
updateAttachments();
}
void expireVisuals()
@@ -836,17 +1008,16 @@ class GenericCAO : public ClientActiveObject
void updateLight(u8 light_at_pos)
{
bool is_visible = (m_hp != 0);
u8 li = decode_light(light_at_pos);
m_last_light = li;
video::SColor color(255,li,li,li);
if(m_meshnode){
setMeshColor(m_meshnode->getMesh(), color);
m_meshnode->setVisible(is_visible);
}
if(m_spritenode){
m_spritenode->setColor(color);
m_spritenode->setVisible(is_visible);
if(li != m_last_light){
m_last_light = li;
video::SColor color(255,li,li,li);
if(m_meshnode)
setMeshColor(m_meshnode->getMesh(), color);
if(m_animated_meshnode)
setMeshColor(m_animated_meshnode->getMesh(), color);
if(m_spritenode)
m_spritenode->setColor(color);
}
}
@@ -857,12 +1028,21 @@ class GenericCAO : public ClientActiveObject
void updateNodePos()
{
if(getParent() != NULL)
return;
if(m_meshnode){
m_meshnode->setPosition(pos_translator.vect_show);
v3f rot = m_meshnode->getRotation();
rot.Y = -m_yaw;
m_meshnode->setRotation(rot);
}
if(m_animated_meshnode){
m_animated_meshnode->setPosition(pos_translator.vect_show);
v3f rot = m_animated_meshnode->getRotation();
rot.Y = -m_yaw;
m_animated_meshnode->setRotation(rot);
}
if(m_spritenode){
m_spritenode->setPosition(pos_translator.vect_show);
}
@@ -870,56 +1050,116 @@ class GenericCAO : public ClientActiveObject
void step(float dtime, ClientEnvironment *env)
{
v3f lastpos = pos_translator.vect_show;
if(m_visuals_expired && m_smgr && m_irr){
m_visuals_expired = false;
removeFromScene();
// Attachments, part 1: All attached objects must be unparented first, or Irrlicht causes a segmentation fault
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->Y == getId()) // This is a child of our parent
{
ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
if(obj)
{
scene::IMeshSceneNode *m_child_meshnode = obj->getMeshSceneNode();
scene::IAnimatedMeshSceneNode *m_child_animated_meshnode = obj->getAnimatedMeshSceneNode();
scene::IBillboardSceneNode *m_child_spritenode = obj->getSpriteSceneNode();
if(m_child_meshnode)
m_child_meshnode->setParent(m_smgr->getRootSceneNode());
if(m_child_animated_meshnode)
m_child_animated_meshnode->setParent(m_smgr->getRootSceneNode());
if(m_child_spritenode)
m_child_spritenode->setParent(m_smgr->getRootSceneNode());
}
}
}
removeFromScene(false);
addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
// Attachments, part 2: Now that the parent has been refreshed, put its attachments back
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->Y == getId()) // This is a child of our parent
{
ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
if(obj)
obj->setAttachments();
}
}
}
if(m_prop.physical){
core::aabbox3d<f32> box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
f32 pos_max_d = BS*0.125; // Distance per iteration
f32 stepheight = 0;
v3f p_pos = m_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = env->getGameDef();
moveresult = collisionMoveSimple(&env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime);
updateNodePos();
} else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
pos_translator.translate(dtime);
updateNodePos();
}
// Make sure m_is_visible is always applied
if(m_meshnode)
m_meshnode->setVisible(m_is_visible);
if(m_animated_meshnode)
m_animated_meshnode->setVisible(m_is_visible);
if(m_spritenode)
m_spritenode->setVisible(m_is_visible);
if(m_textnode)
m_textnode->setVisible(m_is_visible);
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
m_step_distance_counter += moved;
if(m_step_distance_counter > 1.5*BS){
m_step_distance_counter = 0;
if(!m_is_local_player && m_prop.makes_footstep_sound){
INodeDefManager *ndef = m_gamedef->ndef();
v3s16 p = floatToInt(getPosition() + v3f(0,
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
MapNode n = m_env->getMap().getNodeNoEx(p);
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
m_gamedef->sound()->playSoundAt(spec, false, getPosition());
if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
{
// Set these for later
m_position = getPosition();
m_velocity = v3f(0,0,0);
m_acceleration = v3f(0,0,0);
pos_translator.vect_show = m_position;
if(m_is_local_player) // Update local player attachment position
{
LocalPlayer *player = m_env->getLocalPlayer();
player->overridePosition = getParent()->getPosition();
}
}
else
{
v3f lastpos = pos_translator.vect_show;
if(m_prop.physical){
core::aabbox3d<f32> box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
f32 pos_max_d = BS*0.125; // Distance per iteration
f32 stepheight = 0;
v3f p_pos = m_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = env->getGameDef();
moveresult = collisionMoveSimple(&env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime);
updateNodePos();
} else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
pos_translator.translate(dtime);
updateNodePos();
}
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
m_step_distance_counter += moved;
if(m_step_distance_counter > 1.5*BS){
m_step_distance_counter = 0;
if(!m_is_local_player && m_prop.makes_footstep_sound){
INodeDefManager *ndef = m_gamedef->ndef();
v3s16 p = floatToInt(getPosition() + v3f(0,
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
MapNode n = m_env->getMap().getNodeNoEx(p);
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
m_gamedef->sound()->playSoundAt(spec, false, getPosition());
}
}
}
@@ -940,7 +1180,7 @@ class GenericCAO : public ClientActiveObject
updateTextures("");
}
}
if(fabs(m_prop.automatic_rotate) > 0.001){
if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001){
m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
updateNodePos();
}
@@ -998,6 +1238,10 @@ class GenericCAO : public ClientActiveObject
{
ITextureSource *tsrc = m_gamedef->tsrc();
bool use_trilinear_filter = g_settings->getBool("trilinear_filter");
bool use_bilinear_filter = g_settings->getBool("bilinear_filter");
bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter");
if(m_spritenode)
{
if(m_prop.visual == "sprite")
@@ -1008,6 +1252,58 @@ class GenericCAO : public ClientActiveObject
texturestring += mod;
m_spritenode->setMaterialTexture(0,
tsrc->getTextureRaw(texturestring));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() >= 1)
{
m_spritenode->getMaterial(0).AmbientColor = m_prop.colors[0];
m_spritenode->getMaterial(0).DiffuseColor = m_prop.colors[0];
m_spritenode->getMaterial(0).SpecularColor = m_prop.colors[0];
}
m_spritenode->getMaterial(0).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_spritenode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
m_spritenode->getMaterial(0).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
}
if(m_animated_meshnode)
{
if(m_prop.visual == "mesh")
{
for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i)
{
std::string texturestring = m_prop.textures[i];
if(texturestring == "")
continue; // Empty texture string means don't modify that material
texturestring += mod;
video::ITexture* texture = tsrc->getTextureRaw(texturestring);
if(!texture)
{
errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
continue;
}
// Set material flags and texture
m_animated_meshnode->setMaterialTexture(i, texture);
video::SMaterial& material = m_animated_meshnode->getMaterial(i);
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
m_animated_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_animated_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
m_animated_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
for (u32 i = 0; i < m_prop.colors.size() && i < m_animated_meshnode->getMaterialCount(); ++i)
{
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
}
}
}
if(m_meshnode)
@@ -1034,6 +1330,20 @@ class GenericCAO : public ClientActiveObject
material.setTexture(0, atlas);
material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() > i)
{
m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
}
m_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
m_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
}
else if(m_prop.visual == "upright_sprite")
@@ -1047,6 +1357,20 @@ class GenericCAO : public ClientActiveObject
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
buf->getMaterial().setTexture(0,
tsrc->getTextureRaw(tname));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() >= 1)
{
buf->getMaterial().AmbientColor = m_prop.colors[0];
buf->getMaterial().DiffuseColor = m_prop.colors[0];
buf->getMaterial().SpecularColor = m_prop.colors[0];
}
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
{
std::string tname = "unknown_object.png";
@@ -1058,11 +1382,213 @@ class GenericCAO : public ClientActiveObject
scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
buf->getMaterial().setTexture(0,
tsrc->getTextureRaw(tname));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
// has directional lighting, it should work automatically.
if(m_prop.colors.size() >= 2)
{
buf->getMaterial().AmbientColor = m_prop.colors[1];
buf->getMaterial().DiffuseColor = m_prop.colors[1];
buf->getMaterial().SpecularColor = m_prop.colors[1];
}
else if(m_prop.colors.size() >= 1)
{
buf->getMaterial().AmbientColor = m_prop.colors[0];
buf->getMaterial().DiffuseColor = m_prop.colors[0];
buf->getMaterial().SpecularColor = m_prop.colors[0];
}
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
}
}
}
}
void updateAnimation()
{
if(m_animated_meshnode == NULL)
return;
m_animated_meshnode->setFrameLoop((int)m_animation_range.X, (int)m_animation_range.Y);
m_animated_meshnode->setAnimationSpeed(m_animation_speed);
m_animated_meshnode->setTransitionTime(m_animation_blend);
}
void updateBonePosition()
{
if(!m_bone_position.size() || m_animated_meshnode == NULL)
return;
m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
std::string bone_name = (*ii).first;
v3f bone_pos = (*ii).second.X;
v3f bone_rot = (*ii).second.Y;
irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
if(bone)
{
bone->setPosition(bone_pos);
bone->setRotation(bone_rot);
}
}
}
void updateAttachments()
{
m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer();
m_is_visible = !m_attached_to_local; // Objects attached to the local player should always be hidden
if(getParent() == NULL || m_attached_to_local) // Detach or don't attach
{
if(m_meshnode)
{
v3f old_position = m_meshnode->getAbsolutePosition();
v3f old_rotation = m_meshnode->getRotation();
m_meshnode->setParent(m_smgr->getRootSceneNode());
m_meshnode->setPosition(old_position);
m_meshnode->setRotation(old_rotation);
m_meshnode->updateAbsolutePosition();
}
if(m_animated_meshnode)
{
v3f old_position = m_animated_meshnode->getAbsolutePosition();
v3f old_rotation = m_animated_meshnode->getRotation();
m_animated_meshnode->setParent(m_smgr->getRootSceneNode());
m_animated_meshnode->setPosition(old_position);
m_animated_meshnode->setRotation(old_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
if(m_spritenode)
{
v3f old_position = m_spritenode->getAbsolutePosition();
v3f old_rotation = m_spritenode->getRotation();
m_spritenode->setParent(m_smgr->getRootSceneNode());
m_spritenode->setPosition(old_position);
m_spritenode->setRotation(old_rotation);
m_spritenode->updateAbsolutePosition();
}
if(m_is_local_player)
{
LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = false;
}
}
else // Attach
{
scene::IMeshSceneNode *parent_mesh = NULL;
if(getParent()->getMeshSceneNode())
parent_mesh = getParent()->getMeshSceneNode();
scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL;
if(getParent()->getAnimatedMeshSceneNode())
parent_animated_mesh = getParent()->getAnimatedMeshSceneNode();
scene::IBillboardSceneNode *parent_sprite = NULL;
if(getParent()->getSpriteSceneNode())
parent_sprite = getParent()->getSpriteSceneNode();
scene::IBoneSceneNode *parent_bone = NULL;
if(parent_animated_mesh && m_attachment_bone != "")
parent_bone = parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
// The spaghetti code below makes sure attaching works if either the parent or child is a spritenode, meshnode, or animatedmeshnode
// TODO: Perhaps use polymorphism here to save code duplication
if(m_meshnode){
if(parent_bone){
m_meshnode->setParent(parent_bone);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
else
{
if(parent_mesh){
m_meshnode->setParent(parent_mesh);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
else if(parent_animated_mesh){
m_meshnode->setParent(parent_animated_mesh);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
else if(parent_sprite){
m_meshnode->setParent(parent_sprite);
m_meshnode->setPosition(m_attachment_position);
m_meshnode->setRotation(m_attachment_rotation);
m_meshnode->updateAbsolutePosition();
}
}
}
if(m_animated_meshnode){
if(parent_bone){
m_animated_meshnode->setParent(parent_bone);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
else
{
if(parent_mesh){
m_animated_meshnode->setParent(parent_mesh);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
else if(parent_animated_mesh){
m_animated_meshnode->setParent(parent_animated_mesh);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
else if(parent_sprite){
m_animated_meshnode->setParent(parent_sprite);
m_animated_meshnode->setPosition(m_attachment_position);
m_animated_meshnode->setRotation(m_attachment_rotation);
m_animated_meshnode->updateAbsolutePosition();
}
}
}
if(m_spritenode){
if(parent_bone){
m_spritenode->setParent(parent_bone);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
else
{
if(parent_mesh){
m_spritenode->setParent(parent_mesh);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
else if(parent_animated_mesh){
m_spritenode->setParent(parent_animated_mesh);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
else if(parent_sprite){
m_spritenode->setParent(parent_sprite);
m_spritenode->setPosition(m_attachment_position);
m_spritenode->setRotation(m_attachment_rotation);
m_spritenode->updateAbsolutePosition();
}
}
}
if(m_is_local_player)
{
LocalPlayer *player = m_env->getLocalPlayer();
player->isAttached = true;
}
}
}
void processMessage(const std::string &data)
{
//infostream<<"GenericCAO: Got message"<<std::endl;
@@ -1089,6 +1615,8 @@ class GenericCAO : public ClientActiveObject
}
else if(cmd == GENERIC_CMD_UPDATE_POSITION)
{
// Not sent by the server if this object is an attachment.
// We might however get here if the server notices the object being detached before the client.
m_position = readV3F1000(is);
m_velocity = readV3F1000(is);
m_acceleration = readV3F1000(is);
@@ -1102,7 +1630,10 @@ class GenericCAO : public ClientActiveObject
// the ground due to sucky collision detection...
if(m_prop.physical)
m_position += v3f(0,0.002,0);
if(getParent() != NULL) // Just in case
return;
if(do_interpolate){
if(!m_prop.physical)
pos_translator.update(m_position, is_end_position, update_interval);
@@ -1130,6 +1661,41 @@ class GenericCAO : public ClientActiveObject
updateTexturePos();
}
else if(cmd == GENERIC_CMD_SET_ANIMATION)
{
m_animation_range = readV2F1000(is);
m_animation_speed = readF1000(is);
m_animation_blend = readF1000(is);
updateAnimation();
}
else if(cmd == GENERIC_CMD_SET_BONE_POSITION)
{
std::string bone = deSerializeString(is);
v3f position = readV3F1000(is);
v3f rotation = readV3F1000(is);
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
updateBonePosition();
}
else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
{
// If an entry already exists for this object, delete it first to avoid duplicates
for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
{
if(ii->X == getId()) // This is the ID of our object
{
m_env->attachment_list.erase(ii);
break;
}
}
m_env->attachment_list.push_back(core::vector2d<int>(getId(), readS16(is)));
m_attachment_bone = deSerializeString(is);
m_attachment_position = readV3F1000(is);
m_attachment_rotation = readV3F1000(is);
updateAttachments();
}
else if(cmd == GENERIC_CMD_PUNCHED)
{
/*s16 damage =*/ readS16(is);

View File

@@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tile.h"
#include "gamedef.h"
#include "util/numeric.h"
#include "util/serialize.h"
#include "util/directiontables.h"
// Create a cuboid.
@@ -172,7 +171,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
continue;
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(f.alpha, l);
video::SColor c = MapBlock_LightColor(f.alpha, l, decode_light(f.light_source));
video::S3DVertex vertices[4] =
{
@@ -227,7 +226,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// Otherwise use the light of this node (the liquid)
else
l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(f.alpha, l);
video::SColor c = MapBlock_LightColor(f.alpha, l, decode_light(f.light_source));
// Neighbor liquid levels (key = relative position)
// Includes current node
@@ -492,15 +491,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// -Z towards +Z, thus the direction is +Z.
// Rotate texture to make animation go in flow direction
// Positive if liquid moves towards +Z
int dz = (corner_levels[side_corners[2][0]] +
corner_levels[side_corners[2][1]] <
corner_levels[side_corners[3][0]] +
corner_levels[side_corners[3][1]]);
int dz = (corner_levels[side_corners[3][0]] +
corner_levels[side_corners[3][1]]) -
(corner_levels[side_corners[2][0]] +
corner_levels[side_corners[2][1]]);
// Positive if liquid moves towards +X
int dx = (corner_levels[side_corners[0][0]] +
corner_levels[side_corners[0][1]] <
corner_levels[side_corners[1][0]] +
corner_levels[side_corners[1][1]]);
int dx = (corner_levels[side_corners[1][0]] +
corner_levels[side_corners[1][1]]) -
(corner_levels[side_corners[0][0]] +
corner_levels[side_corners[0][1]]);
// -X
if(-dx >= abs(dz))
{
@@ -545,7 +544,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
for(u32 j=0; j<6; j++)
{
@@ -605,7 +604,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
AtlasPointer pa_leaves = tile_leaves.texture;
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
v3f pos = intToFloat(p, BS);
aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
@@ -639,7 +638,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
AtlasPointer ap = tile.texture;
video::SColor c(255,255,255,255);
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
// Wall at X+ of node
video::S3DVertex vertices[4] =
@@ -684,7 +684,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
float d = (float)BS/16;
// Wall at X+ of node
@@ -731,7 +731,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
for(u32 j=0; j<4; j++)
{
@@ -794,7 +794,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
tile.texture.id) + "^[transformR90");
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
const f32 post_rad=(f32)BS/8;
const f32 bar_rad=(f32)BS/16;
@@ -997,7 +997,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
float d = (float)BS/64;
@@ -1046,7 +1046,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
}
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
v3f pos = intToFloat(p, BS);

View File

@@ -245,7 +245,7 @@ class ItemSAO : public ServerActiveObject
}
}
std::string getClientInitializationData()
std::string getClientInitializationData(u16 protocol_version)
{
std::ostringstream os(std::ios::binary);
// version
@@ -355,7 +355,10 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
m_last_sent_velocity(0,0,0),
m_last_sent_position_timer(0),
m_last_sent_move_precision(0),
m_armor_groups_sent(false)
m_armor_groups_sent(false),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_sent(false)
{
// Only register type if no environment supplied
if(env == NULL){
@@ -376,9 +379,9 @@ LuaEntitySAO::~LuaEntitySAO()
}
}
void LuaEntitySAO::addedToEnvironment()
void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
{
ServerActiveObject::addedToEnvironment();
ServerActiveObject::addedToEnvironment(dtime_s);
// Create entity from name
lua_State *L = m_env->getLua();
@@ -390,7 +393,7 @@ void LuaEntitySAO::addedToEnvironment()
// Initialize HP from properties
m_hp = m_prop.hp_max;
// Activate entity, supplying serialized state
scriptapi_luaentity_activate(L, m_id, m_init_state.c_str());
scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s);
}
}
@@ -429,6 +432,17 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
return sao;
}
bool LuaEntitySAO::isAttached()
{
if(!m_attachment_parent_id)
return false;
// Check if the parent still exists
ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
if(obj)
return true;
return false;
}
void LuaEntitySAO::step(float dtime, bool send_recommended)
{
if(!m_properties_sent)
@@ -440,30 +454,52 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_messages_out.push_back(aom);
}
// If attached, check that our parent is still there. If it isn't, detach.
if(m_attachment_parent_id && !isAttached())
{
m_attachment_parent_id = 0;
m_attachment_bone = "";
m_attachment_position = v3f(0,0,0);
m_attachment_rotation = v3f(0,0,0);
sendPosition(false, true);
}
m_last_sent_position_timer += dtime;
if(m_prop.physical){
core::aabbox3d<f32> box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
f32 pos_max_d = BS*0.25; // Distance per iteration
f32 stepheight = 0; // Maximum climbable step height
v3f p_pos = m_base_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_base_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
} else {
m_base_position += dtime * m_velocity + 0.5 * dtime
* dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
// Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
// If the object gets detached this comes into effect automatically from the last known origin
if(isAttached())
{
v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
m_base_position = pos;
m_velocity = v3f(0,0,0);
m_acceleration = v3f(0,0,0);
}
else
{
if(m_prop.physical){
core::aabbox3d<f32> box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
f32 pos_max_d = BS*0.25; // Distance per iteration
f32 stepheight = 0; // Maximum climbable step height
v3f p_pos = m_base_position;
v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results
m_base_position = p_pos;
m_velocity = p_velocity;
m_acceleration = p_acceleration;
} else {
m_base_position += dtime * m_velocity + 0.5 * dtime
* dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
}
}
if(m_registered){
@@ -473,20 +509,23 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
if(send_recommended == false)
return;
// TODO: force send when acceleration changes enough?
float minchange = 0.2*BS;
if(m_last_sent_position_timer > 1.0){
minchange = 0.01*BS;
} else if(m_last_sent_position_timer > 0.2){
minchange = 0.05*BS;
}
float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
move_d += m_last_sent_move_precision;
float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
if(move_d > minchange || vel_d > minchange ||
fabs(m_yaw - m_last_sent_yaw) > 1.0){
sendPosition(true, false);
if(!isAttached())
{
// TODO: force send when acceleration changes enough?
float minchange = 0.2*BS;
if(m_last_sent_position_timer > 1.0){
minchange = 0.01*BS;
} else if(m_last_sent_position_timer > 0.2){
minchange = 0.05*BS;
}
float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
move_d += m_last_sent_move_precision;
float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
if(move_d > minchange || vel_d > minchange ||
fabs(m_yaw - m_last_sent_yaw) > 1.0){
sendPosition(true, false);
}
}
if(m_armor_groups_sent == false){
@@ -497,20 +536,70 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
if(m_animation_sent == false){
m_animation_sent = true;
std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
if(m_bone_position_sent == false){
m_bone_position_sent = true;
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
}
if(m_attachment_sent == false){
m_attachment_sent = true;
std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
}
std::string LuaEntitySAO::getClientInitializationData()
std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
{
std::ostringstream os(std::ios::binary);
writeU8(os, 0); // version
os<<serializeString(""); // name
writeU8(os, 0); // is_player
writeV3F1000(os, m_base_position);
writeF1000(os, m_yaw);
writeS16(os, m_hp);
writeU8(os, 2); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
if(protocol_version >= 14)
{
writeU8(os, 1); // version
os<<serializeString(""); // name
writeU8(os, 0); // is_player
writeS16(os, getId()); //id
writeV3F1000(os, m_base_position);
writeF1000(os, m_yaw);
writeS16(os, m_hp);
writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
}
os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
}
else
{
writeU8(os, 0); // version
os<<serializeString(""); // name
writeU8(os, 0); // is_player
writeV3F1000(os, m_base_position);
writeF1000(os, m_yaw);
writeS16(os, m_hp);
writeU8(os, 2); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
}
// return result
return os.str();
}
@@ -550,6 +639,10 @@ int LuaEntitySAO::punch(v3f dir,
m_removed = true;
return 0;
}
// It's best that attachments cannot be punched
if(isAttached())
return 0;
ItemStack *punchitem = NULL;
ItemStack punchitem_static;
@@ -594,18 +687,25 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
{
if(!m_registered)
return;
// It's best that attachments cannot be clicked
if(isAttached())
return;
lua_State *L = m_env->getLua();
scriptapi_luaentity_rightclick(L, m_id, clicker);
}
void LuaEntitySAO::setPos(v3f pos)
{
if(isAttached())
return;
m_base_position = pos;
sendPosition(false, true);
}
void LuaEntitySAO::moveTo(v3f pos, bool continuous)
{
if(isAttached())
return;
m_base_position = pos;
if(!continuous)
sendPosition(true, true);
@@ -644,6 +744,37 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
m_armor_groups_sent = false;
}
void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
{
m_animation_range = frame_range;
m_animation_speed = frame_speed;
m_animation_blend = frame_blend;
m_animation_sent = false;
}
void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
{
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
m_bone_position_sent = false;
}
void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. Attachments
// are still sent to clients at an interval so players might see them lagging, plus we can't
// read and attach to skeletal bones.
// If we just attach on the client, the server still sees the child at its original location.
// This breaks some things so we also give the server the most accurate representation
// even if players only see the client changes.
m_attachment_parent_id = parent_id;
m_attachment_bone = bone;
m_attachment_position = position;
m_attachment_rotation = rotation;
m_attachment_sent = false;
}
ObjectProperties* LuaEntitySAO::accessObjectProperties()
{
return &m_prop;
@@ -718,6 +849,10 @@ std::string LuaEntitySAO::getPropertyPacket()
void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
{
// If the object is attached client-side, don't waste bandwidth sending its position to clients
if(isAttached())
return;
m_last_sent_move_precision = m_base_position.getDistanceFrom(
m_last_sent_position);
m_last_sent_position_timer = 0;
@@ -765,8 +900,11 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_properties_sent(true),
m_privs(privs),
m_is_singleplayer(is_singleplayer),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_sent(false),
// public
m_teleported(false),
m_moved(false),
m_inventory_not_sent(false),
m_hp_not_sent(false),
m_wielded_item_not_sent(false)
@@ -782,13 +920,17 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_prop.physical = false;
m_prop.weight = 75;
m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
// start of default appearance, this should be overwritten by LUA
m_prop.visual = "upright_sprite";
m_prop.visual_size = v2f(1, 2);
m_prop.textures.clear();
m_prop.textures.push_back("player.png");
m_prop.textures.push_back("player_back.png");
m_prop.colors.clear();
m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
m_prop.spritediv = v2s16(1,1);
m_prop.is_visible = (getHP() != 0);
// end of default appearance
m_prop.is_visible = true;
m_prop.makes_footstep_sound = true;
}
@@ -805,9 +947,9 @@ std::string PlayerSAO::getDescription()
}
// Called after id has been set and has been inserted in environment
void PlayerSAO::addedToEnvironment()
void PlayerSAO::addedToEnvironment(u32 dtime_s)
{
ServerActiveObject::addedToEnvironment();
ServerActiveObject::addedToEnvironment(dtime_s);
ServerActiveObject::setBasePosition(m_player->getPosition());
m_player->setPlayerSAO(this);
m_player->peer_id = m_peer_id;
@@ -836,18 +978,43 @@ bool PlayerSAO::unlimitedTransferDistance() const
return g_settings->getBool("unlimited_player_transfer_distance");
}
std::string PlayerSAO::getClientInitializationData()
std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
{
std::ostringstream os(std::ios::binary);
writeU8(os, 0); // version
os<<serializeString(m_player->getName()); // name
writeU8(os, 1); // is_player
writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
writeF1000(os, m_player->getYaw());
writeS16(os, getHP());
writeU8(os, 2); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
if(protocol_version >= 15)
{
writeU8(os, 1); // version
os<<serializeString(m_player->getName()); // name
writeU8(os, 1); // is_player
writeS16(os, getId()); //id
writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
writeF1000(os, m_player->getYaw());
writeS16(os, getHP());
writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
}
os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
}
else
{
writeU8(os, 0); // version
os<<serializeString(m_player->getName()); // name
writeU8(os, 1); // is_player
writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
writeF1000(os, m_player->getYaw());
writeS16(os, getHP());
writeU8(os, 2); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
}
// return result
return os.str();
}
@@ -857,6 +1024,17 @@ std::string PlayerSAO::getStaticData()
return "";
}
bool PlayerSAO::isAttached()
{
if(!m_attachment_parent_id)
return false;
// Check if the parent still exists
ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
if(obj)
return true;
return false;
}
void PlayerSAO::step(float dtime, bool send_recommended)
{
if(!m_properties_sent)
@@ -868,73 +1046,102 @@ void PlayerSAO::step(float dtime, bool send_recommended)
m_messages_out.push_back(aom);
}
// If attached, check that our parent is still there. If it isn't, detach.
if(m_attachment_parent_id && !isAttached())
{
m_attachment_parent_id = 0;
m_attachment_bone = "";
m_attachment_position = v3f(0,0,0);
m_attachment_rotation = v3f(0,0,0);
m_player->setPosition(m_last_good_position);
m_moved = true;
}
m_time_from_last_punch += dtime;
m_nocheat_dig_time += dtime;
if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
// Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
// If the object gets detached this comes into effect automatically from the last known origin
if(isAttached())
{
m_last_good_position = m_player->getPosition();
v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
m_last_good_position = pos;
m_last_good_position_age = 0;
m_player->setPosition(pos);
}
else
{
/*
Check player movements
NOTE: Actually the server should handle player physics like the
client does and compare player's position to what is calculated
on our side. This is required when eg. players fly due to an
explosion. Altough a node-based alternative might be possible
too, and much more lightweight.
*/
float player_max_speed = 0;
float player_max_speed_up = 0;
if(m_privs.count("fast") != 0){
// Fast speed
player_max_speed = BS * 20;
player_max_speed_up = BS * 20;
} else {
// Normal speed
player_max_speed = BS * 4.0;
player_max_speed_up = BS * 4.0;
}
// Tolerance
player_max_speed *= 2.5;
player_max_speed_up *= 2.5;
m_last_good_position_age += dtime;
if(m_last_good_position_age >= 1.0){
float age = m_last_good_position_age;
v3f diff = (m_player->getPosition() - m_last_good_position);
float d_vert = diff.Y;
diff.Y = 0;
float d_horiz = diff.getLength();
/*infostream<<m_player->getName()<<"'s horizontal speed is "
<<(d_horiz/age)<<std::endl;*/
if(d_horiz <= age * player_max_speed &&
(d_vert < 0 || d_vert < age * player_max_speed_up)){
m_last_good_position = m_player->getPosition();
} else {
actionstream<<"Player "<<m_player->getName()
<<" moved too fast; resetting position"
<<std::endl;
m_player->setPosition(m_last_good_position);
m_teleported = true;
}
if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
{
m_last_good_position = m_player->getPosition();
m_last_good_position_age = 0;
}
else
{
/*
Check player movements
NOTE: Actually the server should handle player physics like the
client does and compare player's position to what is calculated
on our side. This is required when eg. players fly due to an
explosion. Altough a node-based alternative might be possible
too, and much more lightweight.
*/
float player_max_speed = 0;
float player_max_speed_up = 0;
if(m_privs.count("fast") != 0){
// Fast speed
player_max_speed = BS * 20;
player_max_speed_up = BS * 20;
} else {
// Normal speed
player_max_speed = BS * 4.0;
player_max_speed_up = BS * 4.0;
}
// Tolerance
player_max_speed *= 2.5;
player_max_speed_up *= 2.5;
m_last_good_position_age += dtime;
if(m_last_good_position_age >= 1.0){
float age = m_last_good_position_age;
v3f diff = (m_player->getPosition() - m_last_good_position);
float d_vert = diff.Y;
diff.Y = 0;
float d_horiz = diff.getLength();
/*infostream<<m_player->getName()<<"'s horizontal speed is "
<<(d_horiz/age)<<std::endl;*/
if(d_horiz <= age * player_max_speed &&
(d_vert < 0 || d_vert < age * player_max_speed_up)){
m_last_good_position = m_player->getPosition();
} else {
actionstream<<"Player "<<m_player->getName()
<<" moved too fast; resetting position"
<<std::endl;
m_player->setPosition(m_last_good_position);
m_moved = true;
}
m_last_good_position_age = 0;
}
}
}
if(send_recommended == false)
return;
if(m_position_not_sent)
// If the object is attached client-side, don't waste bandwidth sending its position to clients
if(m_position_not_sent && !isAttached())
{
m_position_not_sent = false;
float update_interval = m_env->getSendRecommendedInterval();
v3f pos;
if(isAttached()) // Just in case we ever do send attachment position too
pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
else
pos = m_player->getPosition() + v3f(0,BS*1,0);
std::string str = gob_cmd_update_position(
m_player->getPosition() + v3f(0,BS*1,0),
pos,
v3f(0,0,0),
v3f(0,0,0),
m_player->getYaw(),
@@ -961,32 +1168,63 @@ void PlayerSAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
if(m_animation_sent == false){
m_animation_sent = true;
std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
if(m_bone_position_sent == false){
m_bone_position_sent = true;
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
}
if(m_attachment_sent == false){
m_attachment_sent = true;
std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push_back(aom);
}
}
void PlayerSAO::setBasePosition(const v3f &position)
{
// This needs to be ran for attachments too
ServerActiveObject::setBasePosition(position);
m_position_not_sent = true;
}
void PlayerSAO::setPos(v3f pos)
{
if(isAttached())
return;
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
m_last_good_position_age = 0;
// Force position change on client
m_teleported = true;
m_moved = true;
}
void PlayerSAO::moveTo(v3f pos, bool continuous)
{
if(isAttached())
return;
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
m_last_good_position_age = 0;
// Force position change on client
m_teleported = true;
m_moved = true;
}
int PlayerSAO::punch(v3f dir,
@@ -994,6 +1232,10 @@ int PlayerSAO::punch(v3f dir,
ServerActiveObject *puncher,
float time_from_last_punch)
{
// It's best that attachments cannot be punched
if(isAttached())
return 0;
if(!toolcap)
return 0;
@@ -1075,6 +1317,39 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
m_armor_groups_sent = false;
}
void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
{
// store these so they can be updated to clients
m_animation_range = frame_range;
m_animation_speed = frame_speed;
m_animation_blend = frame_blend;
m_animation_sent = false;
}
void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
{
// store these so they can be updated to clients
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
m_bone_position_sent = false;
}
void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. Attachments
// are still sent to clients at an interval so players might see them lagging, plus we can't
// read and attach to skeletal bones.
// If we just attach on the client, the server still sees the child at its original location.
// This breaks some things so we also give the server the most accurate representation
// even if players only see the client changes.
m_attachment_parent_id = parent_id;
m_attachment_bone = bone;
m_attachment_position = position;
m_attachment_rotation = rotation;
m_attachment_sent = false;
}
ObjectProperties* PlayerSAO::accessObjectProperties()
{
return &m_prop;
@@ -1138,7 +1413,7 @@ void PlayerSAO::disconnected()
std::string PlayerSAO::getPropertyPacket()
{
m_prop.is_visible = (getHP() != 0);
m_prop.is_visible = (true);
return gob_cmd_set_properties(m_prop);
}

View File

@@ -43,11 +43,12 @@ class LuaEntitySAO : public ServerActiveObject
{ return ACTIVEOBJECT_TYPE_LUAENTITY; }
u8 getSendType() const
{ return ACTIVEOBJECT_TYPE_GENERIC; }
virtual void addedToEnvironment();
virtual void addedToEnvironment(u32 dtime_s);
static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
const std::string &data);
bool isAttached();
void step(float dtime, bool send_recommended);
std::string getClientInitializationData();
std::string getClientInitializationData(u16 protocol_version);
std::string getStaticData();
int punch(v3f dir,
const ToolCapabilities *toolcap=NULL,
@@ -61,6 +62,9 @@ class LuaEntitySAO : public ServerActiveObject
void setHP(s16 hp);
s16 getHP() const;
void setArmorGroups(const ItemGroupList &armor_groups);
void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
void setBonePosition(std::string bone, v3f position, v3f rotation);
void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified();
/* LuaEntitySAO-specific */
@@ -96,6 +100,20 @@ class LuaEntitySAO : public ServerActiveObject
float m_last_sent_position_timer;
float m_last_sent_move_precision;
bool m_armor_groups_sent;
v2f m_animation_range;
float m_animation_speed;
float m_animation_blend;
bool m_animation_sent;
std::map<std::string, core::vector2d<v3f> > m_bone_position;
bool m_bone_position_sent;
int m_attachment_parent_id;
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attachment_sent;
};
/*
@@ -118,12 +136,13 @@ class PlayerSAO : public ServerActiveObject
Active object <-> environment interface
*/
void addedToEnvironment();
void addedToEnvironment(u32 dtime_s);
void removingFromEnvironment();
bool isStaticAllowed() const;
bool unlimitedTransferDistance() const;
std::string getClientInitializationData();
std::string getClientInitializationData(u16 protocol_version);
std::string getStaticData();
bool isAttached();
void step(float dtime, bool send_recommended);
void setBasePosition(const v3f &position);
void setPos(v3f pos);
@@ -142,6 +161,9 @@ class PlayerSAO : public ServerActiveObject
void setHP(s16 hp);
void setArmorGroups(const ItemGroupList &armor_groups);
void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
void setBonePosition(std::string bone, v3f position, v3f rotation);
void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified();
@@ -229,15 +251,32 @@ class PlayerSAO : public ServerActiveObject
bool m_position_not_sent;
ItemGroupList m_armor_groups;
bool m_armor_groups_sent;
bool m_properties_sent;
struct ObjectProperties m_prop;
// Cached privileges for enforcement
std::set<std::string> m_privs;
bool m_is_singleplayer;
v2f m_animation_range;
float m_animation_speed;
float m_animation_blend;
bool m_animation_sent;
std::map<std::string, core::vector2d<v3f> > m_bone_position; // Stores position and rotation for each bone name
bool m_bone_position_sent;
int m_attachment_parent_id;
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attachment_sent;
public:
// Some flags used by Server
bool m_teleported;
bool m_moved;
bool m_inventory_not_sent;
bool m_hp_not_sent;
bool m_wielded_item_not_sent;

View File

@@ -20,23 +20,49 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef DAYNIGHTRATIO_HEADER
#define DAYNIGHTRATIO_HEADER
inline u32 time_to_daynight_ratio(u32 time_of_day)
inline u32 time_to_daynight_ratio(float time_of_day, bool smooth)
{
s32 t = time_of_day%24000;
if(t < 4500 || t >= 19500)
return 150;
else if(t < 4750 || t >= 19250)
return 250;
else if(t < 5000 || t >= 19000)
return 350;
else if(t < 5250 || t >= 18750)
return 500;
else if(t < 5500 || t >= 18500)
return 675;
else if(t < 5750 || t >= 18250)
return 875;
else
float t = time_of_day;
if(t < 0)
t += ((int)(-t)/24000)*24000;
if(t >= 24000)
t -= ((int)(t)/24000)*24000;
if(t > 12000)
t = 24000 - t;
float values[][2] = {
{4250+125, 150},
{4500+125, 150},
{4750+125, 250},
{5000+125, 350},
{5250+125, 500},
{5500+125, 675},
{5750+125, 875},
{6000+125, 1000},
{6250+125, 1000},
};
if(!smooth){
float lastt = values[0][0];
for(u32 i=1; i<sizeof(values)/sizeof(*values); i++){
float t0 = values[i][0];
float switch_t = (t0 + lastt) / 2;
lastt = t0;
if(switch_t <= t)
continue;
return values[i][1];
}
return 1000;
} else {
for(u32 i=0; i<sizeof(values)/sizeof(*values); i++){
if(values[i][0] <= t)
continue;
if(i == 0)
return values[i][1];
float td0 = values[i][0] - values[i-1][0];
float f = (t - values[i-1][0]) / td0;
return f * values[i][1] + (1.0 - f) * values[i-1][1];
}
return 1000;
}
}
#endif

View File

@@ -52,7 +52,10 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_toggle_profiler", "KEY_F6");
settings->setDefault("keymap_increase_viewing_range_min", "+");
settings->setDefault("keymap_decrease_viewing_range_min", "-");
settings->setDefault("anaglyph", "false");
settings->setDefault("anaglyph_strength", "0.1");
settings->setDefault("aux1_descends", "false");
// Some (temporary) keys for debugging
settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
settings->setDefault("keymap_quicktune_prev", "KEY_HOME");
@@ -70,7 +73,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("wanted_fps", "30");
settings->setDefault("fps_max", "60");
// A bit more than the server will send around the player, to make fog blend well
settings->setDefault("viewing_range_nodes_max", "160");
settings->setDefault("viewing_range_nodes_max", "240");
settings->setDefault("viewing_range_nodes_min", "35");
settings->setDefault("screenW", "800");
settings->setDefault("screenH", "600");
@@ -89,6 +92,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("smooth_lighting", "true");
settings->setDefault("enable_texture_atlas", "true");
settings->setDefault("texture_path", "");
settings->setDefault("shader_path", "");
settings->setDefault("video_driver", "opengl");
settings->setDefault("free_move", "false");
settings->setDefault("continuous_forward", "false");
@@ -106,12 +110,19 @@ void set_default_settings(Settings *settings)
settings->setDefault("sound_volume", "0.8");
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
settings->setDefault("mip_map", "false");
settings->setDefault("anisotropic_filter", "false");
settings->setDefault("bilinear_filter", "false");
settings->setDefault("trilinear_filter", "false");
settings->setDefault("preload_item_visuals", "true");
settings->setDefault("enable_shaders", "2");
// Server stuff
// "map-dir" doesn't exist by default.
settings->setDefault("default_game", "minetest");
settings->setDefault("motd", "");
settings->setDefault("max_users", "100");
settings->setDefault("strict_protocol_version_checking", "true");
settings->setDefault("strict_protocol_version_checking", "false");
settings->setDefault("creative_mode", "false");
settings->setDefault("enable_damage", "true");
settings->setDefault("only_peaceful_mobs", "false");
@@ -140,7 +151,10 @@ void set_default_settings(Settings *settings)
settings->setDefault("server_unload_unused_data_timeout", "29");
settings->setDefault("server_map_save_interval", "5.3");
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
settings->setDefault("dedicated_server_step", "0.05");
settings->setDefault("dedicated_server_step", "0.1");
settings->setDefault("ignore_world_load_errors", "false");
settings->setDefault("congestion_control_aim_rtt", "0.2");
settings->setDefault("congestion_control_max_rate", "400");
settings->setDefault("congestion_control_min_rate", "10");
}

View File

@@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif
#include "daynightratio.h"
#include "map.h"
#include "util/serialize.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -202,7 +203,8 @@ void Environment::printPlayers(std::ostream &o)
u32 Environment::getDayNightRatio()
{
return time_to_daynight_ratio(m_time_of_day);
bool smooth = (g_settings->getS32("enable_shaders") != 0);
return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
}
void Environment::stepTimeOfDay(float dtime)
@@ -328,7 +330,8 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
m_send_recommended_timer(0),
m_active_block_interval_overload_skip(0),
m_game_time(0),
m_game_time_fraction_counter(0)
m_game_time_fraction_counter(0),
m_recommended_send_interval(0.1)
{
}
@@ -743,19 +746,25 @@ class ABMHandler
u32 active_object_count = block->m_static_objects.m_active.size();
// Find out how many objects this and all the neighbors contain
u32 active_object_count_wider = 0;
u32 wider_unknown_count = 0;
for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)
for(s16 z=-1; z<=1; z++)
{
MapBlock *block2 = map->getBlockNoCreateNoEx(
block->getPos() + v3s16(x,y,z));
if(block2==NULL)
if(block2==NULL){
wider_unknown_count = 0;
continue;
}
active_object_count_wider +=
block2->m_static_objects.m_active.size()
+ block2->m_static_objects.m_stored.size();
}
// Extrapolate
u32 wider_known_count = 3*3*3 - wider_unknown_count;
active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
// Call all the trigger variations
i->abm->trigger(m_env, p, n);
i->abm->trigger(m_env, p, n,
@@ -784,7 +793,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
<<dtime_s<<" seconds old."<<std::endl;*/
// Activate stored objects
activateObjects(block);
activateObjects(block, dtime_s);
// Run node timers
std::map<v3s16, NodeTimer> elapsed_timers =
@@ -933,6 +942,11 @@ void ServerEnvironment::step(float dtime)
/* Step time of day */
stepTimeOfDay(dtime);
// Update this one
// NOTE: This is kind of funny on a singleplayer game, but doesn't
// really matter that much.
m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
/*
Increment game time
*/
@@ -1243,7 +1257,7 @@ u16 getFreeServerActiveObjectId(
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
{
assert(object);
u16 id = addActiveObjectRaw(object, true);
u16 id = addActiveObjectRaw(object, true, 0);
return id;
}
@@ -1402,7 +1416,7 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
*/
u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
bool set_changed)
bool set_changed, u32 dtime_s)
{
assert(object);
if(object->getId() == 0){
@@ -1442,7 +1456,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
// Register reference in scripting api (must be done before post-init)
scriptapi_add_object_reference(m_lua, object);
// Post-initialize object
object->addedToEnvironment();
object->addedToEnvironment(dtime_s);
// Add static data to block
if(object->isStaticAllowed())
@@ -1465,9 +1479,10 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
"addActiveObjectRaw");
}
else{
v3s16 p = floatToInt(objectpos, BS);
errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"could not find block for storing id="<<object->getId()
<<" statically"<<std::endl;
<<" statically (pos="<<PP(p)<<")"<<std::endl;
}
}
@@ -1578,7 +1593,7 @@ static void print_hexdump(std::ostream &o, const std::string &data)
/*
Convert stored objects from blocks near the players to active.
*/
void ServerEnvironment::activateObjects(MapBlock *block)
void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
{
if(block==NULL)
return;
@@ -1602,7 +1617,7 @@ void ServerEnvironment::activateObjects(MapBlock *block)
"large amount of objects");
return;
}
// A list for objects that couldn't be converted to static for some
// A list for objects that couldn't be converted to active for some
// reason. They will be stored back.
core::list<StaticObject> new_stored;
// Loop through stored static objects
@@ -1632,7 +1647,7 @@ void ServerEnvironment::activateObjects(MapBlock *block)
<<"activated static object pos="<<PP(s_obj.pos/BS)
<<" type="<<(int)s_obj.type<<std::endl;
// This will also add the object to the active static list
addActiveObjectRaw(obj, false);
addActiveObjectRaw(obj, false, dtime_s);
}
// Clear stored list
block->m_static_objects.m_stored.clear();
@@ -1756,7 +1771,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
// Get or generate the block
MapBlock *block = m_map->emergeBlock(blockpos);
MapBlock *block = NULL;
try{
block = m_map->emergeBlock(blockpos);
} catch(InvalidPositionException &e){
// Handled via NULL pointer
}
if(block)
{
@@ -1793,9 +1813,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
}
else{
if(!force_delete){
v3s16 p = floatToInt(objectpos, BS);
errorstream<<"ServerEnv: Could not find or generate "
<<"a block for storing id="<<obj->getId()
<<" statically"<<std::endl;
<<" statically (pos="<<PP(p)<<")"<<std::endl;
continue;
}
}
@@ -2112,15 +2133,15 @@ void ClientEnvironment::step(float dtime)
}
// Update lighting on all players on client
u8 light = LIGHT_MAX;
float light = 1.0;
try{
// Get node at head
v3s16 p = player->getLightPosition();
MapNode n = m_map->getNode(p);
light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
}
catch(InvalidPositionException &e){
light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
}
player->light = light;
}
@@ -2129,6 +2150,7 @@ void ClientEnvironment::step(float dtime)
Step active objects and update lighting of them
*/
bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
for(core::map<u16, ClientActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
@@ -2137,7 +2159,7 @@ void ClientEnvironment::step(float dtime)
// Step object
obj->step(dtime, this);
if(m_active_object_light_update_interval.step(dtime, 0.21))
if(update_lighting)
{
// Update lighting
u8 light = 0;
@@ -2282,8 +2304,9 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
{
errorstream<<"ClientEnvironment::addActiveObject():"
<<" id="<<id<<" type="<<type
<<": SerializationError in initialize(),"
<<" init_data="<<serializeJsonString(init_data)
<<": SerializationError in initialize(): "
<<e.what()
<<": init_data="<<serializeJsonString(init_data)
<<std::endl;
}
@@ -2301,7 +2324,7 @@ void ClientEnvironment::removeActiveObject(u16 id)
<<"id="<<id<<" not found"<<std::endl;
return;
}
obj->removeFromScene();
obj->removeFromScene(true);
delete obj;
m_active_objects.remove(id);
}

View File

@@ -205,9 +205,7 @@ class ServerEnvironment : public Environment
{ return m_gamedef; }
float getSendRecommendedInterval()
{
return 0.10;
}
{ return m_recommended_send_interval; }
/*
Save players
@@ -312,7 +310,7 @@ class ServerEnvironment : public Environment
Returns the id of the object.
Returns 0 if not added and thus deleted.
*/
u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed);
u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s);
/*
Remove all objects that satisfy (m_removed && m_known_by_count==0)
@@ -322,7 +320,7 @@ class ServerEnvironment : public Environment
/*
Convert stored objects from block to active
*/
void activateObjects(MapBlock *block);
void activateObjects(MapBlock *block, u32 dtime_s);
/*
Convert objects that are not in active blocks to static.
@@ -367,6 +365,8 @@ class ServerEnvironment : public Environment
// A helper variable for incrementing the latter
float m_game_time_fraction_counter;
core::list<ABMWithState> m_abms;
// An interval for generally sending object positions and stuff
float m_recommended_send_interval;
};
#ifndef SERVER
@@ -463,6 +463,8 @@ class ClientEnvironment : public Environment
// Get event from queue. CEE_NONE is returned if queue is empty.
ClientEnvEvent getClientEvent();
std::vector<core::vector2d<int> > attachment_list; // X is child ID, Y is parent ID
private:
ClientMap *m_map;

View File

@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include <IMaterialRendererServices.h>
#include "client.h"
#include "server.h"
#include "guiPauseMenu.h"
@@ -50,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" // For g_settings
#include "itemdef.h"
#include "tile.h" // For TextureSource
#include "shader.h" // For ShaderSource
#include "logoutputbuffer.h"
#include "subgame.h"
#include "quicktune_shortcutter.h"
@@ -834,6 +836,53 @@ class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
}
};
class GameGlobalShaderConstantSetter : public IShaderConstantSetter
{
Sky *m_sky;
bool *m_force_fog_off;
f32 *m_fog_range;
Client *m_client;
public:
GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
f32 *fog_range, Client *client):
m_sky(sky),
m_force_fog_off(force_fog_off),
m_fog_range(fog_range),
m_client(client)
{}
~GameGlobalShaderConstantSetter() {}
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel)
{
if(!is_highlevel)
return;
// Background color
video::SColor bgcolor = m_sky->getBgColor();
video::SColorf bgcolorf(bgcolor);
float bgcolorfa[4] = {
bgcolorf.r,
bgcolorf.g,
bgcolorf.b,
bgcolorf.a,
};
services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4);
// Fog distance
float fog_distance = *m_fog_range;
if(*m_force_fog_off)
fog_distance = 10000*BS;
services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
// Day-night ratio
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
float daynight_ratio_f = (float)daynight_ratio / 1000.0;
services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1);
}
};
void the_game(
bool &kill,
bool random_input,
@@ -876,6 +925,9 @@ void the_game(
// Create texture source
IWritableTextureSource *tsrc = createTextureSource(device);
// Create shader source
IWritableShaderSource *shsrc = createShaderSource(device);
// These will be filled by data received from the server
// Create item definition manager
IWritableItemDefManager *itemdef = createItemDefManager();
@@ -943,7 +995,7 @@ void the_game(
MapDrawControl draw_control;
Client client(device, playername.c_str(), password, draw_control,
tsrc, itemdef, nodedef, sound, &eventmgr);
tsrc, shsrc, itemdef, nodedef, sound, &eventmgr);
// Client acts as our GameDef
IGameDef *gamedef = &client;
@@ -1162,7 +1214,7 @@ void the_game(
// First line of debug text
gui::IGUIStaticText *guitext = guienv->addStaticText(
L"Minetest-c55",
L"Minetest",
core::rect<s32>(5, 5, 795, 5+text_height),
false, false);
// Second line of debug text
@@ -1246,6 +1298,7 @@ void the_game(
bool show_hud = true;
bool show_chat = true;
bool force_fog_off = false;
f32 fog_range = 100*BS;
bool disable_camera_update = false;
bool show_debug = g_settings->getBool("show_debug");
bool show_profiler_graph = false;
@@ -1255,6 +1308,12 @@ void the_game(
float time_of_day = 0;
float time_of_day_smooth = 0;
/*
Shader constants
*/
shsrc->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
sky, &force_fog_off, &fog_range, &client));
/*
Main loop
*/
@@ -1416,11 +1475,17 @@ void the_game(
g_gamecallback->changepassword_requested = false;
}
/*
Process TextureSource's queue
*/
/* Process TextureSource's queue */
tsrc->processQueue();
/* Process ItemDefManager's queue */
itemdef->processQueue(gamedef);
/*
Process ShaderSource's queue
*/
shsrc->processQueue();
/*
Random calculations
*/
@@ -1513,7 +1578,7 @@ void the_game(
<<"Launching inventory"<<std::endl;
GUIFormSpecMenu *menu =
new GUIFormSpecMenu(guienv, guiroot, -1,
new GUIFormSpecMenu(device, guiroot, -1,
&g_menumgr,
&client, gamedef);
@@ -1881,6 +1946,8 @@ void the_game(
bool a_jump,
bool a_superspeed,
bool a_sneak,
bool a_LMB,
bool a_RMB,
float a_pitch,
float a_yaw*/
PlayerControl control(
@@ -1891,10 +1958,24 @@ void the_game(
input->isKeyDown(getKeySetting("keymap_jump")),
input->isKeyDown(getKeySetting("keymap_special1")),
input->isKeyDown(getKeySetting("keymap_sneak")),
input->getLeftState(),
input->getRightState(),
camera_pitch,
camera_yaw
);
client.setPlayerControl(control);
u32 keyPressed=
1*(int)input->isKeyDown(getKeySetting("keymap_forward"))+
2*(int)input->isKeyDown(getKeySetting("keymap_backward"))+
4*(int)input->isKeyDown(getKeySetting("keymap_left"))+
8*(int)input->isKeyDown(getKeySetting("keymap_right"))+
16*(int)input->isKeyDown(getKeySetting("keymap_jump"))+
32*(int)input->isKeyDown(getKeySetting("keymap_special1"))+
64*(int)input->isKeyDown(getKeySetting("keymap_sneak"))+
128*(int)input->getLeftState()+
256*(int)input->getRightState();
LocalPlayer* player = client.getEnv().getLocalPlayer();
player->keyPressed=keyPressed;
}
/*
@@ -2280,7 +2361,7 @@ void the_game(
/* Create menu */
GUIFormSpecMenu *menu =
new GUIFormSpecMenu(guienv, guiroot, -1,
new GUIFormSpecMenu(device, guiroot, -1,
&g_menumgr,
&client, gamedef);
menu->setFormSpec(meta->getString("formspec"),
@@ -2408,7 +2489,6 @@ void the_game(
Fog range
*/
f32 fog_range;
if(farmesh)
{
fog_range = BS*farmesh_range;
@@ -2425,8 +2505,7 @@ void the_game(
Calculate general brightness
*/
u32 daynight_ratio = client.getEnv().getDayNightRatio();
float time_brightness = (float)decode_light(
(daynight_ratio * LIGHT_SUN) / 1000) / 255.0;
float time_brightness = decode_light_f((float)daynight_ratio/1000.0);
float direct_brightness = 0;
bool sunlight_seen = false;
if(g_settings->getBool("free_move")){
@@ -2528,7 +2607,7 @@ void the_game(
//TimeTaker guiupdatetimer("Gui updating");
const char program_name_and_version[] =
"Minetest-c55 " VERSION_STRING;
"Minetest " VERSION_STRING;
if(show_debug)
{
@@ -2734,6 +2813,76 @@ void the_game(
{
TimeTaker timer("smgr");
smgr->drawAll();
if(g_settings->getBool("anaglyph"))
{
irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition();
irr::core::vector3df oldTarget = camera.getCameraNode()->getTarget();
irr::core::matrix4 startMatrix = camera.getCameraNode()->getAbsoluteTransformation();
irr::core::vector3df focusPoint = (camera.getCameraNode()->getTarget() -
camera.getCameraNode()->getAbsolutePosition()).setLength(1) +
camera.getCameraNode()->getAbsolutePosition() ;
//Left eye...
irr::core::vector3df leftEye;
irr::core::matrix4 leftMove;
leftMove.setTranslation( irr::core::vector3df(-g_settings->getFloat("anaglyph_strength"),0.0f,0.0f) );
leftEye=(startMatrix*leftMove).getTranslation();
//clear the depth buffer, and color
driver->beginScene( true, true, irr::video::SColor(0,200,200,255) );
driver->getOverrideMaterial().Material.ColorMask = irr::video::ECP_RED;
driver->getOverrideMaterial().EnableFlags = irr::video::EMF_COLOR_MASK;
driver->getOverrideMaterial().EnablePasses = irr::scene::ESNRP_SKY_BOX +
irr::scene::ESNRP_SOLID +
irr::scene::ESNRP_TRANSPARENT +
irr::scene::ESNRP_TRANSPARENT_EFFECT +
irr::scene::ESNRP_SHADOW;
camera.getCameraNode()->setPosition( leftEye );
camera.getCameraNode()->setTarget( focusPoint );
smgr->drawAll(); // 'smgr->drawAll();' may go here
//Right eye...
irr::core::vector3df rightEye;
irr::core::matrix4 rightMove;
rightMove.setTranslation( irr::core::vector3df(g_settings->getFloat("anaglyph_strength"),0.0f,0.0f) );
rightEye=(startMatrix*rightMove).getTranslation();
//clear the depth buffer
driver->clearZBuffer();
driver->getOverrideMaterial().Material.ColorMask = irr::video::ECP_GREEN + irr::video::ECP_BLUE;
driver->getOverrideMaterial().EnableFlags = irr::video::EMF_COLOR_MASK;
driver->getOverrideMaterial().EnablePasses = irr::scene::ESNRP_SKY_BOX +
irr::scene::ESNRP_SOLID +
irr::scene::ESNRP_TRANSPARENT +
irr::scene::ESNRP_TRANSPARENT_EFFECT +
irr::scene::ESNRP_SHADOW;
camera.getCameraNode()->setPosition( rightEye );
camera.getCameraNode()->setTarget( focusPoint );
smgr->drawAll(); // 'smgr->drawAll();' may go here
//driver->endScene();
driver->getOverrideMaterial().Material.ColorMask=irr::video::ECP_ALL;
driver->getOverrideMaterial().EnableFlags=0;
driver->getOverrideMaterial().EnablePasses=0;
camera.getCameraNode()->setPosition( oldPosition );
camera.getCameraNode()->setTarget( oldTarget );
}
scenetime = timer.stop(true);
}
@@ -2809,11 +2958,6 @@ void the_game(
//timer10.stop();
//TimeTaker //timer11("//timer11");
/*
Draw gui
*/
// 0-1ms
guienv->drawAll();
/*
Draw hotbar
@@ -2839,6 +2983,12 @@ void the_game(
NULL);
}
/*
Draw gui
*/
// 0-1ms
guienv->drawAll();
/*
End scene
*/
@@ -2915,9 +3065,11 @@ void the_game(
if(!sound_is_dummy)
delete sound;
delete tsrc;
delete shsrc;
delete nodedef;
delete itemdef;
delete tsrc;
}

View File

@@ -28,6 +28,7 @@ class INodeDefManager;
class ICraftDefManager;
class ITextureSource;
class ISoundManager;
class IShaderSource;
class MtEventManager;
class IRollbackReportSink;
@@ -48,6 +49,8 @@ class IGameDef
// This is always thread-safe, but referencing the irrlicht texture
// pointers in other threads than main thread will make things explode.
virtual ITextureSource* getTextureSource()=0;
virtual IShaderSource* getShaderSource()=0;
// Used for keeping track of names/ids of unknown nodes
virtual u16 allocateUnknownNodeId(const std::string &name)=0;
@@ -70,6 +73,7 @@ class IGameDef
ICraftDefManager* cdef(){return getCraftDefManager();}
ITextureSource* tsrc(){return getTextureSource();}
ISoundManager* sound(){return getSoundManager();}
IShaderSource* shsrc(){return getShaderSource();}
MtEventManager* event(){return getEventManager();}
IRollbackReportSink* rollback(){return getRollbackReportSink();}
};

View File

@@ -117,4 +117,40 @@ std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups)
return os.str();
}
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend)
{
std::ostringstream os(std::ios::binary);
// command
writeU8(os, GENERIC_CMD_SET_ANIMATION);
// parameters
writeV2F1000(os, frames);
writeF1000(os, frame_speed);
writeF1000(os, frame_blend);
return os.str();
}
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation)
{
std::ostringstream os(std::ios::binary);
// command
writeU8(os, GENERIC_CMD_SET_BONE_POSITION);
// parameters
os<<serializeString(bone);
writeV3F1000(os, position);
writeV3F1000(os, rotation);
return os.str();
}
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
std::ostringstream os(std::ios::binary);
// command
writeU8(os, GENERIC_CMD_SET_ATTACHMENT);
// parameters
writeS16(os, parent_id);
os<<serializeString(bone);
writeV3F1000(os, position);
writeV3F1000(os, rotation);
return os.str();
}

View File

@@ -30,6 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define GENERIC_CMD_SET_SPRITE 3
#define GENERIC_CMD_PUNCHED 4
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 5
#define GENERIC_CMD_SET_ANIMATION 6
#define GENERIC_CMD_SET_BONE_POSITION 7
#define GENERIC_CMD_SET_ATTACHMENT 8
#include "object_properties.h"
std::string gob_cmd_set_properties(const ObjectProperties &prop);
@@ -59,5 +62,11 @@ std::string gob_cmd_punched(s16 damage, s16 result_hp);
#include "itemgroup.h"
std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups);
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend);
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation);
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation);
#endif

View File

@@ -46,7 +46,7 @@ void drawItemStack(video::IVideoDriver *driver,
return;
const ItemDefinition &def = item.getDefinition(gamedef->idef());
video::ITexture *texture = def.inventory_texture;
video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
// Draw the inventory texture
if(texture != NULL)
@@ -125,13 +125,14 @@ void drawItemStack(video::IVideoDriver *driver,
GUIFormSpecMenu
*/
GUIFormSpecMenu::GUIFormSpecMenu(gui::IGUIEnvironment* env,
GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
InventoryManager *invmgr,
IGameDef *gamedef
):
GUIModalMenu(env, parent, id, menumgr),
GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
m_device(dev),
m_invmgr(invmgr),
m_gamedef(gamedef),
m_form_src(NULL),
@@ -199,6 +200,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_inventorylists.clear();
m_images.clear();
m_backgrounds.clear();
m_itemimages.clear();
m_fields.clear();
Strfnd f(m_formspec_string);
@@ -278,9 +281,43 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
if(bp_set != 2)
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos, geom));
}
else if(type == "item_image")
{
v2s32 pos = basepos;
pos.X += stof(f.next(",")) * (float)spacing.X;
pos.Y += stof(f.next(";")) * (float)spacing.Y;
v2s32 geom;
geom.X = stof(f.next(",")) * (float)imgsize.X;
geom.Y = stof(f.next(";")) * (float)imgsize.Y;
std::string name = f.next("]");
errorstream<<"item name="<<name
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
if(bp_set != 2)
errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
}
else if(type == "background")
{
v2s32 pos = basepos;
pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
v2s32 geom;
geom.X = stof(f.next(",")) * (float)spacing.X;
geom.Y = stof(f.next(";")) * (float)spacing.Y;
std::string name = f.next("]");
infostream<<"image name="<<name
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
if(bp_set != 2)
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
}
else if(type == "field")
{
std::string fname = f.next(";");
@@ -458,12 +495,50 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
e->setUseAlphaChannel(true);
e->setImage(texture);
e->setPressedImage(texture);
e->setScaleImage(true);
m_fields.push_back(spec);
}
else if(type == "item_image_button")
{
v2s32 pos = padding;
pos.X += stof(f.next(",")) * (float)spacing.X;
pos.Y += stof(f.next(";")) * (float)spacing.Y;
v2s32 geom;
geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
std::string fimage = f.next(";");
std::string fname = f.next(";");
std::string flabel = f.next("]");
if(bp_set != 2)
errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
item.deSerialize(fimage, idef);
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
std::string tooltip = item.getDefinition(idef).description;
FieldSpec spec = FieldSpec(
narrow_to_wide(fname.c_str()),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(fimage.c_str()),
258+m_fields.size()
);
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
e->setUseAlphaChannel(true);
e->setImage(texture);
e->setPressedImage(texture);
e->setScaleImage(true);
spec.is_button = true;
rect+=basepos-padding;
spec.rect=rect;
if (tooltip!="")
spec.tooltip=tooltip;
m_fields.push_back(spec);
}
else
{
// Ignore others
@@ -679,6 +754,8 @@ void GUIFormSpecMenu::drawMenu()
}
}
m_pointer = m_device->getCursorControl()->getPosition();
updateSelectedItem();
gui::IGUISkin* skin = Environment->getSkin();
@@ -692,17 +769,28 @@ void GUIFormSpecMenu::drawMenu()
m_tooltip_element->setVisible(false);
/*
Draw items
Phase 0: Item slot rectangles
Phase 1: Item images; prepare tooltip
Draw backgrounds
*/
for(int phase=0; phase<=1; phase++)
for(u32 i=0; i<m_inventorylists.size(); i++)
for(u32 i=0; i<m_backgrounds.size(); i++)
{
drawList(m_inventorylists[i], phase);
const ImageDrawSpec &spec = m_backgrounds[i];
video::ITexture *texture =
m_gamedef->tsrc()->getTextureRaw(spec.name);
// Image size on screen
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
}
/*
Draw images
*/
for(u32 i=0; i<m_images.size(); i++)
{
const ImageDrawSpec &spec = m_images[i];
@@ -721,14 +809,53 @@ void GUIFormSpecMenu::drawMenu()
}
/*
Draw dragged item stack
Draw items
Phase 0: Item slot rectangles
Phase 1: Item images; prepare tooltip
If backgrounds used, do not draw Item slot rectangles
*/
drawSelectedItem();
int start_phase=0;
if (m_backgrounds.size() > 0) start_phase=1;
for(int phase=start_phase; phase<=1; phase++)
for(u32 i=0; i<m_inventorylists.size(); i++)
{
drawList(m_inventorylists[i], phase);
}
/*
Call base class
*/
gui::IGUIElement::draw();
/*
Draw fields/buttons tooltips
*/
for(u32 i=0; i<m_fields.size(); i++)
{
const FieldSpec &spec = m_fields[i];
if (spec.tooltip != "")
{
core::rect<s32> rect = spec.rect;
if (rect.isPointInside(m_pointer))
{
m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element);
m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
s32 tooltip_x = m_pointer.X + 15;
s32 tooltip_y = m_pointer.Y + 15;
s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
m_tooltip_element->setRelativePosition(core::rect<s32>(
core::position2d<s32>(tooltip_x, tooltip_y),
core::dimension2d<s32>(tooltip_width, tooltip_height)));
}
}
}
/*
Draw dragged item stack
*/
drawSelectedItem();
}
void GUIFormSpecMenu::updateSelectedItem()
@@ -893,24 +1020,15 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
return true;
}
}
if(event.EventType==EET_MOUSE_INPUT_EVENT
&& event.MouseInput.Event == EMIE_MOUSE_MOVED)
{
// Mouse moved
m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y);
}
if(event.EventType==EET_MOUSE_INPUT_EVENT
&& event.MouseInput.Event != EMIE_MOUSE_MOVED)
{
// Mouse event other than movement
v2s32 p(event.MouseInput.X, event.MouseInput.Y);
m_pointer = p;
// Get selected item and hovered/clicked item (s)
updateSelectedItem();
ItemSpec s = getItemAtPos(p);
ItemSpec s = getItemAtPos(m_pointer);
Inventory *inv_selected = NULL;
Inventory *inv_s = NULL;

View File

@@ -133,6 +133,7 @@ class GUIFormSpecMenu : public GUIModalMenu
send = false;
is_button = false;
is_exit = false;
tooltip="";
}
std::wstring fname;
std::wstring flabel;
@@ -141,10 +142,12 @@ class GUIFormSpecMenu : public GUIModalMenu
bool send;
bool is_button;
bool is_exit;
core::rect<s32> rect;
std::string tooltip;
};
public:
GUIFormSpecMenu(gui::IGUIEnvironment* env,
GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
InventoryManager *invmgr,
@@ -197,6 +200,7 @@ class GUIFormSpecMenu : public GUIModalMenu
v2s32 spacing;
v2s32 imgsize;
irr::IrrlichtDevice* m_device;
InventoryManager *m_invmgr;
IGameDef *m_gamedef;
@@ -206,7 +210,9 @@ class GUIFormSpecMenu : public GUIModalMenu
TextDest *m_text_dst;
core::array<ListDrawSpec> m_inventorylists;
core::array<ImageDrawSpec> m_backgrounds;
core::array<ImageDrawSpec> m_images;
core::array<ImageDrawSpec> m_itemimages;
core::array<FieldSpec> m_fields;
ItemSpec *m_selected_item;

View File

@@ -98,6 +98,12 @@ enum
GUI_ID_SMOOTH_LIGHTING_CB,
GUI_ID_3D_CLOUDS_CB,
GUI_ID_OPAQUE_WATER_CB,
GUI_ID_MIPMAP_CB,
GUI_ID_ANISOTROPIC_CB,
GUI_ID_BILINEAR_CB,
GUI_ID_TRILINEAR_CB,
GUI_ID_SHADERS_CB,
GUI_ID_PRELOAD_ITEM_VISUALS_CB,
GUI_ID_DAMAGE_CB,
GUI_ID_CREATIVE_CB,
GUI_ID_JOIN_GAME_BUTTON,
@@ -202,7 +208,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
core::rect<s32> rect(0, 0, size.X, 40);
rect += v2s32(4, 0);
Environment->addStaticText(narrow_to_wide(
"Minetest-c55 " VERSION_STRING).c_str(),
"Minetest " VERSION_STRING).c_str(),
rect, false, true, this, -1);
}
@@ -580,6 +586,53 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
Environment->addCheckBox(m_data->opaque_water, rect, this,
GUI_ID_OPAQUE_WATER_CB, wgettext("Opaque water"));
}
// Anisotropic/mipmap/bi-/trilinear settings
{
core::rect<s32> rect(0, 0, option_w+20, 30);
rect += m_topleft_client + v2s32(option_x+175, option_y);
Environment->addCheckBox(m_data->mip_map, rect, this,
GUI_ID_MIPMAP_CB, wgettext("Mip-Mapping"));
}
{
core::rect<s32> rect(0, 0, option_w+20, 30);
rect += m_topleft_client + v2s32(option_x+175, option_y+20);
Environment->addCheckBox(m_data->anisotropic_filter, rect, this,
GUI_ID_ANISOTROPIC_CB, wgettext("Anisotropic Filtering"));
}
{
core::rect<s32> rect(0, 0, option_w+20, 30);
rect += m_topleft_client + v2s32(option_x+175, option_y+20*2);
Environment->addCheckBox(m_data->bilinear_filter, rect, this,
GUI_ID_BILINEAR_CB, wgettext("Bi-Linear Filtering"));
}
{
core::rect<s32> rect(0, 0, option_w+20, 30);
rect += m_topleft_client + v2s32(option_x+175, option_y+20*3);
Environment->addCheckBox(m_data->trilinear_filter, rect, this,
GUI_ID_TRILINEAR_CB, wgettext("Tri-Linear Filtering"));
}
// shader/on demand image loading settings
{
core::rect<s32> rect(0, 0, option_w+20, 30);
rect += m_topleft_client + v2s32(option_x+175*2, option_y);
Environment->addCheckBox(m_data->enable_shaders, rect, this,
GUI_ID_SHADERS_CB, wgettext("Shaders"));
}
{
core::rect<s32> rect(0, 0, option_w+20+20, 30);
rect += m_topleft_client + v2s32(option_x+175*2, option_y+20);
Environment->addCheckBox(m_data->preload_item_visuals, rect, this,
GUI_ID_PRELOAD_ITEM_VISUALS_CB, wgettext("Preload item visuals"));
}
// Key change button
{
core::rect<s32> rect(0, 0, 120, 30);
@@ -606,11 +659,11 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
core::rect<s32> rect(0, 0, 454, 250);
rect += m_topleft_client + v2s32(110, 50+35);
Environment->addStaticText(narrow_to_wide(
"Minetest-c55 " VERSION_STRING "\n"
"Minetest " VERSION_STRING "\n"
"http://minetest.net/\n"
"\n"
"by Perttu Ahola <celeron55@gmail.com>\n"
"and contributors: tango_, kahrl (kaaaaaahrl?), erlehmann (the hippie), SpeedProg, JacobF (sqlite worlds), teddydestodes, marktraceur, darkrose, Jonathan Neuschäfer (who the hell?), Felix Krausse (broke liquids, IIRC), sfan5... and >10 more random people."
"and contributors: PilzAdam, Taoki, tango_, kahrl (kaaaaaahrl?), darkrose, matttpt, erlehmann, SpeedProg, JacobF, teddydestodes, marktraceur, Jonathan Neuschäfer, thexyz, VanessaE, sfan5... and tens of more random people."
).c_str(), rect, false, true, this, -1);
}
}
@@ -760,6 +813,42 @@ void GUIMainMenu::readInput(MainMenuData *dst)
dst->opaque_water = ((gui::IGUICheckBox*)e)->isChecked();
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_MIPMAP_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->mip_map = ((gui::IGUICheckBox*)e)->isChecked();
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_ANISOTROPIC_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->anisotropic_filter = ((gui::IGUICheckBox*)e)->isChecked();
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_BILINEAR_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->bilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_TRILINEAR_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->trilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_SHADERS_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->enable_shaders = ((gui::IGUICheckBox*)e)->isChecked() ? 2 : 0;
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_PRELOAD_ITEM_VISUALS_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->preload_item_visuals = ((gui::IGUICheckBox*)e)->isChecked();
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)

View File

@@ -41,6 +41,12 @@ struct MainMenuData
bool smooth_lighting;
bool clouds_3d;
bool opaque_water;
bool mip_map;
bool anisotropic_filter;
bool bilinear_filter;
bool trilinear_filter;
int enable_shaders;
bool preload_item_visuals;
// Server options
bool creative_mode;
bool enable_damage;

View File

@@ -169,23 +169,8 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
max_texture_size = driver->getMaxTextureSize();
}
/*wchar_t text[200];
swprintf(text, 200,
L"Minetest-c55\n"
L"by Perttu Ahola\n"
L"celeron55@gmail.com\n\n"
SWPRINTF_CHARSTRING L"\n"
L"userdata path = "
SWPRINTF_CHARSTRING
,
BUILD_INFO,
porting::path_user.c_str()
);*/
std::ostringstream os;
os<<"Minetest\n";
os<<"by Perttu Ahola and contributors\n";
os<<"celeron55@gmail.com\n";
os<<BUILD_INFO<<"\n";
os<<"path_user = "<<wrap_rows(porting::path_user, 20)<<"\n";

View File

@@ -30,7 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tile.h"
#endif
#include "log.h"
#include "main.h" // g_settings
#include "settings.h"
#include "util/serialize.h"
#include "util/container.h"
#include "util/thread.h"
#include <map>
#include <set>
@@ -71,14 +75,6 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
}
groups = def.groups;
node_placement_prediction = def.node_placement_prediction;
#ifndef SERVER
inventory_texture = def.inventory_texture;
if(def.wield_mesh)
{
wield_mesh = def.wield_mesh;
wield_mesh->grab();
}
#endif
return *this;
}
@@ -91,10 +87,6 @@ void ItemDefinition::resetInitial()
{
// Initialize pointers to NULL so reset() does not delete undefined pointers
tool_capabilities = NULL;
#ifndef SERVER
inventory_texture = NULL;
wield_mesh = NULL;
#endif
reset();
}
@@ -117,15 +109,6 @@ void ItemDefinition::reset()
groups.clear();
node_placement_prediction = "";
#ifndef SERVER
inventory_texture = NULL;
if(wield_mesh)
{
wield_mesh->drop();
wield_mesh = NULL;
}
#endif
}
void ItemDefinition::serialize(std::ostream &os) const
@@ -203,13 +186,39 @@ void ItemDefinition::deSerialize(std::istream &is)
class CItemDefManager: public IWritableItemDefManager
{
#ifndef SERVER
struct ClientCached
{
video::ITexture *inventory_texture;
scene::IMesh *wield_mesh;
ClientCached():
inventory_texture(NULL),
wield_mesh(NULL)
{}
};
#endif
public:
CItemDefManager()
{
#ifndef SERVER
m_main_thread = get_current_thread_id();
#endif
clear();
}
virtual ~CItemDefManager()
{
#ifndef SERVER
const core::list<ClientCached*> &values = m_clientcached.getValues();
for(core::list<ClientCached*>::ConstIterator
i = values.begin(); i != values.end(); ++i)
{
ClientCached *cc = *i;
cc->wield_mesh->drop();
}
#endif
}
virtual const ItemDefinition& get(const std::string &name_) const
{
@@ -256,6 +265,223 @@ class CItemDefManager: public IWritableItemDefManager
std::map<std::string, ItemDefinition*>::const_iterator i;
return m_item_definitions.find(name) != m_item_definitions.end();
}
#ifndef SERVER
ClientCached* createClientCachedDirect(const std::string &name,
IGameDef *gamedef) const
{
infostream<<"Lazily creating item texture and mesh for \""
<<name<<"\""<<std::endl;
// This is not thread-safe
assert(get_current_thread_id() == m_main_thread);
// Skip if already in cache
ClientCached *cc = NULL;
m_clientcached.get(name, &cc);
if(cc)
return cc;
ITextureSource *tsrc = gamedef->getTextureSource();
INodeDefManager *nodedef = gamedef->getNodeDefManager();
IrrlichtDevice *device = tsrc->getDevice();
video::IVideoDriver *driver = device->getVideoDriver();
const ItemDefinition *def = &get(name);
// Create new ClientCached
cc = new ClientCached();
bool need_node_mesh = false;
// Create an inventory texture
cc->inventory_texture = NULL;
if(def->inventory_image != "")
{
cc->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
}
else if(def->type == ITEM_NODE)
{
need_node_mesh = true;
}
// Create a wield mesh
if(cc->wield_mesh != NULL)
{
cc->wield_mesh->drop();
cc->wield_mesh = NULL;
}
if(def->type == ITEM_NODE && def->wield_image == "")
{
need_node_mesh = true;
}
else if(def->wield_image != "" || def->inventory_image != "")
{
// Extrude the wield image into a mesh
std::string imagename;
if(def->wield_image != "")
imagename = def->wield_image;
else
imagename = def->inventory_image;
cc->wield_mesh = createExtrudedMesh(
tsrc->getTextureRaw(imagename),
driver,
def->wield_scale * v3f(40.0, 40.0, 4.0));
if(cc->wield_mesh == NULL)
{
infostream<<"ItemDefManager: WARNING: "
<<"updateTexturesAndMeshes(): "
<<"Unable to create extruded mesh for item "
<<def->name<<std::endl;
}
}
if(need_node_mesh)
{
/*
Get node properties
*/
content_t id = nodedef->getId(def->name);
const ContentFeatures &f = nodedef->get(id);
u8 param1 = 0;
if(f.param_type == CPT_LIGHT)
param1 = 0xee;
/*
Make a mesh from the node
*/
MeshMakeData mesh_make_data(gamedef);
MapNode mesh_make_node(id, param1, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data);
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
assert(node_mesh);
video::SColor c(255, 255, 255, 255);
if(g_settings->getS32("enable_shaders") != 0)
c = MapBlock_LightColor(255, 0xffff, decode_light(f.light_source));
setMeshColor(node_mesh, c);
/*
Scale and translate the mesh so it's a unit cube
centered on the origin
*/
scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
/*
Draw node mesh into a render target texture
*/
if(cc->inventory_texture == NULL)
{
core::dimension2d<u32> dim(64,64);
std::string rtt_texture_name = "INVENTORY_"
+ def->name + "_RTT";
v3f camera_position(0, 1.0, -1.5);
camera_position.rotateXZBy(45);
v3f camera_lookat(0, 0, 0);
core::CMatrix4<f32> camera_projection_matrix;
// Set orthogonal projection
camera_projection_matrix.buildProjectionMatrixOrthoLH(
1.65, 1.65, 0, 100);
video::SColorf ambient_light(0.2,0.2,0.2);
v3f light_position(10, 100, -50);
video::SColorf light_color(0.5,0.5,0.5);
f32 light_radius = 1000;
cc->inventory_texture = generateTextureFromMesh(
node_mesh, device, dim, rtt_texture_name,
camera_position,
camera_lookat,
camera_projection_matrix,
ambient_light,
light_position,
light_color,
light_radius);
// render-to-target didn't work
if(cc->inventory_texture == NULL)
{
cc->inventory_texture =
tsrc->getTextureRaw(f.tiledef[0].name);
}
}
/*
Use the node mesh as the wield mesh
*/
if(cc->wield_mesh == NULL)
{
// Scale to proper wield mesh proportions
scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
* def->wield_scale);
cc->wield_mesh = node_mesh;
cc->wield_mesh->grab();
}
// falling outside of here deletes node_mesh
}
// Put in cache
m_clientcached.set(name, cc);
return cc;
}
ClientCached* getClientCached(const std::string &name,
IGameDef *gamedef) const
{
ClientCached *cc = NULL;
m_clientcached.get(name, &cc);
if(cc)
return cc;
if(get_current_thread_id() == m_main_thread)
{
return createClientCachedDirect(name, gamedef);
}
else
{
// We're gonna ask the result to be put into here
ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
// Throw a request in
m_get_clientcached_queue.add(name, 0, 0, &result_queue);
try{
// Wait result for a second
GetResult<std::string, ClientCached*, u8, u8>
result = result_queue.pop_front(1000);
// Check that at least something worked OK
assert(result.key == name);
// Return it
return result.item;
}
catch(ItemNotFoundException &e)
{
errorstream<<"Waiting for clientcached timed out."<<std::endl;
return &m_dummy_clientcached;
}
}
}
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
IGameDef *gamedef) const
{
ClientCached *cc = getClientCached(name, gamedef);
if(!cc)
return NULL;
return cc->inventory_texture;
}
// Get item wield mesh
virtual scene::IMesh* getWieldMesh(const std::string &name,
IGameDef *gamedef) const
{
ClientCached *cc = getClientCached(name, gamedef);
if(!cc)
return NULL;
return cc->wield_mesh;
}
#endif
void clear()
{
for(std::map<std::string, ItemDefinition*>::const_iterator
@@ -321,157 +547,6 @@ class CItemDefManager: public IWritableItemDefManager
m_aliases[name] = convert_to;
}
}
virtual void updateTexturesAndMeshes(IGameDef *gamedef)
{
#ifndef SERVER
infostream<<"ItemDefManager::updateTexturesAndMeshes(): Updating "
<<"textures and meshes in item definitions"<<std::endl;
ITextureSource *tsrc = gamedef->getTextureSource();
INodeDefManager *nodedef = gamedef->getNodeDefManager();
IrrlichtDevice *device = tsrc->getDevice();
video::IVideoDriver *driver = device->getVideoDriver();
for(std::map<std::string, ItemDefinition*>::iterator
i = m_item_definitions.begin();
i != m_item_definitions.end(); i++)
{
ItemDefinition *def = i->second;
bool need_node_mesh = false;
// Create an inventory texture
def->inventory_texture = NULL;
if(def->inventory_image != "")
{
def->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
}
else if(def->type == ITEM_NODE)
{
need_node_mesh = true;
}
// Create a wield mesh
if(def->wield_mesh != NULL)
{
def->wield_mesh->drop();
def->wield_mesh = NULL;
}
if(def->type == ITEM_NODE && def->wield_image == "")
{
need_node_mesh = true;
}
else if(def->wield_image != "" || def->inventory_image != "")
{
// Extrude the wield image into a mesh
std::string imagename;
if(def->wield_image != "")
imagename = def->wield_image;
else
imagename = def->inventory_image;
def->wield_mesh = createExtrudedMesh(
tsrc->getTextureRaw(imagename),
driver,
def->wield_scale * v3f(40.0, 40.0, 4.0));
if(def->wield_mesh == NULL)
{
infostream<<"ItemDefManager: WARNING: "
<<"updateTexturesAndMeshes(): "
<<"Unable to create extruded mesh for item "
<<def->name<<std::endl;
}
}
if(need_node_mesh)
{
/*
Get node properties
*/
content_t id = nodedef->getId(def->name);
const ContentFeatures &f = nodedef->get(id);
u8 param1 = 0;
if(f.param_type == CPT_LIGHT)
param1 = 0xee;
/*
Make a mesh from the node
*/
MeshMakeData mesh_make_data(gamedef);
MapNode mesh_make_node(id, param1, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data);
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
assert(node_mesh);
setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
/*
Scale and translate the mesh so it's a unit cube
centered on the origin
*/
scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
/*
Draw node mesh into a render target texture
*/
if(def->inventory_texture == NULL)
{
core::dimension2d<u32> dim(64,64);
std::string rtt_texture_name = "INVENTORY_"
+ def->name + "_RTT";
v3f camera_position(0, 1.0, -1.5);
camera_position.rotateXZBy(45);
v3f camera_lookat(0, 0, 0);
core::CMatrix4<f32> camera_projection_matrix;
// Set orthogonal projection
camera_projection_matrix.buildProjectionMatrixOrthoLH(
1.65, 1.65, 0, 100);
video::SColorf ambient_light(0.2,0.2,0.2);
v3f light_position(10, 100, -50);
video::SColorf light_color(0.5,0.5,0.5);
f32 light_radius = 1000;
def->inventory_texture = generateTextureFromMesh(
node_mesh, device, dim, rtt_texture_name,
camera_position,
camera_lookat,
camera_projection_matrix,
ambient_light,
light_position,
light_color,
light_radius);
// render-to-target didn't work
if(def->inventory_texture == NULL)
{
def->inventory_texture =
tsrc->getTextureRaw(f.tiledef[0].name);
}
}
/*
Use the node mesh as the wield mesh
*/
if(def->wield_mesh == NULL)
{
// Scale to proper wield mesh proportions
scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
* def->wield_scale);
def->wield_mesh = node_mesh;
def->wield_mesh->grab();
}
// falling outside of here deletes node_mesh
}
}
#endif
}
void serialize(std::ostream &os)
{
writeU8(os, 0); // version
@@ -521,11 +596,37 @@ class CItemDefManager: public IWritableItemDefManager
registerAlias(name, convert_to);
}
}
void processQueue(IGameDef *gamedef)
{
#ifndef SERVER
while(m_get_clientcached_queue.size() > 0)
{
GetRequest<std::string, ClientCached*, u8, u8>
request = m_get_clientcached_queue.pop();
GetResult<std::string, ClientCached*, u8, u8>
result;
result.key = request.key;
result.callers = request.callers;
result.item = createClientCachedDirect(request.key, gamedef);
request.dest->push_back(result);
}
#endif
}
private:
// Key is name
std::map<std::string, ItemDefinition*> m_item_definitions;
// Aliases
std::map<std::string, std::string> m_aliases;
#ifndef SERVER
// The id of the thread that is allowed to use irrlicht directly
threadid_t m_main_thread;
// A reference to this can be returned when nothing is found, to avoid NULLs
mutable ClientCached m_dummy_clientcached;
// Cached textures and meshes
mutable MutexedMap<std::string, ClientCached*> m_clientcached;
// Queued clientcached fetches (to be processed by the main thread)
mutable RequestQueue<std::string, ClientCached*, u8, u8> m_get_clientcached_queue;
#endif
};
IWritableItemDefManager* createItemDefManager()

View File

@@ -72,14 +72,6 @@ struct ItemDefinition
// "" = no prediction
std::string node_placement_prediction;
/*
Cached stuff
*/
#ifndef SERVER
video::ITexture *inventory_texture;
scene::IMesh *wield_mesh;
#endif
/*
Some helpful methods
*/
@@ -108,6 +100,14 @@ class IItemDefManager
virtual std::set<std::string> getAll() const=0;
// Check if item is known
virtual bool isKnown(const std::string &name) const=0;
#ifndef SERVER
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
IGameDef *gamedef) const=0;
// Get item wield mesh
virtual scene::IMesh* getWieldMesh(const std::string &name,
IGameDef *gamedef) const=0;
#endif
virtual void serialize(std::ostream &os)=0;
};
@@ -126,6 +126,14 @@ class IWritableItemDefManager : public IItemDefManager
virtual std::set<std::string> getAll() const=0;
// Check if item is known
virtual bool isKnown(const std::string &name) const=0;
#ifndef SERVER
// Get item inventory texture
virtual video::ITexture* getInventoryTexture(const std::string &name,
IGameDef *gamedef) const=0;
// Get item wield mesh
virtual scene::IMesh* getWieldMesh(const std::string &name,
IGameDef *gamedef) const=0;
#endif
// Remove all registered item and node definitions and aliases
// Then re-add the builtin item definitions
@@ -138,15 +146,11 @@ class IWritableItemDefManager : public IItemDefManager
virtual void registerAlias(const std::string &name,
const std::string &convert_to)=0;
/*
Update inventory textures and wield meshes to latest
return values of ITextureSource and INodeDefManager.
Call after updating the texture atlas of a texture source.
*/
virtual void updateTexturesAndMeshes(IGameDef *gamedef)=0;
virtual void serialize(std::ostream &os)=0;
virtual void deSerialize(std::istream &is)=0;
// Do stuff asked by threads that can only be done in the main thread
virtual void processQueue(IGameDef *gamedef)=0;
};
IWritableItemDefManager* createItemDefManager();

View File

@@ -85,6 +85,24 @@ inline u8 decode_light(u8 light)
return light_decode_table[light];
}
// 0.0 <= light <= 1.0
// 0.0 <= return value <= 1.0
inline float decode_light_f(float light_f)
{
s32 i = (u32)(light_f * LIGHT_MAX + 0.5);
if(i <= 0)
return (float)light_decode_table[0] / 255.0;
if(i >= LIGHT_MAX)
return (float)light_decode_table[LIGHT_MAX] / 255.0;
float v1 = (float)light_decode_table[i-1] / 255.0;
float v2 = (float)light_decode_table[i] / 255.0;
float f0 = (float)i - 0.5;
float f = light_f * LIGHT_MAX - f0;
return f * v2 + (1.0 - f) * v1;
}
// 0 <= daylight_factor <= 1000
// 0 <= lightday, lightnight <= LIGHT_SUN
// 0 <= return value <= LIGHT_SUN
@@ -97,5 +115,15 @@ inline u8 blend_light(u32 daylight_factor, u8 lightday, u8 lightnight)
return l;
}
// 0.0 <= daylight_factor <= 1.0
// 0 <= lightday, lightnight <= LIGHT_SUN
// 0 <= return value <= 255
inline u8 blend_light_f1(float daylight_factor, u8 lightday, u8 lightnight)
{
u8 l = ((daylight_factor * decode_light(lightday) +
(1.0-daylight_factor) * decode_light(lightnight)));
return l;
}
#endif

View File

@@ -34,6 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
LocalPlayer::LocalPlayer(IGameDef *gamedef):
Player(gamedef),
isAttached(false),
overridePosition(v3f(0,0,0)),
last_position(v3f(0,0,0)),
last_speed(v3f(0,0,0)),
last_pitch(0),
last_yaw(0),
last_keyPressed(0),
m_sneak_node(32767,32767,32767),
m_sneak_node_exists(false),
m_old_node_below(32767,32767,32767),
@@ -59,6 +66,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
v3f old_speed = m_speed;
// Copy parent position if local player is attached
if(isAttached)
{
setPosition(overridePosition);
return;
}
// Skip collision detection if a special movement mode is used
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
bool free_move = fly_allowed && g_settings->getBool("free_move");
@@ -314,7 +328,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
v3s16 camera_np = floatToInt(getEyePosition(), BS);
MapNode n = map.getNodeNoEx(camera_np);
if(n.getContent() != CONTENT_IGNORE){
if(nodemgr->get(n).walkable){
if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
camera_barely_in_ceiling = true;
}
}
@@ -352,7 +366,14 @@ void LocalPlayer::applyControl(float dtime)
setPitch(control.pitch);
setYaw(control.yaw);
// Nullify speed and don't run positioning code if the player is attached
if(isAttached)
{
setSpeed(v3f(0,0,0));
return;
}
v3f move_direction = v3f(0,0,1);
move_direction.rotateXZBy(getYaw());

View File

@@ -22,53 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
struct PlayerControl
{
PlayerControl()
{
up = false;
down = false;
left = false;
right = false;
jump = false;
aux1 = false;
sneak = false;
pitch = 0;
yaw = 0;
}
PlayerControl(
bool a_up,
bool a_down,
bool a_left,
bool a_right,
bool a_jump,
bool a_aux1,
bool a_sneak,
float a_pitch,
float a_yaw
)
{
up = a_up;
down = a_down;
left = a_left;
right = a_right;
jump = a_jump;
aux1 = a_aux1;
sneak = a_sneak;
pitch = a_pitch;
yaw = a_yaw;
}
bool up;
bool down;
bool left;
bool right;
bool jump;
bool aux1;
bool sneak;
float pitch;
float yaw;
};
class LocalPlayer : public Player
{
public:
@@ -79,6 +32,10 @@ class LocalPlayer : public Player
{
return true;
}
bool isAttached;
v3f overridePosition;
void move(f32 dtime, Map &map, f32 pos_max_d,
core::list<CollisionInfo> *collision_info);
@@ -87,8 +44,13 @@ class LocalPlayer : public Player
void applyControl(float dtime);
v3s16 getStandingNodePos();
PlayerControl control;
// Used to check if anything changed and prevent sending packets if not
v3f last_position;
v3f last_speed;
float last_pitch;
float last_yaw;
unsigned int last_keyPressed;
private:
// This is used for determining the sneaking range

View File

@@ -1292,9 +1292,6 @@ int main(int argc, char *argv[])
video::IVideoDriver* driver = device->getVideoDriver();
// Disable mipmaps (because some of them look ugly)
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
/*
This changes the minimum allowed number of vertices in a VBO.
Default is 500.
@@ -1352,7 +1349,13 @@ int main(int argc, char *argv[])
skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
// Irrlicht 1.8 input colours
skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128));
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
#endif
/*
GUI stuff
*/
@@ -1439,6 +1442,13 @@ int main(int argc, char *argv[])
menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
menudata.opaque_water = g_settings->getBool("opaque_water");
menudata.mip_map = g_settings->getBool("mip_map");
menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
menudata.enable_shaders = g_settings->getS32("enable_shaders");
menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
menudata.creative_mode = g_settings->getBool("creative_mode");
menudata.enable_damage = g_settings->getBool("enable_damage");
// Default to selecting nothing
@@ -1552,6 +1562,15 @@ int main(int argc, char *argv[])
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
g_settings->set("opaque_water", itos(menudata.opaque_water));
g_settings->set("mip_map", itos(menudata.mip_map));
g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
g_settings->setS32("enable_shaders", menudata.enable_shaders);
g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
g_settings->set("creative_mode", itos(menudata.creative_mode));
g_settings->set("enable_damage", itos(menudata.enable_damage));
g_settings->set("name", playername);

View File

@@ -1746,12 +1746,12 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
content_t new_node_content;
s8 new_node_level = -1;
s8 max_node_level = -1;
if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
// or the flowing alternative of the first of the surrounding sources (if it's air), so
// it's perfectly safe to use liquid_kind here to determine the new node content.
new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
} else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
} else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
// liquid_kind is set properly, see above
new_node_content = liquid_kind;
max_node_level = new_node_level = LIQUID_LEVEL_MAX;

View File

@@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock_mesh.h"
#endif
#include "util/string.h"
#include "util/serialize.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"

View File

@@ -28,9 +28,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mesh.h"
#include "content_mapblock.h"
#include "noise.h"
#include "shader.h"
#include "settings.h"
#include "util/directiontables.h"
float srgb_linear_multiply(float f, float m, float max)
{
f = f * f; // SRGB -> Linear
f *= m;
f = sqrt(f); // Linear -> SRGB
if(f > max)
f = max;
return f;
}
/*
MeshMakeData
*/
@@ -77,9 +88,9 @@ void MeshMakeData::fill(MapBlock *block)
// Get map
Map *map = block->getParent();
for(u16 i=0; i<6; i++)
for(u16 i=0; i<26; i++)
{
const v3s16 &dir = g_6dirs[i];
const v3s16 &dir = g_26dirs[i];
v3s16 bp = m_blockpos + dir;
MapBlock *b = map->getBlockNoCreateNoEx(bp);
if(b)
@@ -434,7 +445,7 @@ struct FastFace
};
static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
v3f p, v3s16 dir, v3f scale, u8 light_source, core::array<FastFace> &dest)
{
FastFace face;
@@ -476,16 +487,16 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
float h = tile.texture.size.Y;
face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
MapBlock_LightColor(alpha, li0),
MapBlock_LightColor(alpha, li0, light_source),
core::vector2d<f32>(x0+w*abs_scale, y0+h));
face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
MapBlock_LightColor(alpha, li1),
MapBlock_LightColor(alpha, li1, light_source),
core::vector2d<f32>(x0, y0+h));
face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
MapBlock_LightColor(alpha, li2),
MapBlock_LightColor(alpha, li2, light_source),
core::vector2d<f32>(x0, y0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
MapBlock_LightColor(alpha, li3),
MapBlock_LightColor(alpha, li3, light_source),
core::vector2d<f32>(x0+w*abs_scale, y0));
face.tile = tile;
@@ -657,7 +668,8 @@ static void getTileInfo(
v3s16 &p_corrected,
v3s16 &face_dir_corrected,
u16 *lights,
TileSpec &tile
TileSpec &tile,
u8 &light_source
)
{
VoxelManipulator &vmanip = data->m_vmanip;
@@ -687,18 +699,20 @@ static void getTileInfo(
tile = tile0;
p_corrected = p;
face_dir_corrected = face_dir;
light_source = ndef->get(n0).light_source;
}
else
{
tile = tile1;
p_corrected = p + face_dir;
face_dir_corrected = -face_dir;
light_source = ndef->get(n1).light_source;
}
// eg. water and glass
if(equivalent)
tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
if(data->m_smooth_lighting == false)
{
lights[0] = lights[1] = lights[2] = lights[3] =
@@ -742,9 +756,10 @@ static void updateFastFaceRow(
v3s16 face_dir_corrected;
u16 lights[4] = {0,0,0,0};
TileSpec tile;
u8 light_source = 0;
getTileInfo(data, p, face_dir,
makes_face, p_corrected, face_dir_corrected,
lights, tile);
lights, tile, light_source);
for(u16 j=0; j<MAP_BLOCKSIZE; j++)
{
@@ -758,6 +773,7 @@ static void updateFastFaceRow(
v3s16 next_face_dir_corrected;
u16 next_lights[4] = {0,0,0,0};
TileSpec next_tile;
u8 next_light_source = 0;
// If at last position, there is nothing to compare to and
// the face must be drawn anyway
@@ -768,7 +784,7 @@ static void updateFastFaceRow(
getTileInfo(data, p_next, face_dir,
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
next_tile);
next_tile, next_light_source);
if(next_makes_face == makes_face
&& next_p_corrected == p_corrected + translate_dir
@@ -777,7 +793,8 @@ static void updateFastFaceRow(
&& next_lights[1] == lights[1]
&& next_lights[2] == lights[2]
&& next_lights[3] == lights[3]
&& next_tile == tile)
&& next_tile == tile
&& next_light_source == light_source)
{
next_is_different = false;
}
@@ -853,7 +870,7 @@ static void updateFastFaceRow(
}
makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
sp, face_dir_corrected, scale,
sp, face_dir_corrected, scale, light_source,
dest);
g_profiler->avg("Meshgen: faces drawn by tiling", 0);
@@ -872,6 +889,7 @@ static void updateFastFaceRow(
lights[2] = next_lights[2];
lights[3] = next_lights[3];
tile = next_tile;
light_source = next_light_source;
}
p = p_next;
@@ -1011,6 +1029,11 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
Convert MeshCollector to SMesh
Also store animation info
*/
bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
getShader("test_shader_1").material;
video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
getShader("test_shader_2").material;
for(u32 i = 0; i < collector.prebuffers.size(); i++)
{
PreMeshBuffer &p = collector.prebuffers[i];
@@ -1052,18 +1075,28 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
p.tile.texture = tsrc->getTexture(os.str());
}
// - Lighting
for(u32 j = 0; j < p.vertices.size(); j++)
// - Classic lighting (shaders handle this by themselves)
if(!enable_shaders)
{
video::SColor &vc = p.vertices[j].Color;
u8 day = vc.getRed();
u8 night = vc.getGreen();
finalColorBlend(vc, day, night, 1000);
if(day != night)
m_daynight_diffs[i][j] = std::make_pair(day, night);
for(u32 j = 0; j < p.vertices.size(); j++)
{
video::SColor &vc = p.vertices[j].Color;
// Set initial real color and store for later updates
u8 day = vc.getRed();
u8 night = vc.getGreen();
finalColorBlend(vc, day, night, 1000);
if(day != night)
m_daynight_diffs[i][j] = std::make_pair(day, night);
// Brighten topside (no shaders)
if(p.vertices[j].Normal.Y > 0.5)
{
vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
}
}
}
// Create material
video::SMaterial material;
material.setFlag(video::EMF_LIGHTING, false);
@@ -1075,7 +1108,10 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.setTexture(0, p.tile.texture.atlas);
p.tile.applyMaterialOptions(material);
if(enable_shaders)
p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2);
else
p.tile.applyMaterialOptions(material);
// Create meshbuffer
@@ -1211,6 +1247,14 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
u8 night = j->second.second;
finalColorBlend(vertices[vertexIndex].Color,
day, night, daynight_ratio);
// Brighten topside (no shaders)
if(vertices[vertexIndex].Normal.Y > 0.5)
{
video::SColor &vc = vertices[vertexIndex].Color;
vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
}
}
}
m_last_daynight_ratio = daynight_ratio;

View File

@@ -160,9 +160,10 @@ struct MeshCollector
// alpha in the A channel of the returned SColor
// day light (0-255) in the R channel of the returned SColor
// night light (0-255) in the G channel of the returned SColor
inline video::SColor MapBlock_LightColor(u8 alpha, u16 light)
// light source (0-255) in the B channel of the returned SColor
inline video::SColor MapBlock_LightColor(u8 alpha, u16 light, u8 light_source=0)
{
return video::SColor(alpha, (light & 0xff), (light >> 8), 0);
return video::SColor(alpha, (light & 0xff), (light >> 8), light_source);
}
// Compute light at node

View File

@@ -132,7 +132,8 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
@@ -1228,7 +1229,7 @@ double base_rock_level_2d(u64 seed, v2s16 p)
0.5+(float)p.X/125., 0.5+(float)p.Y/125.,
seed-932, 5, 0.7);
b = rangelim(b, 0.0, 1000.0);
b = pow(b, 7);
b = b*b*b*b*b*b*b;
b *= 5;
b = rangelim(b, 0.5, 1000.0);
// Values 1.5...100 give quite horrible looking slopes

View File

@@ -163,6 +163,16 @@ struct MapNode
return blend_light(daylight_factor, lightday, lightnight);
}
// 0.0 <= daylight_factor <= 1.0
// 0 <= return value <= LIGHT_SUN
u8 getLightBlendF1(float daylight_factor, INodeDefManager *nodemgr) const
{
u8 lightday = 0;
u8 lightnight = 0;
getLightBanks(lightday, lightnight, nodemgr);
return blend_light_f1(daylight_factor, lightday, lightnight);
}
u8 getFaceDir(INodeDefManager *nodemgr) const;
u8 getWallMounted(INodeDefManager *nodemgr) const;
v3s16 getWallMountedDir(INodeDefManager *nodemgr) const;

View File

@@ -443,7 +443,7 @@ video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
}
// Set render target
driver->setRenderTarget(rtt, true, true, video::SColor(0,0,0,0));
driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0));
// Get a scene manager
scene::ISceneManager *smgr_main = device->getSceneManager();
@@ -478,7 +478,7 @@ video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
smgr->drop();
// Unset render target
driver->setRenderTarget(0, true, true, 0);
driver->setRenderTarget(0, false, true, 0);
return rtt;
}

View File

@@ -39,6 +39,10 @@ static void collectMods(const std::string &modspath,
if(!dirlist[j].dir)
continue;
std::string modname = dirlist[j].name;
// Ignore all directories beginning with a ".", especially
// VCS directories like ".git" or ".svn"
if(modname[0] == '.')
continue;
std::string modpath = modspath + DIR_DELIM + modname;
TRACESTREAM(<<indentation<<"collectMods: "<<modname<<" at \""<<modpath<<"\""<<std::endl);
// Handle modpacks (defined by containing modpack.txt)

View File

@@ -203,6 +203,7 @@ void ContentFeatures::reset()
liquid_alternative_flowing = "";
liquid_alternative_source = "";
liquid_viscosity = 0;
liquid_renewable = true;
light_source = 0;
damage_per_second = 0;
node_box = NodeBox();
@@ -214,9 +215,14 @@ void ContentFeatures::reset()
sound_dug = SimpleSoundSpec();
}
void ContentFeatures::serialize(std::ostream &os)
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
{
writeU8(os, 5); // version
if(protocol_version < 14){
serializeOld(os, protocol_version);
return;
}
writeU8(os, 6); // version
os<<serializeString(name);
writeU16(os, groups.size());
for(ItemGroupList::const_iterator
@@ -253,6 +259,7 @@ void ContentFeatures::serialize(std::ostream &os)
os<<serializeString(liquid_alternative_flowing);
os<<serializeString(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, liquid_renewable);
writeU8(os, light_source);
writeU32(os, damage_per_second);
node_box.serialize(os);
@@ -262,13 +269,18 @@ void ContentFeatures::serialize(std::ostream &os)
serializeSimpleSoundSpec(sound_footstep, os);
serializeSimpleSoundSpec(sound_dig, os);
serializeSimpleSoundSpec(sound_dug, os);
// Stuff below should be moved to correct place in a version that otherwise changes
// the protocol version
}
void ContentFeatures::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 5)
throw SerializationError("unsupported ContentFeatures version");
if(version != 6){
deSerializeOld(is, version);
return;
}
name = deSerializeString(is);
groups.clear();
u32 groups_size = readU16(is);
@@ -307,6 +319,7 @@ void ContentFeatures::deSerialize(std::istream &is)
liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is);
liquid_viscosity = readU8(is);
liquid_renewable = readU8(is);
light_source = readU8(is);
damage_per_second = readU32(is);
node_box.deSerialize(is);
@@ -319,6 +332,8 @@ void ContentFeatures::deSerialize(std::istream &is)
// If you add anything here, insert it primarily inside the try-catch
// block to not need to increase the version.
try{
// Stuff below should be moved to correct place in a version that
// otherwise changes the protocol version
}catch(SerializationError &e) {};
}
@@ -540,6 +555,7 @@ class CNodeDefManager: public IWritableNodeDefManager
tiledef[j].name = "unknown_block.png";
}
bool is_liquid = false;
switch(f->drawtype){
default:
case NDT_NORMAL:
@@ -558,12 +574,14 @@ class CNodeDefManager: public IWritableNodeDefManager
f->solidness = 1;
f->backface_culling = false;
}
is_liquid = true;
break;
case NDT_FLOWINGLIQUID:
assert(f->liquid_type == LIQUID_FLOWING);
f->solidness = 0;
if(opaque_water)
f->alpha = 255;
is_liquid = true;
break;
case NDT_GLASSLIKE:
f->solidness = 0;
@@ -596,16 +614,24 @@ class CNodeDefManager: public IWritableNodeDefManager
break;
}
u8 material_type = 0;
if(is_liquid){
if(f->alpha == 255)
material_type = TILE_MATERIAL_LIQUID_OPAQUE;
else
material_type = TILE_MATERIAL_LIQUID_TRANSPARENT;
} else{
material_type = TILE_MATERIAL_BASIC;
}
// Tiles (fill in f->tiles[])
for(u16 j=0; j<6; j++){
// Texture
f->tiles[j].texture = tsrc->getTexture(tiledef[j].name);
// Alpha
f->tiles[j].alpha = f->alpha;
if(f->alpha == 255)
f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else
f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
// Material type
f->tiles[j].material_type = material_type;
// Material flags
f->tiles[j].material_flags = 0;
if(f->backface_culling)
@@ -646,10 +672,8 @@ class CNodeDefManager: public IWritableNodeDefManager
tsrc->getTexture(f->tiledef_special[j].name);
// Alpha
f->special_tiles[j].alpha = f->alpha;
if(f->alpha == 255)
f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else
f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
// Material type
f->special_tiles[j].material_type = material_type;
// Material flags
f->special_tiles[j].material_flags = 0;
if(f->tiledef_special[j].backface_culling)
@@ -686,7 +710,7 @@ class CNodeDefManager: public IWritableNodeDefManager
}
#endif
}
void serialize(std::ostream &os)
void serialize(std::ostream &os, u16 protocol_version)
{
writeU8(os, 1); // version
u16 count = 0;
@@ -702,7 +726,7 @@ class CNodeDefManager: public IWritableNodeDefManager
// Wrap it in a string to allow different lengths without
// strict version incompatibilities
std::ostringstream wrapper_os(std::ios::binary);
f->serialize(wrapper_os);
f->serialize(wrapper_os, protocol_version);
os2<<serializeString(wrapper_os.str());
count++;
}
@@ -759,3 +783,122 @@ IWritableNodeDefManager* createNodeDefManager()
return new CNodeDefManager();
}
/*
Serialization of old ContentFeatures formats
*/
void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
{
if(protocol_version == 13)
{
writeU8(os, 5); // version
os<<serializeString(name);
writeU16(os, groups.size());
for(ItemGroupList::const_iterator
i = groups.begin(); i != groups.end(); i++){
os<<serializeString(i->first);
writeS16(os, i->second);
}
writeU8(os, drawtype);
writeF1000(os, visual_scale);
writeU8(os, 6);
for(u32 i=0; i<6; i++)
tiledef[i].serialize(os);
writeU8(os, CF_SPECIAL_COUNT);
for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
tiledef_special[i].serialize(os);
}
writeU8(os, alpha);
writeU8(os, post_effect_color.getAlpha());
writeU8(os, post_effect_color.getRed());
writeU8(os, post_effect_color.getGreen());
writeU8(os, post_effect_color.getBlue());
writeU8(os, param_type);
writeU8(os, param_type_2);
writeU8(os, is_ground_content);
writeU8(os, light_propagates);
writeU8(os, sunlight_propagates);
writeU8(os, walkable);
writeU8(os, pointable);
writeU8(os, diggable);
writeU8(os, climbable);
writeU8(os, buildable_to);
os<<serializeString(""); // legacy: used to be metadata_name
writeU8(os, liquid_type);
os<<serializeString(liquid_alternative_flowing);
os<<serializeString(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, light_source);
writeU32(os, damage_per_second);
node_box.serialize(os);
selection_box.serialize(os);
writeU8(os, legacy_facedir_simple);
writeU8(os, legacy_wallmounted);
serializeSimpleSoundSpec(sound_footstep, os);
serializeSimpleSoundSpec(sound_dig, os);
serializeSimpleSoundSpec(sound_dug, os);
}
else
{
throw SerializationError("ContentFeatures::serialize(): Unsupported version requested");
}
}
void ContentFeatures::deSerializeOld(std::istream &is, int version)
{
if(version == 5) // In PROTOCOL_VERSION 13
{
name = deSerializeString(is);
groups.clear();
u32 groups_size = readU16(is);
for(u32 i=0; i<groups_size; i++){
std::string name = deSerializeString(is);
int value = readS16(is);
groups[name] = value;
}
drawtype = (enum NodeDrawType)readU8(is);
visual_scale = readF1000(is);
if(readU8(is) != 6)
throw SerializationError("unsupported tile count");
for(u32 i=0; i<6; i++)
tiledef[i].deSerialize(is);
if(readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for(u32 i=0; i<CF_SPECIAL_COUNT; i++)
tiledef_special[i].deSerialize(is);
alpha = readU8(is);
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
post_effect_color.setGreen(readU8(is));
post_effect_color.setBlue(readU8(is));
param_type = (enum ContentParamType)readU8(is);
param_type_2 = (enum ContentParamType2)readU8(is);
is_ground_content = readU8(is);
light_propagates = readU8(is);
sunlight_propagates = readU8(is);
walkable = readU8(is);
pointable = readU8(is);
diggable = readU8(is);
climbable = readU8(is);
buildable_to = readU8(is);
deSerializeString(is); // legacy: used to be metadata_name
liquid_type = (enum LiquidType)readU8(is);
liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is);
liquid_viscosity = readU8(is);
light_source = readU8(is);
damage_per_second = readU32(is);
node_box.deSerialize(is);
selection_box.deSerialize(is);
legacy_facedir_simple = readU8(is);
legacy_wallmounted = readU8(is);
deSerializeSimpleSoundSpec(sound_footstep, is);
deSerializeSimpleSoundSpec(sound_dig, is);
deSerializeSimpleSoundSpec(sound_dug, is);
}
else
{
throw SerializationError("unsupported ContentFeatures version");
}
}

View File

@@ -209,6 +209,8 @@ struct ContentFeatures
// 1 giving almost instantaneous propagation and 7 being
// the slowest possible
u8 liquid_viscosity;
// Is liquid renewable (new liquid source will be created between 2 existing)
bool liquid_renewable;
// Amount of light the node emits
u8 light_source;
u32 damage_per_second;
@@ -232,8 +234,10 @@ struct ContentFeatures
ContentFeatures();
~ContentFeatures();
void reset();
void serialize(std::ostream &os);
void serialize(std::ostream &os, u16 protocol_version);
void deSerialize(std::istream &is);
void serializeOld(std::ostream &os, u16 protocol_version);
void deSerializeOld(std::istream &is, int version);
/*
Some handy methods
@@ -262,7 +266,7 @@ class INodeDefManager
const=0;
virtual const ContentFeatures& get(const std::string &name) const=0;
virtual void serialize(std::ostream &os)=0;
virtual void serialize(std::ostream &os, u16 protocol_version)=0;
};
class IWritableNodeDefManager : public INodeDefManager
@@ -303,7 +307,7 @@ class IWritableNodeDefManager : public INodeDefManager
*/
virtual void updateTextures(ITextureSource *tsrc)=0;
virtual void serialize(std::ostream &os)=0;
virtual void serialize(std::ostream &os, u16 protocol_version)=0;
virtual void deSerialize(std::istream &is)=0;
};

View File

@@ -37,7 +37,7 @@ double dotProduct(double vx, double vy, double wx, double wy){
}
double easeCurve(double t){
return 6*pow(t,5)-15*pow(t,4)+10*pow(t,3);
return t * t * t * (6. * t * t - 15. * t + 10.);
}
double linearInterpolation(double x0, double x1, double t){

View File

@@ -18,8 +18,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "object_properties.h"
#include "irrlichttypes_bloated.h"
#include "util/serialize.h"
#include <sstream>
#include <map>
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#define PP2(x) "("<<(x).X<<","<<(x).Y<<")"
@@ -30,6 +32,7 @@ ObjectProperties::ObjectProperties():
weight(5),
collisionbox(-0.5,-0.5,-0.5, 0.5,0.5,0.5),
visual("sprite"),
mesh(""),
visual_size(1,1),
spritediv(1,1),
initial_sprite_basepos(0,0),
@@ -38,6 +41,7 @@ ObjectProperties::ObjectProperties():
automatic_rotate(0)
{
textures.push_back("unknown_object.png");
colors.push_back(video::SColor(255,255,255,255));
}
std::string ObjectProperties::dump()
@@ -48,12 +52,18 @@ std::string ObjectProperties::dump()
os<<", weight="<<weight;
os<<", collisionbox="<<PP(collisionbox.MinEdge)<<","<<PP(collisionbox.MaxEdge);
os<<", visual="<<visual;
os<<", mesh="<<mesh;
os<<", visual_size="<<PP2(visual_size);
os<<", textures=[";
for(u32 i=0; i<textures.size(); i++){
os<<"\""<<textures[i]<<"\" ";
}
os<<"]";
os<<", colors=[";
for(u32 i=0; i<colors.size(); i++){
os<<"\""<<colors[i].getAlpha()<<","<<colors[i].getRed()<<","<<colors[i].getGreen()<<","<<colors[i].getBlue()<<"\" ";
}
os<<"]";
os<<", spritediv="<<PP2(spritediv);
os<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos);
os<<", is_visible="<<is_visible;
@@ -81,32 +91,49 @@ void ObjectProperties::serialize(std::ostream &os) const
writeU8(os, is_visible);
writeU8(os, makes_footstep_sound);
writeF1000(os, automatic_rotate);
// Added in protocol version 14
os<<serializeString(mesh);
writeU16(os, colors.size());
for(u32 i=0; i<colors.size(); i++){
writeARGB8(os, colors[i]);
}
// Add stuff only at the bottom.
// Never remove anything, because we don't want new versions of this
}
void ObjectProperties::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version != 1) throw SerializationError(
"unsupported ObjectProperties version");
hp_max = readS16(is);
physical = readU8(is);
weight = readF1000(is);
collisionbox.MinEdge = readV3F1000(is);
collisionbox.MaxEdge = readV3F1000(is);
visual = deSerializeString(is);
visual_size = readV2F1000(is);
textures.clear();
u32 texture_count = readU16(is);
for(u32 i=0; i<texture_count; i++){
textures.push_back(deSerializeString(is));
if(version == 1)
{
try{
hp_max = readS16(is);
physical = readU8(is);
weight = readF1000(is);
collisionbox.MinEdge = readV3F1000(is);
collisionbox.MaxEdge = readV3F1000(is);
visual = deSerializeString(is);
visual_size = readV2F1000(is);
textures.clear();
u32 texture_count = readU16(is);
for(u32 i=0; i<texture_count; i++){
textures.push_back(deSerializeString(is));
}
spritediv = readV2S16(is);
initial_sprite_basepos = readV2S16(is);
is_visible = readU8(is);
makes_footstep_sound = readU8(is);
automatic_rotate = readF1000(is);
mesh = deSerializeString(is);
u32 color_count = readU16(is);
for(u32 i=0; i<color_count; i++){
colors.push_back(readARGB8(is));
}
}catch(SerializationError &e){}
}
else
{
throw SerializationError("unsupported ObjectProperties version");
}
spritediv = readV2S16(is);
initial_sprite_basepos = readV2S16(is);
is_visible = readU8(is);
makes_footstep_sound = readU8(is);
try{
automatic_rotate = readF1000(is);
}catch(SerializationError &e){}
}

View File

@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include "irrlichttypes_bloated.h"
#include <iostream>
#include <map>
struct ObjectProperties
{
@@ -32,14 +33,17 @@ struct ObjectProperties
float weight;
core::aabbox3d<f32> collisionbox;
std::string visual;
std::string mesh;
v2f visual_size;
core::array<std::string> textures;
core::array<video::SColor> colors;
v2s16 spritediv;
v2s16 initial_sprite_basepos;
bool is_visible;
bool makes_footstep_sound;
float automatic_rotate;
ObjectProperties();
std::string dump();
void serialize(std::ostream &os) const;

View File

@@ -28,6 +28,61 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
struct PlayerControl
{
PlayerControl()
{
up = false;
down = false;
left = false;
right = false;
jump = false;
aux1 = false;
sneak = false;
LMB = false;
RMB = false;
pitch = 0;
yaw = 0;
}
PlayerControl(
bool a_up,
bool a_down,
bool a_left,
bool a_right,
bool a_jump,
bool a_aux1,
bool a_sneak,
bool a_LMB,
bool a_RMB,
float a_pitch,
float a_yaw
)
{
up = a_up;
down = a_down;
left = a_left;
right = a_right;
jump = a_jump;
aux1 = a_aux1;
sneak = a_sneak;
LMB = a_LMB;
RMB = a_RMB;
pitch = a_pitch;
yaw = a_yaw;
}
bool up;
bool down;
bool left;
bool right;
bool jump;
bool aux1;
bool sneak;
bool LMB;
bool RMB;
float pitch;
float yaw;
};
class Map;
class IGameDef;
struct CollisionInfo;
@@ -155,8 +210,16 @@ class Player
u16 hp;
u16 peer_id;
std::string inventory_formspec;
PlayerControl control;
PlayerControl getPlayerControl()
{
return control;
}
u32 keyPressed;
protected:
IGameDef *m_gamedef;
@@ -182,7 +245,7 @@ class RemotePlayer : public Player
void setPlayerSAO(PlayerSAO *sao)
{ m_sao = sao; }
void setPosition(const v3f &position);
private:
PlayerSAO *m_sao;
};

View File

@@ -936,6 +936,8 @@ static void read_object_properties(lua_State *L, int index,
lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
lua_getfield(L, -1, "visual_size");
if(lua_istable(L, -1))
@@ -958,6 +960,23 @@ static void read_object_properties(lua_State *L, int index,
}
}
lua_pop(L, 1);
lua_getfield(L, -1, "colors");
if(lua_istable(L, -1)){
prop->colors.clear();
int table = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
if(lua_isstring(L, -1))
prop->colors.push_back(readARGB8(L, -1));
else
prop->colors.push_back(video::SColor(255, 255, 255, 255));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, -1, "spritediv");
if(lua_istable(L, -1))
@@ -1238,6 +1257,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
// the slowest possible
f.liquid_viscosity = getintfield_default(L, index,
"liquid_viscosity", f.liquid_viscosity);
getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
// Amount of light the node emits
f.light_source = getintfield_default(L, index,
"light_source", f.light_source);
@@ -2542,7 +2562,7 @@ class ObjectRef
return 0;
}
// punch(self, puncher, tool_capabilities, direction, time_from_last_punch)
// punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
static int l_punch(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
@@ -2551,13 +2571,18 @@ class ObjectRef
ServerActiveObject *puncher = getobject(puncher_ref);
if(co == NULL) return 0;
if(puncher == NULL) return 0;
ToolCapabilities toolcap = read_tool_capabilities(L, 3);
v3f dir = read_v3f(L, 4);
v3f dir;
if(lua_type(L, 5) != LUA_TTABLE)
dir = co->getBasePosition() - puncher->getBasePosition();
else
dir = read_v3f(L, 5);
float time_from_last_punch = 1000000;
if(lua_isnumber(L, 5))
time_from_last_punch = lua_tonumber(L, 5);
if(lua_isnumber(L, 3))
time_from_last_punch = lua_tonumber(L, 3);
ToolCapabilities toolcap = read_tool_capabilities(L, 4);
dir.normalize();
// Do it
puncher->punch(dir, &toolcap, puncher, time_from_last_punch);
co->punch(dir, &toolcap, puncher, time_from_last_punch);
return 0;
}
@@ -2691,6 +2716,80 @@ class ObjectRef
return 0;
}
// set_animation(self, frame_range, frame_speed, frame_blend)
static int l_set_animation(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
if(co == NULL) return 0;
// Do it
v2f frames = v2f(1, 1);
if(!lua_isnil(L, 2))
frames = read_v2f(L, 2);
float frame_speed = 15;
if(!lua_isnil(L, 3))
frame_speed = lua_tonumber(L, 3);
float frame_blend = 0;
if(!lua_isnil(L, 4))
frame_blend = lua_tonumber(L, 4);
co->setAnimation(frames, frame_speed, frame_blend);
return 0;
}
// set_bone_position(self, std::string bone, v3f position, v3f rotation)
static int l_set_bone_position(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
if(co == NULL) return 0;
// Do it
std::string bone = "";
if(!lua_isnil(L, 2))
bone = lua_tostring(L, 2);
v3f position = v3f(0, 0, 0);
if(!lua_isnil(L, 3))
position = read_v3f(L, 3);
v3f rotation = v3f(0, 0, 0);
if(!lua_isnil(L, 4))
rotation = read_v3f(L, 4);
co->setBonePosition(bone, position, rotation);
return 0;
}
// set_attach(self, parent, bone, position, rotation)
static int l_set_attach(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ObjectRef *parent_ref = checkobject(L, 2);
ServerActiveObject *co = getobject(ref);
ServerActiveObject *parent = getobject(parent_ref);
if(co == NULL) return 0;
if(parent == NULL) return 0;
// Do it
std::string bone = "";
if(!lua_isnil(L, 3))
bone = lua_tostring(L, 3);
v3f position = v3f(0, 0, 0);
if(!lua_isnil(L, 4))
position = read_v3f(L, 4);
v3f rotation = v3f(0, 0, 0);
if(!lua_isnil(L, 5))
rotation = read_v3f(L, 5);
co->setAttachment(parent->getId(), bone, position, rotation);
return 0;
}
// set_detach(self)
static int l_set_detach(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
if(co == NULL) return 0;
// Do it
co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
return 0;
}
// set_properties(self, properties)
static int l_set_properties(lua_State *L)
{
@@ -2926,7 +3025,54 @@ class ObjectRef
lua_pushlstring(L, formspec.c_str(), formspec.size());
return 1;
}
// get_player_control(self)
static int l_get_player_control(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if(player == NULL){
lua_pushlstring(L, "", 0);
return 1;
}
// Do it
PlayerControl control = player->getPlayerControl();
lua_newtable(L);
lua_pushboolean(L, control.up);
lua_setfield(L, -2, "up");
lua_pushboolean(L, control.down);
lua_setfield(L, -2, "down");
lua_pushboolean(L, control.left);
lua_setfield(L, -2, "left");
lua_pushboolean(L, control.right);
lua_setfield(L, -2, "right");
lua_pushboolean(L, control.jump);
lua_setfield(L, -2, "jump");
lua_pushboolean(L, control.aux1);
lua_setfield(L, -2, "aux1");
lua_pushboolean(L, control.sneak);
lua_setfield(L, -2, "sneak");
lua_pushboolean(L, control.LMB);
lua_setfield(L, -2, "LMB");
lua_pushboolean(L, control.RMB);
lua_setfield(L, -2, "RMB");
return 1;
}
// get_player_control_bits(self)
static int l_get_player_control_bits(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if(player == NULL){
lua_pushlstring(L, "", 0);
return 1;
}
// Do it
lua_pushnumber(L, player->keyPressed);
return 1;
}
public:
ObjectRef(ServerActiveObject *object):
m_object(object)
@@ -3005,6 +3151,10 @@ const luaL_reg ObjectRef::methods[] = {
method(ObjectRef, get_wielded_item),
method(ObjectRef, set_wielded_item),
method(ObjectRef, set_armor_groups),
method(ObjectRef, set_animation),
method(ObjectRef, set_bone_position),
method(ObjectRef, set_attach),
method(ObjectRef, set_detach),
method(ObjectRef, set_properties),
// LuaEntitySAO-only
method(ObjectRef, setvelocity),
@@ -3025,6 +3175,8 @@ const luaL_reg ObjectRef::methods[] = {
method(ObjectRef, get_look_yaw),
method(ObjectRef, set_inventory_formspec),
method(ObjectRef, get_inventory_formspec),
method(ObjectRef, get_player_control),
method(ObjectRef, get_player_control_bits),
{0,0}
};
@@ -3452,7 +3604,7 @@ class EnvRef
if(lua_isnumber(L, 3))
time_of_day = 24000.0 * lua_tonumber(L, 3);
time_of_day %= 24000;
u32 dnr = time_to_daynight_ratio(time_of_day);
u32 dnr = time_to_daynight_ratio(time_of_day, true);
MapNode n = env->getMap().getNodeNoEx(pos);
try{
MapNode n = env->getMap().getNode(pos);
@@ -5424,6 +5576,19 @@ bool scriptapi_on_chat_message(lua_State *L, const std::string &name,
return ate;
}
void scriptapi_on_shutdown(lua_State *L)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
// Get registered shutdown hooks
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "registered_on_shutdown");
// Call callbacks
scriptapi_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST);
}
void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player)
{
realitycheck(L);
@@ -6478,7 +6643,7 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
}
void scriptapi_luaentity_activate(lua_State *L, u16 id,
const std::string &staticdata)
const std::string &staticdata, u32 dtime_s)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
@@ -6496,8 +6661,9 @@ void scriptapi_luaentity_activate(lua_State *L, u16 id,
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
lua_pushlstring(L, staticdata.c_str(), staticdata.size());
// Call with 2 arguments, 0 results
if(lua_pcall(L, 2, 0, 0))
lua_pushinteger(L, dtime_s);
// Call with 3 arguments, 0 results
if(lua_pcall(L, 3, 0, 0))
script_error(L, "error running function on_activate: %s\n",
lua_tostring(L, -1));
}
@@ -6581,6 +6747,8 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
// Deprecated: read object properties directly
read_object_properties(L, -1, prop);

View File

@@ -55,6 +55,9 @@ void scriptapi_environment_step(lua_State *L, float dtime);
void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp,
u32 blockseed);
/* server */
void scriptapi_on_shutdown(lua_State *L);
/* misc */
void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player);
@@ -167,7 +170,7 @@ void scriptapi_detached_inventory_on_take(lua_State *L,
// Returns true if succesfully added into Lua; false otherwise.
bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name);
void scriptapi_luaentity_activate(lua_State *L, u16 id,
const std::string &staticdata);
const std::string &staticdata, u32 dtime_s);
void scriptapi_luaentity_rm(lua_State *L, u16 id);
std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id);
void scriptapi_luaentity_get_properties(lua_State *L, u16 id,

View File

@@ -54,6 +54,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/pointedthing.h"
#include "util/mathconstants.h"
#include "rollback.h"
#include "util/serialize.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -1111,7 +1112,17 @@ Server::~Server()
{}
}
}
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
/*
Execute script shutdown hooks
*/
scriptapi_on_shutdown(m_lua);
}
{
JMutexAutoLock envlock(m_env_mutex);
@@ -1143,14 +1154,6 @@ Server::~Server()
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
/*// Delete player
// NOTE: These are removed by env destructor
{
u16 peer_id = i.getNode()->getKey();
JMutexAutoLock envlock(m_env_mutex);
m_env->removePlayer(peer_id);
}*/
// Delete client
delete i.getNode()->getValue();
}
@@ -1371,9 +1374,9 @@ void Server::AsyncRunStep()
/*
Send player inventories and HPs if necessary
*/
if(playersao->m_teleported){
if(playersao->m_moved){
SendMovePlayer(client->peer_id);
playersao->m_teleported = false;
playersao->m_moved = false;
}
if(playersao->m_inventory_not_sent){
UpdateCrafting(client->peer_id);
@@ -1567,7 +1570,7 @@ void Server::AsyncRunStep()
if(obj)
data_buffer.append(serializeLongString(
obj->getClientInitializationData()));
obj->getClientInitializationData(client->net_proto_version)));
else
data_buffer.append(serializeLongString(""));
@@ -2037,40 +2040,74 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
Read and check network protocol version
*/
u16 net_proto_version = 0;
u16 min_net_proto_version = 0;
if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
// Use same version as minimum and maximum if maximum version field
// doesn't exist (backwards compatibility)
u16 max_net_proto_version = min_net_proto_version;
if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
// Start with client's maximum version
u16 net_proto_version = max_net_proto_version;
// Figure out a working version if it is possible at all
if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
{
net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
// If maximum is larger than our maximum, go with our maximum
if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
// Else go with client's maximum
else
net_proto_version = max_net_proto_version;
}
verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
<<min_net_proto_version<<", max: "<<max_net_proto_version
<<", chosen: "<<net_proto_version<<std::endl;
getClient(peer_id)->net_proto_version = net_proto_version;
if(net_proto_version == 0)
if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
{
actionstream<<"Server: An old tried to connect from "<<addr_s
actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
<<std::endl;
SendAccessDenied(m_con, peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(VERSION_STRING) + L"."
+ narrow_to_wide(VERSION_STRING) + L",\n"
+ L"server's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
+ L"..."
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
+ L", client's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(min_net_proto_version))
+ L"..."
+ narrow_to_wide(itos(max_net_proto_version))
);
return;
}
if(g_settings->getBool("strict_protocol_version_checking"))
{
if(net_proto_version != PROTOCOL_VERSION)
if(net_proto_version != LATEST_PROTOCOL_VERSION)
{
actionstream<<"Server: A mismatched client tried to connect"
<<" from "<<addr_s<<std::endl;
actionstream<<"Server: A mismatched (strict) client tried to "
<<"connect from "<<addr_s<<std::endl;
SendAccessDenied(m_con, peer_id, std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(VERSION_STRING) + L",\n"
+ L"server's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(PROTOCOL_VERSION))
+ L"server's PROTOCOL_VERSION (strict) is "
+ narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
+ L", client's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(net_proto_version))
+ narrow_to_wide(itos(min_net_proto_version))
+ L"..."
+ narrow_to_wide(itos(max_net_proto_version))
);
return;
}
@@ -2212,11 +2249,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
Answer with a TOCLIENT_INIT
*/
{
SharedBuffer<u8> reply(2+1+6+8);
SharedBuffer<u8> reply(2+1+6+8+4);
writeU16(&reply[0], TOCLIENT_INIT);
writeU8(&reply[2], deployed);
writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
// Send as reliable
m_con.Send(peer_id, 0, reply, true);
@@ -2242,8 +2280,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return;
}
getClient(peer_id)->serialization_version
= getClient(peer_id)->pending_serialization_version;
RemoteClient *client = getClient(peer_id);
client->serialization_version =
getClient(peer_id)->pending_serialization_version;
/*
Send some initialization data
@@ -2256,7 +2295,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
SendItemDef(m_con, peer_id, m_itemdef);
// Send node definitions
SendNodeDef(m_con, peer_id, m_nodedef);
SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
// Send media announcement
sendMediaAnnouncement(peer_id);
@@ -2310,9 +2349,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
// Warnings about protocol version can be issued here
if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
{
SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
}
/*
@@ -2377,6 +2417,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
v3s32 ss = readV3S32(&data[start+2+12]);
f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
u32 keyPressed = 0;
if(datasize >= 2+12+12+4+4+4)
keyPressed = (u32)readU32(&data[2+12+12+4+4]);
v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
pitch = wrapDegrees(pitch);
@@ -2386,6 +2429,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
player->setSpeed(speed);
player->setPitch(pitch);
player->setYaw(yaw);
player->keyPressed=keyPressed;
player->control.up = (bool)(keyPressed&1);
player->control.down = (bool)(keyPressed&2);
player->control.left = (bool)(keyPressed&4);
player->control.right = (bool)(keyPressed&8);
player->control.jump = (bool)(keyPressed&16);
player->control.aux1 = (bool)(keyPressed&32);
player->control.sneak = (bool)(keyPressed&64);
player->control.LMB = (bool)(keyPressed&128);
player->control.RMB = (bool)(keyPressed&256);
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
@@ -3166,6 +3219,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
} // action == 4
/*
Catch invalid actions
@@ -3496,7 +3550,7 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id,
}
void Server::SendNodeDef(con::Connection &con, u16 peer_id,
INodeDefManager *nodedef)
INodeDefManager *nodedef, u16 protocol_version)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
@@ -3508,7 +3562,7 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id,
*/
writeU16(os, TOCLIENT_NODEDEF);
std::ostringstream tmp_os(std::ios::binary);
nodedef->serialize(tmp_os);
nodedef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
os<<serializeLongString(tmp_os2.str());
@@ -4029,6 +4083,7 @@ void Server::fillMediaCache()
paths.push_back(mod.path + DIR_DELIM + "textures");
paths.push_back(mod.path + DIR_DELIM + "sounds");
paths.push_back(mod.path + DIR_DELIM + "media");
paths.push_back(mod.path + DIR_DELIM + "models");
}
std::string path_all = "textures";
paths.push_back(path_all + DIR_DELIM + "all");
@@ -4054,6 +4109,7 @@ void Server::fillMediaCache()
".png", ".jpg", ".bmp", ".tga",
".pcx", ".ppm", ".psd", ".wal", ".rgb",
".ogg",
".x", ".b3d", ".md2", ".obj",
NULL
};
if(removeStringEnd(filename, supported_ext) == ""){
@@ -4632,6 +4688,10 @@ ITextureSource* Server::getTextureSource()
{
return NULL;
}
IShaderSource* Server::getShaderSource()
{
return NULL;
}
u16 Server::allocateUnknownNodeId(const std::string &name)
{
return m_nodedef->allocateDummy(name);

View File

@@ -560,6 +560,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
virtual INodeDefManager* getNodeDefManager();
virtual ICraftDefManager* getCraftDefManager();
virtual ITextureSource* getTextureSource();
virtual IShaderSource* getShaderSource();
virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager();
virtual MtEventManager* getEventManager();
@@ -602,7 +603,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
static void SendItemDef(con::Connection &con, u16 peer_id,
IItemDefManager *itemdef);
static void SendNodeDef(con::Connection &con, u16 peer_id,
INodeDefManager *nodedef);
INodeDefManager *nodedef, u16 protocol_version);
/*
Non-static send methods.

View File

@@ -62,7 +62,7 @@ class ServerActiveObject : public ActiveObject
{ return getType(); }
// Called after id has been set and has been inserted in environment
virtual void addedToEnvironment(){};
virtual void addedToEnvironment(u32 dtime_s){};
// Called before removing from environment
virtual void removingFromEnvironment(){};
// Returns true if object's deletion is the job of the
@@ -118,7 +118,7 @@ class ServerActiveObject : public ActiveObject
The return value of this is passed to the client-side object
when it is created
*/
virtual std::string getClientInitializationData(){return "";}
virtual std::string getClientInitializationData(u16 protocol_version){return "";}
/*
The return value of this is passed to the server-side object
@@ -152,6 +152,12 @@ class ServerActiveObject : public ActiveObject
virtual void setArmorGroups(const ItemGroupList &armor_groups)
{}
virtual void setAnimation(v2f frames, float frame_speed, float frame_blend)
{}
virtual void setBonePosition(std::string bone, v3f position, v3f rotation)
{}
virtual void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
{}
virtual ObjectProperties* accessObjectProperties()
{ return NULL; }
virtual void notifyObjectPropertiesModified()

793
src/shader.cpp Normal file
View File

@@ -0,0 +1,793 @@
/*
Minetest-c55
Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2012 Kahrl <kahrl@gmx.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "shader.h"
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
#include "main.h" // for g_settings
#include "filesys.h"
#include "util/container.h"
#include "util/thread.h"
#include "settings.h"
#include <iterator>
#include <ICameraSceneNode.h>
#include <IGPUProgrammingServices.h>
#include <IMaterialRenderer.h>
#include <IMaterialRendererServices.h>
#include <IShaderConstantSetCallBack.h>
#include "EShaderTypes.h"
#include "log.h"
#include "gamedef.h"
#include "strfnd.h" // trim()
/*
A cache from shader name to shader path
*/
MutexedMap<std::string, std::string> g_shadername_to_path_cache;
/*
Gets the path to a shader by first checking if the file
name_of_shader/filename
exists in shader_path and if not, using the data path.
If not found, returns "".
Utilizes a thread-safe cache.
*/
std::string getShaderPath(const std::string &name_of_shader,
const std::string &filename)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
std::string fullpath = "";
/*
Check from cache
*/
bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
if(incache)
return fullpath;
/*
Check from shader_path
*/
std::string shader_path = g_settings->get("shader_path");
if(shader_path != "")
{
std::string testpath = shader_path + DIR_DELIM + combined;
if(fs::PathExists(testpath))
fullpath = testpath;
}
/*
Check from default data directory
*/
if(fullpath == "")
{
std::string rel_path = std::string("client") + DIR_DELIM
+ "shaders" + DIR_DELIM
+ name_of_shader + DIR_DELIM
+ filename;
std::string testpath = porting::path_share + DIR_DELIM + rel_path;
if(fs::PathExists(testpath))
fullpath = testpath;
}
// Add to cache (also an empty result is cached)
g_shadername_to_path_cache.set(combined, fullpath);
// Finally return it
return fullpath;
}
/*
SourceShaderCache: A cache used for storing source shaders.
*/
class SourceShaderCache
{
public:
void insert(const std::string &name_of_shader,
const std::string &filename,
const std::string &program,
bool prefer_local)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
// Try to use local shader instead if asked to
if(prefer_local){
std::string path = getShaderPath(name_of_shader, filename);
if(path != ""){
std::string p = readFile(path);
if(p != ""){
m_programs[combined] = p;
return;
}
}
}
m_programs[combined] = program;
}
std::string get(const std::string &name_of_shader,
const std::string &filename)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
core::map<std::string, std::string>::Node *n;
n = m_programs.find(combined);
if(n)
return n->getValue();
return "";
}
// Primarily fetches from cache, secondarily tries to read from filesystem
std::string getOrLoad(const std::string &name_of_shader,
const std::string &filename)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
core::map<std::string, std::string>::Node *n;
n = m_programs.find(combined);
if(n)
return n->getValue();
std::string path = getShaderPath(name_of_shader, filename);
if(path == ""){
infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
<<combined<<"\""<<std::endl;
return "";
}
infostream<<"SourceShaderCache::getOrLoad(): Loading path \""<<path
<<"\""<<std::endl;
std::string p = readFile(path);
if(p != ""){
m_programs[combined] = p;
return p;
}
return "";
}
private:
core::map<std::string, std::string> m_programs;
std::string readFile(const std::string &path)
{
std::ifstream is(path.c_str(), std::ios::binary);
if(!is.is_open())
return "";
std::ostringstream tmp_os;
tmp_os << is.rdbuf();
return tmp_os.str();
}
};
/*
ShaderCallback: Sets constants that can be used in shaders
*/
class IShaderConstantSetterRegistry
{
public:
virtual ~IShaderConstantSetterRegistry(){};
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel, const std::string &name) = 0;
};
class ShaderCallback : public video::IShaderConstantSetCallBack
{
IShaderConstantSetterRegistry *m_scsr;
std::string m_name;
public:
ShaderCallback(IShaderConstantSetterRegistry *scsr, const std::string &name):
m_scsr(scsr),
m_name(name)
{}
~ShaderCallback() {}
virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
{
video::IVideoDriver *driver = services->getVideoDriver();
assert(driver);
bool is_highlevel = userData;
m_scsr->onSetConstants(services, is_highlevel, m_name);
}
};
/*
MainShaderConstantSetter: Set basic constants required for almost everything
*/
class MainShaderConstantSetter : public IShaderConstantSetter
{
public:
MainShaderConstantSetter(IrrlichtDevice *device):
m_device(device)
{}
~MainShaderConstantSetter() {}
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel)
{
video::IVideoDriver *driver = services->getVideoDriver();
assert(driver);
// set inverted world matrix
core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
invWorld.makeInverse();
if(is_highlevel)
services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
else
services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
// set clip matrix
core::matrix4 worldViewProj;
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
worldViewProj *= driver->getTransform(video::ETS_VIEW);
worldViewProj *= driver->getTransform(video::ETS_WORLD);
if(is_highlevel)
services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
else
services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
// set transposed world matrix
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
world = world.getTransposed();
if(is_highlevel)
services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
else
services->setVertexShaderConstant(world.pointer(), 8, 4);
}
private:
IrrlichtDevice *m_device;
};
/*
ShaderSource
*/
class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
{
public:
ShaderSource(IrrlichtDevice *device);
~ShaderSource();
/*
Gets a shader material id from cache or
- if main thread, from getShaderIdDirect
- if other thread, adds to request queue and waits for main thread
*/
u32 getShaderId(const std::string &name);
/*
- If shader material specified by name is found from cache,
return the cached id.
- Otherwise generate the shader material, add to cache and return id.
The id 0 points to a null shader. Its material is EMT_SOLID.
*/
u32 getShaderIdDirect(const std::string &name);
// Finds out the name of a cached shader.
std::string getShaderName(u32 id);
/*
If shader specified by the name pointed by the id doesn't
exist, create it, then return the cached shader.
Can be called from any thread. If called from some other thread
and not found in cache, the call is queued to the main thread
for processing.
*/
ShaderInfo getShader(u32 id);
ShaderInfo getShader(const std::string &name)
{
return getShader(getShaderId(name));
}
// Processes queued shader requests from other threads.
// Shall be called from the main thread.
void processQueue();
// Insert a shader program into the cache without touching the
// filesystem. Shall be called from the main thread.
void insertSourceShader(const std::string &name_of_shader,
const std::string &filename, const std::string &program);
// Rebuild shaders from the current set of source shaders
// Shall be called from the main thread.
void rebuildShaders();
void addGlobalConstantSetter(IShaderConstantSetter *setter)
{
m_global_setters.push_back(setter);
}
void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel, const std::string &name);
private:
// The id of the thread that is allowed to use irrlicht directly
threadid_t m_main_thread;
// The irrlicht device
IrrlichtDevice *m_device;
// The set-constants callback
ShaderCallback *m_shader_callback;
// Cache of source shaders
// This should be only accessed from the main thread
SourceShaderCache m_sourcecache;
// A shader id is index in this array.
// The first position contains a dummy shader.
core::array<ShaderInfo> m_shaderinfo_cache;
// Maps a shader name to an index in the former.
core::map<std::string, u32> m_name_to_id;
// The two former containers are behind this mutex
JMutex m_shaderinfo_cache_mutex;
// Queued shader fetches (to be processed by the main thread)
RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
// Global constant setters
// TODO: Delete these in the destructor
core::array<IShaderConstantSetter*> m_global_setters;
};
IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
{
return new ShaderSource(device);
}
/*
Generate shader given the shader name.
*/
ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
video::IShaderConstantSetCallBack *callback,
SourceShaderCache *sourcecache);
/*
Load shader programs
*/
void load_shaders(std::string name, SourceShaderCache *sourcecache,
video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
std::string &vertex_program, std::string &pixel_program,
std::string &geometry_program, bool &is_highlevel);
ShaderSource::ShaderSource(IrrlichtDevice *device):
m_device(device)
{
assert(m_device);
m_shader_callback = new ShaderCallback(this, "default");
m_shaderinfo_cache_mutex.Init();
m_main_thread = get_current_thread_id();
// Add a dummy ShaderInfo as the first index, named ""
m_shaderinfo_cache.push_back(ShaderInfo());
m_name_to_id[""] = 0;
// Add main global constant setter
addGlobalConstantSetter(new MainShaderConstantSetter(device));
}
ShaderSource::~ShaderSource()
{
//m_shader_callback->drop();
}
u32 ShaderSource::getShaderId(const std::string &name)
{
//infostream<<"getShaderId(): \""<<name<<"\""<<std::endl;
{
/*
See if shader already exists
*/
JMutexAutoLock lock(m_shaderinfo_cache_mutex);
core::map<std::string, u32>::Node *n;
n = m_name_to_id.find(name);
if(n != NULL)
return n->getValue();
}
/*
Get shader
*/
if(get_current_thread_id() == m_main_thread){
return getShaderIdDirect(name);
} else {
infostream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;
// We're gonna ask the result to be put into here
ResultQueue<std::string, u32, u8, u8> result_queue;
// Throw a request in
m_get_shader_queue.add(name, 0, 0, &result_queue);
infostream<<"Waiting for shader from main thread, name=\""
<<name<<"\""<<std::endl;
try{
// Wait result for a second
GetResult<std::string, u32, u8, u8>
result = result_queue.pop_front(1000);
// Check that at least something worked OK
assert(result.key == name);
return result.item;
}
catch(ItemNotFoundException &e){
infostream<<"Waiting for shader timed out."<<std::endl;
return 0;
}
}
infostream<<"getShaderId(): Failed"<<std::endl;
return 0;
}
/*
This method generates all the shaders
*/
u32 ShaderSource::getShaderIdDirect(const std::string &name)
{
//infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
// Empty name means shader 0
if(name == ""){
infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
return 0;
}
/*
Calling only allowed from main thread
*/
if(get_current_thread_id() != m_main_thread){
errorstream<<"ShaderSource::getShaderIdDirect() "
"called not from main thread"<<std::endl;
return 0;
}
/*
See if shader already exists
*/
{
JMutexAutoLock lock(m_shaderinfo_cache_mutex);
core::map<std::string, u32>::Node *n;
n = m_name_to_id.find(name);
if(n != NULL){
/*infostream<<"getShaderIdDirect(): \""<<name
<<"\" found in cache"<<std::endl;*/
return n->getValue();
}
}
/*infostream<<"getShaderIdDirect(): \""<<name
<<"\" NOT found in cache. Creating it."<<std::endl;*/
ShaderInfo info = generate_shader(name, m_device,
m_shader_callback, &m_sourcecache);
/*
Add shader to caches (add dummy shaders too)
*/
JMutexAutoLock lock(m_shaderinfo_cache_mutex);
u32 id = m_shaderinfo_cache.size();
m_shaderinfo_cache.push_back(info);
m_name_to_id.insert(name, id);
/*infostream<<"getShaderIdDirect(): "
<<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
return id;
}
std::string ShaderSource::getShaderName(u32 id)
{
JMutexAutoLock lock(m_shaderinfo_cache_mutex);
if(id >= m_shaderinfo_cache.size()){
errorstream<<"ShaderSource::getShaderName(): id="<<id
<<" >= m_shaderinfo_cache.size()="
<<m_shaderinfo_cache.size()<<std::endl;
return "";
}
return m_shaderinfo_cache[id].name;
}
ShaderInfo ShaderSource::getShader(u32 id)
{
JMutexAutoLock lock(m_shaderinfo_cache_mutex);
if(id >= m_shaderinfo_cache.size())
return ShaderInfo();
return m_shaderinfo_cache[id];
}
void ShaderSource::processQueue()
{
/*
Fetch shaders
*/
if(m_get_shader_queue.size() > 0){
GetRequest<std::string, u32, u8, u8>
request = m_get_shader_queue.pop();
/*infostream<<"ShaderSource::processQueue(): "
<<"got shader request with "
<<"name=\""<<request.key<<"\""
<<std::endl;*/
GetResult<std::string, u32, u8, u8>
result;
result.key = request.key;
result.callers = request.callers;
result.item = getShaderIdDirect(request.key);
request.dest->push_back(result);
}
}
void ShaderSource::insertSourceShader(const std::string &name_of_shader,
const std::string &filename, const std::string &program)
{
/*infostream<<"ShaderSource::insertSourceShader(): "
"name_of_shader=\""<<name_of_shader<<"\", "
"filename=\""<<filename<<"\""<<std::endl;*/
assert(get_current_thread_id() == m_main_thread);
m_sourcecache.insert(name_of_shader, filename, program, true);
}
void ShaderSource::rebuildShaders()
{
JMutexAutoLock lock(m_shaderinfo_cache_mutex);
/*// Oh well... just clear everything, they'll load sometime.
m_shaderinfo_cache.clear();
m_name_to_id.clear();*/
/*
FIXME: Old shader materials can't be deleted in Irrlicht,
or can they?
(This would be nice to do in the destructor too)
*/
// Recreate shaders
for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
ShaderInfo *info = &m_shaderinfo_cache[i];
*info = generate_shader(info->name, m_device,
m_shader_callback, &m_sourcecache);
}
}
void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel, const std::string &name)
{
for(u32 i=0; i<m_global_setters.size(); i++){
IShaderConstantSetter *setter = m_global_setters[i];
setter->onSetConstants(services, is_highlevel);
}
}
ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
video::IShaderConstantSetCallBack *callback,
SourceShaderCache *sourcecache)
{
/*infostream<<"generate_shader(): "
"\""<<name<<"\""<<std::endl;*/
ShaderInfo shaderinfo;
shaderinfo.name = name;
shaderinfo.material = video::EMT_SOLID;
/*
Get the base material
*/
std::string base_material_name =
trim(sourcecache->getOrLoad(name, "base.txt"));
for(s32 i = 0; video::sBuiltInMaterialTypeNames[i] != 0; i++){
if(video::sBuiltInMaterialTypeNames[i] == base_material_name){
shaderinfo.material = (video::E_MATERIAL_TYPE) i;
break;
}
}
// 0 = off, 1 = assembly shaders only, 2 = highlevel or assembly
s32 enable_shaders = g_settings->getS32("enable_shaders");
if(enable_shaders <= 0)
return shaderinfo;
video::IVideoDriver* driver = device->getVideoDriver();
assert(driver);
video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
if(!gpu){
errorstream<<"generate_shader(): "
"failed to generate \""<<name<<"\", "
"GPU programming not supported."
<<std::endl;
return shaderinfo;
}
// Choose shader language depending on driver type and settings
// Then load shaders
std::string vertex_program;
std::string pixel_program;
std::string geometry_program;
bool is_highlevel;
load_shaders(name, sourcecache, driver->getDriverType(),
enable_shaders, vertex_program, pixel_program,
geometry_program, is_highlevel);
// Check hardware/driver support
if(vertex_program != "" &&
!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
infostream<<"generate_shader(): vertex shaders disabled "
"because of missing driver/hardware support."
<<std::endl;
vertex_program = "";
}
if(pixel_program != "" &&
!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
infostream<<"generate_shader(): pixel shaders disabled "
"because of missing driver/hardware support."
<<std::endl;
pixel_program = "";
}
if(geometry_program != "" &&
!driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
infostream<<"generate_shader(): geometry shaders disabled "
"because of missing driver/hardware support."
<<std::endl;
geometry_program = "";
}
// If no shaders are used, don't make a separate material type
if(vertex_program == "" && pixel_program == "" && geometry_program == "")
return shaderinfo;
// Call addHighLevelShaderMaterial() or addShaderMaterial()
const c8* vertex_program_ptr = 0;
const c8* pixel_program_ptr = 0;
const c8* geometry_program_ptr = 0;
if(vertex_program != "")
vertex_program_ptr = vertex_program.c_str();
if(pixel_program != "")
pixel_program_ptr = pixel_program.c_str();
if(geometry_program != "")
geometry_program_ptr = geometry_program.c_str();
s32 shadermat = -1;
if(is_highlevel){
infostream<<"Compiling high level shaders for "<<name<<std::endl;
shadermat = gpu->addHighLevelShaderMaterial(
vertex_program_ptr, // Vertex shader program
"vertexMain", // Vertex shader entry point
video::EVST_VS_1_1, // Vertex shader version
pixel_program_ptr, // Pixel shader program
"pixelMain", // Pixel shader entry point
video::EPST_PS_1_1, // Pixel shader version
geometry_program_ptr, // Geometry shader program
"geometryMain", // Geometry shader entry point
video::EGST_GS_4_0, // Geometry shader version
scene::EPT_TRIANGLES, // Geometry shader input
scene::EPT_TRIANGLE_STRIP, // Geometry shader output
0, // Support maximum number of vertices
callback, // Set-constant callback
shaderinfo.material, // Base material
1 // Userdata passed to callback
);
if(shadermat == -1){
errorstream<<"generate_shader(): "
"failed to generate \""<<name<<"\", "
"addHighLevelShaderMaterial failed."
<<std::endl;
return shaderinfo;
}
}
else{
infostream<<"Compiling assembly shaders for "<<name<<std::endl;
shadermat = gpu->addShaderMaterial(
vertex_program_ptr, // Vertex shader program
pixel_program_ptr, // Pixel shader program
callback, // Set-constant callback
shaderinfo.material, // Base material
0 // Userdata passed to callback
);
if(shadermat == -1){
errorstream<<"generate_shader(): "
"failed to generate \""<<name<<"\", "
"addShaderMaterial failed."
<<std::endl;
return shaderinfo;
}
}
// HACK, TODO: investigate this better
// Grab the material renderer once more so minetest doesn't crash on exit
driver->getMaterialRenderer(shadermat)->grab();
// Apply the newly created material type
shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
return shaderinfo;
}
void load_shaders(std::string name, SourceShaderCache *sourcecache,
video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
std::string &vertex_program, std::string &pixel_program,
std::string &geometry_program, bool &is_highlevel)
{
vertex_program = "";
pixel_program = "";
geometry_program = "";
is_highlevel = false;
if(enable_shaders >= 2){
// Look for high level shaders
if(drivertype == video::EDT_DIRECT3D9){
// Direct3D 9: HLSL
// (All shaders in one file)
vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
pixel_program = vertex_program;
geometry_program = vertex_program;
}
else if(drivertype == video::EDT_OPENGL){
// OpenGL: GLSL
vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
}
if(vertex_program != "" || pixel_program != "" || geometry_program != ""){
is_highlevel = true;
return;
}
}
if(enable_shaders >= 1){
// Look for assembly shaders
if(drivertype == video::EDT_DIRECT3D8){
// Direct3D 8 assembly shaders
vertex_program = sourcecache->getOrLoad(name, "d3d8_vertex.asm");
pixel_program = sourcecache->getOrLoad(name, "d3d8_pixel.asm");
}
else if(drivertype == video::EDT_DIRECT3D9){
// Direct3D 9 assembly shaders
vertex_program = sourcecache->getOrLoad(name, "d3d9_vertex.asm");
pixel_program = sourcecache->getOrLoad(name, "d3d9_pixel.asm");
}
else if(drivertype == video::EDT_OPENGL){
// OpenGL assembly shaders
vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.asm");
pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.asm");
}
if(vertex_program != "" || pixel_program != "")
return;
}
}

106
src/shader.h Normal file
View File

@@ -0,0 +1,106 @@
/*
Minetest-c55
Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2012 Kahrl <kahrl@gmx.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef SHADER_HEADER
#define SHADER_HEADER
#include "irrlichttypes_extrabloated.h"
#include "threads.h"
#include <string>
class IGameDef;
/*
shader.{h,cpp}: Shader handling stuff.
*/
/*
Gets the path to a shader by first checking if the file
name_of_shader/filename
exists in shader_path and if not, using the data path.
If not found, returns "".
Utilizes a thread-safe cache.
*/
std::string getShaderPath(const std::string &name_of_shader,
const std::string &filename);
struct ShaderInfo
{
std::string name;
video::E_MATERIAL_TYPE material;
ShaderInfo(): name(""), material(video::EMT_SOLID) {}
};
/*
Setter of constants for shaders
*/
namespace irr { namespace video {
class IMaterialRendererServices;
} }
class IShaderConstantSetter
{
public:
virtual ~IShaderConstantSetter(){};
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel) = 0;
};
/*
ShaderSource creates and caches shaders.
*/
class IShaderSource
{
public:
IShaderSource(){}
virtual ~IShaderSource(){}
virtual u32 getShaderId(const std::string &name){return 0;}
virtual u32 getShaderIdDirect(const std::string &name){return 0;}
virtual std::string getShaderName(u32 id){return "";}
virtual ShaderInfo getShader(u32 id){return ShaderInfo();}
virtual ShaderInfo getShader(const std::string &name){return ShaderInfo();}
};
class IWritableShaderSource : public IShaderSource
{
public:
IWritableShaderSource(){}
virtual ~IWritableShaderSource(){}
virtual u32 getShaderId(const std::string &name){return 0;}
virtual u32 getShaderIdDirect(const std::string &name){return 0;}
virtual std::string getShaderName(u32 id){return "";}
virtual ShaderInfo getShader(u32 id){return ShaderInfo();}
virtual ShaderInfo getShader(const std::string &name){return ShaderInfo();}
virtual void processQueue()=0;
virtual void insertSourceShader(const std::string &name_of_shader,
const std::string &filename, const std::string &program)=0;
virtual void rebuildShaders()=0;
virtual void addGlobalConstantSetter(IShaderConstantSetter *setter)=0;
};
IWritableShaderSource* createShaderSource(IrrlichtDevice *device);
#endif

92
src/staticobject.cpp Normal file
View File

@@ -0,0 +1,92 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "staticobject.h"
#include "util/serialize.h"
void StaticObject::serialize(std::ostream &os)
{
char buf[12];
// type
buf[0] = type;
os.write(buf, 1);
// pos
writeV3S32((u8*)buf, v3s32(pos.X*1000,pos.Y*1000,pos.Z*1000));
os.write(buf, 12);
// data
os<<serializeString(data);
}
void StaticObject::deSerialize(std::istream &is, u8 version)
{
char buf[12];
// type
is.read(buf, 1);
type = buf[0];
// pos
is.read(buf, 12);
v3s32 intp = readV3S32((u8*)buf);
pos.X = (f32)intp.X/1000;
pos.Y = (f32)intp.Y/1000;
pos.Z = (f32)intp.Z/1000;
// data
data = deSerializeString(is);
}
void StaticObjectList::serialize(std::ostream &os)
{
char buf[12];
// version
buf[0] = 0;
os.write(buf, 1);
// count
u16 count = m_stored.size() + m_active.size();
writeU16((u8*)buf, count);
os.write(buf, 2);
for(core::list<StaticObject>::Iterator
i = m_stored.begin();
i != m_stored.end(); i++)
{
StaticObject &s_obj = *i;
s_obj.serialize(os);
}
for(core::map<u16, StaticObject>::Iterator
i = m_active.getIterator();
i.atEnd()==false; i++)
{
StaticObject s_obj = i.getNode()->getValue();
s_obj.serialize(os);
}
}
void StaticObjectList::deSerialize(std::istream &is)
{
char buf[12];
// version
is.read(buf, 1);
u8 version = buf[0];
// count
is.read(buf, 2);
u16 count = readU16((u8*)buf);
for(u16 i=0; i<count; i++)
{
StaticObject s_obj;
s_obj.deSerialize(is, version);
m_stored.push_back(s_obj);
}
}

View File

@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include <string>
#include <sstream>
#include "util/serialize.h"
#include "debug.h"
struct StaticObject
{
@@ -43,33 +43,8 @@ struct StaticObject
{
}
void serialize(std::ostream &os)
{
char buf[12];
// type
buf[0] = type;
os.write(buf, 1);
// pos
writeV3S32((u8*)buf, v3s32(pos.X*1000,pos.Y*1000,pos.Z*1000));
os.write(buf, 12);
// data
os<<serializeString(data);
}
void deSerialize(std::istream &is, u8 version)
{
char buf[12];
// type
is.read(buf, 1);
type = buf[0];
// pos
is.read(buf, 12);
v3s32 intp = readV3S32((u8*)buf);
pos.X = (f32)intp.X/1000;
pos.Y = (f32)intp.Y/1000;
pos.Z = (f32)intp.Z/1000;
// data
data = deSerializeString(is);
}
void serialize(std::ostream &os);
void deSerialize(std::istream &is, u8 version);
};
class StaticObjectList
@@ -110,47 +85,8 @@ class StaticObjectList
m_active.remove(id);
}
void serialize(std::ostream &os)
{
char buf[12];
// version
buf[0] = 0;
os.write(buf, 1);
// count
u16 count = m_stored.size() + m_active.size();
writeU16((u8*)buf, count);
os.write(buf, 2);
for(core::list<StaticObject>::Iterator
i = m_stored.begin();
i != m_stored.end(); i++)
{
StaticObject &s_obj = *i;
s_obj.serialize(os);
}
for(core::map<u16, StaticObject>::Iterator
i = m_active.getIterator();
i.atEnd()==false; i++)
{
StaticObject s_obj = i.getNode()->getValue();
s_obj.serialize(os);
}
}
void deSerialize(std::istream &is)
{
char buf[12];
// version
is.read(buf, 1);
u8 version = buf[0];
// count
is.read(buf, 2);
u16 count = readU16((u8*)buf);
for(u16 i=0; i<count; i++)
{
StaticObject s_obj;
s_obj.deSerialize(is, version);
m_stored.push_back(s_obj);
}
}
void serialize(std::ostream &os);
void deSerialize(std::istream &is);
/*
NOTE: When an object is transformed to active, it is removed

View File

@@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "util/serialize.h"
#include "noise.h" // PseudoRandom used for random data for compression
#include "clientserver.h" // LATEST_PROTOCOL_VERSION
/*
Asserts that the exception occurs
@@ -314,6 +315,26 @@ struct TestSerialization: public TestBase
}
};
struct TestNodedefSerialization: public TestBase
{
void Run()
{
ContentFeatures f;
f.name = "default:stone";
for(int i = 0; i < 6; i++)
f.tiledef[i].name = "default_stone.png";
f.is_ground_content = true;
std::ostringstream os(std::ios::binary);
f.serialize(os, LATEST_PROTOCOL_VERSION);
verbosestream<<"Test ContentFeatures size: "<<os.str().size()<<std::endl;
std::istringstream is(os.str(), std::ios::binary);
ContentFeatures f2;
f2.deSerialize(is);
UASSERT(f.walkable == f2.walkable);
UASSERT(f.node_box.type == f2.node_box.type);
}
};
struct TestCompress: public TestBase
{
void Run()
@@ -1736,6 +1757,7 @@ void run_tests()
TEST(TestSettings);
TEST(TestCompress);
TEST(TestSerialization);
TEST(TestNodedefSerialization);
TESTPARAMS(TestMapNode, ndef);
TESTPARAMS(TestVoxelManipulator, ndef);
TESTPARAMS(TestVoxelAlgorithms, ndef);

View File

@@ -372,6 +372,18 @@ class TextureSource : public IWritableTextureSource
// Update new texture pointer and texture coordinates to an
// AtlasPointer based on it's texture id
void updateAP(AtlasPointer &ap);
bool isKnownSourceImage(const std::string &name)
{
bool is_known = false;
bool cache_found = m_source_image_existence.get(name, &is_known);
if(cache_found)
return is_known;
// Not found in cache; find out if a local file exists
is_known = (getTexturePath(name) != "");
m_source_image_existence.set(name, is_known);
return is_known;
}
// Processes queued texture requests from other threads.
// Shall be called from the main thread.
@@ -400,6 +412,9 @@ class TextureSource : public IWritableTextureSource
// This should be only accessed from the main thread
SourceImageCache m_sourcecache;
// Thread-safe cache of what source images are known (true = known)
MutexedMap<std::string, bool> m_source_image_existence;
// A texture id is index in this array.
// The first position contains a NULL texture.
core::array<SourceAtlasPointer> m_atlaspointer_cache;
@@ -781,6 +796,7 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im
assert(get_current_thread_id() == m_main_thread);
m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
m_source_image_existence.set(name, true);
}
void TextureSource::rebuildImagesAndTextures()

View File

@@ -131,6 +131,7 @@ class ITextureSource
virtual IrrlichtDevice* getDevice()
{return NULL;}
virtual void updateAP(AtlasPointer &ap){};
virtual bool isKnownSourceImage(const std::string &name)=0;
};
class IWritableTextureSource : public ITextureSource
@@ -149,6 +150,7 @@ class IWritableTextureSource : public ITextureSource
virtual IrrlichtDevice* getDevice()
{return NULL;}
virtual void updateAP(AtlasPointer &ap){};
virtual bool isKnownSourceImage(const std::string &name)=0;
virtual void processQueue()=0;
virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
@@ -159,10 +161,9 @@ class IWritableTextureSource : public ITextureSource
IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
enum MaterialType{
MATERIAL_ALPHA_NONE,
MATERIAL_ALPHA_VERTEX,
MATERIAL_ALPHA_SIMPLE, // >127 = opaque
MATERIAL_ALPHA_BLEND,
TILE_MATERIAL_BASIC,
TILE_MATERIAL_LIQUID_TRANSPARENT,
TILE_MATERIAL_LIQUID_OPAQUE,
};
// Material flags
@@ -176,6 +177,8 @@ enum MaterialType{
// Animation made up by splitting the texture to vertical frames, as
// defined by extra parameters
#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08
// Whether liquid shader should be used
#define MATERIAL_FLAG_
/*
This fully defines the looks of a tile.
@@ -186,9 +189,7 @@ struct TileSpec
TileSpec():
texture(0),
alpha(255),
//material_type(MATERIAL_ALPHA_NONE),
// Use this so that leaves don't need a separate material
material_type(MATERIAL_ALPHA_SIMPLE),
material_type(TILE_MATERIAL_BASIC),
material_flags(
//0 // <- DEBUG, Use the one below
MATERIAL_FLAG_BACKFACE_CULLING
@@ -216,15 +217,34 @@ struct TileSpec
// Sets everything else except the texture in the material
void applyMaterialOptions(video::SMaterial &material) const
{
if(material_type == MATERIAL_ALPHA_NONE)
material.MaterialType = video::EMT_SOLID;
else if(material_type == MATERIAL_ALPHA_VERTEX)
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
else if(material_type == MATERIAL_ALPHA_SIMPLE)
switch(material_type){
case TILE_MATERIAL_BASIC:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
else if(material_type == MATERIAL_ALPHA_BLEND)
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false;
}
void applyMaterialOptionsWithShaders(video::SMaterial &material,
const video::E_MATERIAL_TYPE &basic,
const video::E_MATERIAL_TYPE &liquid) const
{
switch(material_type){
case TILE_MATERIAL_BASIC:
material.MaterialType = basic;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = liquid;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = liquid;
break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false;
}

View File

@@ -107,6 +107,17 @@ class MutexedMap
return true;
}
core::list<Value> getValues()
{
core::list<Value> result;
for(typename core::map<Key, Value>::Iterator
i = m_values.getIterator();
i.atEnd() == false; i++){
result.push_back(i.getNode()->getValue());
}
return result;
}
private:
core::map<Key, Value> m_values;
JMutex m_mutex;

View File

@@ -222,7 +222,7 @@ class SharedBuffer
/*
Copies whole buffer
*/
SharedBuffer(T *t, unsigned int size)
SharedBuffer(const T *t, unsigned int size)
{
m_size = size;
if(m_size != 0)

View File

@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_SERIALIZE_HEADER
#include "../irrlichttypes.h"
#include "../irrlichttypes_bloated.h"
#include "../irr_v2d.h"
#include "../irr_v3d.h"
#include <iostream>
@@ -59,7 +60,7 @@ inline void writeU8(u8 *data, u8 i)
data[0] = ((i>> 0)&0xff);
}
inline u64 readU64(u8 *data)
inline u64 readU64(const u8 *data)
{
return ((u64)data[0]<<56) | ((u64)data[1]<<48)
| ((u64)data[2]<<40) | ((u64)data[3]<<32)
@@ -67,17 +68,17 @@ inline u64 readU64(u8 *data)
| ((u64)data[6]<<8) | ((u64)data[7]<<0);
}
inline u32 readU32(u8 *data)
inline u32 readU32(const u8 *data)
{
return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);
}
inline u16 readU16(u8 *data)
inline u16 readU16(const u8 *data)
{
return (data[0]<<8) | (data[1]<<0);
}
inline u8 readU8(u8 *data)
inline u8 readU8(const u8 *data)
{
return (data[0]<<0);
}
@@ -85,28 +86,28 @@ inline u8 readU8(u8 *data)
inline void writeS32(u8 *data, s32 i){
writeU32(data, (u32)i);
}
inline s32 readS32(u8 *data){
inline s32 readS32(const u8 *data){
return (s32)readU32(data);
}
inline void writeS16(u8 *data, s16 i){
writeU16(data, (u16)i);
}
inline s16 readS16(u8 *data){
inline s16 readS16(const u8 *data){
return (s16)readU16(data);
}
inline void writeS8(u8 *data, s8 i){
writeU8(data, (u8)i);
}
inline s8 readS8(u8 *data){
inline s8 readS8(const u8 *data){
return (s8)readU8(data);
}
inline void writeF1000(u8 *data, f32 i){
writeS32(data, i*1000);
}
inline f32 readF1000(u8 *data){
inline f32 readF1000(const u8 *data){
return (f32)readS32(data)/1000.;
}
@@ -116,7 +117,7 @@ inline void writeV3S32(u8 *data, v3s32 p)
writeS32(&data[4], p.Y);
writeS32(&data[8], p.Z);
}
inline v3s32 readV3S32(u8 *data)
inline v3s32 readV3S32(const u8 *data)
{
v3s32 p;
p.X = readS32(&data[0]);
@@ -131,7 +132,7 @@ inline void writeV3F1000(u8 *data, v3f p)
writeF1000(&data[4], p.Y);
writeF1000(&data[8], p.Z);
}
inline v3f readV3F1000(u8 *data)
inline v3f readV3F1000(const u8 *data)
{
v3f p;
p.X = (float)readF1000(&data[0]);
@@ -145,7 +146,7 @@ inline void writeV2F1000(u8 *data, v2f p)
writeF1000(&data[0], p.X);
writeF1000(&data[4], p.Y);
}
inline v2f readV2F1000(u8 *data)
inline v2f readV2F1000(const u8 *data)
{
v2f p;
p.X = (float)readF1000(&data[0]);
@@ -159,7 +160,7 @@ inline void writeV2S16(u8 *data, v2s16 p)
writeS16(&data[2], p.Y);
}
inline v2s16 readV2S16(u8 *data)
inline v2s16 readV2S16(const u8 *data)
{
v2s16 p;
p.X = readS16(&data[0]);
@@ -173,7 +174,7 @@ inline void writeV2S32(u8 *data, v2s32 p)
writeS32(&data[2], p.Y);
}
inline v2s32 readV2S32(u8 *data)
inline v2s32 readV2S32(const u8 *data)
{
v2s32 p;
p.X = readS32(&data[0]);
@@ -188,7 +189,7 @@ inline void writeV3S16(u8 *data, v3s16 p)
writeS16(&data[4], p.Z);
}
inline v3s16 readV3S16(u8 *data)
inline v3s16 readV3S16(const u8 *data)
{
v3s16 p;
p.X = readS16(&data[0]);
@@ -197,6 +198,24 @@ inline v3s16 readV3S16(u8 *data)
return p;
}
inline void writeARGB8(u8 *data, video::SColor p)
{
writeU8(&data[0], p.getAlpha());
writeU8(&data[1], p.getRed());
writeU8(&data[2], p.getGreen());
writeU8(&data[3], p.getBlue());
}
inline video::SColor readARGB8(const u8 *data)
{
video::SColor p;
p.setAlpha(readU8(&data[0]));
p.setRed(readU8(&data[1]));
p.setGreen(readU8(&data[2]));
p.setBlue(readU8(&data[3]));
return p;
}
/*
The above stuff directly interfaced to iostream
*/
@@ -344,6 +363,20 @@ inline v3s16 readV3S16(std::istream &is)
return readV3S16((u8*)buf);
}
inline void writeARGB8(std::ostream &os, video::SColor p)
{
char buf[4] = {0};
writeARGB8((u8*)buf, p);
os.write(buf, 4);
}
inline video::SColor readARGB8(std::istream &is)
{
char buf[4] = {0};
is.read(buf, 4);
return readARGB8((u8*)buf);
}
/*
More serialization stuff
*/