Node placement client-side prediction
authorPerttu Ahola <celeron55@gmail.com>
Sun, 10 Jun 2012 09:46:48 +0000 (12:46 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sun, 10 Jun 2012 09:46:48 +0000 (12:46 +0300)
builtin/item.lua
doc/lua_api.txt
src/game.cpp
src/itemdef.cpp
src/itemdef.h
src/nodedef.cpp
src/scriptapi.cpp
src/server.cpp

index 6d75fbf706a9f47b4199d55ee98afb8c5e9b920f..f249d9e5a9159e9ee5b4be0421d9ef717c0a0d28 100644 (file)
@@ -342,6 +342,7 @@ minetest.nodedef_default = {
        usable = false,
        liquids_pointable = false,
        tool_capabilities = nil,
+       node_placement_prediction = nil,
 
        -- Interaction callbacks
        on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
index 61bc8e1c22b98a01d5babe4f72dad54aef42c63b..c1ac13773212fc682193f6687c2be57016c51c50 100644 (file)
@@ -1100,6 +1100,13 @@ Item definition (register_node, register_craftitem, register_tool)
             choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
         }
     }
+    node_placement_prediction = nil,
+    ^ If nil and item is node, prediction is made automatically
+    ^ If nil and item is not a node, no prediction is made
+    ^ If "" and item is anything, no prediction is made
+    ^ Otherwise should be name of node which the client immediately places
+      on ground when the player places the item. Server will always update
+      actual result to client in a short moment.
 
     on_place = func(itemstack, placer, pointed_thing),
     ^ default: minetest.item_place
index 485bcbb50914100d034a27fa5a3e3a72644dfe7e..6ccf02677cc1bee5a93d85b339792b654d554779 100644 (file)
@@ -2366,8 +2366,41 @@ void the_game(
                                // Otherwise report right click to server
                                else
                                {
+                                       // Report to server
                                        client.interact(3, pointed);
                                        camera.setDigging(1);  // right click animation
+                                       
+                                       // If the wielded item has node placement prediction,
+                                       // make that happen
+                                       const ItemDefinition &def =
+                                                       playeritem.getDefinition(itemdef);
+                                       if(def.node_placement_prediction != "")
+                                       do{ // breakable
+                                               verbosestream<<"Node placement prediction for "
+                                                               <<playeritem.name<<" is "
+                                                               <<def.node_placement_prediction<<std::endl;
+                                               v3s16 p = neighbourpos;
+                                               content_t id;
+                                               bool found =
+                                                       nodedef->getId(def.node_placement_prediction, id);
+                                               if(!found){
+                                                       errorstream<<"Node placement prediction failed for "
+                                                                       <<playeritem.name<<" (places "
+                                                                       <<def.node_placement_prediction
+                                                                       <<") - Name not known"<<std::endl;
+                                                       break;
+                                               }
+                                               MapNode n(id);
+                                               try{
+                                                       // This triggers the required mesh update too
+                                                       client.addNode(p, n);
+                                               }catch(InvalidPositionException &e){
+                                                       errorstream<<"Node placement prediction failed for "
+                                                                       <<playeritem.name<<" (places "
+                                                                       <<def.node_placement_prediction
+                                                                       <<") - Position not loaded"<<std::endl;
+                                               }
+                                       }while(0);
                                }
                        }
                }
index fc0942c470d576de364e116f66f8be82bf2d56d8..e8de063876e58499c4f895a0be0ac1eb0ad23cdc 100644 (file)
@@ -70,6 +70,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
                                *def.tool_capabilities);
        }
        groups = def.groups;
+       node_placement_prediction = def.node_placement_prediction;
 #ifndef SERVER
        inventory_texture = def.inventory_texture;
        if(def.wield_mesh)
@@ -115,6 +116,8 @@ void ItemDefinition::reset()
        }
        groups.clear();
 
+       node_placement_prediction = "";
+       
 #ifndef SERVER
        inventory_texture = NULL;
        if(wield_mesh)
@@ -150,6 +153,7 @@ void ItemDefinition::serialize(std::ostream &os) const
                os<<serializeString(i->first);
                writeS16(os, i->second);
        }
+       os<<serializeString(node_placement_prediction);
 }
 
 void ItemDefinition::deSerialize(std::istream &is)
@@ -184,6 +188,11 @@ void ItemDefinition::deSerialize(std::istream &is)
                int value = readS16(is);
                groups[name] = value;
        }
+       // If you add anything here, insert it primarily inside the try-catch
+       // block to not need to increase the version.
+       try{
+               node_placement_prediction = deSerializeString(is);
+       }catch(SerializationError &e) {};
 }
 
 /*
index 385becd4868381f5574fab2cfefeede1043b0271..ac3a5ab879f72fa2088551ccdf50be08e37d9368 100644 (file)
@@ -67,6 +67,11 @@ struct ItemDefinition
        ToolCapabilities *tool_capabilities;
        ItemGroupList groups;
 
+       // Client shall immediately place this node when player places the item.
+       // Server will update the precise end result a moment later.
+       // "" = no prediction
+       std::string node_placement_prediction;
+
        /*
                Cached stuff
        */
index eaf0612873083b6a88d51e5d5939f11e90283dad..72f1ea2ea070b68352be809cf3ea200b393d3758 100644 (file)
@@ -633,6 +633,7 @@ public:
                        std::string wrapper = deSerializeString(is2);
                        std::istringstream wrapper_is(wrapper, std::ios::binary);
                        f->deSerialize(wrapper_is);
+                       verbosestream<<"deserialized "<<f->name<<std::endl;
                        if(f->name != "")
                                addNameIdMapping(i, f->name);
                }
index 3766496a0a0c0b8cabf2156dd5dc81c555b2ef39..afd8546d9a789be0cebabee3bb0bf4ed8174f629 100644 (file)
@@ -930,13 +930,14 @@ static void read_object_properties(lua_State *L, int index,
        ItemDefinition
 */
 
-static ItemDefinition read_item_definition(lua_State *L, int index)
+static ItemDefinition read_item_definition(lua_State *L, int index,
+               ItemDefinition default_def = ItemDefinition())
 {
        if(index < 0)
                index = lua_gettop(L) + 1 + index;
 
        // Read the item definition
-       ItemDefinition def;
+       ItemDefinition def = default_def;
 
        def.type = (ItemType)getenumfield(L, index, "type",
                        es_ItemType, ITEM_NONE);
@@ -981,6 +982,12 @@ static ItemDefinition read_item_definition(lua_State *L, int index)
        read_groups(L, -1, def.groups);
        lua_pop(L, 1);
 
+       // Client shall immediately place this node when player places the item.
+       // Server will update the precise end result a moment later.
+       // "" = no prediction
+       getstringfield(L, index, "node_placement_prediction",
+                       def.node_placement_prediction);
+
        return def;
 }
 
@@ -3891,9 +3898,10 @@ static int l_register_item_raw(lua_State *L)
                        get_server(L)->getWritableNodeDefManager();
 
        // Check if name is defined
+       std::string name;
        lua_getfield(L, table, "name");
        if(lua_isstring(L, -1)){
-               std::string name = lua_tostring(L, -1);
+               name = lua_tostring(L, -1);
                verbosestream<<"register_item_raw: "<<name<<std::endl;
        } else {
                throw LuaError(L, "register_item_raw: name is not defined or not a string");
@@ -3901,8 +3909,20 @@ static int l_register_item_raw(lua_State *L)
 
        // Check if on_use is defined
 
-       // Read the item definition and register it
-       ItemDefinition def = read_item_definition(L, table);
+       ItemDefinition def;
+       // Set a distinctive default value to check if this is set
+       def.node_placement_prediction = "__default";
+
+       // Read the item definition
+       def = read_item_definition(L, table, def);
+
+       // Default to having client-side placement prediction for nodes
+       // ("" in item definition sets it off)
+       if(def.type == ITEM_NODE && def.node_placement_prediction == "__default"){
+               def.node_placement_prediction = name;
+       }
+       
+       // Register item definition
        idef->registerItem(def);
 
        // Read the node definition (content features) and register it
index a7bd5e9539491658b7ccfd243921fd0ab4ea98eb..98072e8545e67025a7426136b098d3de016252af 100644 (file)
@@ -2971,8 +2971,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        <<std::endl;
                        // Re-send block to revert change on client-side
                        RemoteClient *client = getClient(peer_id);
-                       v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
-                       client->SetBlockNotSent(blockpos);
+                       // Digging completed -> under
+                       if(action == 2){
+                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+                               client->SetBlockNotSent(blockpos);
+                       }
+                       // Placement -> above
+                       if(action == 3){
+                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+                               client->SetBlockNotSent(blockpos);
+                       }
                        return;
                }
 
@@ -3104,7 +3112,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                if(g_settings->getBool("creative_mode") == false)
                                        playersao->setWieldedItem(item);
                        }
-
+                       
+                       // If item has node placement prediction, always send the above
+                       // node to make sure the client knows what exactly happened
+                       if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
+                               RemoteClient *client = getClient(peer_id);
+                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+                               client->SetBlockNotSent(blockpos);
+                       }
                } // action == 3
 
                /*