Compare commits

..

205 Commits
0.4.1 ... 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
Perttu Ahola
9696ed31a4 Version 0.4.3 2012-09-05 01:31:06 +03:00
Matthew I
c8895dd43b Prevent world creation if the world already exists 2012-09-05 01:17:44 +03:00
Matthew I
5dd1d354f8 Enforce stricter world names using a blacklist
Blacklisted characters are: / \
2012-09-05 01:17:28 +03:00
Perttu Ahola
a0da6bcf43 Add aux1_descends to key change menu 2012-09-05 00:55:19 +03:00
Perttu Ahola
1b63f8d6b9 Version 0.4.3-rc0; increase PROTOCOL_VERSION due to backwards-incompatible InventoryList width field 2012-09-05 00:39:43 +03:00
Perttu Ahola
60932d2bd7 Remove "Unknown inventory identifier" deserialization errors, because they can break forward compatibility 2012-09-04 23:02:56 +03:00
Perttu Ahola
ee2d9d973a Reorganize ClientMap rendering code for a bit more performance
- Don't select blocks for drawing in every frame
- Sort meshbuffers by material before drawing
2012-09-04 22:41:03 +03:00
Perttu Ahola
0e6f7a21c6 Fix shift-descend to ladders from a floor 2012-09-03 23:31:51 +03:00
Perttu Ahola
cc47ff7c4c Fix moving stuff into a mismatched stack in a "infinite" inventory 2012-09-02 23:01:42 +03:00
Perttu Ahola
e1a495ee30 Make inventory GUI do sane things when server-side inventory acts unusually 2012-09-02 22:51:38 +03:00
Perttu Ahola
6495007924 Fix bug in moving a larger sized stack in inventory on top of another
- You have 2 stacks: a with x items and b with y<x items
- Take a with your mouse and place it on b
- Whole stack a moves to b
- (y * 2 - x) items move from b to a
- (x - y) items are left to the mouse cursor
2012-09-02 20:24:45 +03:00
Perttu Ahola
d53a6cd851 Add overridable function for handling dropped items from nodes 2012-09-02 19:38:44 +03:00
Perttu Ahola
b6c12d2aa3 Fix github issue #224 2012-09-01 18:32:15 +03:00
Thomas Lauro
7a14171be4 Fix issue with openal mac os x compile 2012-09-01 18:19:59 +03:00
Perttu Ahola
5194505407 Fix GUIKeyChangeMenu so that '/' can be inserted on a finnish keyboard 2012-09-01 18:03:50 +03:00
Thomas Lauro
3e7957512b A bunch of GUIKeyChangeMenu fixes
- Display message when associating a key already in use
- Fix issue for not beeing able to assign space to an action
- Cleaning up
- Make it build (by celeron55)
2012-09-01 18:03:40 +03:00
Perttu Ahola
c0bd96d9b3 Attempt to fix flying gravel and dirt 2012-09-01 13:41:15 +03:00
Sergey Gilfanov
0f7728f84d Smooth transition to the desert biome. 2012-09-01 13:22:07 +03:00
Perttu Ahola
c129b3852b Fix the bouncy node related stuff a bit 2012-09-01 13:21:52 +03:00
Perttu Ahola
3b43c69df4 Add bouncy node group 2012-09-01 12:58:37 +03:00
Perttu Ahola
b17d3e7ad7 Add disable_jump and fall_damage_add_percent node groups 2012-09-01 12:07:27 +03:00
Ilya Zhuravlev
6a16075912 Add InventoryList width property & allow custom crafting grids. 2012-09-01 10:01:41 +03:00
sfan5
43ebec2be1 Make shift the default descent control on ladders and when flying
"aux1_descends" setting switches to the old descend control.
2012-09-01 09:48:07 +03:00
Perttu Ahola
f60227f112 Don't crash in "unexpected multibyte character"; just print it in log. Github #222 2012-09-01 09:23:39 +03:00
Perttu Ahola
f3630e95d6 Use proper alpha blit in some more texture modifiers 2012-08-31 18:06:47 +03:00
Perttu Ahola
8887105ffd Properly initialize baseimg for texture modifier [combine:WxH:X,Y=filename:X,Y=filename2 2012-08-31 17:58:55 +03:00
Matthew I
1d7408a7b8 Fix black display inside opaque water
Opaque water's solidness was being set to 2, like a normal node.
When you swim, it is treated like a solid block, and the display
goes black.  Setting it to 1 like transparent water allows you to
see.

It looks somewhat awkward when you swim, look up, and see an opaque
wall of water (the surface), but there isn't much that can be done
about it.  If you made the water transparent so it looked good,
it would defeat the purpose :) .
2012-08-31 17:50:12 +03:00
Perttu Ahola
0b1ab41a95 Fix github issue #213: Dropping unknown items crashes the game
The items will now just disappear when dropped.
2012-08-19 10:55:04 +03:00
Perttu Ahola
b717b6505b Fix crash when furnace is full (minimal game) 2012-08-12 17:11:23 +03:00
Matthew I
b29d609b0b Move chat commands to Lua and remove servercommand.{cpp,h}
Commands moved:
 /me
 /status
 /time
 /shutdown
 /ban
 /clearobjects
2012-08-12 16:45:58 +03:00
Matthew I
8a3c777c40 Add ban management routines to Lua API
Those functions are:
 * minetest.get_ban_list()
 * minetest.get_ban_description(ip_or_name)
 * minetest.ban_player(name)
 * minetest.unban_player_or_ip(ip_or_name)
2012-08-12 16:45:58 +03:00
Matthew I
548da26ddc Extend Lua API in order to implement chat commands
minetest.get_server_status()
minetest.request_shutdown()
EnvRef:clear_objects()
2012-08-12 16:44:55 +03:00
Matthew I
8e0ac70c4d Remove trailing comma after player list in server status string 2012-08-12 16:44:54 +03:00
Matthew I
1ed559bd24 Allow digging of unknown nodes
This allows the removal of nodes with unknown types.
get_item_callback() (C++) would fail if a node has an unknown type.  Now it
will try using the callback from minetest.nodedef_default in this case.
Also, minetest.node_dig() (Lua) was altered to always allow digging when
the node definition is empty (i.e. unknown node).
2012-08-12 16:04:18 +03:00
Perttu Ahola
e3b831e975 Fix wrong amount of nodes being dropped from inventory 2012-08-12 15:49:23 +03:00
darkrose
d05e3adbc7 fix node timers so on_timer gets the correct position 2012-08-12 15:28:31 +03:00
Perttu Ahola
a9a475ad50 Remove unwanted ! from ifs in inventory record-for-rollback code 2012-08-12 11:20:36 +03:00
Perttu Ahola
d38b465b7c Call this 0.4.2-rc1 2012-07-28 16:31:45 +03:00
Perttu Ahola
fd7ec2da91 Fix inventory segfault when rollback recording is disabled 2012-07-28 14:44:18 +03:00
Perttu Ahola
c9ed379e39 Add enable_rollback_recording setting, defaulting to false 2012-07-28 03:08:09 +03:00
Perttu Ahola
e64feefc61 Handle max<min in LuaPseudoRandom::l_next() 2012-07-27 19:03:15 +03:00
Perttu Ahola
3e754382af Tweak rollback and liquids 2012-07-27 15:46:51 +03:00
Perttu Ahola
7ef0a13250 Tweak rollback stuff 2012-07-27 14:52:29 +03:00
Perttu Ahola
0de3fb786d Increase automatic suspect guess timeframe 2012-07-27 13:54:14 +03:00
Perttu Ahola
1d44a98f2f ABM and liquid overload skip 2012-07-27 13:45:49 +03:00
Perttu Ahola
98ff4eb4ee Fix server build (a missing header) 2012-07-27 13:43:25 +03:00
Perttu Ahola
a9d8df83d2 Make the rollback system VERY FUCKING GOD DAMN POWERFUL 2012-07-27 13:24:28 +03:00
Perttu Ahola
508b7b5e51 Don't track liquids for rollback because of too much log 2012-07-27 02:46:54 +03:00
Perttu Ahola
f7dc72f8aa Properly rollback chat command triggered things 2012-07-27 02:37:04 +03:00
Perttu Ahola
0190f9b077 Experimental-ish rollback functionality 2012-07-27 02:27:18 +03:00
Perttu Ahola
0c91a0d59d Working group-shapeless and multigroup recipes 2012-07-26 13:49:13 +03:00
Perttu Ahola
a26a66a8c4 Restore focus to formspec menu when pressing a button 2012-07-25 18:28:40 +03:00
Perttu Ahola
100345f1e4 Deprecate minetest.add_to_creative_inventory and use group not_in_creative_inventory instead 2012-07-25 18:05:31 +03:00
Perttu Ahola
4535166a3b Add notice in the minimal game 2012-07-25 17:10:31 +03:00
Perttu Ahola
0346e68deb Add special return value -1 to inventry callbacks 2012-07-25 16:52:00 +03:00
Perttu Ahola
db62c227c8 Improve formspec positioning 2012-07-25 16:30:23 +03:00
Perttu Ahola
983e45ae92 Improve inventory callbacks a bit 2012-07-25 15:39:39 +03:00
Perttu Ahola
0a18dda158 Remove special handling of creative mode 2012-07-25 14:07:45 +03:00
Perttu Ahola
9eaf93d41d Detached inventory callbacks and reworked node metadata callbacks 2012-07-25 02:36:54 +03:00
Perttu Ahola
2ac20982e0 Detached inventories 2012-07-24 20:57:17 +03:00
Perttu Ahola
96eac87d47 builtin/item.lua: callbacks with copies of positions and nodes rather than recycle the same ones, which callbacks can modify 2012-07-24 17:46:17 +03:00
Perttu Ahola
0cf1ed544c darkrose should work at a nuclear power plant.
It'd take years to figure out what caused the accident.
2012-07-24 16:36:50 +03:00
Perttu Ahola
558e284e25 Update minetestmapper.py to support ver. 24 and 25 2012-07-24 15:17:00 +03:00
Perttu Ahola
5c31445117 Improve node timer format (map format version 25) and update mapformat.txt 2012-07-24 15:03:46 +03:00
Perttu Ahola
717ae67995 Add node timer test in minimal/experimental 2012-07-24 14:51:13 +03:00
Perttu Ahola
e8331f0c1d Add oldnode parameter to minetest.register_on_placenode callback 2012-07-23 20:44:56 +03:00
Perttu Ahola
c009aa3a22 Fix building on top of (pointable && buildable_to) nodes 2012-07-23 20:42:08 +03:00
Perttu Ahola
9af9d8f5d0 Describe node definition fields better in lua_api.txt 2012-07-23 20:17:44 +03:00
Perttu Ahola
2c027b03db Move /give, /giveme, /spawnentity and /pulverize to builtin/chatcommands.lua 2012-07-23 17:43:08 +03:00
Perttu Ahola
aef1332e42 Improve build configuration options 2012-07-23 15:23:33 +03:00
Perttu Ahola
16fc8b5fc2 Update lua_api.txt a bit 2012-07-23 08:48:55 +03:00
Perttu Ahola
fd845f27f5 Fix map deserialization and remove old serialization code 2012-07-23 08:18:39 +03:00
darkrose
ea62ee4b61 Increase node id/param0 to 16 bits, leaving param2 always with 8 bits 2012-07-23 08:18:39 +03:00
darkrose
cd6becd442 Implement node timers 2012-07-23 08:18:37 +03:00
Perttu Ahola
829f262c79 Fix terrible grammar in comment! 2012-07-22 20:36:06 +03:00
Perttu Ahola
246520b5cb Fix compressZlib() 2012-07-22 20:29:09 +03:00
Perttu Ahola
38bb649582 Test zlib wrapper's handling of large data 2012-07-22 20:27:55 +03:00
Perttu Ahola
82855a04ec Tweak test.c overally a bit 2012-07-22 20:26:54 +03:00
Perttu Ahola
6dfefaf229 Formspec button_exit[] and image_button_exit[] 2012-07-22 17:40:48 +03:00
darkrose
d44f8a854b Doc updates for formspec 2012-07-22 17:40:48 +03:00
Perttu Ahola
acf3a43095 Add /test1 command to minimal for testing a more complicated player inventory form 2012-07-22 17:40:48 +03:00
Perttu Ahola
4cc98d7add minetest.register_on_player_receive_fields() 2012-07-22 17:40:48 +03:00
darkrose
506203345b Implement formspec 2012-07-22 17:40:41 +03:00
Matthew I
c259f7c8bd Update Lua API documentation to include minetest.get_modnames() 2012-07-22 13:36:17 +03:00
Matthew I
c62a121cca Add "/mods" command to list mods to client 2012-07-22 13:36:10 +03:00
Matthew I
136eb32389 Add minetest.get_modnames() to Lua API 2012-07-22 13:36:03 +03:00
130 changed files with 10277 additions and 3489 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 1)
set(VERSION_PATCH 4)
if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif()
@@ -62,7 +62,7 @@ if(WIN32)
elseif(APPLE)
# Random placeholders; this isn't usually used and may not work
# See https://github.com/toabi/minetest-mac/
set(SHAREDIR "share/${PROJECT_NAME}")
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
set(BINDIR "bin")
set(DOCDIR "share/doc/${PROJECT_NAME}")
set(EXAMPLE_CONF_DIR ${DOCDIR})
@@ -78,18 +78,60 @@ elseif(UNIX) # Linux, BSD etc
set(ICONDIR "unix/icons")
set(LOCALEDIR "locale")
else()
set(SHAREDIR "share/${PROJECT_NAME}")
set(BINDIR "bin")
set(DOCDIR "share/doc/${PROJECT_NAME}")
set(MANDIR "share/man")
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
set(EXAMPLE_CONF_DIR ${DOCDIR})
set(XDG_APPS_DIR "share/applications")
set(ICONDIR "share/icons")
set(LOCALEDIR "share/locale")
set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications")
set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons")
set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/locale")
endif()
endif()
set(CUSTOM_SHAREDIR "" CACHE STRING "Directory to install data files into")
if(NOT CUSTOM_SHAREDIR STREQUAL "")
set(SHAREDIR "${CUSTOM_SHAREDIR}")
message(STATUS "Using SHAREDIR=${SHAREDIR}")
endif()
set(CUSTOM_BINDIR "" CACHE STRING "Directory to install binaries into")
if(NOT CUSTOM_BINDIR STREQUAL "")
set(BINDIR "${CUSTOM_BINDIR}")
message(STATUS "Using BINDIR=${BINDIR}")
endif()
set(CUSTOM_DOCDIR "" CACHE STRING "Directory to install documentation into")
if(NOT CUSTOM_DOCDIR STREQUAL "")
set(DOCDIR "${CUSTOM_DOCDIR}")
message(STATUS "Using DOCDIR=${DOCDIR}")
endif()
set(CUSTOM_MANDIR "" CACHE STRING "Directory to install manpages into")
if(NOT CUSTOM_MANDIR STREQUAL "")
set(MANDIR "${CUSTOM_MANDIR}")
message(STATUS "Using MANDIR=${MANDIR}")
endif()
set(CUSTOM_EXAMPLE_CONF_DIR "" CACHE STRING "Directory to install example config file into")
if(NOT CUSTOM_EXAMPLE_CONF_DIR STREQUAL "")
set(EXAMPLE_CONF_DIR "${CUSTOM_EXAMPLE_CONF_DIR}")
message(STATUS "Using EXAMPLE_CONF_DIR=${EXAMPLE_CONF_DIR}")
endif()
set(CUSTOM_XDG_APPS_DIR "" CACHE STRING "Directory to install .desktop files into")
if(NOT CUSTOM_XDG_APPS_DIR STREQUAL "")
set(XDG_APPS_DIR "${CUSTOM_XDG_APPS_DIR}")
message(STATUS "Using XDG_APPS_DIR=${XDG_APPS_DIR}")
endif()
set(CUSTOM_ICONDIR "" CACHE STRING "Directory to install icons into")
if(NOT CUSTOM_ICONDIR STREQUAL "")
set(ICONDIR "${CUSTOM_ICONDIR}")
message(STATUS "Using ICONDIR=${ICONDIR}")
endif()
set(CUSTOM_LOCALEDIR "" CACHE STRING "Directory to install l10n files into")
if(NOT CUSTOM_LOCALEDIR STREQUAL "")
set(LOCALEDIR "${CUSTOM_LOCALEDIR}")
message(STATUS "Using LOCALEDIR=${LOCALEDIR}")
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

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

View File

@@ -20,18 +20,13 @@ minetest.register_on_chat_message(function(name, message)
end
local cmd_def = minetest.chatcommands[cmd]
if cmd_def then
if not cmd_def.func then
-- This is a C++ command
return false
local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
if has_privs then
cmd_def.func(name, param)
else
local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
if has_privs then
cmd_def.func(name, param)
else
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
end
return true -- handled chat message
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
end
return true -- handled chat message
end
return false
end)
@@ -39,17 +34,15 @@ end)
--
-- Chat commands
--
minetest.register_chatcommand("me", {
params = "<action>",
description = "chat action (eg. /me orders a pizza)",
privs = {shout=true},
func = function(name, param)
minetest.chat_send_all("* " .. name .. " " .. param)
end,
})
-- Register C++ commands without functions
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)", privs = {shout=true}})
minetest.register_chatcommand("status", {description = "print server status line"})
minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}})
minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}})
minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}})
-- Register other commands
minetest.register_chatcommand("help", {
privs = {},
params = "(nothing)/all/privs/<cmd>",
@@ -145,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, ' '))
@@ -182,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, ' '))
@@ -222,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, '')
@@ -371,3 +366,296 @@ minetest.register_chatcommand("set", {
end,
})
minetest.register_chatcommand("mods", {
params = "",
description = "lists mods installed on the server",
privs = {},
func = function(name, param)
local response = ""
local modnames = minetest.get_modnames()
for i, mod in ipairs(modnames) do
response = response .. mod
-- Add space if not at the end
if i ~= #modnames then
response = response .. " "
end
end
minetest.chat_send_player(name, response)
end,
})
local function handle_give_command(cmd, giver, receiver, stackstring)
minetest.log("action", giver.." invoked "..cmd..', stackstring="'
..stackstring..'"')
minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
local itemstack = ItemStack(stackstring)
if itemstack:is_empty() then
minetest.chat_send_player(giver, 'error: cannot give an empty item')
return
elseif not itemstack:is_known() then
minetest.chat_send_player(giver, 'error: cannot give an unknown item')
return
end
local receiverref = minetest.env:get_player_by_name(receiver)
if receiverref == nil then
minetest.chat_send_player(giver, receiver..' is not a known player')
return
end
local leftover = receiverref:get_inventory():add_item("main", itemstack)
if leftover:is_empty() then
partiality = ""
elseif leftover:get_count() == itemstack:get_count() then
partiality = "could not be "
else
partiality = "partially "
end
-- The actual item stack string may be different from what the "giver"
-- entered (e.g. big numbers are always interpreted as 2^16-1).
stackstring = itemstack:to_string()
if giver == receiver then
minetest.chat_send_player(giver, '"'..stackstring
..'" '..partiality..'added to inventory.');
else
minetest.chat_send_player(giver, '"'..stackstring
..'" '..partiality..'added to '..receiver..'\'s inventory.');
minetest.chat_send_player(receiver, '"'..stackstring
..'" '..partiality..'added to inventory.');
end
end
minetest.register_chatcommand("give", {
params = "<name> <itemstring>",
description = "give item to player",
privs = {give=true},
func = function(name, param)
local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
if not toname or not itemstring then
minetest.chat_send_player(name, "name and itemstring required")
return
end
handle_give_command("/give", name, toname, itemstring)
end,
})
minetest.register_chatcommand("giveme", {
params = "<itemstring>",
description = "give item to yourself",
privs = {give=true},
func = function(name, param)
local itemstring = string.match(param, "(.+)$")
if not itemstring then
minetest.chat_send_player(name, "itemstring required")
return
end
handle_give_command("/giveme", name, name, itemstring)
end,
})
minetest.register_chatcommand("spawnentity", {
params = "<entityname>",
description = "spawn entity at your position",
privs = {give=true, interact=true},
func = function(name, param)
local entityname = string.match(param, "(.+)$")
if not entityname then
minetest.chat_send_player(name, "entityname required")
return
end
print('/spawnentity invoked, entityname="'..entityname..'"')
local player = minetest.env:get_player_by_name(name)
if player == nil then
print("Unable to spawn entity, player is nil")
return true -- Handled chat message
end
local p = player:getpos()
p.y = p.y + 1
minetest.env:add_entity(p, entityname)
minetest.chat_send_player(name, '"'..entityname
..'" spawned.');
end,
})
minetest.register_chatcommand("pulverize", {
params = "",
description = "delete item in hand",
privs = {},
func = function(name, param)
local player = minetest.env:get_player_by_name(name)
if player == nil then
print("Unable to pulverize, player is nil")
return true -- Handled chat message
end
if player:get_wielded_item():is_empty() then
minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
else
player:set_wielded_item(nil)
minetest.chat_send_player(name, 'An item was pulverized.')
end
end,
})
-- Key = player name
minetest.rollback_punch_callbacks = {}
minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
if minetest.rollback_punch_callbacks[name] then
minetest.rollback_punch_callbacks[name](pos, node, puncher)
minetest.rollback_punch_callbacks[name] = nil
end
end)
minetest.register_chatcommand("rollback_check", {
params = "[<range>] [<seconds>]",
description = "check who has last touched a node or near it, "..
"max. <seconds> ago (default range=0, seconds=86400=24h)",
privs = {rollback=true},
func = function(name, param)
local range, seconds = string.match(param, "(%d+) *(%d*)")
range = tonumber(range) or 0
seconds = tonumber(seconds) or 86400
minetest.chat_send_player(name, "Punch a node (limits set: range="..
dump(range).." seconds="..dump(seconds).."s)")
minetest.rollback_punch_callbacks[name] = function(pos, node, puncher)
local name = puncher:get_player_name()
minetest.chat_send_player(name, "Checking...")
local actor, act_p, act_seconds =
minetest.rollback_get_last_node_actor(pos, range, seconds)
if actor == "" then
minetest.chat_send_player(name, "Nobody has touched the "..
"specified location in "..dump(seconds).." seconds")
return
end
local nodedesc = "this node"
if act_p.x ~= pos.x or act_p.y ~= pos.y or act_p.z ~= pos.z then
nodedesc = minetest.pos_to_string(act_p)
end
local nodename = minetest.env:get_node(act_p).name
minetest.chat_send_player(name, "Last actor on "..nodedesc..
" was "..actor..", "..dump(act_seconds)..
"s ago (node is now "..nodename..")")
end
end,
})
minetest.register_chatcommand("rollback", {
params = "<player name> [<seconds>] | :<actor> [<seconds>]",
description = "revert actions of a player; default for <seconds> is 60",
privs = {rollback=true},
func = function(name, param)
local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
if not target_name then
local player_name = nil;
player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
if not player_name then
minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check")
return
end
target_name = "player:"..player_name
end
seconds = tonumber(seconds) or 60
minetest.chat_send_player(name, "Reverting actions of "..
dump(target_name).." since "..dump(seconds).." seconds.")
local success, log = minetest.rollback_revert_actions_by(
target_name, seconds)
if #log > 10 then
minetest.chat_send_player(name, "(log is too long to show)")
else
for _,line in ipairs(log) do
minetest.chat_send_player(name, line)
end
end
if success then
minetest.chat_send_player(name, "Reverting actions succeeded.")
else
minetest.chat_send_player(name, "Reverting actions FAILED.")
end
end,
})
minetest.register_chatcommand("status", {
params = "",
description = "print server status line",
privs = {},
func = function(name, param)
minetest.chat_send_player(name, minetest.get_server_status())
end,
})
minetest.register_chatcommand("time", {
params = "<0...24000>",
description = "set time of day",
privs = {settime=true},
func = function(name, param)
if param == "" then
minetest.chat_send_player(name, "Missing parameter")
return
end
local newtime = tonumber(param)
if newtime == nil then
minetest.chat_send_player(name, "Invalid time")
else
minetest.env:set_timeofday((newtime % 24000) / 24000)
minetest.chat_send_player(name, "Time of day changed.")
minetest.log("action", name .. " sets time " .. newtime)
end
end,
})
minetest.register_chatcommand("shutdown", {
params = "",
description = "shutdown server",
privs = {server=true},
func = function(name, param)
minetest.log("action", name .. " shuts down server")
minetest.request_shutdown()
minetest.chat_send_all("*** Server shutting down (operator request).")
end,
})
minetest.register_chatcommand("ban", {
params = "<name>",
description = "ban IP of player",
privs = {ban=true},
func = function(name, param)
if param == "" then
minetest.chat_send_player(name, "Ban list: " .. minetest.get_ban_list())
return
end
if not minetest.env:get_player_by_name(param) then
minetest.chat_send_player(name, "No such player")
return
end
if not minetest.ban_player(param) then
minetest.chat_send_player(name, "Failed to ban player")
else
local desc = minetest.get_ban_description(param)
minetest.chat_send_player(name, "Banned " .. desc .. ".")
minetest.log("action", name .. " bans " .. desc .. ".")
end
end,
})
minetest.register_chatcommand("unban", {
params = "<name/ip>",
description = "remove IP ban",
privs = {ban=true},
func = function(name, param)
if not minetest.unban_player_or_ip(param) then
minetest.chat_send_player(name, "Failed to unban player/IP")
else
minetest.chat_send_player(name, "Unbanned " .. param)
minetest.log("action", name .. " unbans " .. param)
end
end,
})
minetest.register_chatcommand("clearobjects", {
params = "",
description = "clear all objects in world",
privs = {server=true},
func = function(name, param)
minetest.log("action", name .. " clears all objects")
minetest.chat_send_all("Clearing all objects. This may take long. You may experience a timeout. (by " .. name .. ")")
minetest.env:clear_objects()
minetest.log("action", "object clearing done")
minetest.chat_send_all("*** Cleared all objects.")
end,
})

View File

@@ -16,4 +16,11 @@ minetest.digprop_woodlike = digprop_err
minetest.digprop_leaveslike = digprop_err
minetest.digprop_glasslike = digprop_err
minetest.node_metadata_inventory_move_allow_all = function()
minetest.log("info", "WARNING: minetest.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
end
minetest.add_to_creative_inventory = function(itemstring)
minetest.log('info', "WARNING: minetest.add_to_creative_inventory: This function is deprecated and does nothing.")
end

View File

@@ -0,0 +1,19 @@
-- Minetest: builtin/detached_inventory.lua
minetest.detached_inventories = {}
function minetest.create_detached_inventory(name, callbacks)
local stuff = {}
stuff.name = name
if callbacks then
stuff.allow_move = callbacks.allow_move
stuff.allow_put = callbacks.allow_put
stuff.allow_take = callbacks.allow_take
stuff.on_move = callbacks.on_move
stuff.on_put = callbacks.on_put
stuff.on_take = callbacks.on_take
end
minetest.detached_inventories[name] = stuff
return minetest.create_detached_inventory_raw(name)
end

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 {}
@@ -124,55 +124,92 @@ end
function minetest.item_place_node(itemstack, placer, pointed_thing)
local item = itemstack:peek_item()
local def = itemstack:get_definition()
if def.type == "node" and pointed_thing.type == "node" then
local pos = pointed_thing.above
local oldnode = minetest.env:get_node(pos)
local olddef = ItemStack({name=oldnode.name}):get_definition()
if not olddef.buildable_to then
minetest.log("info", placer:get_player_name() .. " tried to place"
.. " node in invalid position " .. minetest.pos_to_string(pos)
.. ", replacing " .. oldnode.name)
return
end
minetest.log("action", placer:get_player_name() .. " places node "
.. def.name .. " at " .. minetest.pos_to_string(pos))
local newnode = {name = def.name, param1 = 0, param2 = 0}
-- Calculate direction for wall mounted stuff like torches and signs
if def.paramtype2 == 'wallmounted' then
local under = pointed_thing.under
local above = pointed_thing.above
local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
newnode.param2 = minetest.dir_to_wallmounted(dir)
-- Calculate the direction for furnaces and chests and stuff
elseif def.paramtype2 == 'facedir' then
local placer_pos = placer:getpos()
if placer_pos then
local dir = {x = pos.x - placer_pos.x, y = pos.y - placer_pos.y, z = pos.z - placer_pos.z}
newnode.param2 = minetest.dir_to_facedir(dir)
minetest.log("action", "facedir: " .. newnode.param2)
end
end
-- Add node and update
minetest.env:add_node(pos, newnode)
-- Run callback
if def.after_place_node then
def.after_place_node(pos, placer)
end
-- Run script hook (deprecated)
local _, callback
for _, callback in ipairs(minetest.registered_on_placenodes) do
callback(pos, newnode, placer)
end
itemstack:take_item()
if def.type ~= "node" or pointed_thing.type ~= "node" then
return itemstack
end
local under = pointed_thing.under
local oldnode_under = minetest.env:get_node(under)
local olddef_under = ItemStack({name=oldnode_under.name}):get_definition()
olddef_under = olddef_under or minetest.nodedef_default
local above = pointed_thing.above
local oldnode_above = minetest.env:get_node(above)
local olddef_above = ItemStack({name=oldnode_above.name}):get_definition()
olddef_above = olddef_above or minetest.nodedef_default
if not olddef_above.buildable_to and not olddef_under.buildable_to then
minetest.log("info", placer:get_player_name() .. " tried to place"
.. " node in invalid position " .. minetest.pos_to_string(above)
.. ", replacing " .. oldnode_above.name)
return
end
-- Place above pointed node
local place_to = {x = above.x, y = above.y, z = above.z}
-- If node under is buildable_to, place into it instead (eg. snow)
if olddef_under.buildable_to then
minetest.log("info", "node under is buildable to")
place_to = {x = under.x, y = under.y, z = under.z}
end
minetest.log("action", placer:get_player_name() .. " places node "
.. def.name .. " at " .. minetest.pos_to_string(place_to))
local oldnode = minetest.env:get_node(place_to)
local newnode = {name = def.name, param1 = 0, param2 = 0}
-- Calculate direction for wall mounted stuff like torches and signs
if def.paramtype2 == 'wallmounted' then
local dir = {
x = under.x - above.x,
y = under.y - above.y,
z = under.z - above.z
}
newnode.param2 = minetest.dir_to_wallmounted(dir)
-- Calculate the direction for furnaces and chests and stuff
elseif def.paramtype2 == 'facedir' then
local placer_pos = placer:getpos()
if placer_pos then
local dir = {
x = above.x - placer_pos.x,
y = above.y - placer_pos.y,
z = above.z - placer_pos.z
}
newnode.param2 = minetest.dir_to_facedir(dir)
minetest.log("action", "facedir: " .. newnode.param2)
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)
-- Run callback
if def.after_place_node then
-- Copy place_to because callback can modify it
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
def.after_place_node(place_to_copy, placer)
end
-- Run script hook
local _, callback
for _, callback in ipairs(minetest.registered_on_placenodes) do
-- Copy pos and node because callback can modify them
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
callback(place_to_copy, newnode_copy, placer, oldnode_copy)
end
itemstack:take_item()
return itemstack
end
@@ -198,10 +235,12 @@ function minetest.item_drop(itemstack, dropper, pos)
local v = dropper:get_look_dir()
local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z}
local obj = minetest.env:add_item(p, itemstack)
v.x = v.x*2
v.y = v.y*2 + 1
v.z = v.z*2
obj:setvelocity(v)
if obj then
v.x = v.x*2
v.y = v.y*2 + 1
v.z = v.z*2
obj:setvelocity(v)
end
else
minetest.env:add_item(pos, itemstack)
end
@@ -222,16 +261,29 @@ function minetest.node_punch(pos, node, puncher)
-- Run script hook
local _, callback
for _, callback in ipairs(minetest.registered_on_punchnodes) do
callback(pos, node, puncher)
-- Copy pos and node because callback can modify them
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
callback(pos_copy, node_copy, puncher)
end
end
function minetest.handle_node_drops(pos, drops, digger)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
digger:get_inventory():add_item("main", dropped_item)
end
end
end
function minetest.node_dig(pos, node, digger)
minetest.debug("node_dig")
local def = ItemStack({name=node.name}):get_definition()
if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
-- Check if def ~= 0 because we always want to be able to remove unknown nodes
if #def ~= 0 and not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
minetest.debug("not diggable")
minetest.log("info", digger:get_player_name() .. " tried to dig "
.. node.name .. " which is not diggable "
@@ -242,29 +294,20 @@ function minetest.node_dig(pos, node, digger)
minetest.log('action', digger:get_player_name() .. " digs "
.. node.name .. " at " .. minetest.pos_to_string(pos))
if not minetest.setting_getbool("creative_mode") then
local wielded = digger:get_wielded_item()
local drops = minetest.get_node_drops(node.name, wielded:get_name())
local wielded = digger:get_wielded_item()
local drops = minetest.get_node_drops(node.name, wielded:get_name())
-- Wear out tool
tp = wielded:get_tool_capabilities()
dp = minetest.get_dig_params(def.groups, tp)
wielded:add_wear(dp.wear)
digger:set_wielded_item(wielded)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
digger:get_inventory():add_item("main", dropped_item)
end
end
end
-- Wear out tool
local tp = wielded:get_tool_capabilities()
local dp = minetest.get_dig_params(def.groups, tp)
wielded:add_wear(dp.wear)
digger:set_wielded_item(wielded)
local oldnode = nil
-- Handle drops
minetest.handle_node_drops(pos, drops, digger)
local oldmetadata = nil
if def.after_dig_node then
oldnode = node;
oldmetadata = minetest.env:get_meta(pos):to_table()
end
@@ -273,51 +316,22 @@ function minetest.node_dig(pos, node, digger)
-- Run callback
if def.after_dig_node then
def.after_dig_node(pos, oldnode, oldmetadata, digger)
-- Copy pos and node because callback can modify them
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
end
-- Run script hook (deprecated)
-- Run script hook
local _, callback
for _, callback in ipairs(minetest.registered_on_dignodes) do
callback(pos, node, digger)
-- Copy pos and node because callback can modify them
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
callback(pos_copy, node_copy, digger)
end
end
function minetest.node_metadata_inventory_move_allow_all(pos, from_list,
from_index, to_list, to_index, count, player)
minetest.log("verbose", "node_metadata_inventory_move_allow_all")
local meta = minetest.env:get_meta(pos)
local inv = meta:get_inventory()
local from_stack = inv:get_stack(from_list, from_index)
local taken_items = from_stack:take_item(count)
inv:set_stack(from_list, from_index, from_stack)
local to_stack = inv:get_stack(to_list, to_index)
to_stack:add_item(taken_items)
inv:set_stack(to_list, to_index, to_stack)
end
function minetest.node_metadata_inventory_offer_allow_all(pos, listname, index, stack, player)
minetest.log("verbose", "node_metadata_inventory_offer_allow_all")
local meta = minetest.env:get_meta(pos)
local inv = meta:get_inventory()
local the_stack = inv:get_stack(listname, index)
the_stack:add_item(stack)
inv:set_stack(listname, index, the_stack)
return ItemStack("")
end
function minetest.node_metadata_inventory_take_allow_all(pos, listname, index, count, player)
minetest.log("verbose", "node_metadata_inventory_take_allow_all")
local meta = minetest.env:get_meta(pos)
local inv = meta:get_inventory()
local the_stack = inv:get_stack(listname, index)
local taken_items = the_stack:take_item(count)
inv:set_stack(listname, index, the_stack)
return taken_items
end
-- This is used to allow mods to redefine minetest.item_place and so on
-- NOTE: This is not the preferred way. Preferred way is to provide enough
-- callbacks to not require redefining global functions. -celeron55

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

@@ -244,6 +244,7 @@ minetest.register_item(":unknown", {
inventory_image = "unknown_item.png",
on_place = minetest.item_place,
on_drop = minetest.item_drop,
groups = {not_in_creative_inventory=1},
})
minetest.register_node(":air", {
@@ -258,6 +259,8 @@ minetest.register_node(":air", {
diggable = false,
buildable_to = true,
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1},
})
minetest.register_node(":ignore", {
@@ -272,23 +275,16 @@ 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},
})
-- The hand (bare definition)
minetest.register_item(":", {
type = "none",
groups = {not_in_creative_inventory=1},
})
--
-- Creative inventory
--
minetest.creative_inventory = {}
minetest.add_to_creative_inventory = function(itemstring)
table.insert(minetest.creative_inventory, itemstring)
end
--
-- Callback registration
--
@@ -299,8 +295,15 @@ local function make_registration()
return t, registerfunc
end
local function make_registration_reverse()
local t = {}
local registerfunc = function(func) table.insert(t, 1, func) end
return t, registerfunc
end
minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
minetest.registered_on_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()
@@ -310,4 +313,5 @@ minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registr
minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration()
minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration()
minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse()

View File

@@ -44,5 +44,5 @@ minetest.register_privilege("fast", {
description = "Can walk fast using the fast_move mode",
give_to_singleplayer = false,
})
minetest.register_privilege("rollback", "Can use the rollback functionality")

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/
@@ -254,9 +254,9 @@ Nodes are passed by value between Lua and the engine.
They are represented by a table:
{name="name", param1=num, param2=num}
param1 and param2 are 8 bit and 4 bit integers, respectively. The engine
uses them for certain automated functions. If you don't use these
functions, you can use them to store arbitrary values.
param1 and param2 are 8 bit integers. The engine uses them for certain
automated functions. If you don't use these functions, you can use them to
store arbitrary values.
The functions of param1 and param2 are determined by certain fields in the
node definition:
@@ -423,7 +423,7 @@ effective towards.
Groups in crafting recipes
---------------------------
An example:
An example: Make meat soup from any meat, any water and any bowl
{
output = 'food:meat_soup_raw',
recipe = {
@@ -431,7 +431,13 @@ An example:
{'group:water'},
{'group:bowl'},
},
preserve = {'group:bowl'}, -- Not implemented yet (TODO)
-- preserve = {'group:bowl'}, -- Not implemented yet (TODO)
}
An another example: Make red wool from white wool and red dye
{
type = 'shapeless',
output = 'wool:red',
recipe = {'wool:white', 'group:dye,basecolor_red'},
}
Special groups
@@ -447,6 +453,13 @@ Special groups
- 2: node is removed without tool wear after 0.5 seconds or so
(rail, sign)
- 3: node is removed without tool wear immediately (torch)
- 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
----------------------------------------------
@@ -672,32 +685,82 @@ Examples:
Elements:
invsize[<W>,<H>;]
size[<W>,<H>]
^ Define the size of the menu in inventory slots
^ deprecated: invsize[<W>,<H>;]
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;]
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]
^ Show an inventory list
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
^ w and h are the size of the field
^ fields are a set height, but will be vertically centred on h
^ Position and size units are inventory slots
^ name is the name of the field as returned in fields to on_receive_fields
^ label, if not blank, will be text printed on the top left above the field
^ default is the default value of the field
^ default may contain variable references such as '${text}' which
will fill the value from the metadata value 'text'
^ Note: no extra text or more than a single variable is supported ATM.
field[<name>;<label>;<default>]
^ as above but without position/size units
^ special field for creating simple forms, such as sign text input
^ must be used without a size[] element
^ a 'Proceed' button will be added automatically
label[<X>,<Y>;<label>]
^ x and y work as per field
^ label is the text on the label
^ Position and size units are inventory slots
^ Not implemented
button[<X>,<Y>;<W>,<H>;<name>;<label>]
^ Clickable button. When clicked, fields will be sent.
^ Button will be visible as a field, with the value "active".
^ x, y and name work as per field
^ w and h are the size of the button
^ label is the text on the button
^ Position and size units are inventory slots
^ Not implemented
image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ x, y, w, h, and name work as per button
^ 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.
image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ When clicked, fields will be sent and the form will quit.
Inventory location:
- "context": Selected node metadata (deprecated: "current_name")
- "current_player": Player to whom the menu is shown
- "player:<name>": Any player
- "nodemeta:<X>,<Y>,<Z>": Any node metadata
- "detached:<name>": A detached inventory
Helper functions
-----------------
@@ -718,6 +781,8 @@ minetest namespace reference
minetest.get_current_modname() -> string
minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
^ Useful for loading additional .lua modules or static data from mod
minetest.get_modnames() -> list of installed mods
^ Return a list of installed mods, sorted alphabetically
minetest.get_worldpath() -> eg. "/home/user/.minetest/world"
^ Useful for storing custom data
minetest.is_singleplayer()
@@ -740,11 +805,16 @@ minetest.register_craft(recipe)
Global callback registration functions: (Call these only at load time)
minetest.register_globalstep(func(dtime))
^ Called every server step, usually interval of 0.05s
minetest.register_on_placenode(func(pos, newnode, placer))
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
minetest.register_on_dignode(func(pos, oldnode, digger))
^ Called when a node has been dug. digger can be nil.
^ Called when a node has been dug.
^ Deprecated: Use on_destruct or after_dig_node in node definition instead
minetest.register_on_punchnode(func(pos, node, puncher))
^ Called when a node is punched
@@ -764,6 +834,11 @@ minetest.register_on_joinplayer(func(ObjectRef))
minetest.register_on_leaveplayer(func(ObjectRef))
^ Called when a player leaves the game
minetest.register_on_chat_message(func(name, message))
^ Called always when a player says something
minetest.register_on_player_receive_fields(func(player, formname, fields))
^ Called when a button is pressed in player's inventory form
^ Newest functions are called first
^ If function returns true, remaining functions are not called
Other registration functions:
minetest.register_chatcommand(cmd, chatcommand definition)
@@ -808,6 +883,10 @@ Inventory:
minetest.get_inventory(location) -> InvRef
^ location = eg. {type="player", name="celeron55"}
{type="node", pos={x=, y=, z=}}
{type="detached", name="creative"}
minetest.create_detached_inventory(name, callbacks) -> InvRef
^ callbacks: See "Detached inventory callbacks"
^ Creates a detached inventory. If it already exists, it is cleared.
Item handling:
minetest.inventorycube(img1, img2, img3)
@@ -836,6 +915,20 @@ minetest.get_craft_recipe(output) -> input
^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
stack 5, stack 6, stack 7, stack 8, stack 9 }
^ input.items = nil if no recipe found
minetest.handle_node_drops(pos, drops, digger)
^ drops: list of itemstrings
^ Handles drops from nodes after digging: Default action is to put them into
digger's inventory
^ Can be overridden to get different functionality (eg. dropping items on
ground)
Rollbacks:
minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
^ Find who has done something to a node, or near a node
^ actor: "player:<name>", also "liquid".
minetest.rollback_revert_actions_by(actor, seconds) -> bool, log messages
^ Revert latest actions of someone
^ actor: "player:<name>", also "liquid".
Defaults for the on_* item definition functions:
(These return the leftover itemstack)
@@ -868,6 +961,16 @@ minetest.after(time, func, param)
^ Call function after time seconds
^ param is optional; to pass multiple parameters, pass a table.
Server:
minetest.request_shutdown() -> request for server shutdown
minetest.get_server_status() -> server status string
Bans:
minetest.get_ban_list() -> ban list (same as minetest.get_ban_description(""))
minetest.get_ban_description(ip_or_name) -> ban description (string)
minetest.ban_player(name) -> ban a player
minetest.unban_player_or_ip(name) -> unban player or IP address
Random:
minetest.get_connected_players() -> list of ObjectRefs
minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer
@@ -939,12 +1042,14 @@ methods:
^ Dig node with the same effects that a player would cause
- punch_node(pos)
^ Punch node with the same effects that a player would cause
- get_meta(pos) -- Get a NodeMetaRef at that position
- get_node_timer(pos) -- Get NodeTimerRef
- add_entity(pos, name): Spawn Lua-defined entity at position
^ Returns ObjectRef, or nil if failed
- add_item(pos, item): Spawn item
^ Returns ObjectRef, or nil if failed
- get_meta(pos) -- Get a NodeMetaRef at that position
- get_player_by_name(name) -- Get an ObjectRef to a player
- get_objects_inside_radius(pos, radius)
- set_timeofday(val): val: 0...1; 0 = midnight, 0.5 = midday
@@ -955,6 +1060,8 @@ methods:
^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
- get_perlin(seeddiff, octaves, persistence, scale)
^ Return world-specific perlin noise (int(worldseed)+seeddiff)
- clear_objects()
^ clear all objects in the environments
Deprecated:
- add_rat(pos): Add C++ rat object (no-op)
- add_firefly(pos): Add C++ firefly object (no-op)
@@ -973,6 +1080,26 @@ methods:
- to_table() -> nil or {fields = {...}, inventory = {list1 = {}, ...}}
- from_table(nil or {})
^ See "Node Metadata"
NodeTimerRef: Node Timers - a high resolution persistent per-node timer
- Can be gotten via minetest.env:get_node_timer(pos)
methods:
- set(timeout,elapsed)
^ set a timer's state
^ timeout is in seconds, and supports fractional values (0.1 etc)
^ elapsed is in seconds, and supports fractional values (0.1 etc)
^ will trigger the node's on_timer function after timeout-elapsed seconds
- start(timeout)
^ start a timer
^ equivelent to set(timeout,0)
- stop()
^ stops the timer
- get_timeout() -> current timeout in seconds
^ if timeout is 0, timer is inactive
- get_elapsed() -> current elapsed time in seconds
^ the node's on_timer function will be called after timeout-elapsed seconds
- is_started() -> boolean state of timer
^ returns true if timer is started, otherwise false
ObjectRef: Moving things in the game are generally these
(basically reference to a C++ ServerActiveObject)
@@ -984,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)
@@ -993,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})
@@ -1018,12 +1150,18 @@ 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
- get_size(listname): get size of a list
- set_size(listname, size): set size of a list
- get_width(listname): get width of a list
- set_width(listname, width): set width of list; currently used for crafting
- get_stack(listname, i): get a copy of stack index i in list
- set_stack(listname, i, stack): copy stack to index i in list
- get_list(listname): return full list
@@ -1115,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,
@@ -1131,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),
@@ -1219,30 +1359,31 @@ Node definition (register_node)
drawtype = "normal", -- See "Node drawtypes"
visual_scale = 1.0,
tiles = {tile definition 1, def2, def3, def4, def5, def6},
^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images)
^ List can be shortened to needed length
^ Old field name: tile_images
special_tiles = {tile definition 1, Tile definition 2},
^ Special textures of node; used rarely (old field name: special_materials)
^ List can be shortened to needed length
^ Old field name: special_materials
alpha = 255,
post_effect_color = {a=0, r=0, g=0, b=0},
paramtype = "none",
paramtype2 = "none",
is_ground_content = false,
sunlight_propagates = false,
walkable = true,
pointable = true,
diggable = true,
climbable = false,
buildable_to = false,
drop = "",
-- alternatively drop = { max_items = ..., items = { ... } }
liquidtype = "none",
liquid_alternative_flowing = "",
liquid_alternative_source = "",
liquid_viscosity = 0,
light_source = 0,
damage_per_second = 0,
post_effect_color = {a=0, r=0, g=0, b=0}, -- If player is inside node
paramtype = "none", -- See "Nodes"
paramtype2 = "none", -- See "Nodes"
is_ground_content = false, -- Currently not used for anything
sunlight_propagates = false, -- If true, sunlight will go infinitely through this
walkable = true, -- If true, objects collide with node
pointable = true, -- If true, can be pointed at
diggable = true, -- If false, can never be dug
climbable = false, -- If true, can be climbed on (ladder)
buildable_to = false, -- If true, placed nodes can replace this node
drop = "", -- alternatively drop = { max_items = ..., items = { ... } }
liquidtype = "none", -- "none"/"source"/"flowing"
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"
selection_box = {type="regular"}, -- See "Node boxes"
legacy_facedir_simple = false, -- Support maps made in and before January 2012
@@ -1280,44 +1421,47 @@ Node definition (register_node)
on_punch = func(pos, node, puncher),
^ default: minetest.node_punch
^ By default: does nothing
on_dig = func(pos, node, digger),
on_dig = func(pos, node, digger),
^ default: minetest.node_dig
^ By default: checks privileges, wears out tool and removes node
on_timer = function(pos,elapsed),
^ default: nil
^ called by NodeTimers, see EnvRef and NodeTimerRef
^ elapsed is the total time passed since the timer was started
^ return true to run the timer for another cycle with the same timeout value
on_receive_fields = func(pos, formname, fields, sender),
^ fields = {name1 = value1, name2 = value2, ...}
^ Called when an UI form (eg. sign text input) returns data
^ default: nil
on_metadata_inventory_move = func(pos, from_list, from_index,
to_list, to_index, count, player),
^ Called when a player wants to move items inside the metadata
^ Should move items, or some items, if permitted. If not, should do
nothing.
^ The engine ensures the action is valid, i.e. the stack fits at the
given position
^ default: minetest.node_metadata_inventory_move_allow_all
allow_metadata_inventory_move = func(pos, from_list, from_index,
to_list, to_index, count, player),
^ Called when a player wants to move items inside the inventory
^ Return value: number of items allowed to move
allow_metadata_inventory_put = func(pos, listname, index, stack, player),
^ Called when a player wants to put something into the inventory
^ Return value: number of items allowed to put
^ Return value: -1: Allow and don't modify item count in inventory
allow_metadata_inventory_take = func(pos, listname, index, stack, player),
^ Called when a player wants to take something out of the inventory
^ Return value: number of items allowed to take
^ Return value: -1: Allow and don't modify item count in inventory
on_metadata_inventory_offer = func(pos, listname, index, stack, player),
^ Called when a player wants to put something into the metadata
inventory
^ Should check if the action is permitted (the engine ensures the
action is valid, i.e. the stack fits at the given position)
^ If permitted, modify the metadata inventory and return the
"leftover" stack (normally nil).
^ If not permitted, return itemstack.
^ default: minetest.node_metadata_inventory_offer_allow_all
on_metadata_inventory_take = func(pos, listname, index, count, player),
^ Called when a player wants to take something out of the metadata
inventory
^ Should check if the action is permitted (the engine ensures the
action is valid, i.e. there's a stack of at least “count” items at
that position)
^ If permitted, modify the metadata inventory and return the
stack of items
^ If not permitted, return nil.
^ default: minetest.node_metadata_inventory_take_allow_all
on_metadata_inventory_move = func(pos, from_list, from_index,
to_list, to_index, count, player),
on_metadata_inventory_put = func(pos, listname, index, stack, player),
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)
@@ -1374,3 +1518,26 @@ Chatcommand definition (register_chatcommand)
func = function(name, param), -- called when command is run
}
Detached inventory callbacks
{
allow_move = func(inv, from_list, from_index, to_list, to_index, count, player),
^ Called when a player wants to move items inside the inventory
^ Return value: number of items allowed to move
allow_put = func(inv, listname, index, stack, player),
^ Called when a player wants to put something into the inventory
^ Return value: number of items allowed to put
^ Return value: -1: Allow and don't modify item count in inventory
allow_take = func(inv, listname, index, stack, player),
^ Called when a player wants to take something out of the inventory
^ Return value: number of items allowed to take
^ Return value: -1: Allow and don't modify item count in inventory
on_move = func(inv, from_list, from_index, to_list, to_index, count, player),
on_put = func(inv, listname, index, stack, player),
on_take = func(inv, listname, index, stack, player),
^ Called after the actual action has happened, according to what was allowed.
^ No return value
}

View File

@@ -1,11 +1,12 @@
=============================
Minetest World Format 22...23
Minetest World Format 22...25
=============================
This applies to a world format carrying the block serialization version
22...23, used at least in
- 0.4.dev-20120322 ... 0.4.dev-20120606
- 0.4.0.
22...25, used at least in
- 0.4.dev-20120322 ... 0.4.dev-20120606 (22...23)
- 0.4.0 (23)
- 24 was never released as stable and existed for ~2 days
The block serialization version does not fully specify every aspect of this
format; if compliance with this format is to be checked, it needs to be
@@ -262,17 +263,26 @@ u8 flags
u8 content_width
- Number of bytes in the content (param0) fields of nodes
- Always 1
if map format version <= 23:
- Always 1
if map format version >= 24:
- Always 2
u8 params_width
- Number of bytes used for parameters per node
- Always 2
zlib-compressed node data:
- content:
u8[4096]: param0 fields
u8[4096]: param1 fields
u8[4096]: param2 fields
if content_width == 1:
- content:
u8[4096]: param0 fields
u8[4096]: param1 fields
u8[4096]: param2 fields
if content_width == 2:
- content:
u16[4096]: param0 fields
u8[4096]: param1 fields
u8[4096]: param2 fields
- The location of a node in each of those arrays is (z*16*16 + y*16 + x).
zlib-compressed node metadata list
@@ -285,9 +295,19 @@ zlib-compressed node metadata list
u16 content_size
u8[content_size] (content of metadata)
- unused node timers (version will be 24 when they are actually used):
if version == 23:
- Node timers
if map format version == 23:
u8 unused version (always 0)
if map format version == 24: (NOTE: Not released as stable)
u8 nodetimer_version
if nodetimer_version == 0:
(nothing else)
if nodetimer_version == 1:
u16 num_of_timers
foreach num_of_timers:
u16 timer position (z*16*16 + y*16 + x)
s32 timeout*1000
s32 elapsed*1000
u8 static object version:
- Always 0
@@ -317,17 +337,29 @@ foreach num_name_id_mappings
u16 name_len
u8[name_len] name
- Node timers
if map format version == 25:
u8 length of the data of a single timer (always 2+4+4=10)
u16 num_of_timers
foreach num_of_timers:
u16 timer position (z*16*16 + y*16 + x)
s32 timeout*1000
s32 elapsed*1000
EOF.
Format of nodes
----------------
A node is composed of the u8 fields param0, param1 and param2.
The content id of a node is determined as so:
- If param0 < 0x80,
content_id = param0
- Otherwise
content_id = (param0<<4) + (param2>>4)
if map format version <= 23:
The content id of a node is determined as so:
- If param0 < 0x80,
content_id = param0
- Otherwise
content_id = (param0<<4) + (param2>>4)
if map format version >= 24:
The content id of a node is param0.
The purpose of param1 and param2 depend on the definition of the node.

View File

@@ -14,6 +14,14 @@ default = {}
-- Load other files
dofile(minetest.get_modpath("default").."/mapgen.lua")
-- Set a noticeable inventory formspec for players
minetest.register_on_joinplayer(function(player)
local cb = function(player)
minetest.chat_send_player(player:get_player_name(), "This is the [minimal] \"Minimal Development Test\" game. Use [minetest_game] for the real thing.")
end
minetest.after(2.0, cb, player)
end)
--
-- Tool definition
--
@@ -761,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(),
})
@@ -769,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},
}),
@@ -829,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(),
})
@@ -1098,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(),
})
@@ -1119,13 +1127,13 @@ 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)
--local n = minetest.env:get_node(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec", "hack:sign_text_input")
meta:set_string("formspec", "field[text;;${text}]")
meta:set_string("infotext", "\"\"")
end,
on_receive_fields = function(pos, formname, fields, sender)
@@ -1150,7 +1158,7 @@ minetest.register_node("default:chest", {
on_construct = function(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec",
"invsize[8,9;]"..
"size[8,9]"..
"list[current_name;main;0,0;8,4;]"..
"list[current_player;main;0,5;8,4;]")
meta:set_string("infotext", "Chest")
@@ -1162,25 +1170,6 @@ minetest.register_node("default:chest", {
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
on_metadata_inventory_move = function(pos, from_list, from_index,
to_list, to_index, count, player)
minetest.log("action", player:get_player_name()..
" moves stuff in chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_move_allow_all(
pos, from_list, from_index, to_list, to_index, count, player)
end,
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" moves stuff to chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_offer_allow_all(
pos, listname, index, stack, player)
end,
on_metadata_inventory_take = function(pos, listname, index, count, player)
minetest.log("action", player:get_player_name()..
" takes stuff from chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_take_allow_all(
pos, listname, index, count, player)
end,
})
local function has_locked_chest_privilege(meta, player)
@@ -1207,7 +1196,7 @@ minetest.register_node("default:chest_locked", {
on_construct = function(pos)
local meta = minetest.env:get_meta(pos)
meta:set_string("formspec",
"invsize[8,9;]"..
"size[8,9]"..
"list[current_name;main;0,0;8,4;]"..
"list[current_player;main;0,5;8,4;]")
meta:set_string("infotext", "Locked Chest")
@@ -1220,53 +1209,55 @@ minetest.register_node("default:chest_locked", {
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
on_metadata_inventory_move = function(pos, from_list, from_index,
to_list, to_index, count, player)
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return
return 0
end
return count
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return 0
end
return stack:get_count()
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name()..
" moves stuff in locked chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_move_allow_all(
pos, from_list, from_index, to_list, to_index, count, player)
end,
on_metadata_inventory_offer = function(pos, listname, index, stack, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return stack
end
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" moves stuff to locked chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_offer_allow_all(
pos, listname, index, stack, player)
end,
on_metadata_inventory_take = function(pos, listname, index, count, player)
local meta = minetest.env:get_meta(pos)
if not has_locked_chest_privilege(meta, player) then
minetest.log("action", player:get_player_name()..
" tried to access a locked chest belonging to "..
meta:get_string("owner").." at "..
minetest.pos_to_string(pos))
return
end
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" takes stuff from locked chest at "..minetest.pos_to_string(pos))
return minetest.node_metadata_inventory_take_allow_all(
pos, listname, index, count, player)
end,
})
default.furnace_inactive_formspec =
"invsize[8,9;]"..
"size[8,9]"..
"image[2,2;1,1;default_furnace_fire_bg.png]"..
"list[current_name;fuel;2,3;1,1;]"..
"list[current_name;src;2,1;1,1;]"..
@@ -1393,7 +1384,7 @@ minetest.register_abm({
srcstack:take_item()
inv:set_stack("src", 1, srcstack)
else
print("Could not insert '"..cooked.item.."'")
print("Could not insert '"..cooked.item:to_string().."'")
end
meta:set_string("src_time", 0)
end
@@ -1405,7 +1396,7 @@ minetest.register_abm({
meta:set_string("infotext","Furnace active: "..percent.."%")
hacky_swap_node(pos,"default:furnace_active")
meta:set_string("formspec",
"invsize[8,9;]"..
"size[8,9]"..
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100-percent)..":default_furnace_fire_fg.png]"..
"list[current_name;fuel;2,3;1,1;]"..
@@ -1504,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(),
})
@@ -1572,40 +1563,6 @@ minetest.register_craftitem("default:scorched_stuff", {
inventory_image = "default_scorched_stuff.png",
})
--
-- Creative inventory
--
minetest.add_to_creative_inventory('default:pick_mese')
minetest.add_to_creative_inventory('default:pick_steel')
minetest.add_to_creative_inventory('default:axe_steel')
minetest.add_to_creative_inventory('default:shovel_steel')
minetest.add_to_creative_inventory('default:torch')
minetest.add_to_creative_inventory('default:cobble')
minetest.add_to_creative_inventory('default:dirt')
minetest.add_to_creative_inventory('default:stone')
minetest.add_to_creative_inventory('default:sand')
minetest.add_to_creative_inventory('default:sandstone')
minetest.add_to_creative_inventory('default:clay')
minetest.add_to_creative_inventory('default:brick')
minetest.add_to_creative_inventory('default:tree')
minetest.add_to_creative_inventory('default:wood')
minetest.add_to_creative_inventory('default:leaves')
minetest.add_to_creative_inventory('default:cactus')
minetest.add_to_creative_inventory('default:papyrus')
minetest.add_to_creative_inventory('default:bookshelf')
minetest.add_to_creative_inventory('default:glass')
minetest.add_to_creative_inventory('default:fence_wood')
minetest.add_to_creative_inventory('default:rail')
minetest.add_to_creative_inventory('default:mese')
minetest.add_to_creative_inventory('default:chest')
minetest.add_to_creative_inventory('default:furnace')
minetest.add_to_creative_inventory('default:sign_wall')
minetest.add_to_creative_inventory('default:water_source')
minetest.add_to_creative_inventory('default:lava_source')
minetest.add_to_creative_inventory('default:ladder')
--
-- Aliases for the current map generator outputs
--
@@ -1629,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
--
@@ -1708,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)
@@ -1722,133 +1626,24 @@ function on_punchnode(p, node)
end
minetest.register_on_punchnode(on_punchnode)
local function handle_give_command(cmd, giver, receiver, stackstring)
if not minetest.get_player_privs(giver)["give"] then
minetest.chat_send_player(giver, "error: you don't have permission to give")
return
end
minetest.debug("DEBUG: "..cmd..' invoked, stackstring="'..stackstring..'"')
minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
local itemstack = ItemStack(stackstring)
if itemstack:is_empty() then
minetest.chat_send_player(giver, 'error: cannot give an empty item')
return
elseif not itemstack:is_known() then
minetest.chat_send_player(giver, 'error: cannot give an unknown item')
return
end
local receiverref = minetest.env:get_player_by_name(receiver)
if receiverref == nil then
minetest.chat_send_player(giver, receiver..' is not a known player')
return
end
local leftover = receiverref:get_inventory():add_item("main", itemstack)
if leftover:is_empty() then
partiality = ""
elseif leftover:get_count() == itemstack:get_count() then
partiality = "could not be "
else
partiality = "partially "
end
-- The actual item stack string may be different from what the "giver"
-- entered (e.g. big numbers are always interpreted as 2^16-1).
stackstring = itemstack:to_string()
if giver == receiver then
minetest.chat_send_player(giver, '"'..stackstring
..'" '..partiality..'added to inventory.');
else
minetest.chat_send_player(giver, '"'..stackstring
..'" '..partiality..'added to '..receiver..'\'s inventory.');
minetest.chat_send_player(receiver, '"'..stackstring
..'" '..partiality..'added to inventory.');
end
end
minetest.register_on_chat_message(function(name, message)
--print("default on_chat_message: name="..dump(name).." message="..dump(message))
local cmd = "/giveme"
if message:sub(0, #cmd) == cmd then
local stackstring = string.match(message, cmd.." (.*)")
if stackstring == nil then
minetest.chat_send_player(name, 'usage: '..cmd..' stackstring')
return true -- Handled chat message
end
handle_give_command(cmd, name, name, stackstring)
return true
end
local cmd = "/give"
if message:sub(0, #cmd) == cmd then
local receiver, stackstring = string.match(message, cmd.." ([%a%d_-]+) (.*)")
if receiver == nil or stackstring == nil then
minetest.chat_send_player(name, 'usage: '..cmd..' name stackstring')
return true -- Handled chat message
end
handle_give_command(cmd, name, receiver, stackstring)
return true
end
local cmd = "/spawnentity"
if message:sub(0, #cmd) == cmd then
if not minetest.get_player_privs(name)["give"] then
minetest.chat_send_player(name, "you don't have permission to spawn (give)")
return true -- Handled chat message
end
if not minetest.get_player_privs(name)["interact"] then
minetest.chat_send_player(name, "you don't have permission to interact")
return true -- Handled chat message
end
local entityname = string.match(message, cmd.." (.*)")
if entityname == nil then
minetest.chat_send_player(name, 'usage: '..cmd..' entityname')
return true -- Handled chat message
end
print(cmd..' invoked, entityname="'..entityname..'"')
local player = minetest.env:get_player_by_name(name)
if player == nil then
print("Unable to spawn entity, player is nil")
return true -- Handled chat message
end
local p = player:getpos()
p.y = p.y + 1
minetest.env:add_entity(p, entityname)
minetest.chat_send_player(name, '"'..entityname
..'" spawned.');
return true -- Handled chat message
end
local cmd = "/pulverize"
if message:sub(0, #cmd) == cmd then
local player = minetest.env:get_player_by_name(name)
if player == nil then
print("Unable to pulverize, player is nil")
return true -- Handled chat message
end
if player:get_wielded_item():is_empty() then
minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
else
player:set_wielded_item(nil)
minetest.chat_send_player(name, 'An item was pulverized.')
end
return true
end
end)
--
-- Test some things
--
local function test_get_craft_result()
print("test_get_craft_result()")
minetest.log("info", "test_get_craft_result()")
-- normal
local input = {
method = "normal",
width = 2,
items = {"", "default:coal_lump", "", "default:stick"}
}
print("torch crafting input: "..dump(input))
minetest.log("info", "torch crafting input: "..dump(input))
local output, decremented_input = minetest.get_craft_result(input)
print("torch crafting output: "..dump(output))
print("torch crafting decremented input: "..dump(decremented_input))
minetest.log("info", "torch crafting output: "..dump(output))
minetest.log("info", "torch crafting decremented input: "..dump(decremented_input))
assert(output.item)
print("torch crafting output.item:to_table(): "..dump(output.item:to_table()))
minetest.log("info", "torch crafting output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "default:torch")
assert(output.item:get_count() == 4)
-- fuel
@@ -1857,10 +1652,10 @@ local function test_get_craft_result()
width = 1,
items = {"default:coal_lump"}
}
print("coal fuel input: "..dump(input))
minetest.log("info", "coal fuel input: "..dump(input))
local output, decremented_input = minetest.get_craft_result(input)
print("coal fuel output: "..dump(output))
print("coal fuel decremented input: "..dump(decremented_input))
minetest.log("info", "coal fuel output: "..dump(output))
minetest.log("info", "coal fuel decremented input: "..dump(decremented_input))
assert(output.time)
assert(output.time > 0)
-- cook
@@ -1869,14 +1664,14 @@ local function test_get_craft_result()
width = 1,
items = {"default:cobble"}
}
print("cobble cooking input: "..dump(output))
minetest.log("info", "cobble cooking input: "..dump(output))
local output, decremented_input = minetest.get_craft_result(input)
print("cobble cooking output: "..dump(output))
print("cobble cooking decremented input: "..dump(decremented_input))
minetest.log("info", "cobble cooking output: "..dump(output))
minetest.log("info", "cobble cooking decremented input: "..dump(decremented_input))
assert(output.time)
assert(output.time > 0)
assert(output.item)
print("cobble cooking output.item:to_table(): "..dump(output.item:to_table()))
minetest.log("info", "cobble cooking output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "default:stone")
assert(output.item:get_count() == 1)
end

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

@@ -443,7 +443,7 @@ minetest.register_abm({
})--]]
minetest.register_node("experimental:tester_node_1", {
description = "Tester Node 1",
description = "Tester Node 1 (construct/destruct/timer)",
tile_images = {"wieldhand.png"},
groups = {oddly_breakable_by_hand=2},
sounds = default.node_sound_wood_defaults(),
@@ -455,6 +455,8 @@ minetest.register_node("experimental:tester_node_1", {
experimental.print_to_everything("experimental:tester_node_1:on_construct("..minetest.pos_to_string(pos)..")")
local meta = minetest.env:get_meta(pos)
meta:set_string("mine", "test")
local timer = minetest.env:get_node_timer(pos)
timer:start(4, 3)
end,
after_place_node = function(pos, placer)
@@ -478,6 +480,11 @@ minetest.register_node("experimental:tester_node_1", {
after_dig_node = function(pos, oldnode, oldmetadata, digger)
experimental.print_to_everything("experimental:tester_node_1:after_dig_node("..minetest.pos_to_string(pos)..")")
end,
on_timer = function(pos, elapsed)
experimental.print_to_everything("on_timer(): elapsed="..dump(elapsed))
return true
end,
})
minetest.register_craftitem("experimental:tester_tool_1", {
@@ -509,7 +516,7 @@ minetest.register_craft({
--[[minetest.register_on_joinplayer(function(player)
minetest.after(3, function()
player:set_inventory_formspec("invsize[8,7.5;]"..
player:set_inventory_formspec("size[8,7.5]"..
"image[1,0.6;1,2;player.png]"..
"list[current_player;main;0,3.5;8,4;]"..
"list[current_player;craft;3,0;3,3;]"..
@@ -517,6 +524,67 @@ minetest.register_craft({
end)
end)]]
-- Create a detached inventory
local inv = minetest.create_detached_inventory("test_inventory", {
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
experimental.print_to_everything("allow move asked")
return count -- Allow all
end,
allow_put = function(inv, listname, index, stack, player)
experimental.print_to_everything("allow put asked")
return 1 -- Allow only 1
end,
allow_take = function(inv, listname, index, stack, player)
experimental.print_to_everything("allow take asked")
return 4 -- Allow 4 at max
end,
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
experimental.print_to_everything(player:get_player_name().." moved items")
end,
on_put = function(inv, listname, index, stack, player)
experimental.print_to_everything(player:get_player_name().." put items")
end,
on_take = function(inv, listname, index, stack, player)
experimental.print_to_everything(player:get_player_name().." took items")
end,
})
inv:set_size("main", 4*6)
inv:add_item("main", "experimental:tester_tool_1")
inv:add_item("main", "experimental:tnt 5")
minetest.register_chatcommand("test1", {
params = "",
description = "Test 1: Modify player's inventory view",
func = function(name, param)
local player = minetest.env:get_player_by_name(name)
if not player then
return
end
player:set_inventory_formspec(
"size[13,7.5]"..
"image[6,0.6;1,2;player.png]"..
"list[current_player;main;5,3.5;8,4;]"..
"list[current_player;craft;8,0;3,3;]"..
"list[current_player;craftpreview;12,1;1,1;]"..
"list[detached:test_inventory;main;0,0;4,6;0]"..
"button[0.5,7;2,1;button1;Button 1]"..
"button_exit[2.5,7;2,1;button2;Exit Button]"
)
minetest.chat_send_player(name, "Done.");
end,
})
minetest.register_on_player_receive_fields(function(player, formname, fields)
experimental.print_to_everything("Inventory fields 1: player="..player:get_player_name()..", fields="..dump(fields))
end)
minetest.register_on_player_receive_fields(function(player, formname, fields)
experimental.print_to_everything("Inventory fields 2: player="..player:get_player_name()..", fields="..dump(fields))
return true -- Disable the first callback
end)
minetest.register_on_player_receive_fields(function(player, formname, fields)
experimental.print_to_everything("Inventory fields 3: player="..player:get_player_name()..", fields="..dump(fields))
end)
minetest.log("experimental modname="..dump(minetest.get_current_modname()))
minetest.log("experimental modpath="..dump(minetest.get_modpath("experimental")))
minetest.log("experimental worldpath="..dump(minetest.get_worldpath()))

View File

@@ -15,7 +15,7 @@
# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp
#
# A vim command to convert most of defaultsettings.cpp to conf file format:
# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# :'<,'>s/\tsettings->setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# Note: Some of the settings are implemented in Lua
#
@@ -49,8 +49,14 @@
#keymap_freemove = KEY_KEY_K
#keymap_fastmove = KEY_KEY_J
#keymap_screenshot = KEY_F12
# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending
#aux1_descends = false
# Some (temporary) keys for debugging
#keymap_print_debug_stacks = KEY_KEY_P
#keymap_quicktune_prev = KEY_HOME
#keymap_quicktune_next = KEY_END
#keymap_quicktune_dec = KEY_NEXT
#keymap_quicktune_inc = KEY_PRIOR
# Minimum FPS
# The amount of rendered stuff is dynamically set according to this
@@ -111,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)
@@ -120,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
@@ -133,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
@@ -160,6 +180,8 @@
#disallow_empty_password = false
# If true, disable cheat prevention in multiplayer
#disable_anticheat = false
# If true, actions are recorded for rollback
#enable_rollback_recording = false
# Profiler data print interval. #0 = disable.
#profiler_print_interval = 0
@@ -186,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

@@ -1,10 +1,6 @@
project(minetest)
cmake_minimum_required( VERSION 2.6 )
if(RUN_IN_PLACE)
add_definitions ( -DRUN_IN_PLACE )
endif(RUN_IN_PLACE)
# Set some random things default to not being visible in the GUI
mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
mark_as_advanced(JTHREAD_INCLUDE_DIR JTHREAD_LIBRARY)
@@ -139,13 +135,15 @@ else()
#set(CLIENT_PLATFORM_LIBS -lXxf86vm)
# This way Xxf86vm is found on OpenBSD too
find_library(XXF86VM_LIBRARY Xxf86vm)
mark_as_advanced(XXF86VM_LIBRARY)
set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
endif()
find_package(Jthread REQUIRED)
find_package(Sqlite3 REQUIRED)
# TODO: Create proper find script for Lua
# Do not use system-wide installation of Lua, because it'll likely be a
# different version and/or has different build options.
set(LUA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lua/src")
set(LUA_LIBRARY "lua")
@@ -155,6 +153,8 @@ configure_file(
)
set(common_SRCS
rollback_interface.cpp
rollback.cpp
genericobject.cpp
voxelalgorithms.cpp
sound.cpp
@@ -193,7 +193,6 @@ set(common_SRCS
connection.cpp
environment.cpp
server.cpp
servercommand.cpp
socket.cpp
mapblock.cpp
mapsector.cpp
@@ -203,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
@@ -251,7 +252,7 @@ set(minetest_SRCS
guiKeyChangeMenu.cpp
guiMessageMenu.cpp
guiTextInputMenu.cpp
guiInventoryMenu.cpp
guiFormSpecMenu.cpp
guiPauseMenu.cpp
guiPasswordChange.cpp
guiDeathScreen.cpp
@@ -261,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;
@@ -304,6 +310,15 @@ Client::~Client()
sleep_ms(100);
delete m_inventory_from_server;
// Delete detached inventories
{
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
i != m_detached_inventories.end(); i++){
delete i->second;
}
}
}
void Client::connect(Address address)
@@ -489,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);
@@ -503,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);
@@ -646,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();
@@ -812,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();
@@ -846,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;
@@ -988,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;
@@ -1425,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;
@@ -1698,6 +1744,24 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
assert(player != NULL);
player->inventory_formspec = deSerializeLongString(is);
}
else if(command == TOCLIENT_DETACHED_INVENTORY)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
std::string name = deSerializeString(is);
infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
Inventory *inv = NULL;
if(m_detached_inventories.count(name) > 0)
inv = m_detached_inventories[name];
else{
inv = new Inventory(m_itemdef);
m_detached_inventories[name] = inv;
}
inv->deSerialize(is);
}
else
{
infostream<<"Client: Ignoring unknown command "
@@ -1773,6 +1837,29 @@ void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
Send(0, data, true);
}
void Client::sendInventoryFields(const std::string &formname,
const std::map<std::string, std::string> &fields)
{
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_INVENTORY_FIELDS);
os<<serializeString(formname);
writeU16(os, fields.size());
for(std::map<std::string, std::string>::const_iterator
i = fields.begin(); i != fields.end(); i++){
const std::string &name = i->first;
const std::string &value = i->second;
os<<serializeString(name);
os<<serializeLongString(value);
}
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
Send(0, data, true);
}
void Client::sendInventoryAction(InventoryAction *a)
{
std::ostringstream os(std::ios_base::binary);
@@ -1889,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
@@ -1911,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
@@ -1919,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);
}
@@ -2067,6 +2168,13 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
return meta->getInventory();
}
break;
case InventoryLocation::DETACHED:
{
if(m_detached_inventories.count(loc.name) == 0)
return NULL;
return m_detached_inventories[loc.name];
}
break;
default:
assert(0);
}
@@ -2347,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);
@@ -2357,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)
@@ -2412,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,
@@ -212,6 +214,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
void sendNodemetaFields(v3s16 p, const std::string &formname,
const std::map<std::string, std::string> &fields);
void sendInventoryFields(const std::string &formname,
const std::map<std::string, std::string> &fields);
void sendInventoryAction(InventoryAction *a);
void sendChatMessage(const std::wstring &message);
void sendChangePassword(const std::wstring oldpassword,
@@ -303,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();
@@ -335,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;
@@ -380,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
@@ -391,6 +400,10 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
// Privileges
std::set<std::string> m_privileges;
// Detached inventories
// key = name
std::map<std::string, Inventory*> m_detached_inventories;
};
#endif // !CLIENT_HEADER

View File

@@ -157,43 +157,21 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
return false;
}
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
void ClientMap::updateDrawList(video::IVideoDriver* driver)
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
g_profiler->add("CM::updateDrawList() count", 1);
INodeDefManager *nodemgr = m_gamedef->ndef();
//m_dout<<DTIME<<"Rendering map..."<<std::endl;
DSTACK(__FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if(pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if(pass == scene::ESNRP_SOLID)
for(core::map<v3s16, MapBlock*>::Iterator
i = m_drawlist.getIterator();
i.atEnd() == false; i++)
{
m_last_drawn_sectors.clear();
MapBlock *block = i.getNode()->getValue();
block->refDrop();
}
/*
Get time for measuring timeout.
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
m_drawlist.clear();
m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
@@ -201,17 +179,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock();
/*
Get all blocks and draw all visible ones
*/
// Use a higher fov to accomodate faster camera movements.
// Blocks are cropped better when they are drawn.
// Or maybe they aren't? Well whatever.
camera_fov *= 1.2;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
// Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min(
@@ -223,13 +199,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Number of blocks in rendering range
u32 blocks_in_range = 0;
// Number of blocks occlusion culled
@@ -242,18 +211,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Blocks that were drawn and had a mesh
u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass
u32 blocks_had_pass_meshbuf = 0;
//u32 blocks_had_pass_meshbuf = 0;
// Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0;
/*
Collect a set of blocks for drawing
*/
core::map<v3s16, MapBlock*> drawset;
{
ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
//u32 blocks_without_stuff = 0;
for(core::map<v2s16, MapSector*>::Iterator
si = m_sectors.getIterator();
@@ -380,36 +340,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
&& d > m_control.wanted_min_range * BS)
continue;
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
// Add to set
drawset[block->getPos()] = block;
block->refGrab();
m_drawlist[block->getPos()] = block;
sector_blocks_drawn++;
blocks_drawn++;
@@ -418,8 +352,132 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
if(sector_blocks_drawn != 0)
m_last_drawn_sectors[sp] = true;
}
} // ScopeProfiler
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
{
video::SMaterial m;
core::list<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
core::list<MeshBufList> lists;
void clear()
{
lists.clear();
}
void add(scene::IMeshBuffer *buf)
{
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++){
MeshBufList &l = *i;
if(l.m == buf->getMaterial()){
l.bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.m = buf->getMaterial();
l.bufs.push_back(buf);
lists.push_back(l);
}
};
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
DSTACK(__FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if(pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if(pass == scene::ESNRP_SOLID)
{
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.
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
v3f camera_direction = m_camera_direction;
f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock();
/*
Get all blocks and draw all visible ones
*/
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
// Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min(
p_nodes_min.X / MAP_BLOCKSIZE - 3,
p_nodes_min.Y / MAP_BLOCKSIZE - 3,
p_nodes_min.Z / MAP_BLOCKSIZE - 3);
v3s16 p_blocks_max(
p_nodes_max.X / MAP_BLOCKSIZE + 1,
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Blocks that were drawn and had a mesh
u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass
u32 blocks_had_pass_meshbuf = 0;
// Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0;
/*
Draw the selected MapBlocks
*/
@@ -427,10 +485,95 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
int timecheck_counter = 0;
MeshBufListList drawbufs;
for(core::map<v3s16, MapBlock*>::Iterator
i = drawset.getIterator();
i = m_drawlist.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
// If the mesh of the block happened to get deleted, ignore it
if(block->mesh == NULL)
continue;
float d = 0.0;
if(isBlockInSight(block->getPos(), camera_position,
camera_direction, camera_fov,
100000*BS, &d) == false)
{
continue;
}
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
/*
Get the meshbuffers of the block
*/
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
scene::SMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
u32 c = mesh->getMeshBufferCount();
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);
bool transparent = (rnd && rnd->isTransparent());
if(transparent == is_transparent_pass)
{
if(buf->getVertexCount() == 0)
errorstream<<"Block ["<<analyze_block(block)
<<"] contains an empty meshbuf"<<std::endl;
drawbufs.add(buf);
}
}
}
}
core::list<MeshBufList> &lists = drawbufs.lists;
int timecheck_counter = 0;
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++)
{
{
timecheck_counter++;
@@ -447,9 +590,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
}
}
}
MapBlock *block = i.getNode()->getValue();
MeshBufList &list = *i;
driver->setMaterial(list.m);
for(core::list<scene::IMeshBuffer*>::Iterator j = list.bufs.begin();
j != list.bufs.end(); j++)
{
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
meshbuffer_count++;
}
#if 0
/*
Draw the faces of the block
*/
@@ -494,17 +648,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
else
blocks_without_stuff++;
}
#endif
}
} // ScopeProfiler
// Log only on solid pass because values are the same
if(pass == scene::ESNRP_SOLID){
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: animated meshes", mesh_animate_count);
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
}
@@ -517,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

@@ -113,7 +113,8 @@ class ClientMap : public Map, public scene::ISceneNode
{
return m_box;
}
void updateDrawList(video::IVideoDriver* driver);
void renderMap(video::IVideoDriver* driver, s32 pass);
int getBackgroundBrightness(float max_d, u32 daylight_factor,
@@ -141,6 +142,8 @@ class ClientMap : public Map, public scene::ISceneNode
v3f m_camera_direction;
f32 m_camera_fov;
JMutex m_camera_mutex;
core::map<v3s16, MapBlock*> m_drawlist;
core::map<v2s16, bool> m_last_drawn_sectors;
};

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:
@@ -58,11 +60,36 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 11:
TileDef in ContentFeatures
Nodebox drawtype
Added after a release: TOCLIENT_INVENTORY_FORMSPEC
(some dev snapshot)
TOCLIENT_INVENTORY_FORMSPEC
(0.4.0, 0.4.1)
PROTOCOL_VERSION 12:
TOSERVER_INVENTORY_FIELDS
16-bit node ids
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 11
#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
@@ -81,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
@@ -316,6 +344,14 @@ enum ToClientCommand
u32 len
u8[len] formspec
*/
TOCLIENT_DETACHED_INVENTORY = 0x43,
/*
[0] u16 command
u16 len
u8[len] name
[2] serialized inventory
*/
};
enum ToServerCommand
@@ -328,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,
@@ -350,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,
@@ -510,6 +548,19 @@ enum ToServerCommand
u8[len] field value
*/
TOSERVER_INVENTORY_FIELDS = 0x3c,
/*
u16 command
u16 len
u8[len] form name (reserved for future use)
u16 number of fields
for each field:
u16 len
u8[len] field name
u32 len
u8[len] field value
*/
TOSERVER_REQUEST_MEDIA = 0x40,
/*
u16 command
@@ -522,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

@@ -4,16 +4,18 @@
#define CMAKE_CONFIG_H
#define CMAKE_PROJECT_NAME "@PROJECT_NAME@"
#define CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
#define CMAKE_VERSION_STRING "@VERSION_STRING@"
#define CMAKE_RUN_IN_PLACE @RUN_IN_PLACE@
#define CMAKE_USE_GETTEXT @USE_GETTEXT@
#define CMAKE_USE_SOUND @USE_SOUND@
#define CMAKE_STATIC_SHAREDIR "@SHAREDIR@"
#ifdef NDEBUG
#define CMAKE_BUILD_TYPE "Release"
#else
#define CMAKE_BUILD_TYPE "Debug"
#endif
#define CMAKE_USE_GETTEXT @USE_GETTEXT@
#define CMAKE_USE_SOUND @USE_SOUND@
#define CMAKE_BUILD_INFO "VER=@VERSION_STRING@ BUILD_TYPE="CMAKE_BUILD_TYPE" RUN_IN_PLACE=@RUN_IN_PLACE@ USE_GETTEXT=@USE_GETTEXT@ USE_SOUND=@USE_SOUND@ INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@"
#define CMAKE_BUILD_INFO "VER=@VERSION_STRING@ BUILD_TYPE="CMAKE_BUILD_TYPE" RUN_IN_PLACE=@RUN_IN_PLACE@ USE_GETTEXT=@USE_GETTEXT@ USE_SOUND=@USE_SOUND@ STATIC_SHAREDIR=@SHAREDIR@"
#endif

View File

@@ -211,6 +211,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
std::vector<aabb3f> cboxes;
std::vector<bool> is_unloaded;
std::vector<bool> is_step_up;
std::vector<int> bouncy_values;
std::vector<v3s16> node_positions;
{
//TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
@@ -228,11 +230,14 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
{
v3s16 p(x,y,z);
try{
// Object collides into walkable nodes
MapNode n = map->getNode(v3s16(x,y,z));
if(gamedef->getNodeDefManager()->get(n).walkable == false)
MapNode n = map->getNode(p);
const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
if(f.walkable == false)
continue;
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
for(std::vector<aabb3f>::iterator
@@ -245,21 +250,27 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
cboxes.push_back(box);
is_unloaded.push_back(false);
is_step_up.push_back(false);
bouncy_values.push_back(n_bouncy_value);
node_positions.push_back(p);
}
}
catch(InvalidPositionException &e)
{
// Collide with unloaded nodes
aabb3f box = getNodeBox(v3s16(x,y,z), BS);
aabb3f box = getNodeBox(p, BS);
cboxes.push_back(box);
is_unloaded.push_back(true);
is_step_up.push_back(false);
bouncy_values.push_back(0);
node_positions.push_back(p);
}
}
} // tt2
assert(cboxes.size() == is_unloaded.size());
assert(cboxes.size() == is_step_up.size());
assert(cboxes.size() == bouncy_values.size());
assert(cboxes.size() == node_positions.size());
/*
Collision detection
@@ -342,6 +353,10 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
cbox.MaxEdge.Y - movingbox.MinEdge.Y,
d));
// Get bounce multiplier
bool bouncy = (bouncy_values[nearest_boxindex] >= 1);
float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;
// Move to the point of collision and reduce dtime by nearest_dtime
if(nearest_dtime < 0)
{
@@ -361,30 +376,58 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
pos_f += speed_f * nearest_dtime;
dtime -= nearest_dtime;
}
bool is_collision = true;
if(is_unloaded[nearest_boxindex])
is_collision = false;
CollisionInfo info;
info.type = COLLISION_NODE;
info.node_p = node_positions[nearest_boxindex];
info.bouncy = bouncy;
info.old_speed = speed_f;
// Set the speed component that caused the collision to zero
if(step_up)
{
// Special case: Handle stairs
is_step_up[nearest_boxindex] = true;
is_collision = false;
}
else if(nearest_collided == 0) // X
{
speed_f.X = 0;
if(fabs(speed_f.X) > BS*3)
speed_f.X *= bounce;
else
speed_f.X = 0;
result.collides = true;
result.collides_xz = true;
}
else if(nearest_collided == 1) // Y
{
speed_f.Y = 0;
if(fabs(speed_f.Y) > BS*3)
speed_f.Y *= bounce;
else
speed_f.Y = 0;
result.collides = true;
}
else if(nearest_collided == 2) // Z
{
speed_f.Z = 0;
if(fabs(speed_f.Z) > BS*3)
speed_f.Z *= bounce;
else
speed_f.Z = 0;
result.collides = true;
result.collides_xz = true;
}
info.new_speed = speed_f;
if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
is_collision = false;
if(is_collision){
result.collisions.push_back(info);
}
}
}

View File

@@ -26,12 +26,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Map;
class IGameDef;
enum CollisionType
{
COLLISION_NODE
};
struct CollisionInfo
{
enum CollisionType type;
v3s16 node_p; // COLLISION_NODE
bool bouncy;
v3f old_speed;
v3f new_speed;
CollisionInfo():
type(COLLISION_NODE),
node_p(-32768,-32768,-32768),
bouncy(false),
old_speed(0,0,0),
new_speed(0,0,0)
{}
};
struct collisionMoveResult
{
bool touching_ground;
bool collides;
bool collides_xz;
bool standing_on_unloaded;
std::vector<CollisionInfo> collisions;
collisionMoveResult():
touching_ground(false),
@@ -72,16 +95,5 @@ bool wouldCollideWithCeiling(
f32 y_increase, f32 d);
enum CollisionType
{
COLLISION_FALL
};
struct CollisionInfo
{
CollisionType t;
f32 speed;
};
#endif

View File

@@ -8,9 +8,10 @@
#define PROJECT_NAME "Minetest"
#define VERSION_STRING "unknown"
#define BUILD_TYPE "unknown"
#define RUN_IN_PLACE 0
#define USE_GETTEXT 0
#define USE_SOUND 0
#define STATIC_SHAREDIR ""
#define BUILD_INFO "non-cmake"
#ifdef USE_CMAKE_CONFIG_H
@@ -19,12 +20,14 @@
#define PROJECT_NAME CMAKE_PROJECT_NAME
#undef VERSION_STRING
#define VERSION_STRING CMAKE_VERSION_STRING
#undef BUILD_INFO
#define BUILD_INFO CMAKE_BUILD_INFO
#undef RUN_IN_PLACE
#define RUN_IN_PLACE CMAKE_RUN_IN_PLACE
#undef USE_GETTEXT
#define USE_GETTEXT CMAKE_USE_GETTEXT
#undef USE_SOUND
#define USE_SOUND CMAKE_USE_SOUND
#undef STATIC_SHAREDIR
#define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
#undef BUILD_INFO
#define BUILD_INFO CMAKE_BUILD_INFO
#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

@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "mapblock.h" // For getNodeBlockPos
#include "mapgen.h" // For mapgen::make_tree
#include "map.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"

View File

@@ -39,6 +39,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h" // For IntervalLimiter
#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;
@@ -467,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)
{
@@ -550,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;
//
@@ -558,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;
@@ -571,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;
@@ -580,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):
@@ -587,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)),
@@ -603,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),
@@ -610,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);
@@ -620,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);
@@ -666,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;
@@ -693,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;
@@ -789,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,
@@ -814,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){
@@ -824,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()
@@ -835,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);
}
}
@@ -856,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);
}
@@ -869,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());
}
}
}
@@ -939,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();
}
@@ -997,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")
@@ -1007,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)
@@ -1033,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")
@@ -1046,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";
@@ -1057,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;
@@ -1088,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);
@@ -1101,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);
@@ -1129,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

@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h"
#include "gamedef.h"
#include "log.h"
#include "map.h"
static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
float txs, float tys, int col, int row)

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

@@ -102,23 +102,6 @@ content_t trans_table_19[21][2] = {
{CONTENT_BOOKSHELF, 29},
};
MapNode mapnode_translate_from_internal(MapNode n_from, u8 version)
{
MapNode result = n_from;
if(version <= 19)
{
content_t c_from = n_from.getContent();
for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
{
if(trans_table_19[i][0] == c_from)
{
result.setContent(trans_table_19[i][1]);
break;
}
}
}
return result;
}
MapNode mapnode_translate_to_internal(MapNode n_from, u8 version)
{
MapNode result = n_from;

View File

@@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Backwards compatibility for non-extended content types in v19
extern content_t trans_table_19[21][2];
MapNode mapnode_translate_from_internal(MapNode n_from, u8 version);
MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
// Get legacy node name mapping for loading old blocks

View File

@@ -63,7 +63,7 @@ static bool content_nodemeta_deserialize_legacy_body(
//meta->setString("infotext","\"${text}\"");
meta->setString("infotext",
std::string("\"") + meta->getString("text") + "\"");
meta->setString("formspec","hack:sign_text_input");
meta->setString("formspec","field[text;;${text}]");
return false;
}
else if(id == NODEMETA_CHEST) // ChestNodeMetadata
@@ -77,7 +77,7 @@ static bool content_nodemeta_deserialize_legacy_body(
}
assert(inv->getList("main") && !inv->getList("0"));
meta->setString("formspec","invsize[8,9;]"
meta->setString("formspec","size[8,9]"
"list[current_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]");
return false;
@@ -94,7 +94,7 @@ static bool content_nodemeta_deserialize_legacy_body(
}
assert(inv->getList("main") && !inv->getList("0"));
meta->setString("formspec","invsize[8,9;]"
meta->setString("formspec","size[8,9]"
"list[current_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]");
return false;
@@ -115,7 +115,7 @@ static bool content_nodemeta_deserialize_legacy_body(
is>>temp;
meta->setString("src_time", ftos((float)temp/10));
meta->setString("formspec","invsize[8,9;]"
meta->setString("formspec","size[8,9]"
"list[current_name;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]"

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
@@ -292,13 +292,6 @@ class ItemSAO : public ServerActiveObject
ServerActiveObject *puncher,
float time_from_last_punch)
{
// Directly delete item in creative mode
if(g_settings->getBool("creative_mode") == true)
{
m_removed = true;
return 0;
}
// Take item into inventory
ItemStack item = createItemStack();
Inventory *inv = puncher->getInventory();
@@ -362,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){
@@ -383,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();
@@ -397,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);
}
}
@@ -436,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)
@@ -447,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){
@@ -480,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){
@@ -504,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();
}
@@ -557,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;
@@ -601,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);
@@ -651,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;
@@ -725,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;
@@ -772,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)
@@ -789,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;
}
@@ -812,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;
@@ -843,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();
}
@@ -864,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)
@@ -875,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(),
@@ -968,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,
@@ -1001,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;
@@ -1082,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;
@@ -1143,19 +1411,9 @@ void PlayerSAO::disconnected()
}
}
void PlayerSAO::createCreativeInventory()
{
if(m_inventory != &m_player->inventory)
delete m_inventory;
m_inventory = new Inventory(m_player->inventory);
m_inventory->clearContents();
scriptapi_get_creative_inventory(m_env->getLua(), this);
}
std::string PlayerSAO::getPropertyPacket()
{
m_prop.is_visible = (getHP() != 0);
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();
@@ -163,8 +185,6 @@ class PlayerSAO : public ServerActiveObject
void disconnected();
void createCreativeInventory();
Player* getPlayer()
{
return m_player;
@@ -231,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

@@ -23,9 +23,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include <sstream>
#include <set>
#include <algorithm>
#include "gamedef.h"
#include "inventory.h"
#include "util/serialize.h"
#include "strfnd.h"
// Check if input matches recipe
// Takes recipe groups into account
@@ -38,9 +40,17 @@ static bool inputItemMatchesRecipe(const std::string &inp_name,
// Group
if(rec_name.substr(0,6) == "group:" && idef->isKnown(inp_name)){
std::string rec_group = rec_name.substr(6);
const struct ItemDefinition &def = idef->get(inp_name);
if(itemgroup_get(def.groups, rec_group) != 0)
Strfnd f(rec_name.substr(6));
bool all_groups_match = true;
do{
std::string check_group = f.next(",");
if(itemgroup_get(def.groups, check_group) == 0){
all_groups_match = false;
break;
}
}while(!f.atend());
if(all_groups_match)
return true;
}
@@ -140,6 +150,8 @@ static bool craftGetBounds(const std::vector<std::string> &items, unsigned int w
return success;
}
#if 0
// This became useless when group support was added to shapeless recipes
// Convert a list of item names to a multiset
static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names)
{
@@ -153,6 +165,7 @@ static std::multiset<std::string> craftMakeMultiset(const std::vector<std::strin
}
return set;
}
#endif
// Removes 1 from each item stack
static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
@@ -508,17 +521,48 @@ bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef)
{
if(input.method != CRAFT_METHOD_NORMAL)
return false;
// Filter empty items out of input
std::vector<std::string> input_filtered;
for(std::vector<ItemStack>::const_iterator
i = input.items.begin();
i != input.items.end(); i++)
{
if(i->name != "")
input_filtered.push_back(i->name);
}
// Get input item multiset
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
// If there is a wrong number of items in input, no match
if(input_filtered.size() != recipe.size()){
/*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size ("<<recipe.size()<<") "
<<"of recipe with output="<<output<<std::endl;*/
return false;
}
// Get recipe item multiset
std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
// Try with all permutations of the recipe
std::vector<std::string> recipe_copy = recipe;
// Start from the lexicographically first permutation (=sorted)
std::sort(recipe_copy.begin(), recipe_copy.end());
//while(std::prev_permutation(recipe_copy.begin(), recipe_copy.end())){}
do{
// If all items match, the recipe matches
bool all_match = true;
//dstream<<"Testing recipe (output="<<output<<"):";
for(size_t i=0; i<recipe.size(); i++){
//dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
if(!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
gamedef->idef())){
all_match = false;
break;
}
}
//dstream<<" -> match="<<all_match<<std::endl;
if(all_match)
return true;
}while(std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
// Recipe is matched when the multisets coincide
return inp_names_multiset == rec_names_multiset;
return false;
}
CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
@@ -694,16 +738,26 @@ bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) c
if(input.method != CRAFT_METHOD_COOKING)
return false;
// Get input item multiset
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
// Filter empty items out of input
std::vector<std::string> input_filtered;
for(std::vector<ItemStack>::const_iterator
i = input.items.begin();
i != input.items.end(); i++)
{
if(i->name != "")
input_filtered.push_back(i->name);
}
// Get recipe item multiset
std::multiset<std::string> rec_names_multiset;
rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
// Recipe is matched when the multisets coincide
return inp_names_multiset == rec_names_multiset;
// If there is a wrong number of items in input, no match
if(input_filtered.size() != 1){
/*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size (1) "
<<"of cooking recipe with output="<<output<<std::endl;*/
return false;
}
// Check the single input item
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
}
CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
@@ -765,16 +819,26 @@ bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) cons
if(input.method != CRAFT_METHOD_FUEL)
return false;
// Get input item multiset
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
// Filter empty items out of input
std::vector<std::string> input_filtered;
for(std::vector<ItemStack>::const_iterator
i = input.items.begin();
i != input.items.end(); i++)
{
if(i->name != "")
input_filtered.push_back(i->name);
}
// Get recipe item multiset
std::multiset<std::string> rec_names_multiset;
rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
// Recipe is matched when the multisets coincide
return inp_names_multiset == rec_names_multiset;
// If there is a wrong number of items in input, no match
if(input_filtered.size() != 1){
/*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size (1) "
<<"of fuel recipe with burntime="<<burntime<<std::endl;*/
return false;
}
// Check the single input item
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
}
CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const

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,6 +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");
@@ -69,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");
@@ -88,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");
@@ -105,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");
@@ -122,6 +134,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_pvp", "true");
settings->setDefault("disallow_empty_password", "false");
settings->setDefault("disable_anticheat", "false");
settings->setDefault("enable_rollback_recording", "false");
settings->setDefault("profiler_print_interval", "0");
settings->setDefault("enable_mapgen_debug_info", "false");
@@ -138,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

@@ -42,6 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "localplayer.h"
#endif
#include "daynightratio.h"
#include "map.h"
#include "util/serialize.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -201,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)
@@ -325,8 +328,10 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
m_emerger(emerger),
m_random_spawn_timer(3),
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)
{
}
@@ -349,6 +354,17 @@ ServerEnvironment::~ServerEnvironment()
}
}
Map & ServerEnvironment::getMap()
{
return *m_map;
}
ServerMap & ServerEnvironment::getServerMap()
{
return *m_map;
}
void ServerEnvironment::serializePlayers(const std::string &savedir)
{
std::string players_path = savedir + "/players";
@@ -730,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,
@@ -771,13 +793,21 @@ 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, f32> elapsed_timers =
std::map<v3s16, NodeTimer> elapsed_timers =
block->m_node_timers.step((float)dtime_s);
if(!elapsed_timers.empty())
errorstream<<"Node timers don't work yet!"<<std::endl;
if(!elapsed_timers.empty()){
MapNode n;
for(std::map<v3s16, NodeTimer>::iterator
i = elapsed_timers.begin();
i != elapsed_timers.end(); i++){
n = block->getNodeNoEx(i->first);
if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
}
}
/* Handle ActiveBlockModifiers */
ABMHandler abmhandler(m_abms, dtime_s, this, false);
@@ -912,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
*/
@@ -1058,16 +1093,30 @@ void ServerEnvironment::step(float dtime)
"Timestamp older than 60s (step)");
// Run node timers
std::map<v3s16, f32> elapsed_timers =
block->m_node_timers.step(dtime);
if(!elapsed_timers.empty())
errorstream<<"Node timers don't work yet!"<<std::endl;
std::map<v3s16, NodeTimer> elapsed_timers =
block->m_node_timers.step((float)dtime);
if(!elapsed_timers.empty()){
MapNode n;
for(std::map<v3s16, NodeTimer>::iterator
i = elapsed_timers.begin();
i != elapsed_timers.end(); i++){
n = block->getNodeNoEx(i->first);
p = i->first + block->getPosRelative();
if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
}
}
}
}
const float abm_interval = 1.0;
if(m_active_block_modifier_interval.step(dtime, abm_interval))
{
do{ // breakable
if(m_active_block_interval_overload_skip > 0){
ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
m_active_block_interval_overload_skip--;
break;
}
ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
TimeTaker timer("modify in active blocks");
@@ -1100,8 +1149,9 @@ void ServerEnvironment::step(float dtime)
infostream<<"WARNING: active block modifiers took "
<<time_ms<<"ms (longer than "
<<max_time_ms<<"ms)"<<std::endl;
m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
}
}
}while(0);
/*
Step script environment (run global on_step())
@@ -1207,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;
}
@@ -1366,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){
@@ -1406,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())
@@ -1429,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;
}
}
@@ -1542,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;
@@ -1566,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
@@ -1596,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();
@@ -1720,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)
{
@@ -1757,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;
}
}
@@ -1998,19 +2055,32 @@ void ClientEnvironment::step(float dtime)
i != player_collisions.end(); i++)
{
CollisionInfo &info = *i;
if(info.t == COLLISION_FALL)
v3f speed_diff = info.new_speed - info.old_speed;;
// Handle only fall damage
// (because otherwise walking against something in fast_move kills you)
if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
continue;
// Get rid of other components
speed_diff.X = 0;
speed_diff.Z = 0;
f32 pre_factor = 1; // 1 hp per node/s
f32 tolerance = BS*14; // 5 without damage
f32 post_factor = 1; // 1 hp per node/s
if(info.type == COLLISION_NODE)
{
//f32 tolerance = BS*10; // 2 without damage
//f32 tolerance = BS*12; // 3 without damage
f32 tolerance = BS*14; // 5 without damage
f32 factor = 1;
if(info.speed > tolerance)
{
f32 damage_f = (info.speed - tolerance)/BS*factor;
u16 damage = (u16)(damage_f+0.5);
if(damage != 0)
damageLocalPlayer(damage, true);
}
const ContentFeatures &f = m_gamedef->ndef()->
get(m_map->getNodeNoEx(info.node_p));
// Determine fall damage multiplier
int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
pre_factor = 1.0 + (float)addp/100.0;
}
float speed = pre_factor * speed_diff.getLength();
if(speed > tolerance)
{
f32 damage_f = (speed - tolerance)/BS * post_factor;
u16 damage = (u16)(damage_f+0.5);
if(damage != 0)
damageLocalPlayer(damage, true);
}
}
@@ -2063,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;
}
@@ -2080,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++)
@@ -2088,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;
@@ -2233,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;
}
@@ -2252,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

@@ -33,11 +33,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <set>
#include "irrlichttypes_extrabloated.h"
#include "player.h"
#include "map.h"
#include <ostream>
#include "activeobject.h"
#include "util/container.h"
#include "util/numeric.h"
#include "mapnode.h"
#include "mapblock.h"
class Server;
class ServerEnvironment;
@@ -46,6 +47,8 @@ class ServerActiveObject;
typedef struct lua_State lua_State;
class ITextureSource;
class IGameDef;
class Map;
class ServerMap;
class ClientMap;
class Environment
@@ -191,11 +194,9 @@ class ServerEnvironment : public Environment
IBackgroundBlockEmerger *emerger);
~ServerEnvironment();
Map & getMap()
{ return *m_map; }
Map & getMap();
ServerMap & getServerMap()
{ return *m_map; }
ServerMap & getServerMap();
lua_State* getLua()
{ return m_lua; }
@@ -204,9 +205,7 @@ class ServerEnvironment : public Environment
{ return m_gamedef; }
float getSendRecommendedInterval()
{
return 0.10;
}
{ return m_recommended_send_interval; }
/*
Save players
@@ -311,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)
@@ -321,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.
@@ -359,12 +358,15 @@ class ServerEnvironment : public Environment
IntervalLimiter m_active_blocks_management_interval;
IntervalLimiter m_active_block_modifier_interval;
IntervalLimiter m_active_blocks_nodemetadata_interval;
int m_active_block_interval_overload_skip;
// Time from the beginning of the game in seconds.
// Incremented in step().
u32 m_game_time;
// 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
@@ -461,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,11 +24,12 @@ 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"
#include "guiPasswordChange.h"
#include "guiInventoryMenu.h"
#include "guiFormSpecMenu.h"
#include "guiTextInputMenu.h"
#include "guiDeathScreen.h"
#include "tool.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"
@@ -77,6 +79,10 @@ struct TextDestChat : public TextDest
{
m_client->typeChatMessage(text);
}
void gotText(std::map<std::string, std::string> fields)
{
m_client->typeChatMessage(narrow_to_wide(fields["text"]));
}
Client *m_client;
};
@@ -88,20 +94,39 @@ struct TextDestNodeMetadata : public TextDest
m_p = p;
m_client = client;
}
// This is deprecated I guess? -celeron55
void gotText(std::wstring text)
{
std::string ntext = wide_to_narrow(text);
infostream<<"Changing text of a sign node: "
<<ntext<<std::endl;
infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
<<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
std::map<std::string, std::string> fields;
fields["text"] = ntext;
m_client->sendNodemetaFields(m_p, "", fields);
}
void gotText(std::map<std::string, std::string> fields)
{
m_client->sendNodemetaFields(m_p, "", fields);
}
v3s16 m_p;
Client *m_client;
};
struct TextDestPlayerInventory : public TextDest
{
TextDestPlayerInventory(Client *client)
{
m_client = client;
}
void gotText(std::map<std::string, std::string> fields)
{
m_client->sendInventoryFields("", fields);
}
Client *m_client;
};
/* Respawn menu callback */
class MainRespawnInitiator: public IRespawnInitiator
@@ -139,6 +164,13 @@ class NodeMetadataFormSource: public IFormSource
return "";
return meta->getString("formspec");
}
std::string resolveText(std::string str)
{
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
if(!meta)
return str;
return meta->resolveString(str);
}
ClientMap *m_map;
v3s16 m_p;
@@ -804,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,
@@ -846,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();
@@ -913,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;
@@ -1132,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
@@ -1205,6 +1287,9 @@ void the_game(
float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10;
float update_draw_list_timer = 0.0;
v3f update_draw_list_last_cam_dir;
bool invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false;
@@ -1213,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;
@@ -1222,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
*/
@@ -1383,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
*/
@@ -1479,8 +1577,8 @@ void the_game(
infostream<<"the_game: "
<<"Launching inventory"<<std::endl;
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
GUIFormSpecMenu *menu =
new GUIFormSpecMenu(device, guiroot, -1,
&g_menumgr,
&client, gamedef);
@@ -1490,7 +1588,8 @@ void the_game(
PlayerInventoryFormSource *src = new PlayerInventoryFormSource(&client);
assert(src);
menu->setFormSpec(src->getForm(), inventoryloc);
menu->setFormSource(new PlayerInventoryFormSource(&client));
menu->setFormSource(src);
menu->setTextDest(new TextDestPlayerInventory(&client));
menu->drop();
}
else if(input->wasKeyDown(EscapeKey))
@@ -1847,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(
@@ -1857,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;
}
/*
@@ -2219,7 +2334,8 @@ void the_game(
{
infostream<<"Ground right-clicked"<<std::endl;
// sign special case, at least until formspec is properly implemented
// Sign special case, at least until formspec is properly implemented.
// Deprecated?
if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
{
infostream<<"Launching metadata text input"<<std::endl;
@@ -2244,14 +2360,15 @@ void the_game(
/* Create menu */
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
GUIFormSpecMenu *menu =
new GUIFormSpecMenu(device, guiroot, -1,
&g_menumgr,
&client, gamedef);
menu->setFormSpec(meta->getString("formspec"),
inventoryloc);
menu->setFormSource(new NodeMetadataFormSource(
&client.getEnv().getClientMap(), nodepos));
menu->setTextDest(new TextDestNodeMetadata(nodepos, &client));
menu->drop();
}
// Otherwise report right click to server
@@ -2271,6 +2388,13 @@ void the_game(
<<playeritem.name<<" is "
<<def.node_placement_prediction<<std::endl;
v3s16 p = neighbourpos;
// Place inside node itself if buildable_to
try{
MapNode n_under = map.getNode(nodepos);
if(nodedef->get(n_under).buildable_to)
p = nodepos;
}catch(InvalidPositionException &e){}
// Find id of predicted node
content_t id;
bool found =
nodedef->getId(def.node_placement_prediction, id);
@@ -2365,7 +2489,6 @@ void the_game(
Fog range
*/
f32 fog_range;
if(farmesh)
{
fog_range = BS*farmesh_range;
@@ -2382,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")){
@@ -2485,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)
{
@@ -2657,7 +2779,19 @@ void the_game(
item = mlist->getItem(client.getPlayerItem());
camera.wield(item);
}
/*
Update block draw list every 200ms or when camera direction has
changed much
*/
update_draw_list_timer += dtime;
if(update_draw_list_timer >= 0.2 ||
update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2){
update_draw_list_timer = 0;
client.getEnv().getClientMap().updateDrawList(driver);
update_draw_list_last_cam_dir = camera_direction;
}
/*
Drawing begins
*/
@@ -2679,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);
}
@@ -2754,11 +2958,6 @@ void the_game(
//timer10.stop();
//TimeTaker //timer11("//timer11");
/*
Draw gui
*/
// 0-1ms
guienv->drawAll();
/*
Draw hotbar
@@ -2784,6 +2983,12 @@ void the_game(
NULL);
}
/*
Draw gui
*/
// 0-1ms
guienv->drawAll();
/*
End scene
*/
@@ -2860,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,7 +28,9 @@ class INodeDefManager;
class ICraftDefManager;
class ITextureSource;
class ISoundManager;
class IShaderSource;
class MtEventManager;
class IRollbackReportSink;
/*
An interface for fetching game-global definitions like tool and
@@ -47,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;
@@ -54,6 +58,10 @@ class IGameDef
// Only usable on the client
virtual ISoundManager* getSoundManager()=0;
virtual MtEventManager* getEventManager()=0;
// Only usable on the server, and NOT thread-safe. It is usable from the
// environment thread.
virtual IRollbackReportSink* getRollbackReportSink(){return NULL;}
// Used on the client
virtual bool checkLocalPrivilege(const std::string &priv)
@@ -65,7 +73,9 @@ 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();}
};
#endif

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

@@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "guiInventoryMenu.h"
#include "guiFormSpecMenu.h"
#include "constants.h"
#include "gamedef.h"
#include "keycode.h"
@@ -33,6 +33,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h"
#include "util/numeric.h"
#include "gettext.h"
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
@@ -44,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)
@@ -120,19 +122,21 @@ void drawItemStack(video::IVideoDriver *driver,
}
/*
GUIInventoryMenu
GUIFormSpecMenu
*/
GUIInventoryMenu::GUIInventoryMenu(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),
m_text_dst(NULL),
m_selected_item(NULL),
m_selected_amount(0),
m_selected_dragging(false),
@@ -140,15 +144,16 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
{
}
GUIInventoryMenu::~GUIInventoryMenu()
GUIFormSpecMenu::~GUIFormSpecMenu()
{
removeChildren();
delete m_selected_item;
delete m_form_src;
delete m_text_dst;
}
void GUIInventoryMenu::removeChildren()
void GUIFormSpecMenu::removeChildren()
{
const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy;
@@ -175,7 +180,7 @@ void GUIInventoryMenu::removeChildren()
}
}
void GUIInventoryMenu::regenerateGui(v2u32 screensize)
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
// Remove children
removeChildren();
@@ -183,24 +188,39 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
v2s32 size(100,100);
s32 helptext_h = 15;
core::rect<s32> rect;
// Base position of contents of form
v2s32 basepos = getBasePos();
// State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
// Used to adjust form size automatically if needed
// A proceed button is added if there is no size[] element
int bp_set = 0;
/* Convert m_init_draw_spec to m_inventorylists */
m_inventorylists.clear();
m_images.clear();
m_backgrounds.clear();
m_itemimages.clear();
m_fields.clear();
Strfnd f(m_formspec_string);
while(f.atend() == false)
{
std::string type = trim(f.next("["));
if(type == "invsize")
if(type == "invsize" || type == "size")
{
v2f invsize;
invsize.X = stof(f.next(","));
invsize.Y = stof(f.next(";"));
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
f.next("]");
if(type == "size")
{
invsize.Y = stof(f.next("]"));
}
else{
invsize.Y = stof(f.next(";"));
f.next("]");
}
infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
padding = v2s32(screensize.Y/40, screensize.Y/40);
spacing = v2s32(screensize.Y/12, screensize.Y/13);
@@ -218,6 +238,7 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
DesiredRect = rect;
recalculateAbsolutePosition(false);
basepos = getBasePos();
bp_set = 2;
}
else if(type == "list")
{
@@ -238,8 +259,13 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
f.next("]");
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
std::string start_i_s = f.next("]");
s32 start_i = 0;
if(start_i_s != "")
start_i = stoi(start_i_s);
if(bp_set != 2)
errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
}
else if(type == "image")
{
@@ -254,8 +280,265 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
if(bp_set != 2)
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(";");
std::string flabel = f.next(";");
if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
{
if(!bp_set)
{
rect = core::rect<s32>(
screensize.X/2 - 580/2,
screensize.Y/2 - 300/2,
screensize.X/2 + 580/2,
screensize.Y/2 + 300/2
);
DesiredRect = rect;
recalculateAbsolutePosition(false);
basepos = getBasePos();
bp_set = 1;
}
else if(bp_set == 2)
errorstream<<"WARNING: invalid use of unpositioned field in inventory"<<std::endl;
v2s32 pos = basepos;
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
}
else
{
v2s32 pos;
pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
v2s32 geom;
geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
fname = f.next(";");
flabel = f.next(";");
if(bp_set != 2)
errorstream<<"WARNING: invalid use of positioned field without a size[] element"<<std::endl;
}
std::string odefault = f.next("]");
std::string fdefault;
// fdefault may contain a variable reference, which
// needs to be resolved from the node metadata
if(m_form_src)
fdefault = m_form_src->resolveText(odefault);
else
fdefault = odefault;
FieldSpec spec = FieldSpec(
narrow_to_wide(fname.c_str()),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(fdefault.c_str()),
258+m_fields.size()
);
// three cases: field and no label, label and no field, label and field
if (flabel == "")
{
spec.send = true;
gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
Environment->setFocus(e);
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END;
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
}
else if (fname == "")
{
// set spec field id to 0, this stops submit searching for a value that isn't there
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
}
else
{
spec.send = true;
gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
Environment->setFocus(e);
rect.UpperLeftCorner.Y -= 15;
rect.LowerRightCorner.Y -= 15;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END;
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
}
m_fields.push_back(spec);
}
else if(type == "label")
{
v2s32 pos = padding;
pos.X += stof(f.next(",")) * (float)spacing.X;
pos.Y += stof(f.next(";")) * (float)spacing.Y;
rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
std::string flabel = f.next("]");
if(bp_set != 2)
errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
FieldSpec spec = FieldSpec(
narrow_to_wide(""),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(""),
258+m_fields.size()
);
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
m_fields.push_back(spec);
}
else if(type == "button" || type == "button_exit")
{
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);
pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2;
rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
std::string fname = f.next(";");
std::string flabel = f.next("]");
if(bp_set != 2)
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
FieldSpec spec = FieldSpec(
narrow_to_wide(fname.c_str()),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(""),
258+m_fields.size()
);
spec.is_button = true;
if(type == "button_exit")
spec.is_exit = true;
Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
m_fields.push_back(spec);
}
else if(type == "image_button" || type == "image_button_exit")
{
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 image_button without a size[] element"<<std::endl;
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()
);
spec.is_button = true;
if(type == "image_button_exit")
spec.is_exit = true;
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
@@ -265,16 +548,44 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
}
}
// Add children
// If there's inventory, put the usage string at the bottom
if (m_inventorylists.size())
{
changeCtype("");
core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
size.Y-rect.getHeight()-5);
const wchar_t *text =
L"Left click: Move all items, Right click: Move single item";
const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
Environment->addStaticText(text, rect, false, true, this, 256);
changeCtype("C");
}
// If there's fields, add a Proceed button
if (m_fields.size() && bp_set != 2)
{
// if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
rect = core::rect<s32>(
screensize.X/2 - 580/2,
screensize.Y/2 - 300/2,
screensize.X/2 + 580/2,
screensize.Y/2 + 240/2+(m_fields.size()*60)
);
DesiredRect = rect;
recalculateAbsolutePosition(false);
basepos = getBasePos();
// Add tooltip
changeCtype("");
{
v2s32 pos = basepos;
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
Environment->addButton(rect, this, 257, wgettext("Proceed"));
}
changeCtype("C");
}
// Add tooltip
{
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true);
@@ -287,7 +598,7 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
}
}
GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
{
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
@@ -297,13 +608,14 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
{
s32 item_i = i + s.start_item_i;
s32 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y;
v2s32 p0(x,y);
core::rect<s32> rect = imgrect + s.pos + p0;
if(rect.isPointInside(p))
{
return ItemSpec(s.inventoryloc, s.listname, i);
return ItemSpec(s.inventoryloc, s.listname, item_i);
}
}
}
@@ -311,7 +623,7 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
return ItemSpec(InventoryLocation(), "", -1);
}
void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
{
video::IVideoDriver* driver = Environment->getVideoDriver();
@@ -323,7 +635,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv){
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
<<"The inventory location "
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
@@ -331,7 +643,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
}
InventoryList *ilist = inv->getList(s.listname);
if(!ilist){
infostream<<"GUIInventoryMenu::drawList(): WARNING: "
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
<<"The inventory list \""<<s.listname<<"\" @ \""
<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
@@ -342,13 +654,16 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
{
u32 item_i = i + s.start_item_i;
if(item_i >= ilist->getSize())
break;
s32 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y;
v2s32 p(x,y);
core::rect<s32> rect = imgrect + s.pos + p;
ItemStack item;
if(ilist)
item = ilist->getItem(i);
item = ilist->getItem(item_i);
bool selected = m_selected_item
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
@@ -404,7 +719,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
}
}
void GUIInventoryMenu::drawSelectedItem()
void GUIFormSpecMenu::drawSelectedItem()
{
if(!m_selected_item)
return;
@@ -429,7 +744,7 @@ void GUIInventoryMenu::drawSelectedItem()
drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
}
void GUIInventoryMenu::drawMenu()
void GUIFormSpecMenu::drawMenu()
{
if(m_form_src){
std::string newform = m_form_src->getForm();
@@ -439,6 +754,8 @@ void GUIInventoryMenu::drawMenu()
}
}
m_pointer = m_device->getCursorControl()->getPosition();
updateSelectedItem();
gui::IGUISkin* skin = Environment->getSkin();
@@ -452,17 +769,28 @@ void GUIInventoryMenu::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];
@@ -481,18 +809,105 @@ void GUIInventoryMenu::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 GUIInventoryMenu::updateSelectedItem()
void GUIFormSpecMenu::updateSelectedItem()
{
// WARNING: BLACK MAGIC
// See if there is a stack suited for our current guess.
// If such stack does not exist, clear the guess.
if(m_selected_content_guess.name != "")
{
bool found = false;
for(u32 i=0; i<m_inventorylists.size() && !found; i++){
const ListDrawSpec &s = m_inventorylists[i];
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv)
continue;
InventoryList *list = inv->getList(s.listname);
if(!list)
continue;
for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
u32 item_i = i + s.start_item_i;
if(item_i >= list->getSize())
continue;
ItemStack stack = list->getItem(item_i);
if(stack.name == m_selected_content_guess.name &&
stack.count == m_selected_content_guess.count){
found = true;
if(m_selected_item){
// If guessed stack is already selected, all is fine
if(m_selected_item->inventoryloc == s.inventoryloc &&
m_selected_item->listname == s.listname &&
m_selected_item->i == (s32)item_i &&
m_selected_amount == stack.count){
break;
}
delete m_selected_item;
m_selected_item = NULL;
}
infostream<<"Client: Changing selected content guess to "
<<s.inventoryloc.dump()<<" "<<s.listname
<<" "<<item_i<<std::endl;
m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
m_selected_amount = stack.count;
break;
}
}
}
if(!found){
infostream<<"Client: Discarding selected content guess: "
<<m_selected_content_guess.getItemString()<<std::endl;
m_selected_content_guess.name = "";
}
}
// If the selected stack has become empty for some reason, deselect it.
// If the selected stack has become smaller, adjust m_selected_amount.
if(m_selected_item)
@@ -558,7 +973,36 @@ void GUIInventoryMenu::updateSelectedItem()
}
}
bool GUIInventoryMenu::OnEvent(const SEvent& event)
void GUIFormSpecMenu::acceptInput()
{
if(m_text_dst)
{
std::map<std::string, std::string> fields;
gui::IGUIElement *e;
for(u32 i=0; i<m_fields.size(); i++)
{
const FieldSpec &s = m_fields[i];
if(s.send)
{
if(s.is_button)
{
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
}
else
{
e = getElementFromId(s.fid);
if(e != NULL)
{
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
}
}
}
}
m_text_dst->gotText(fields);
}
}
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if(event.EventType==EET_KEY_INPUT_EVENT)
{
@@ -569,25 +1013,22 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
quitMenu();
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.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
{
acceptInput();
quitMenu();
return true;
}
}
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;
@@ -615,14 +1056,14 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
InventoryList *list = inv_s->getList(s.listname);
if(list == NULL){
errorstream<<"InventoryMenu: The selected inventory list \""
verbosestream<<"InventoryMenu: The selected inventory list \""
<<s.listname<<"\" does not exist"<<std::endl;
s.i = -1; // make it invalid again
break;
}
if((u32)s.i >= list->getSize()){
errorstream<<"InventoryMenu: The selected inventory list \""
infostream<<"InventoryMenu: The selected inventory list \""
<<s.listname<<"\" is too small (i="<<s.i<<", size="
<<list->getSize()<<")"<<std::endl;
s.i = -1; // make it invalid again
@@ -779,21 +1220,28 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
// Check how many items can be moved
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
if(leftover.count == stack_from.count)
// If source stack cannot be added to destination stack at all,
// they are swapped
if(leftover.count == stack_from.count && leftover.name == stack_from.name)
{
// Swap the stacks
m_selected_amount -= stack_to.count;
m_selected_amount = stack_to.count;
// In case the server doesn't directly swap them but instead
// moves stack_to somewhere else, set this
m_selected_content_guess = stack_to;
m_selected_content_guess_inventory = s.inventoryloc;
}
// Source stack goes fully into destination stack
else if(leftover.empty())
{
// Item fits
m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
}
// Source stack goes partly into destination stack
else
{
// Item only fits partially
move_amount -= leftover.count;
m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
}
infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
@@ -809,6 +1257,8 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
}
else if(drop_amount > 0)
{
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_DROP
assert(m_selected_item && m_selected_item->isValid());
@@ -832,6 +1282,8 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
}
else if(craft_amount > 0)
{
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_CRAFT
assert(s.isValid());
@@ -851,6 +1303,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
m_selected_item = NULL;
m_selected_amount = 0;
m_selected_dragging = false;
m_selected_content_guess = ItemStack();
}
}
if(event.EventType==EET_GUI_EVENT)
@@ -860,7 +1313,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
{
if(!canTakeFocus(event.GUIEvent.Element))
{
infostream<<"GUIInventoryMenu: Not allowing focus change."
infostream<<"GUIFormSpecMenu: Not allowing focus change."
<<std::endl;
// Returning true disables focus change
return true;
@@ -868,15 +1321,45 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
}
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
{
/*switch(event.GUIEvent.Caller->getID())
switch(event.GUIEvent.Caller->getID())
{
case 256: // continue
setVisible(false);
break;
case 257: // exit
dev->closeDevice();
break;
}*/
case 257:
acceptInput();
quitMenu();
// quitMenu deallocates menu
return true;
}
// find the element that was clicked
for(u32 i=0; i<m_fields.size(); i++)
{
FieldSpec &s = m_fields[i];
// if its a button, set the send field so
// lua knows which button was pressed
if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
{
s.send = true;
acceptInput();
if(s.is_exit){
quitMenu();
return true;
}else{
s.send = false;
// Restore focus to the full form
Environment->setFocus(this);
return true;
}
}
}
}
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
{
if(event.GUIEvent.Caller->getID() > 257)
{
acceptInput();
quitMenu();
// quitMenu deallocates menu
return true;
}
}
}

View File

@@ -29,11 +29,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IGameDef;
class InventoryManager;
struct TextDest
{
virtual ~TextDest() {};
// This is deprecated I guess? -celeron55
virtual void gotText(std::wstring text){}
virtual void gotText(std::map<std::string, std::string> fields) = 0;
};
class IFormSource
{
public:
virtual ~IFormSource(){}
virtual std::string getForm() = 0;
// Fill in variables in field text
virtual std::string resolveText(std::string str){ return str; }
};
void drawItemStack(video::IVideoDriver *driver,
@@ -43,7 +53,7 @@ void drawItemStack(video::IVideoDriver *driver,
const core::rect<s32> *clip,
IGameDef *gamedef);
class GUIInventoryMenu : public GUIModalMenu
class GUIFormSpecMenu : public GUIModalMenu
{
struct ItemSpec
{
@@ -76,11 +86,12 @@ class GUIInventoryMenu : public GUIModalMenu
}
ListDrawSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
v2s32 a_pos, v2s32 a_geom):
v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
inventoryloc(a_inventoryloc),
listname(a_listname),
pos(a_pos),
geom(a_geom)
geom(a_geom),
start_item_i(a_start_item_i)
{
}
@@ -88,6 +99,7 @@ class GUIInventoryMenu : public GUIModalMenu
std::string listname;
v2s32 pos;
v2s32 geom;
s32 start_item_i;
};
struct ImageDrawSpec
@@ -106,15 +118,42 @@ class GUIInventoryMenu : public GUIModalMenu
v2s32 pos;
v2s32 geom;
};
struct FieldSpec
{
FieldSpec()
{
}
FieldSpec(const std::wstring name, const std::wstring label, const std::wstring fdeflt, int id):
fname(name),
flabel(label),
fdefault(fdeflt),
fid(id)
{
send = false;
is_button = false;
is_exit = false;
tooltip="";
}
std::wstring fname;
std::wstring flabel;
std::wstring fdefault;
int fid;
bool send;
bool is_button;
bool is_exit;
core::rect<s32> rect;
std::string tooltip;
};
public:
GUIInventoryMenu(gui::IGUIEnvironment* env,
GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
InventoryManager *invmgr,
IGameDef *gamedef
);
~GUIInventoryMenu();
~GUIFormSpecMenu();
void setFormSpec(const std::string &formspec_string,
InventoryLocation current_inventory_location)
@@ -124,12 +163,18 @@ class GUIInventoryMenu : public GUIModalMenu
regenerateGui(m_screensize_old);
}
// form_src is deleted by this GUIInventoryMenu
// form_src is deleted by this GUIFormSpecMenu
void setFormSource(IFormSource *form_src)
{
m_form_src = form_src;
}
// text_dst is deleted by this GUIFormSpecMenu
void setTextDest(TextDest *text_dst)
{
m_text_dst = text_dst;
}
void removeChildren();
/*
Remove and re-add (or reposition) stuff
@@ -142,6 +187,7 @@ class GUIInventoryMenu : public GUIModalMenu
void drawMenu();
void updateSelectedItem();
void acceptInput();
bool OnEvent(const SEvent& event);
protected:
@@ -154,19 +200,30 @@ class GUIInventoryMenu : public GUIModalMenu
v2s32 spacing;
v2s32 imgsize;
irr::IrrlichtDevice* m_device;
InventoryManager *m_invmgr;
IGameDef *m_gamedef;
std::string m_formspec_string;
InventoryLocation m_current_inventory_location;
IFormSource *m_form_src;
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;
u32 m_selected_amount;
bool m_selected_dragging;
// WARNING: BLACK MAGIC
// Used to guess and keep up with some special things the server can do.
// If name is "", no guess exists.
ItemStack m_selected_content_guess;
InventoryLocation m_selected_content_guess_inventory;
v2s32 m_pointer;
gui::IGUIStaticText *m_tooltip_element;

View File

@@ -30,11 +30,14 @@
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "settings.h"
#include <algorithm>
#define KMaxButtonPerColumns 12
enum
{
GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
//buttons
// buttons
GUI_ID_KEY_FORWARD_BUTTON,
GUI_ID_KEY_BACKWARD_BUTTON,
GUI_ID_KEY_LEFT_BUTTON,
@@ -50,15 +53,21 @@ enum
GUI_ID_KEY_DROP_BUTTON,
GUI_ID_KEY_INVENTORY_BUTTON,
GUI_ID_KEY_DUMP_BUTTON,
GUI_ID_KEY_RANGE_BUTTON
GUI_ID_KEY_RANGE_BUTTON,
// other
GUI_ID_CB_AUX1_DESCENDS,
};
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
GUIModalMenu(env, parent, id, menumgr)
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
GUIModalMenu(env, parent, id, menumgr)
{
shift_down = false;
activeKey = -1;
this->key_used_text = NULL;
init_keys();
for(size_t i=0; i<key_settings.size(); i++)
this->key_used.push_back(key_settings.at(i)->key);
}
GUIKeyChangeMenu::~GUIKeyChangeMenu()
@@ -71,12 +80,12 @@ void GUIKeyChangeMenu::removeChildren()
const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy;
for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
!= children.end(); i++)
!= children.end(); i++)
{
children_copy.push_back(*i);
}
for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
!= children_copy.end(); i++)
!= children_copy.end(); i++)
{
(*i)->remove();
}
@@ -84,20 +93,12 @@ void GUIKeyChangeMenu::removeChildren()
void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
/*
Remove stuff
*/
removeChildren();
/*
Calculate new sizes and positions
*/
v2s32 size(620, 430);
core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
screensize.Y / 2 + size.Y / 2);
screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
screensize.Y / 2 + size.Y / 2);
DesiredRect = rect;
recalculateAbsolutePosition(false);
@@ -109,271 +110,60 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
rect += topleft + v2s32(25, 3);
//gui::IGUIStaticText *t =
Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"),
rect, false, true, this, -1);
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
// Build buttons
v2s32 offset(25, 60);
// buttons
for(size_t i = 0; i < key_settings.size(); i++)
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Forward"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
key_setting *k = key_settings.at(i);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(k->button_name, rect, false, true, this, -1);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
k->button = Environment->addButton(rect, this, k->id, wgettext(k->key.name()));
}
if(i + 1 == KMaxButtonPerColumns)
offset = v2s32(250, 60);
else
offset += v2s32(0, 25);
}
{
s32 option_x = offset.X + 10;
s32 option_y = offset.Y;
u32 option_w = 180;
{
core::rect<s32> rect(0, 0, option_w, 30);
rect += topleft + v2s32(option_x, option_y);
Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down"));
}
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->forward = Environment->addButton(rect, this,
GUI_ID_KEY_FORWARD_BUTTON,
wgettext(key_forward.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Backward"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->backward = Environment->addButton(rect, this,
GUI_ID_KEY_BACKWARD_BUTTON,
wgettext(key_backward.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Left"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->left = Environment->addButton(rect, this, GUI_ID_KEY_LEFT_BUTTON,
wgettext(key_left.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Right"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->right = Environment->addButton(rect, this,
GUI_ID_KEY_RIGHT_BUTTON,
wgettext(key_right.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Use"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->use = Environment->addButton(rect, this, GUI_ID_KEY_USE_BUTTON,
wgettext(key_use.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Sneak"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->sneak = Environment->addButton(rect, this,
GUI_ID_KEY_SNEAK_BUTTON,
wgettext(key_sneak.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Jump"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->jump = Environment->addButton(rect, this, GUI_ID_KEY_JUMP_BUTTON,
wgettext(key_jump.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Drop"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->dropbtn = Environment->addButton(rect, this, GUI_ID_KEY_DROP_BUTTON,
wgettext(key_drop.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Inventory"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->inventory = Environment->addButton(rect, this,
GUI_ID_KEY_INVENTORY_BUTTON,
wgettext(key_inventory.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Chat"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->chat = Environment->addButton(rect, this, GUI_ID_KEY_CHAT_BUTTON,
wgettext(key_chat.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Command"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->cmd = Environment->addButton(rect, this, GUI_ID_KEY_CMD_BUTTON,
wgettext(key_cmd.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Console"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->console = Environment->addButton(rect, this, GUI_ID_KEY_CONSOLE_BUTTON,
wgettext(key_console.name()));
}
//next col
offset = v2s32(250, 60);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Toggle fly"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->fly = Environment->addButton(rect, this, GUI_ID_KEY_FLY_BUTTON,
wgettext(key_fly.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Toggle fast"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->fast = Environment->addButton(rect, this, GUI_ID_KEY_FAST_BUTTON,
wgettext(key_fast.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Range select"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->range = Environment->addButton(rect, this,
GUI_ID_KEY_RANGE_BUTTON,
wgettext(key_range.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Print stacks"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->dump = Environment->addButton(rect, this, GUI_ID_KEY_DUMP_BUTTON,
wgettext(key_dump.name()));
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
wgettext("Save"));
wgettext("Save"));
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
wgettext("Cancel"));
wgettext("Cancel"));
}
changeCtype("C");
}
void GUIKeyChangeMenu::drawMenu()
@@ -396,102 +186,32 @@ void GUIKeyChangeMenu::drawMenu()
bool GUIKeyChangeMenu::acceptInput()
{
g_settings->set("keymap_forward", key_forward.sym());
g_settings->set("keymap_backward", key_backward.sym());
g_settings->set("keymap_left", key_left.sym());
g_settings->set("keymap_right", key_right.sym());
g_settings->set("keymap_jump", key_jump.sym());
g_settings->set("keymap_sneak", key_sneak.sym());
g_settings->set("keymap_drop", key_drop.sym());
g_settings->set("keymap_inventory", key_inventory.sym());
g_settings->set("keymap_chat", key_chat.sym());
g_settings->set("keymap_cmd", key_cmd.sym());
g_settings->set("keymap_console", key_console.sym());
g_settings->set("keymap_rangeselect", key_range.sym());
g_settings->set("keymap_freemove", key_fly.sym());
g_settings->set("keymap_fastmove", key_fast.sym());
g_settings->set("keymap_special1", key_use.sym());
g_settings->set("keymap_print_debug_stacks", key_dump.sym());
for(size_t i = 0; i < key_settings.size(); i++)
{
key_setting *k = key_settings.at(i);
g_settings->set(k->setting_name, k->key.sym());
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
}
clearKeyCache();
return true;
}
void GUIKeyChangeMenu::init_keys()
{
key_forward = getKeySetting("keymap_forward");
key_backward = getKeySetting("keymap_backward");
key_left = getKeySetting("keymap_left");
key_right = getKeySetting("keymap_right");
key_jump = getKeySetting("keymap_jump");
key_sneak = getKeySetting("keymap_sneak");
key_drop = getKeySetting("keymap_drop");
key_inventory = getKeySetting("keymap_inventory");
key_chat = getKeySetting("keymap_chat");
key_cmd = getKeySetting("keymap_cmd");
key_console = getKeySetting("keymap_console");
key_range = getKeySetting("keymap_rangeselect");
key_fly = getKeySetting("keymap_freemove");
key_fast = getKeySetting("keymap_fastmove");
key_use = getKeySetting("keymap_special1");
key_dump = getKeySetting("keymap_print_debug_stacks");
}
bool GUIKeyChangeMenu::resetMenu()
{
if (activeKey >= 0)
{
switch (activeKey)
for(size_t i = 0; i < key_settings.size(); i++)
{
case GUI_ID_KEY_FORWARD_BUTTON:
this->forward->setText(
wgettext(key_forward.name()));
break;
case GUI_ID_KEY_BACKWARD_BUTTON:
this->backward->setText(
wgettext(key_backward.name()));
break;
case GUI_ID_KEY_LEFT_BUTTON:
this->left->setText(wgettext(key_left.name()));
break;
case GUI_ID_KEY_RIGHT_BUTTON:
this->right->setText(wgettext(key_right.name()));
break;
case GUI_ID_KEY_JUMP_BUTTON:
this->jump->setText(wgettext(key_jump.name()));
break;
case GUI_ID_KEY_SNEAK_BUTTON:
this->sneak->setText(wgettext(key_sneak.name()));
break;
case GUI_ID_KEY_DROP_BUTTON:
this->dropbtn->setText(wgettext(key_drop.name()));
break;
case GUI_ID_KEY_INVENTORY_BUTTON:
this->inventory->setText(
wgettext(key_inventory.name()));
break;
case GUI_ID_KEY_CHAT_BUTTON:
this->chat->setText(wgettext(key_chat.name()));
break;
case GUI_ID_KEY_CMD_BUTTON:
this->cmd->setText(wgettext(key_cmd.name()));
break;
case GUI_ID_KEY_CONSOLE_BUTTON:
this->console->setText(wgettext(key_console.name()));
break;
case GUI_ID_KEY_RANGE_BUTTON:
this->range->setText(wgettext(key_range.name()));
break;
case GUI_ID_KEY_FLY_BUTTON:
this->fly->setText(wgettext(key_fly.name()));
break;
case GUI_ID_KEY_FAST_BUTTON:
this->fast->setText(wgettext(key_fast.name()));
break;
case GUI_ID_KEY_USE_BUTTON:
this->use->setText(wgettext(key_use.name()));
break;
case GUI_ID_KEY_DUMP_BUTTON:
this->dump->setText(wgettext(key_dump.name()));
break;
key_setting *k = key_settings.at(i);
if(k->id == activeKey)
{
k->button->setText(wgettext(k->key.name()));
break;
}
}
activeKey = -1;
return false;
@@ -501,104 +221,72 @@ bool GUIKeyChangeMenu::resetMenu()
bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
{
if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
&& event.KeyInput.PressedDown)
&& event.KeyInput.PressedDown)
{
changeCtype("");
KeyPress kp(event.KeyInput);
bool prefer_character = shift_down;
KeyPress kp(event.KeyInput, prefer_character);
bool shift_went_down = false;
if(!shift_down &&
(event.KeyInput.Key == irr::KEY_SHIFT ||
event.KeyInput.Key == irr::KEY_LSHIFT ||
event.KeyInput.Key == irr::KEY_RSHIFT))
shift_went_down = true;
if (activeKey == GUI_ID_KEY_FORWARD_BUTTON)
// Remove Key already in use message
if(this->key_used_text)
{
this->forward->setText(wgettext(kp.name()));
this->key_forward = kp;
this->key_used_text->remove();
this->key_used_text = NULL;
}
else if (activeKey == GUI_ID_KEY_BACKWARD_BUTTON)
// Display Key already in use message
if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
{
this->backward->setText(wgettext(kp.name()));
this->key_backward = kp;
core::rect < s32 > rect(0, 0, 600, 40);
rect += v2s32(0, 0) + v2s32(25, 30);
this->key_used_text = Environment->addStaticText(wgettext("Key already in use"),
rect, false, true, this, -1);
//infostream << "Key already in use" << std::endl;
}
else if (activeKey == GUI_ID_KEY_LEFT_BUTTON)
// But go on
{
this->left->setText(wgettext(kp.name()));
this->key_left = kp;
key_setting *k=NULL;
for(size_t i = 0; i < key_settings.size(); i++)
{
if(key_settings.at(i)->id == activeKey)
{
k = key_settings.at(i);
break;
}
}
assert(k);
k->key = kp;
k->button->setText(wgettext(k->key.name()));
this->key_used.push_back(kp);
changeCtype("C");
// Allow characters made with shift
if(shift_went_down){
shift_down = true;
return false;
}else{
activeKey = -1;
return true;
}
}
else if (activeKey == GUI_ID_KEY_RIGHT_BUTTON)
{
this->right->setText(wgettext(kp.name()));
this->key_right = kp;
}
else if (activeKey == GUI_ID_KEY_JUMP_BUTTON)
{
this->jump->setText(wgettext(kp.name()));
this->key_jump = kp;
}
else if (activeKey == GUI_ID_KEY_SNEAK_BUTTON)
{
this->sneak->setText(wgettext(kp.name()));
this->key_sneak = kp;
}
else if (activeKey == GUI_ID_KEY_DROP_BUTTON)
{
this->dropbtn->setText(wgettext(kp.name()));
this->key_drop = kp;
}
else if (activeKey == GUI_ID_KEY_INVENTORY_BUTTON)
{
this->inventory->setText(wgettext(kp.name()));
this->key_inventory = kp;
}
else if (activeKey == GUI_ID_KEY_CHAT_BUTTON)
{
this->chat->setText(wgettext(kp.name()));
this->key_chat = kp;
}
else if (activeKey == GUI_ID_KEY_CMD_BUTTON)
{
this->cmd->setText(wgettext(kp.name()));
this->key_cmd = kp;
}
else if (activeKey == GUI_ID_KEY_CONSOLE_BUTTON)
{
this->console->setText(wgettext(kp.name()));
this->key_console = kp;
}
else if (activeKey == GUI_ID_KEY_RANGE_BUTTON)
{
this->range->setText(wgettext(kp.name()));
this->key_range = kp;
}
else if (activeKey == GUI_ID_KEY_FLY_BUTTON)
{
this->fly->setText(wgettext(kp.name()));
this->key_fly = kp;
}
else if (activeKey == GUI_ID_KEY_FAST_BUTTON)
{
this->fast->setText(wgettext(kp.name()));
this->key_fast = kp;
}
else if (activeKey == GUI_ID_KEY_USE_BUTTON)
{
this->use->setText(wgettext(kp.name()));
this->key_use = kp;
}
else if (activeKey == GUI_ID_KEY_DUMP_BUTTON)
{
this->dump->setText(wgettext(kp.name()));
this->key_dump = kp;
}
changeCtype("C");
activeKey = -1;
return true;
}
if (event.EventType == EET_GUI_EVENT)
{
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible())
&& isVisible())
{
if (!canTakeFocus(event.GUIEvent.Element))
{
dstream << "GUIMainMenu: Not allowing focus change."
<< std::endl;
<< std::endl;
// Returning true disables focus change
return true;
}
@@ -606,106 +294,74 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
{
if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON &&
event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
{
changeCtype("");
}
switch (event.GUIEvent.Caller->getID())
{
case GUI_ID_BACK_BUTTON: //back
acceptInput();
quitMenu();
return true;
case GUI_ID_ABORT_BUTTON: //abort
quitMenu();
return true;
case GUI_ID_KEY_FORWARD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->forward->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_BACKWARD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->backward->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_LEFT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->left->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_RIGHT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->right->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_USE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->use->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_FLY_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->fly->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_FAST_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->fast->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_JUMP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->jump->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_DROP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->dropbtn->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CHAT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->chat->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CMD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->cmd->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CONSOLE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->console->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_SNEAK_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->sneak->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_INVENTORY_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->inventory->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_DUMP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->dump->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_RANGE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->range->setText(wgettext("press Key"));
break;
case GUI_ID_BACK_BUTTON: //back
acceptInput();
quitMenu();
return true;
case GUI_ID_ABORT_BUTTON: //abort
quitMenu();
return true;
default:
key_setting *k = NULL;
for(size_t i = 0; i < key_settings.size(); i++)
{
if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
{
k = key_settings.at(i);
break;
}
}
assert(k);
resetMenu();
shift_down = false;
activeKey = event.GUIEvent.Caller->getID();
k->button->setText(wgettext("press key"));
this->key_used.erase(std::remove(this->key_used.begin(),
this->key_used.end(), k->key), this->key_used.end());
break;
}
Environment->setFocus(this);
//Buttons
changeCtype("C");
}
}
return Parent ? Parent->OnEvent(event) : false;
}
void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string setting_name)
{
key_setting *k = new key_setting;
k->id = id;
k->button_name = wgettext(button_name.c_str());
k->setting_name = setting_name;
k->key = getKeySetting(k->setting_name.c_str());
key_settings.push_back(k);
}
void GUIKeyChangeMenu::init_keys()
{
this->add_key(GUI_ID_KEY_FORWARD_BUTTON, "Forward", "keymap_forward");
this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, "Backward", "keymap_backward");
this->add_key(GUI_ID_KEY_LEFT_BUTTON, "Left", "keymap_left");
this->add_key(GUI_ID_KEY_RIGHT_BUTTON, "Right", "keymap_right");
this->add_key(GUI_ID_KEY_USE_BUTTON, "Use", "keymap_special1");
this->add_key(GUI_ID_KEY_JUMP_BUTTON, "Jump", "keymap_jump");
this->add_key(GUI_ID_KEY_SNEAK_BUTTON, "Sneak", "keymap_sneak");
this->add_key(GUI_ID_KEY_DROP_BUTTON, "Drop", "keymap_drop");
this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, "Inventory", "keymap_inventory");
this->add_key(GUI_ID_KEY_CHAT_BUTTON, "Chat", "keymap_chat");
this->add_key(GUI_ID_KEY_CMD_BUTTON, "Command", "keymap_cmd");
this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console");
this->add_key(GUI_ID_KEY_FLY_BUTTON, "Toggle fly", "keymap_freemove");
this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove");
this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect");
this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks");
}

View File

@@ -28,6 +28,16 @@
#include "gettext.h"
#include "keycode.h"
#include <string>
#include <vector>
typedef struct {
int id;
wchar_t *button_name;
KeyPress key;
std::string setting_name;
gui::IGUIButton *button;
} key_setting;
class GUIKeyChangeMenu: public GUIModalMenu
{
@@ -54,40 +64,15 @@ class GUIKeyChangeMenu: public GUIModalMenu
bool resetMenu();
gui::IGUIButton *forward;
gui::IGUIButton *backward;
gui::IGUIButton *left;
gui::IGUIButton *right;
gui::IGUIButton *use;
gui::IGUIButton *sneak;
gui::IGUIButton *jump;
gui::IGUIButton *dropbtn;
gui::IGUIButton *inventory;
gui::IGUIButton *fly;
gui::IGUIButton *fast;
gui::IGUIButton *range;
gui::IGUIButton *dump;
gui::IGUIButton *chat;
gui::IGUIButton *cmd;
gui::IGUIButton *console;
void add_key(int id, std::string setting_name, std::string button_name);
bool shift_down;
s32 activeKey;
KeyPress key_forward;
KeyPress key_backward;
KeyPress key_left;
KeyPress key_right;
KeyPress key_use;
KeyPress key_sneak;
KeyPress key_jump;
KeyPress key_drop;
KeyPress key_inventory;
KeyPress key_fly;
KeyPress key_fast;
KeyPress key_range;
KeyPress key_chat;
KeyPress key_cmd;
KeyPress key_console;
KeyPress key_dump;
std::vector<KeyPress> key_used;
gui::IGUIStaticText *key_used_text;
std::vector<key_setting *> key_settings;
};
#endif

View File

@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tile.h" // getTexturePath
#include "filesys.h"
#include "util/string.h"
#include "subgame.h"
struct CreateWorldDestMainMenu : public CreateWorldDest
{
@@ -47,6 +48,22 @@ struct CreateWorldDestMainMenu : public CreateWorldDest
{}
void accepted(std::wstring name, std::string gameid)
{
std::string name_narrow = wide_to_narrow(name);
if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS))
{
m_menu->displayMessageMenu(wgettext("Cannot create world: Name contains invalid characters"));
return;
}
std::vector<WorldSpec> worlds = getAvailableWorlds();
for(std::vector<WorldSpec>::iterator i = worlds.begin();
i != worlds.end(); i++)
{
if((*i).name == name_narrow)
{
m_menu->displayMessageMenu(wgettext("Cannot create world: A world by this name already exists"));
return;
}
}
m_menu->createNewWorld(name, gameid);
}
GUIMainMenu *m_menu;
@@ -81,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,
@@ -185,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);
}
@@ -563,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);
@@ -589,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);
}
}
@@ -743,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)
@@ -929,3 +1035,7 @@ int GUIMainMenu::getTab()
return TAB_SINGLEPLAYER; // Default
}
void GUIMainMenu::displayMessageMenu(std::wstring msg)
{
(new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
}

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;
@@ -92,6 +98,7 @@ class GUIMainMenu : public GUIModalMenu
void createNewWorld(std::wstring name, std::string gameid);
void deleteWorld(const std::vector<std::string> &paths);
int getTab();
void displayMessageMenu(std::wstring msg);
private:
MainMenuData *m_data;

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

@@ -22,14 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "modalMenu.h"
#include "guiFormSpecMenu.h"
#include <string>
struct TextDest
{
virtual void gotText(std::wstring text) = 0;
virtual ~TextDest() {};
};
class GUITextInputMenu : public GUIModalMenu
{
public:

View File

@@ -431,6 +431,7 @@ InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemde
{
m_name = name;
m_size = size;
m_width = 0;
m_itemdef = itemdef;
clearItems();
//m_dirty = false;
@@ -459,6 +460,11 @@ void InventoryList::setSize(u32 newsize)
m_size = newsize;
}
void InventoryList::setWidth(u32 newwidth)
{
m_width = newwidth;
}
void InventoryList::setName(const std::string &name)
{
m_name = name;
@@ -468,6 +474,8 @@ void InventoryList::serialize(std::ostream &os) const
{
//os.imbue(std::locale("C"));
os<<"Width "<<m_width<<"\n";
for(u32 i=0; i<m_items.size(); i++)
{
const ItemStack &item = m_items[i];
@@ -492,6 +500,7 @@ void InventoryList::deSerialize(std::istream &is)
clearItems();
u32 item_i = 0;
m_width = 0;
for(;;)
{
@@ -513,6 +522,12 @@ void InventoryList::deSerialize(std::istream &is)
{
break;
}
else if(name == "Width")
{
iss >> m_width;
if (iss.fail())
throw SerializationError("incorrect width property");
}
else if(name == "Item")
{
if(item_i > getSize() - 1)
@@ -527,10 +542,6 @@ void InventoryList::deSerialize(std::istream &is)
throw SerializationError("too many items");
m_items[item_i++].clear();
}
else
{
throw SerializationError("Unknown inventory identifier");
}
}
}
@@ -543,6 +554,7 @@ InventoryList & InventoryList::operator = (const InventoryList &other)
{
m_items = other.m_items;
m_size = other.m_size;
m_width = other.m_width;
m_name = other.m_name;
m_itemdef = other.m_itemdef;
//setDirty(true);
@@ -560,6 +572,11 @@ u32 InventoryList::getSize() const
return m_items.size();
}
u32 InventoryList::getWidth() const
{
return m_width;
}
u32 InventoryList::getUsedSlots() const
{
u32 num = 0;
@@ -886,10 +903,6 @@ void Inventory::deSerialize(std::istream &is)
m_lists.push_back(list);
}
else
{
throw SerializationError("Unknown inventory identifier");
}
}
}

View File

@@ -176,6 +176,7 @@ class InventoryList
~InventoryList();
void clearItems();
void setSize(u32 newsize);
void setWidth(u32 newWidth);
void setName(const std::string &name);
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
@@ -185,6 +186,7 @@ class InventoryList
const std::string &getName() const;
u32 getSize() const;
u32 getWidth() const;
// Count used slots
u32 getUsedSlots() const;
u32 getFreeSlots() const;
@@ -240,7 +242,7 @@ class InventoryList
private:
std::vector<ItemStack> m_items;
u32 m_size;
u32 m_size, m_width;
std::string m_name;
IItemDefManager *m_itemdef;
};

View File

@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" // for g_settings
#include "settings.h"
#include "craftdef.h"
#include "rollback_interface.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -41,30 +42,25 @@ std::string InventoryLocation::dump() const
void InventoryLocation::serialize(std::ostream &os) const
{
switch(type){
case InventoryLocation::UNDEFINED:
{
switch(type){
case InventoryLocation::UNDEFINED:
os<<"undefined";
}
break;
case InventoryLocation::CURRENT_PLAYER:
{
break;
case InventoryLocation::CURRENT_PLAYER:
os<<"current_player";
}
break;
case InventoryLocation::PLAYER:
{
break;
case InventoryLocation::PLAYER:
os<<"player:"<<name;
}
break;
case InventoryLocation::NODEMETA:
{
break;
case InventoryLocation::NODEMETA:
os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
}
break;
default:
assert(0);
}
break;
case InventoryLocation::DETACHED:
os<<"detached:"<<name;
break;
default:
assert(0);
}
}
void InventoryLocation::deSerialize(std::istream &is)
@@ -94,6 +90,11 @@ void InventoryLocation::deSerialize(std::istream &is)
p.Y = stoi(fn.next(","));
p.Z = stoi(fn.next(","));
}
else if(tname == "detached")
{
type = InventoryLocation::DETACHED;
std::getline(is, name, '\n');
}
else
{
infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
@@ -198,78 +199,114 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
<<", to_list=\""<<to_list<<"\""<<std::endl;
return;
}
/*
Do not handle rollback if both inventories are that of the same player
*/
bool ignore_rollback = (
from_inv.type == InventoryLocation::PLAYER &&
to_inv.type == InventoryLocation::PLAYER &&
from_inv.name == to_inv.name);
/*
Collect information of endpoints
*/
int try_take_count = count;
if(try_take_count == 0)
try_take_count = list_from->getItem(from_i).count;
int src_can_take_count = 0xffff;
int dst_can_put_count = 0xffff;
// Handle node metadata move
if(from_inv.type == InventoryLocation::NODEMETA &&
to_inv.type == InventoryLocation::NODEMETA &&
from_inv.p != to_inv.p)
/* Query detached inventories */
// Move occurs in the same detached inventory
if(from_inv.type == InventoryLocation::DETACHED &&
to_inv.type == InventoryLocation::DETACHED &&
from_inv.name == to_inv.name)
{
errorstream<<"Directly moving items between two nodes is "
<<"disallowed."<<std::endl;
return;
lua_State *L = player->getEnv()->getLua();
src_can_take_count = scriptapi_detached_inventory_allow_move(
L, from_inv.name, from_list, from_i,
to_list, to_i, try_take_count, player);
dst_can_put_count = src_can_take_count;
}
else if(from_inv.type == InventoryLocation::NODEMETA &&
else
{
// Destination is detached
if(to_inv.type == InventoryLocation::DETACHED)
{
lua_State *L = player->getEnv()->getLua();
ItemStack src_item = list_from->getItem(from_i);
src_item.count = try_take_count;
dst_can_put_count = scriptapi_detached_inventory_allow_put(
L, to_inv.name, to_list, to_i, src_item, player);
}
// Source is detached
if(from_inv.type == InventoryLocation::DETACHED)
{
lua_State *L = player->getEnv()->getLua();
ItemStack src_item = list_from->getItem(from_i);
src_item.count = try_take_count;
src_can_take_count = scriptapi_detached_inventory_allow_take(
L, from_inv.name, from_list, from_i, src_item, player);
}
}
/* Query node metadata inventories */
// Both endpoints are nodemeta
// Move occurs in the same nodemeta inventory
if(from_inv.type == InventoryLocation::NODEMETA &&
to_inv.type == InventoryLocation::NODEMETA &&
from_inv.p == to_inv.p)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
infostream<<player->getDescription()<<" moving "<<count0
<<" items inside node at "<<PP(from_inv.p)<<std::endl;
scriptapi_node_on_metadata_inventory_move(L, from_inv.p,
from_list, from_i, to_list, to_i, count0, player);
src_can_take_count = scriptapi_nodemeta_inventory_allow_move(
L, from_inv.p, from_list, from_i,
to_list, to_i, try_take_count, player);
dst_can_put_count = src_can_take_count;
}
// Handle node metadata take
else if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
infostream<<player->getDescription()<<" taking "<<count0
<<" items from node at "<<PP(from_inv.p)<<std::endl;
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
L, from_inv.p, from_list, from_i, count0, player);
if(return_stack.count == 0)
infostream<<"Node metadata gave no items"<<std::endl;
return_stack = list_to->addItem(to_i, return_stack);
list_to->addItem(return_stack); // Force return of everything
}
// Handle node metadata offer
else if(to_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
ItemStack offer_stack = list_from->takeItem(from_i, count0);
infostream<<player->getDescription()<<" offering "
<<offer_stack.count<<" items to node at "
<<PP(to_inv.p)<<std::endl;
ItemStack reject_stack = scriptapi_node_on_metadata_inventory_offer(
L, to_inv.p, to_list, to_i, offer_stack, player);
if(reject_stack.count == offer_stack.count)
infostream<<"Node metadata rejected all items"<<std::endl;
else if(reject_stack.count != 0)
infostream<<"Node metadata rejected some items"<<std::endl;
reject_stack = list_from->addItem(from_i, reject_stack);
list_from->addItem(reject_stack); // Force return of everything
}
// Handle regular move
else
{
/*
This performs the actual movement
// Destination is nodemeta
if(to_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
ItemStack src_item = list_from->getItem(from_i);
src_item.count = try_take_count;
dst_can_put_count = scriptapi_nodemeta_inventory_allow_put(
L, to_inv.p, to_list, to_i, src_item, player);
}
// Source is nodemeta
if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
ItemStack src_item = list_from->getItem(from_i);
src_item.count = try_take_count;
src_can_take_count = scriptapi_nodemeta_inventory_allow_take(
L, from_inv.p, from_list, from_i, src_item, player);
}
}
If something is wrong (source item is empty, destination is the
same as source), nothing happens
*/
list_from->moveItem(from_i, list_to, to_i, count);
infostream<<"IMoveAction::apply(): moved "
<<" count="<<count
int old_count = count;
/* Modify count according to collected data */
count = try_take_count;
if(src_can_take_count != -1 && count > src_can_take_count)
count = src_can_take_count;
if(dst_can_put_count != -1 && count > dst_can_put_count)
count = dst_can_put_count;
/* Limit according to source item count */
if(count > list_from->getItem(from_i).count)
count = list_from->getItem(from_i).count;
/* If no items will be moved, don't go further */
if(count == 0)
{
infostream<<"IMoveAction::apply(): move was completely disallowed:"
<<" count="<<old_count
<<" from inv=\""<<from_inv.dump()<<"\""
<<" list=\""<<from_list<<"\""
<<" i="<<from_i
@@ -277,8 +314,156 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
<<" list=\""<<to_list<<"\""
<<" i="<<to_i
<<std::endl;
return;
}
ItemStack src_item = list_from->getItem(from_i);
src_item.count = count;
ItemStack from_stack_was = list_from->getItem(from_i);
ItemStack to_stack_was = list_to->getItem(to_i);
/*
Perform actual move
If something is wrong (source item is empty, destination is the
same as source), nothing happens
*/
list_from->moveItem(from_i, list_to, to_i, count);
// If source is infinite, reset it's stack
if(src_can_take_count == -1){
// If destination stack is of different type and there are leftover
// items, attempt to put the leftover items to a different place in the
// destination inventory.
// The client-side GUI will try to guess if this happens.
if(from_stack_was.name != to_stack_was.name){
for(u32 i=0; i<list_to->getSize(); i++){
if(list_to->getItem(i).empty()){
list_to->changeItem(i, to_stack_was);
break;
}
}
}
list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was);
}
// If destination is infinite, reset it's stack and take count from source
if(dst_can_put_count == -1){
list_to->deleteItem(to_i);
list_to->addItem(to_i, to_stack_was);
list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was);
list_from->takeItem(from_i, count);
}
infostream<<"IMoveAction::apply(): moved"
<<" count="<<count
<<" from inv=\""<<from_inv.dump()<<"\""
<<" list=\""<<from_list<<"\""
<<" i="<<from_i
<<" to inv=\""<<to_inv.dump()<<"\""
<<" list=\""<<to_list<<"\""
<<" i="<<to_i
<<std::endl;
/*
Record rollback information
*/
if(!ignore_rollback && gamedef->rollback())
{
IRollbackReportSink *rollback = gamedef->rollback();
// If source is not infinite, record item take
if(src_can_take_count != -1){
RollbackAction action;
std::string loc;
{
std::ostringstream os(std::ios::binary);
from_inv.serialize(os);
loc = os.str();
}
action.setModifyInventoryStack(loc, from_list, from_i, false,
src_item.getItemString());
rollback->reportAction(action);
}
// If destination is not infinite, record item put
if(dst_can_put_count != -1){
RollbackAction action;
std::string loc;
{
std::ostringstream os(std::ios::binary);
to_inv.serialize(os);
loc = os.str();
}
action.setModifyInventoryStack(loc, to_list, to_i, true,
src_item.getItemString());
rollback->reportAction(action);
}
}
/*
Report move to endpoints
*/
/* Detached inventories */
// Both endpoints are same detached
if(from_inv.type == InventoryLocation::DETACHED &&
to_inv.type == InventoryLocation::DETACHED &&
from_inv.name == to_inv.name)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_detached_inventory_on_move(
L, from_inv.name, from_list, from_i,
to_list, to_i, count, player);
}
else
{
// Destination is detached
if(to_inv.type == InventoryLocation::DETACHED)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_detached_inventory_on_put(
L, to_inv.name, to_list, to_i, src_item, player);
}
// Source is detached
if(from_inv.type == InventoryLocation::DETACHED)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_detached_inventory_on_take(
L, from_inv.name, from_list, from_i, src_item, player);
}
}
/* Node metadata inventories */
// Both endpoints are same nodemeta
if(from_inv.type == InventoryLocation::NODEMETA &&
to_inv.type == InventoryLocation::NODEMETA &&
from_inv.p == to_inv.p)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_nodemeta_inventory_on_move(
L, from_inv.p, from_list, from_i,
to_list, to_i, count, player);
}
else{
// Destination is nodemeta
if(to_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_nodemeta_inventory_on_put(
L, to_inv.p, to_list, to_i, src_item, player);
}
// Source is nodemeta
else if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_nodemeta_inventory_on_take(
L, from_inv.p, from_list, from_i, src_item, player);
}
}
mgr->setInventoryModified(from_inv);
if(inv_from != inv_to)
mgr->setInventoryModified(to_inv);
@@ -361,47 +546,70 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
return;
}
ItemStack item1;
/*
Do not handle rollback if inventory is player's
*/
bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER);
// Handle node metadata take
/*
Collect information of endpoints
*/
int take_count = list_from->getItem(from_i).count;
if(count != 0 && count < take_count)
take_count = count;
int src_can_take_count = take_count;
// Source is detached
if(from_inv.type == InventoryLocation::DETACHED)
{
lua_State *L = player->getEnv()->getLua();
ItemStack src_item = list_from->getItem(from_i);
src_item.count = take_count;
src_can_take_count = scriptapi_detached_inventory_allow_take(
L, from_inv.name, from_list, from_i, src_item, player);
}
// Source is nodemeta
if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
int count0 = count;
if(count0 == 0)
count0 = list_from->getItem(from_i).count;
infostream<<player->getDescription()<<" dropping "<<count0
<<" items from node at "<<PP(from_inv.p)<<std::endl;
ItemStack return_stack = scriptapi_node_on_metadata_inventory_take(
L, from_inv.p, from_list, from_i, count0, player);
if(return_stack.count == 0)
infostream<<"Node metadata gave no items"<<std::endl;
item1 = return_stack;
}
else
{
// Take item from source list
if(count == 0)
item1 = list_from->changeItem(from_i, ItemStack());
else
item1 = list_from->takeItem(from_i, count);
ItemStack src_item = list_from->getItem(from_i);
src_item.count = take_count;
src_can_take_count = scriptapi_nodemeta_inventory_allow_take(
L, from_inv.p, from_list, from_i, src_item, player);
}
// Drop the item and apply the returned ItemStack
ItemStack item2 = item1;
if(scriptapi_item_on_drop(player->getEnv()->getLua(), item2, player,
if(src_can_take_count != -1 && src_can_take_count < take_count)
take_count = src_can_take_count;
int actually_dropped_count = 0;
ItemStack src_item = list_from->getItem(from_i);
// Drop the item
ItemStack item1 = list_from->getItem(from_i);
item1.count = take_count;
if(scriptapi_item_on_drop(player->getEnv()->getLua(), item1, player,
player->getBasePosition() + v3f(0,1,0)))
{
if(g_settings->getBool("creative_mode") == true
&& from_inv.type == InventoryLocation::PLAYER)
item2 = item1; // creative mode
actually_dropped_count = take_count - item1.count;
list_from->addItem(from_i, item2);
if(actually_dropped_count == 0){
infostream<<"Actually dropped no items"<<std::endl;
return;
}
// If source isn't infinite
if(src_can_take_count != -1){
// Take item from source list
ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count);
if(item2.count != actually_dropped_count)
errorstream<<"Could not take dropped count of items"<<std::endl;
// Unless we have put the same amount back as we took in the first place,
// set inventory modified flag
if(item2.count != item1.count)
mgr->setInventoryModified(from_inv);
}
}
infostream<<"IDropAction::apply(): dropped "
@@ -409,6 +617,50 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
<<" list=\""<<from_list<<"\""
<<" i="<<from_i
<<std::endl;
src_item.count = actually_dropped_count;
/*
Report drop to endpoints
*/
// Source is detached
if(from_inv.type == InventoryLocation::DETACHED)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_detached_inventory_on_take(
L, from_inv.name, from_list, from_i, src_item, player);
}
// Source is nodemeta
if(from_inv.type == InventoryLocation::NODEMETA)
{
lua_State *L = player->getEnv()->getLua();
scriptapi_nodemeta_inventory_on_take(
L, from_inv.p, from_list, from_i, src_item, player);
}
/*
Record rollback information
*/
if(!ignore_src_rollback && gamedef->rollback())
{
IRollbackReportSink *rollback = gamedef->rollback();
// If source is not infinite, record item take
if(src_can_take_count != -1){
RollbackAction action;
std::string loc;
{
std::ostringstream os(std::ios::binary);
from_inv.serialize(os);
loc = os.str();
}
action.setModifyInventoryStack(loc, from_list, from_i,
false, src_item.getItemString());
rollback->reportAction(action);
}
}
}
void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
@@ -531,18 +783,16 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
result.clear();
// TODO: Allow different sizes of crafting grids
// Get the InventoryList in which we will operate
InventoryList *clist = inv->getList("craft");
if(!clist || clist->getSize() != 9)
if(!clist)
return false;
// Mangle crafting grid to an another format
CraftInput ci;
ci.method = CRAFT_METHOD_NORMAL;
ci.width = 3;
for(u16 i=0; i<9; i++)
ci.width = clist->getWidth() ? clist->getWidth() : 3;
for(u16 i=0; i<clist->getSize(); i++)
ci.items.push_back(clist->getItem(i));
// Find out what is crafted and add it to result item slot
@@ -555,7 +805,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
if(found && decrementInput)
{
// CraftInput has been changed, apply changes in clist
for(u16 i=0; i<9; i++)
for(u16 i=0; i<clist->getSize(); i++)
{
clist->changeItem(i, ci.items[i]);
}

View File

@@ -32,9 +32,10 @@ struct InventoryLocation
CURRENT_PLAYER,
PLAYER,
NODEMETA,
DETACHED,
} type;
std::string name; // PLAYER
std::string name; // PLAYER, DETACHED
v3s16 p; // NODEMETA
InventoryLocation()
@@ -59,6 +60,34 @@ struct InventoryLocation
type = NODEMETA;
p = p_;
}
void setDetached(const std::string &name_)
{
type = DETACHED;
name = name_;
}
bool operator==(const InventoryLocation &other) const
{
if(type != other.type)
return false;
switch(type){
case UNDEFINED:
return false;
case CURRENT_PLAYER:
return true;
case PLAYER:
return (name == other.name);
case NODEMETA:
return (p == other.p);
case DETACHED:
return (name == other.name);
}
return false;
}
bool operator!=(const InventoryLocation &other) const
{
return !(*this == other);
}
void applyCurrentPlayer(const std::string &name_)
{
@@ -80,13 +109,11 @@ class InventoryManager
InventoryManager(){}
virtual ~InventoryManager(){}
// Get an inventory or set it modified (so it will be updated over
// network or so)
// Get an inventory (server and client)
virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;}
virtual std::string getInventoryOwner(const InventoryLocation &loc){return "";}
// Set modified (will be saved and sent over network; only on server)
virtual void setInventoryModified(const InventoryLocation &loc){}
// Used on the client to send an action to the server
// Send inventory action to server (only on client)
virtual void inventoryAction(InventoryAction *a){}
};

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

@@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" // For g_settings
#include "exceptions.h"
#include "settings.h"
#include "log.h"
#include "hex.h"
class UnknownKeycode : public BaseException
{
@@ -286,16 +288,30 @@ KeyPress::KeyPress(const char *name)
m_name = name[0];
}
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in)
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
{
Key = in.Key;
Char = in.Char;
if(prefer_character){
m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char);
if(written > 0){
infostream<<"KeyPress: Preferring character for "<<m_name<<std::endl;
Key = irr::KEY_KEY_CODES_COUNT;
return;
}
}
if (valid_kcode(Key)) {
m_name = KeyNames[Key];
} else {
m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char);
assert (written >= 0 && "unexpected multibyte character");
if(written < 0){
std::string hexstr = hex_encode((const char*)&Char, sizeof(Char));
errorstream<<"KeyPress: Unexpected multibyte character "<<hexstr<<std::endl;
}
}
}

View File

@@ -33,7 +33,7 @@ class KeyPress
KeyPress();
KeyPress(const char *name);
KeyPress(const irr::SEvent::SKeyInput &in);
KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character=false);
bool operator==(const KeyPress &o) const
{

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,11 +34,19 @@ 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),
m_old_node_below_type("air"),
m_need_to_get_new_sneak_node(true)
m_need_to_get_new_sneak_node(true),
m_can_jump(false)
{
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
@@ -58,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");
@@ -149,13 +164,16 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
f32 min_y = lwn_f.Y + 0.5*BS;
if(position.Y < min_y)
if(!is_climbing)
{
position.Y = min_y;
f32 min_y = lwn_f.Y + 0.5*BS;
if(position.Y < min_y)
{
position.Y = min_y;
if(m_speed.Y < 0)
m_speed.Y = 0;
if(m_speed.Y < 0)
m_speed.Y = 0;
}
}
}
@@ -188,7 +206,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
bool standing_on_unloaded = result.standing_on_unloaded;
//bool standing_on_unloaded = result.standing_on_unloaded;
/*
Check the nodes under the player to see from which node the
@@ -281,18 +299,25 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
/*
Report collisions
*/
bool bouncy_jump = false;
if(collision_info)
{
// Report fall collision
if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
for(size_t i=0; i<result.collisions.size(); i++){
const CollisionInfo &info = result.collisions[i];
collision_info->push_back(info);
if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
info.bouncy)
bouncy_jump = true;
}
}
if(bouncy_jump && control.jump){
m_speed.Y += 6.5*BS;
touching_ground = false;
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_gamedef->event()->put(e);
}
if(!touching_ground_was && touching_ground){
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_gamedef->event()->put(e);
@@ -303,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;
}
}
@@ -314,6 +339,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
*/
m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
/*
Check properties of the node on which the player is standing
*/
const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
// Determine if jumping is possible
m_can_jump = touching_ground;
if(itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
}
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
@@ -332,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());
@@ -359,31 +400,70 @@ void LocalPlayer::applyControl(float dtime)
if(free_move && fast_move)
superspeed = true;
// Auxiliary button 1 (E)
if(control.aux1)
// Old descend control
if(g_settings->getBool("aux1_descends"))
{
if(free_move)
// Auxiliary button 1 (E)
if(control.aux1)
{
// In free movement mode, aux1 descends
v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
if(free_move)
{
// In free movement mode, aux1 descends
v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
setSpeed(speed);
}
else if(is_climbing)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
}
else
speed.Y = -walkspeed_max;
setSpeed(speed);
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
}
}
else if(is_climbing)
}
// New minecraft-like descend control
else
{
// Auxiliary button 1 (E)
if(control.aux1)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
if(!free_move && !is_climbing)
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
}
}
else
if(control.sneak)
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
if(free_move)
{
// In free movement mode, sneak descends
v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
setSpeed(speed);
}
else if(is_climbing)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
}
}
}
@@ -420,7 +500,7 @@ void LocalPlayer::applyControl(float dtime)
speed.Y = walkspeed_max;
setSpeed(speed);
}
else if(touching_ground)
else if(m_can_jump)
{
/*
NOTE: The d value in move() affects jump height by

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
@@ -101,6 +63,7 @@ class LocalPlayer : public Player
std::string m_old_node_below_type;
// Whether recalculation of the sneak node is needed
bool m_need_to_get_new_sneak_node;
bool m_can_jump;
};
#endif

View File

@@ -45,6 +45,7 @@ if(DEFAULT_DLOPEN)
else()
option(LUA_USE_DLOPEN "Enable dlopen support." OFF)
endif()
mark_as_advanced(LUA_USE_DLOPEN)
if(DEFAULT_POSIX)
else()
@@ -55,6 +56,7 @@ if(DEFAULT_ANSI)
else()
option(LUA_ANSI "Disable non-ansi features." OFF)
endif()
mark_as_advanced(LUA_ANSI)
#
# Lua version

View File

@@ -878,7 +878,7 @@ int main(int argc, char *argv[])
// Initialize debug streams
#define DEBUGFILE "debug.txt"
#ifdef RUN_IN_PLACE
#if RUN_IN_PLACE
std::string logfile = DEBUGFILE;
#else
std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
@@ -962,7 +962,7 @@ int main(int argc, char *argv[])
// Legacy configuration file location
filenames.push_back(porting::path_user +
DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
#ifdef RUN_IN_PLACE
#if RUN_IN_PLACE
// Try also from a lower level (to aid having the same configuration
// for many RUN_IN_PLACE installs)
filenames.push_back(porting::path_user +
@@ -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

@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "gamedef.h"
#include "util/directiontables.h"
#include "rollback_interface.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -932,12 +933,12 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
core::map<v3s16, MapBlock*> &modified_blocks)
{
INodeDefManager *nodemgr = m_gamedef->ndef();
INodeDefManager *ndef = m_gamedef->ndef();
/*PrintInfo(m_dout);
m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
/*
From this node to nodes underneath:
If lighting is sunlight (1.0), unlight neighbours and
@@ -950,6 +951,11 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
bool node_under_sunlight = true;
core::map<v3s16, bool> light_sources;
/*
Collect old node for rollback
*/
RollbackNode rollback_oldnode(this, p, m_gamedef);
/*
If there is a node at top and it doesn't have sunlight,
@@ -960,7 +966,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
try{
MapNode topnode = getNode(toppos);
if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
node_under_sunlight = false;
}
catch(InvalidPositionException &e)
@@ -980,7 +986,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
{
enum LightBank bank = banks[i];
u8 lightwas = getNode(p).getLight(bank, nodemgr);
u8 lightwas = getNode(p).getLight(bank, ndef);
// Add the block of the added node to modified_blocks
v3s16 blockpos = getNodeBlockPos(p);
@@ -997,16 +1003,16 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
// light again into this.
unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
n.setLight(bank, 0, nodemgr);
n.setLight(bank, 0, ndef);
}
/*
If node lets sunlight through and is under sunlight, it has
sunlight too.
*/
if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
if(node_under_sunlight && ndef->get(n).sunlight_propagates)
{
n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
}
/*
@@ -1028,7 +1034,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
TODO: This could be optimized by mass-unlighting instead
of looping
*/
if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
{
s16 y = p.Y - 1;
for(;; y--){
@@ -1044,12 +1050,12 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
break;
}
if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
{
unLightNeighbors(LIGHTBANK_DAY,
n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
light_sources, modified_blocks);
n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
n2.setLight(LIGHTBANK_DAY, 0, ndef);
setNode(n2pos, n2);
}
else
@@ -1078,6 +1084,17 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
block->expireDayNightDiff();
}
/*
Report for rollback
*/
if(m_gamedef->rollback())
{
RollbackNode rollback_newnode(this, p, m_gamedef);
RollbackAction action;
action.setSetNode(p, rollback_oldnode, rollback_newnode);
m_gamedef->rollback()->reportAction(action);
}
/*
Add neighboring liquid nodes and the node itself if it is
liquid (=water node was added) to transform queue.
@@ -1099,7 +1116,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
v3s16 p2 = p + dirs[i];
MapNode n2 = getNode(p2);
if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
{
m_transforming_liquid.push_back(p2);
}
@@ -1115,7 +1132,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
void Map::removeNodeAndUpdate(v3s16 p,
core::map<v3s16, MapBlock*> &modified_blocks)
{
INodeDefManager *nodemgr = m_gamedef->ndef();
INodeDefManager *ndef = m_gamedef->ndef();
/*PrintInfo(m_dout);
m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
@@ -1128,6 +1145,11 @@ void Map::removeNodeAndUpdate(v3s16 p,
// Node will be replaced with this
content_t replace_material = CONTENT_AIR;
/*
Collect old node for rollback
*/
RollbackNode rollback_oldnode(this, p, m_gamedef);
/*
If there is a node at top and it doesn't have sunlight,
there will be no sunlight going down.
@@ -1135,7 +1157,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
try{
MapNode topnode = getNode(toppos);
if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
node_under_sunlight = false;
}
catch(InvalidPositionException &e)
@@ -1157,7 +1179,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
Unlight neighbors (in case the node is a light source)
*/
unLightNeighbors(bank, p,
getNode(p).getLight(bank, nodemgr),
getNode(p).getLight(bank, ndef),
light_sources, modified_blocks);
}
@@ -1219,7 +1241,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
// TODO: Is this needed? Lighting is cleared up there already.
try{
MapNode n = getNode(p);
n.setLight(LIGHTBANK_DAY, 0, nodemgr);
n.setLight(LIGHTBANK_DAY, 0, ndef);
setNode(p, n);
}
catch(InvalidPositionException &e)
@@ -1254,6 +1276,17 @@ void Map::removeNodeAndUpdate(v3s16 p,
block->expireDayNightDiff();
}
/*
Report for rollback
*/
if(m_gamedef->rollback())
{
RollbackNode rollback_newnode(this, p, m_gamedef);
RollbackAction action;
action.setSetNode(p, rollback_oldnode, rollback_newnode);
m_gamedef->rollback()->reportAction(action);
}
/*
Add neighboring liquid nodes and this node to transform queue.
(it's vital for the node itself to get updated last.)
@@ -1275,7 +1308,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
v3s16 p2 = p + dirs[i];
MapNode n2 = getNode(p2);
if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
{
m_transforming_liquid.push_back(p2);
}
@@ -1435,8 +1468,8 @@ void Map::timerUpdate(float dtime, float unload_timeout,
MapBlock *block = (*i);
block->incrementUsageTimer(dtime);
if(block->getUsageTimer() > unload_timeout)
if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
{
v3s16 p = block->getPos();
@@ -1603,7 +1636,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
while(m_transforming_liquid.size() != 0)
{
// This should be done here so that it is done when continue is used
if(loopcount >= initial_size * 3)
if(loopcount >= initial_size || loopcount >= 10000)
break;
loopcount++;
@@ -1713,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;
@@ -1791,7 +1824,30 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
}
n0.setContent(new_node_content);
setNode(p0, n0);
// Find out whether there is a suspect for this action
std::string suspect;
if(m_gamedef->rollback()){
suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
}
if(!suspect.empty()){
// Blame suspect
RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
// Get old node for rollback
RollbackNode rollback_oldnode(this, p0, m_gamedef);
// Set node
setNode(p0, n0);
// Report
RollbackNode rollback_newnode(this, p0, m_gamedef);
RollbackAction action;
action.setSetNode(p0, rollback_oldnode, rollback_newnode);
m_gamedef->rollback()->reportAction(action);
} else {
// Set node
setNode(p0, n0);
}
v3s16 blockpos = getNodeBlockPos(p0);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block != NULL) {
@@ -1881,6 +1937,59 @@ void Map::removeNodeMetadata(v3s16 p)
block->m_node_metadata.remove(p_rel);
}
NodeTimer Map::getNodeTimer(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(!block){
infostream<<"Map::getNodeTimer(): Need to emerge "
<<PP(blockpos)<<std::endl;
block = emergeBlock(blockpos, false);
}
if(!block)
{
infostream<<"WARNING: Map::getNodeTimer(): Block not found"
<<std::endl;
return NodeTimer();
}
NodeTimer t = block->m_node_timers.get(p_rel);
return t;
}
void Map::setNodeTimer(v3s16 p, NodeTimer t)
{
v3s16 blockpos = getNodeBlockPos(p);
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(!block){
infostream<<"Map::setNodeTimer(): Need to emerge "
<<PP(blockpos)<<std::endl;
block = emergeBlock(blockpos, false);
}
if(!block)
{
infostream<<"WARNING: Map::setNodeTimer(): Block not found"
<<std::endl;
return;
}
block->m_node_timers.set(p_rel, t);
}
void Map::removeNodeTimer(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block == NULL)
{
infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
<<std::endl;
return;
}
block->m_node_timers.remove(p_rel);
}
/*
ServerMap
*/

View File

@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h"
#include "modifiedstate.h"
#include "util/container.h"
#include "nodetimer.h"
extern "C" {
#include "sqlite3.h"
@@ -43,6 +44,7 @@ class ServerMapSector;
class MapBlock;
class NodeMetadata;
class IGameDef;
class IRollbackReportSink;
namespace mapgen{
struct BlockMakeData;
@@ -168,7 +170,7 @@ class Map /*: public NodeContainer*/
void removeEventReceiver(MapEventReceiver *event_receiver);
// event shall be deleted by caller after the call.
void dispatchEvent(MapEditEvent *event);
// On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
// Same as the above (there exists no lock anymore)
@@ -310,6 +312,15 @@ class Map /*: public NodeContainer*/
void setNodeMetadata(v3s16 p, NodeMetadata *meta);
void removeNodeMetadata(v3s16 p);
/*
Node Timers
These are basically coordinate wrappers to MapBlock
*/
NodeTimer getNodeTimer(v3s16 p);
void setNodeTimer(v3s16 p, NodeTimer t);
void removeNodeTimer(v3s16 p);
/*
Misc.
*/
@@ -326,7 +337,7 @@ class Map /*: public NodeContainer*/
IGameDef *m_gamedef;
core::map<MapEventReceiver*, bool> m_event_receivers;
core::map<v2s16, MapSector*> m_sectors;
// Be sure to set this to NULL when the cached sector is deleted

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<<")"
@@ -56,7 +57,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_generated(false),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0)
m_usage_timer(0),
m_refcount(0)
{
data = NULL;
if(dummy == false)
@@ -552,12 +554,12 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
throw SerializationError("ERROR: Not writing dummy block.");
}
if(version <= 21)
{
serialize_pre22(os, version, disk);
return;
}
// Can't do this anymore; we have 16-bit dynamically allocated node IDs
// in memory; conversion just won't work in this direction.
if(version < 24)
throw SerializationError("MapBlock::serialize: serialization to "
"version < 24 not possible");
// First byte
u8 flags = 0;
if(is_underground)
@@ -582,8 +584,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
tmp_nodes[i] = data[i];
getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
u8 content_width = 1;
/*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/
u8 content_width = 2;
u8 params_width = 2;
writeU8(os, content_width);
writeU8(os, params_width);
@@ -593,8 +594,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
}
else
{
u8 content_width = 1;
/*u8 content_width = 2;*/
u8 content_width = 2;
u8 params_width = 2;
writeU8(os, content_width);
writeU8(os, params_width);
@@ -606,10 +606,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
Node metadata
*/
std::ostringstream oss(std::ios_base::binary);
if(version >= 23)
m_node_metadata.serialize(oss);
else
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
m_node_metadata.serialize(oss);
compressZlib(oss.str(), os);
/*
@@ -617,13 +614,10 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
*/
if(disk)
{
// Version 23 doesn't actually contain node timers
// (this field should have not been added)
if(version == 23)
writeU8(os, 0);
// Node timers (uncomment when node timers are taken into use)
/*if(version >= 24)
m_node_timers.serialize(os);*/
if(version <= 24){
// Node timers
m_node_timers.serialize(os, version);
}
// Static objects
m_static_objects.serialize(os);
@@ -633,10 +627,14 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
// Write block-specific node definition id mapping
nimap.serialize(os);
if(version >= 25){
// Node timers
m_node_timers.serialize(os, version);
}
}
}
void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
{
if(!ser_ver_supported(version))
@@ -666,7 +664,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
u8 content_width = readU8(is);
u8 params_width = readU8(is);
if(content_width != 1)
if(content_width != 1 && content_width != 2)
throw SerializationError("MapBlock::deSerialize(): invalid content_width");
if(params_width != 2)
throw SerializationError("MapBlock::deSerialize(): invalid params_width");
@@ -703,15 +701,15 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
if(disk)
{
// Node timers
if(version == 23)
if(version == 23){
// Read unused zero
readU8(is);
// Uncomment when node timers are taken into use
/*else if(version >= 24){
}
if(version == 24){
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Node timers"<<std::endl);
m_node_timers.deSerialize(is);
}*/
<<": Node timers (ver==24)"<<std::endl);
m_node_timers.deSerialize(is, version);
}
// Static objects
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
@@ -730,6 +728,12 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
NameIdMapping nimap;
nimap.deSerialize(is);
correctBlockNodeIds(&nimap, data, m_gamedef);
if(version >= 25){
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Node timers (ver>=25)"<<std::endl);
m_node_timers.deSerialize(is, version);
}
}
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
@@ -740,219 +744,6 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
Legacy serialization
*/
// List relevant id-name pairs for ids in the block using nodedef
// Before serialization version 22
static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
INodeDefManager *nodedef)
{
std::set<content_t> unknown_contents;
for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
{
content_t id = nodes[i].getContent();
const ContentFeatures &f = nodedef->get(id);
const std::string &name = f.name;
if(name == "")
unknown_contents.insert(id);
else
nimap->set(id, name);
}
for(std::set<content_t>::const_iterator
i = unknown_contents.begin();
i != unknown_contents.end(); i++){
errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
<<"Name for node id "<<(*i)<<" not known"<<std::endl;
}
}
void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
{
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
MapNode *tmp_data = new MapNode[nodecount];
// Legacy data changes
// This code has to change from post-22 to pre-22 format.
INodeDefManager *nodedef = m_gamedef->ndef();
for(u32 i=0; i<nodecount; i++)
{
const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
// Mineral
if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
{
tmp_data[i].setContent(nodedef->getId("default:stone"));
tmp_data[i].setParam1(1); // MINERAL_COAL
}
else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
{
tmp_data[i].setContent(nodedef->getId("default:stone"));
tmp_data[i].setParam1(2); // MINERAL_IRON
}
// facedir_simple
if(f.legacy_facedir_simple)
{
tmp_data[i].setParam1(tmp_data[i].getParam2());
tmp_data[i].setParam2(0);
}
// wall_mounted
if(f.legacy_wallmounted)
{
u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
tmp_data[i].setParam2(dir_old_format);
}
}
// Serialize nodes
u32 ser_length = MapNode::serializedLength(version);
SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
for(u32 i=0; i<nodecount; i++)
{
tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
}
delete[] tmp_data;
// These have no compression
if(version <= 3 || version == 5 || version == 6)
{
writeU8(os, is_underground);
os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
}
else if(version <= 10)
{
/*
With compression.
Compress the materials and the params separately.
*/
// First byte
writeU8(os, is_underground);
// Get and compress materials
SharedBuffer<u8> materialdata(nodecount);
for(u32 i=0; i<nodecount; i++)
{
materialdata[i] = databuf_nodelist[i*ser_length];
}
compress(materialdata, os, version);
// Get and compress lights
SharedBuffer<u8> lightdata(nodecount);
for(u32 i=0; i<nodecount; i++)
{
lightdata[i] = databuf_nodelist[i*ser_length+1];
}
compress(lightdata, os, version);
if(version >= 10)
{
// Get and compress param2
SharedBuffer<u8> param2data(nodecount);
for(u32 i=0; i<nodecount; i++)
{
param2data[i] = databuf_nodelist[i*ser_length+2];
}
compress(param2data, os, version);
}
}
// All other versions (newest)
else
{
// First byte
u8 flags = 0;
if(is_underground)
flags |= 0x01;
if(getDayNightDiff())
flags |= 0x02;
if(m_lighting_expired)
flags |= 0x04;
if(version >= 18)
{
if(m_generated == false)
flags |= 0x08;
}
writeU8(os, flags);
/*
Get data
*/
// Create buffer with different parameters sorted
SharedBuffer<u8> databuf(nodecount*3);
for(u32 i=0; i<nodecount; i++)
{
databuf[i] = databuf_nodelist[i*ser_length];
databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
}
/*
Compress data to output stream
*/
compress(databuf, os, version);
/*
NodeMetadata
*/
if(version >= 14)
{
if(version <= 15)
{
try{
std::ostringstream oss(std::ios_base::binary);
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
os<<serializeString(oss.str());
}
// This will happen if the string is longer than 65535
catch(SerializationError &e)
{
// Use an empty string
os<<serializeString("");
}
}
else
{
std::ostringstream oss(std::ios_base::binary);
content_nodemeta_serialize_legacy(oss, &m_node_metadata);
compressZlib(oss.str(), os);
//os<<serializeLongString(oss.str());
}
}
}
if(disk)
{
// Versions up from 9 have block objects. (DEPRECATED)
if(version >= 9)
{
// count=0
writeU16(os, 0);
}
// Versions up from 15 have static objects.
if(version >= 15)
{
m_static_objects.serialize(os);
}
// Timestamp
if(version >= 17)
{
writeU32(os, getTimestamp());
}
// Scan and write node definition id mapping
if(version >= 21)
{
NameIdMapping nimap;
getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
nimap.serialize(os);
}
}
}
void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
{
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;

View File

@@ -431,6 +431,42 @@ class MapBlock /*: public NodeContainer*/
return m_usage_timer;
}
/*
See m_refcount
*/
void refGrab()
{
m_refcount++;
}
void refDrop()
{
m_refcount--;
}
int refGet()
{
return m_refcount;
}
/*
Node Timers
*/
// Get timer
NodeTimer getNodeTimer(v3s16 p){
return m_node_timers.get(p);
}
// Deletes timer
void removeNodeTimer(v3s16 p){
m_node_timers.remove(p);
}
// Deletes old timer and sets a new one
void setNodeTimer(v3s16 p, NodeTimer t){
m_node_timers.set(p,t);
}
// Deletes all timers
void clearNodeTimers(){
m_node_timers.clear();
}
/*
Serialization
*/
@@ -447,7 +483,6 @@ class MapBlock /*: public NodeContainer*/
Private methods
*/
void serialize_pre22(std::ostream &os, u8 version, bool disk);
void deSerialize_pre22(std::istream &is, u8 version, bool disk);
/*
@@ -547,6 +582,12 @@ class MapBlock /*: public NodeContainer*/
Map will unload the block when this reaches a timeout.
*/
float m_usage_timer;
/*
Reference count; currently used for determining if this block is in
the list of blocks to be drawn.
*/
int m_refcount;
};
inline bool blockpos_over_limit(v3s16 p)

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
@@ -1297,7 +1298,9 @@ BiomeType get_biome(u64 seed, v2s16 p2d)
double d = noise2d_perlin(
0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
seed+9130, 3, 0.50);
if(d > 0.35)
if(d > 0.45)
return BT_DESERT;
if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 )
return BT_DESERT;
return BT_NORMAL;
};
@@ -1759,8 +1762,9 @@ void make_block(BlockMakeData *data)
vmanip.m_data[i] = airnode;
}
} else {
// Don't replace air or water or lava
if(vmanip.m_data[i].getContent() == CONTENT_AIR ||
// Don't replace air or water or lava or ignore
if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
vmanip.m_data[i].getContent() == CONTENT_AIR ||
vmanip.m_data[i].getContent() == c_water_source ||
vmanip.m_data[i].getContent() == c_lava_source)
continue;

View File

@@ -39,11 +39,9 @@ MapNode::MapNode(INodeDefManager *ndef, const std::string &name,
{
content_t id = CONTENT_IGNORE;
ndef->getId(name, id);
param0 = id;
param1 = a_param1;
param2 = a_param2;
// Set content (param0 and (param2&0xf0)) after other params
// because this needs to override part of param2
setContent(id);
}
void MapNode::setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr)
@@ -236,23 +234,25 @@ u32 MapNode::serializedLength(u8 version)
return 1;
else if(version <= 9)
return 2;
else
else if(version <= 23)
return 3;
else
return 4;
}
void MapNode::serialize(u8 *dest, u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
if(version <= 21)
{
serialize_pre22(dest, version);
return;
}
writeU8(dest+0, param0);
writeU8(dest+1, param1);
writeU8(dest+2, param2);
// Can't do this anymore; we have 16-bit dynamically allocated node IDs
// in memory; conversion just won't work in this direction.
if(version < 24)
throw SerializationError("MapNode::serialize: serialization to "
"version < 24 not possible");
writeU16(dest+0, param0);
writeU8(dest+2, param1);
writeU8(dest+3, param2);
}
void MapNode::deSerialize(u8 *source, u8 version)
{
@@ -265,9 +265,20 @@ void MapNode::deSerialize(u8 *source, u8 version)
return;
}
param0 = readU8(source+0);
param1 = readU8(source+1);
param2 = readU8(source+2);
if(version >= 24){
param0 = readU16(source+0);
param1 = readU8(source+2);
param2 = readU8(source+3);
}
else{
param0 = readU8(source+0);
param1 = readU8(source+1);
param2 = readU8(source+2);
if(param0 > 0x7F){
param0 |= ((param2&0xF0)<<4);
param2 &= 0x0F;
}
}
}
void MapNode::serializeBulk(std::ostream &os, int version,
const MapNode *nodes, u32 nodecount,
@@ -276,24 +287,20 @@ void MapNode::serializeBulk(std::ostream &os, int version,
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
assert(version >= 22);
assert(content_width == 1);
assert(content_width == 2);
assert(params_width == 2);
// Can't do this anymore; we have 16-bit dynamically allocated node IDs
// in memory; conversion just won't work in this direction.
if(version < 24)
throw SerializationError("MapNode::serializeBulk: serialization to "
"version < 24 not possible");
SharedBuffer<u8> databuf(nodecount * (content_width + params_width));
// Serialize content
if(content_width == 1)
{
for(u32 i=0; i<nodecount; i++)
writeU8(&databuf[i], nodes[i].param0);
}
/* If param0 is extended to two bytes, use something like this: */
/*else if(content_width == 2)
{
for(u32 i=0; i<nodecount; i++)
writeU16(&databuf[i*2], nodes[i].param0);
}*/
for(u32 i=0; i<nodecount; i++)
writeU16(&databuf[i*2], nodes[i].param0);
// Serialize param1
u32 start1 = content_width * nodecount;
@@ -328,7 +335,7 @@ void MapNode::deSerializeBulk(std::istream &is, int version,
throw VersionMismatchException("ERROR: MapNode format not supported");
assert(version >= 22);
assert(content_width == 1);
assert(content_width == 1 || content_width == 2);
assert(params_width == 2);
// Uncompress or read data
@@ -358,12 +365,11 @@ void MapNode::deSerializeBulk(std::istream &is, int version,
for(u32 i=0; i<nodecount; i++)
nodes[i].param0 = readU8(&databuf[i]);
}
/* If param0 is extended to two bytes, use something like this: */
/*else if(content_width == 2)
else if(content_width == 2)
{
for(u32 i=0; i<nodecount; i++)
nodes[i].param0 = readU16(&databuf[i*2]);
}*/
}
// Deserialize param1
u32 start1 = content_width * nodecount;
@@ -372,47 +378,27 @@ void MapNode::deSerializeBulk(std::istream &is, int version,
// Deserialize param2
u32 start2 = (content_width + 1) * nodecount;
for(u32 i=0; i<nodecount; i++)
nodes[i].param2 = readU8(&databuf[start2 + i]);
if(content_width == 1)
{
for(u32 i=0; i<nodecount; i++) {
nodes[i].param2 = readU8(&databuf[start2 + i]);
if(nodes[i].param0 > 0x7F){
nodes[i].param0 <<= 4;
nodes[i].param0 |= (nodes[i].param2&0xF0)>>4;
nodes[i].param2 &= 0x0F;
}
}
}
else if(content_width == 2)
{
for(u32 i=0; i<nodecount; i++)
nodes[i].param2 = readU8(&databuf[start2 + i]);
}
}
/*
Legacy serialization
*/
void MapNode::serialize_pre22(u8 *dest, u8 version)
{
// Translate to wanted version
MapNode n_foreign = mapnode_translate_from_internal(*this, version);
u8 actual_param0 = n_foreign.param0;
// Convert special values from new version to old
if(version <= 18)
{
// In these versions, CONTENT_IGNORE and CONTENT_AIR
// are 255 and 254
if(actual_param0 == CONTENT_IGNORE)
actual_param0 = 255;
else if(actual_param0 == CONTENT_AIR)
actual_param0 = 254;
}
if(version == 0)
{
dest[0] = actual_param0;
}
else if(version <= 9)
{
dest[0] = actual_param0;
dest[1] = n_foreign.param1;
}
else
{
dest[0] = actual_param0;
dest[1] = n_foreign.param1;
dest[2] = n_foreign.param2;
}
}
void MapNode::deSerialize_pre22(u8 *source, u8 version)
{
if(version <= 1)
@@ -429,6 +415,11 @@ void MapNode::deSerialize_pre22(u8 *source, u8 version)
param0 = source[0];
param1 = source[1];
param2 = source[2];
if(param0 > 0x7f){
param0 <<= 4;
param0 |= (param2&0xf0)>>4;
param2 &= 0x0f;
}
}
// Convert special values from old version to new

View File

@@ -33,10 +33,6 @@ class INodeDefManager;
- Material = irrlicht's Material class
- Content = (content_t) content of a node
- Tile = TileSpec at some side of a node of some content type
Content ranges:
0x000...0x07f: param2 is fully usable
0x800...0xfff: param2 lower 4 bits are free
*/
typedef u16 content_t;
#define MAX_CONTENT 0xfff
@@ -84,10 +80,8 @@ struct MapNode
{
/*
Main content
0x00-0x7f: Short content type
0x80-0xff: Long content type (param2>>4 makes up low bytes)
*/
u8 param0;
u16 param0;
/*
Misc parameter. Initialized to 0.
@@ -102,7 +96,6 @@ struct MapNode
/*
The second parameter. Initialized to 0.
E.g. direction for torches and flowing water.
If param0 >= 0x80, bits 0xf0 of this is extended content type data
*/
u8 param2;
@@ -113,11 +106,9 @@ struct MapNode
MapNode(content_t content=CONTENT_AIR, u8 a_param1=0, u8 a_param2=0)
{
param0 = content;
param1 = a_param1;
param2 = a_param2;
// Set content (param0 and (param2&0xf0)) after other params
// because this needs to override part of param2
setContent(content);
}
// Create directly from a nodename
@@ -135,25 +126,11 @@ struct MapNode
// To be used everywhere
content_t getContent() const
{
if(param0 < 0x80)
return param0;
else
return (param0<<4) + (param2>>4);
return param0;
}
void setContent(content_t c)
{
if(c < 0x80)
{
if(param0 >= 0x80)
param2 &= ~(0xf0);
param0 = c;
}
else
{
param0 = c>>4;
param2 &= ~(0xf0);
param2 |= (c&0x0f)<<4;
}
param0 = c;
}
u8 getParam1() const
{
@@ -165,19 +142,11 @@ struct MapNode
}
u8 getParam2() const
{
if(param0 < 0x80)
return param2;
else
return param2 & 0x0f;
return param2;
}
void setParam2(u8 p)
{
if(param0 < 0x80)
param2 = p;
else{
param2 &= 0xf0;
param2 |= (p&0x0f);
}
param2 = p;
}
void setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr);
@@ -194,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;
@@ -233,7 +212,6 @@ struct MapNode
private:
// Deprecated serialization methods
void serialize_pre22(u8 *dest, u8 version);
void deSerialize_pre22(u8 *source, u8 version);
};

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) {};
}
@@ -377,18 +392,9 @@ class CNodeDefManager: public IWritableNodeDefManager
}
}
// CONTENT_IGNORE = not found
content_t getFreeId(bool require_full_param2)
content_t getFreeId()
{
// If allowed, first search in the large 4-bit-param2 pool
if(!require_full_param2){
for(u16 i=0x800; i<=0xfff; i++){
const ContentFeatures &f = m_content_features[i];
if(f.name == "")
return i;
}
}
// Then search from the small 8-bit-param2 pool
for(u16 i=0; i<=125; i++){
for(u32 i=0; i<=0xffff; i++){
const ContentFeatures &f = m_content_features[i];
if(f.name == "")
return i;
@@ -492,16 +498,8 @@ class CNodeDefManager: public IWritableNodeDefManager
u16 id = CONTENT_IGNORE;
bool found = m_name_id_mapping.getId(name, id); // ignore aliases
if(!found){
// Determine if full param2 is required
bool require_full_param2 = (
def.param_type_2 == CPT2_FULL
||
def.param_type_2 == CPT2_FLOWINGLIQUID
||
def.legacy_wallmounted
);
// Get some id
id = getFreeId(require_full_param2);
id = getFreeId();
if(id == CONTENT_IGNORE)
return CONTENT_IGNORE;
if(name != "")
@@ -557,6 +555,7 @@ class CNodeDefManager: public IWritableNodeDefManager
tiledef[j].name = "unknown_block.png";
}
bool is_liquid = false;
switch(f->drawtype){
default:
case NDT_NORMAL:
@@ -573,16 +572,16 @@ class CNodeDefManager: public IWritableNodeDefManager
f->solidness = 0;
} else {
f->solidness = 1;
if(f->alpha == 255)
f->solidness = 2;
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;
@@ -615,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)
@@ -665,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)
@@ -705,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;
@@ -721,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++;
}
@@ -778,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

@@ -55,7 +55,7 @@ class NodeMetadata
i = m_stringvars.find(name);
if(i == m_stringvars.end())
return "";
return i->second;
return resolveString(i->second);
}
void setString(const std::string &name, const std::string &var)
{
@@ -64,6 +64,13 @@ class NodeMetadata
else
m_stringvars[name] = var;
}
// support variable names in values
std::string resolveString(const std::string &str) const
{
if(str.substr(0,2) == "${" && str[str.length()-1] == '}')
return resolveString(getString(str.substr(2,str.length()-3)));
return str;
}
std::map<std::string, std::string> getStrings() const
{
return m_stringvars;

View File

@@ -28,13 +28,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
void NodeTimer::serialize(std::ostream &os) const
{
writeF1000(os, duration);
writeF1000(os, timeout);
writeF1000(os, elapsed);
}
void NodeTimer::deSerialize(std::istream &is)
{
duration = readF1000(is);
timeout = readF1000(is);
elapsed = readF1000(is);
}
@@ -42,19 +42,22 @@ void NodeTimer::deSerialize(std::istream &is)
NodeTimerList
*/
void NodeTimerList::serialize(std::ostream &os) const
void NodeTimerList::serialize(std::ostream &os, u8 map_format_version) const
{
/*
Version 0 is a placeholder for "nothing to see here; go away."
*/
if(m_data.size() == 0){
writeU8(os, 0); // version
return;
if(map_format_version == 24){
// Version 0 is a placeholder for "nothing to see here; go away."
if(m_data.size() == 0){
writeU8(os, 0); // version
return;
}
writeU8(os, 1); // version
writeU16(os, m_data.size());
}
writeU8(os, 1); // version
writeU16(os, m_data.size());
if(map_format_version >= 25){
writeU8(os, 2+4+4);
writeU16(os, m_data.size());
}
for(std::map<v3s16, NodeTimer>::const_iterator
i = m_data.begin();
@@ -68,15 +71,23 @@ void NodeTimerList::serialize(std::ostream &os) const
}
}
void NodeTimerList::deSerialize(std::istream &is)
void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
{
m_data.clear();
if(map_format_version == 24){
u8 timer_version = readU8(is);
if(timer_version == 0)
return;
if(timer_version != 1)
throw SerializationError("unsupported NodeTimerList version");
}
u8 version = readU8(is);
if(version == 0)
return;
if(version != 1)
throw SerializationError("unsupported NodeTimerList version");
if(map_format_version >= 25){
u8 timer_data_len = readU8(is);
if(timer_data_len != 2+4+4)
throw SerializationError("unsupported NodeTimer data length");
}
u16 count = readU16(is);
@@ -94,7 +105,7 @@ void NodeTimerList::deSerialize(std::istream &is)
NodeTimer t;
t.deSerialize(is);
if(t.duration <= 0)
if(t.timeout <= 0)
{
infostream<<"WARNING: NodeTimerList::deSerialize(): "
<<"invalid data at position"
@@ -116,9 +127,9 @@ void NodeTimerList::deSerialize(std::istream &is)
}
}
std::map<v3s16, f32> NodeTimerList::step(float dtime)
std::map<v3s16, NodeTimer> NodeTimerList::step(float dtime)
{
std::map<v3s16, f32> elapsed_timers;
std::map<v3s16, NodeTimer> elapsed_timers;
// Increment timers
for(std::map<v3s16, NodeTimer>::iterator
i = m_data.begin();
@@ -126,13 +137,13 @@ std::map<v3s16, f32> NodeTimerList::step(float dtime)
v3s16 p = i->first;
NodeTimer t = i->second;
t.elapsed += dtime;
if(t.elapsed >= t.duration)
elapsed_timers.insert(std::make_pair(p, t.elapsed));
if(t.elapsed >= t.timeout)
elapsed_timers.insert(std::make_pair(p, t));
else
i->second = t;
}
// Delete elapsed timers
for(std::map<v3s16, f32>::const_iterator
for(std::map<v3s16, NodeTimer>::const_iterator
i = elapsed_timers.begin();
i != elapsed_timers.end(); i++){
v3s16 p = i->first;

View File

@@ -35,15 +35,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class NodeTimer
{
public:
NodeTimer(): duration(0.), elapsed(0.) {}
NodeTimer(f32 duration_, f32 elapsed_):
duration(duration_), elapsed(elapsed_) {}
NodeTimer(): timeout(0.), elapsed(0.) {}
NodeTimer(f32 timeout_, f32 elapsed_):
timeout(timeout_), elapsed(elapsed_) {}
~NodeTimer() {}
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
f32 duration;
f32 timeout;
f32 elapsed;
};
@@ -57,8 +57,8 @@ class NodeTimerList
NodeTimerList() {}
~NodeTimerList() {}
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
void serialize(std::ostream &os, u8 map_format_version) const;
void deSerialize(std::istream &is, u8 map_format_version);
// Get timer
NodeTimer get(v3s16 p){
@@ -81,7 +81,7 @@ class NodeTimerList
}
// A step in time. Returns map of elapsed timers.
std::map<v3s16, f32> step(float dtime);
std::map<v3s16, NodeTimer> step(float dtime);
private:
std::map<v3s16, NodeTimer> m_data;

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;

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