Force send a mapblock to a player (#8140)
authorsofar <sofar@foo-projects.org>
Sat, 27 Apr 2019 23:42:13 +0000 (16:42 -0700)
committerParamat <paramat@users.noreply.github.com>
Sat, 27 Apr 2019 23:42:13 +0000 (00:42 +0100)
* Force send a mapblock to a player.

Send a single mapblock to a specific remote player.

This is badly needed for mods and games where players are teleported
into terrain which may be not generated, loaded, or modified
significantly since the last player visit.

In all these cases, the player currently ends up in void, air, or
inside blocks which not only looks bad, but has the effect that the
player might end up falling and then the server needs to correct for
the player position again later, which is a hack.

The best solution is to send at least the single mapblock that the
player will be teleported to. I've tested this with ITB which does this
all the time, and I can see it functioning as expected (it even shows
a half loaded entry hallway, as the further blocks aren't loaded yet).

The parameter is a blockpos (table of x, y, z), not a regular pos.

The function may return false if the call failed. This is most likely
due to the target position not being generated or emerged yet, or
another internal failure, such as the player not being initialized.

* Always send mapblock on teleport or respawn.

This avoids the need for mods to send a mapblock on teleport or
respawn, since any call to `player:set_pos()` will pass this code.

doc/lua_api.txt
src/content_sao.cpp
src/script/lua_api/l_object.cpp
src/script/lua_api/l_object.h
src/server.cpp
src/server.h

index c7035bb7282cf9431199352bc8c43c9f9c65a769..c75800cf423dfd5cfbf9de74e5c6d2cfcebf80fe 100644 (file)
@@ -5382,6 +5382,11 @@ This is basically a reference to a C++ `ServerActiveObject`
     * in first person view
     * in third person view (max. values `{x=-10/10,y=-10,15,z=-5/5}`)
 * `get_eye_offset()`: returns `offset_first` and `offset_third`
+* `send_mapblock(blockpos)`:
+    * Sends a server-side loaded mapblock to the player.
+    * Returns `false` if failed.
+    * Resource intensive - use sparsely
+    * To get blockpos, integer divide pos by 16
 
 `PcgRandom`
 -----------
index 7acf03684c7171689db2443cc295bfbe15fe7418..cefdeb597e74610bba502d3288703ff9ce0bc0c4 100644 (file)
@@ -1205,6 +1205,10 @@ void PlayerSAO::setPos(const v3f &pos)
        if(isAttached())
                return;
 
+       // Send mapblock of target location
+       v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE);
+       m_env->getGameDef()->SendBlock(m_peer_id, blockpos);
+
        setBasePosition(pos);
        // Movement caused by this command is always valid
        m_last_good_position = pos;
index 2efcd894a5f2b6af7cf767d310d22d79fd956a05..b1f4e3da5272111f2b1bf14445741663236eca61 100644 (file)
@@ -584,6 +584,24 @@ int ObjectRef::l_get_eye_offset(lua_State *L)
        return 2;
 }
 
+// send_mapblock(self, pos)
+int ObjectRef::l_send_mapblock(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       ObjectRef *ref = checkobject(L, 1);
+
+       RemotePlayer *player = getplayer(ref);
+       if (!player)
+               return 0;
+       v3s16 p = read_v3s16(L, 2);
+
+       session_t peer_id = player->getPeerId();
+       bool r = getServer(L)->SendBlock(peer_id, p);
+
+       lua_pushboolean(L, r);
+       return 1;
+}
+
 // set_animation_frame_speed(self, frame_speed)
 int ObjectRef::l_set_animation_frame_speed(lua_State *L)
 {
@@ -1958,5 +1976,6 @@ luaL_Reg ObjectRef::methods[] = {
        luamethod(ObjectRef, get_local_animation),
        luamethod(ObjectRef, set_eye_offset),
        luamethod(ObjectRef, get_eye_offset),
+       luamethod(ObjectRef, send_mapblock),
        {0,0}
 };
index c7d963d87e5b8d63b133452817bff28dc0d9c9c9..653d833f674da68a289858b2e615c8b4bb76604d 100644 (file)
@@ -351,4 +351,6 @@ private:
        // get_nametag_attributes(self)
        static int l_get_nametag_attributes(lua_State *L);
 
+       // send_mapblock(pos)
+       static int l_send_mapblock(lua_State *L);
 };
index 8694989651219ad026942aaf40a53a428ff616f5..496170da3f9689f4300f2f94078b9385589f0f3f 100644 (file)
@@ -2312,6 +2312,28 @@ void Server::SendBlocks(float dtime)
        m_clients.unlock();
 }
 
+bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
+{
+       MapBlock *block = nullptr;
+       try {
+               block = m_env->getMap().getBlockNoCreate(blockpos);
+       } catch (InvalidPositionException &e) {};
+       if (!block)
+               return false;
+
+       m_clients.lock();
+       RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
+       if (!client || client->isBlockSent(blockpos)) {
+               m_clients.unlock();
+               return false;
+       }
+       SendBlockNoLock(peer_id, block, client->serialization_version,
+                       client->net_proto_version);
+       m_clients.unlock();
+
+       return true;
+}
+
 void Server::fillMediaCache()
 {
        infostream<<"Server: Calculating media file checksums"<<std::endl;
index 5949d9bf153eb9c099ee1830c5ea2a33bcebd09b..5e58ac9e66688b5f9a5fe0c2593d3a67b1fb0dc6 100644 (file)
@@ -344,6 +344,9 @@ public:
        bool sendModChannelMessage(const std::string &channel, const std::string &message);
        ModChannel *getModChannel(const std::string &channel);
 
+       // Send block to specific player only
+       bool SendBlock(session_t peer_id, const v3s16 &blockpos);
+
        // Bind address
        Address m_bind_addr;