Add 'fly' and 'fast' privileges and the underlying privileges-to-client system
authorPerttu Ahola <celeron55@gmail.com>
Sat, 31 Mar 2012 13:23:26 +0000 (16:23 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 31 Mar 2012 13:25:02 +0000 (16:25 +0300)
18 files changed:
builtin/builtin.lua
doc/lua_api.txt
src/CMakeLists.txt
src/camera.cpp
src/client.cpp
src/client.h
src/clientserver.h
src/content_cao.cpp
src/environment.cpp
src/game.cpp
src/gamedef.h
src/localplayer.cpp [new file with mode: 0644]
src/localplayer.h [new file with mode: 0644]
src/player.cpp
src/player.h
src/scriptapi.cpp
src/server.cpp
src/server.h

index d6782a49a03e22e460b42627b8e36acc85a415c0..35501787ca49198d6acb71a96146cbdbe9233d10 100644 (file)
@@ -870,8 +870,24 @@ end
 --
 
 minetest.registered_privileges = {}
-function minetest.register_privilege(name, description)
-       minetest.registered_privileges[name] = description
+
+function minetest.register_privilege(name, param)
+       local function fill_defaults(def)
+               if def.give_to_singleplayer == nil then
+                       def.give_to_singleplayer = true
+               end
+               if def.description == nil then
+                       def.description = "(no description)"
+               end
+       end
+       local def = {}
+       if type(param) == "table" then
+               def = param
+       else
+               def = {description = param}
+       end
+       fill_defaults(def)
+       minetest.registered_privileges[name] = def
 end
 
 minetest.register_privilege("interact", "Can interact with things and modify the world")
@@ -884,6 +900,14 @@ minetest.register_privilege("shout", "Can speak in chat")
 minetest.register_privilege("ban", "Can ban and unban players")
 minetest.register_privilege("give", "Can use /give and /giveme")
 minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
+minetest.register_privilege("fly", {
+       description = "Can fly using the free_move mode",
+       give_to_singleplayer = false,
+})
+minetest.register_privilege("fast", {
+       description = "Can walk fast using the fast_move mode",
+       give_to_singleplayer = false,
+})
 
 --
 -- Chat commands
@@ -929,8 +953,8 @@ minetest.register_chatcommand("help", {
                        end
                elseif param == "privs" then
                        minetest.chat_send_player(name, "Available privileges:")
-                       for priv, desc in pairs(minetest.registered_privileges) do
-                               minetest.chat_send_player(name, priv..": "..desc)
+                       for priv, def in pairs(minetest.registered_privileges) do
+                               minetest.chat_send_player(name, priv..": "..def.description)
                        end
                else
                        local cmd = param
@@ -1004,7 +1028,7 @@ minetest.register_chatcommand("revoke", {
                local revokeprivs = minetest.string_to_privs(revokeprivstr)
                local privs = minetest.get_player_privs(revokename)
                for priv, _ in pairs(revokeprivs) do
-                       table.remove(privs, priv)
+                       privs[priv] = nil
                end
                minetest.set_player_privs(revokename, privs)
        end,
@@ -1200,6 +1224,7 @@ local function read_auth_file()
        end
        io.close(file)
        minetest.auth_table = newtable
+       minetest.notify_authentication_modified()
 end
 
 local function save_auth_file()
@@ -1228,24 +1253,41 @@ read_auth_file()
 minetest.builtin_auth_handler = {
        get_auth = function(name)
                assert(type(name) == "string")
+               -- Figure out what password to use for a new player (singleplayer
+               -- always has an empty password, otherwise use default, which is
+               -- usually empty too)
+               local new_password_hash = ""
+               if not minetest.is_singleplayer() then
+                       new_password_hash = minetest.get_password_hash(name, minetest.setting_get("default_password"))
+               end
+               -- Add player to authentication table if not there already
                if not minetest.auth_table[name] then
                        minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
                end
+               -- Figure out what privileges the player should have.
+               -- Take a copy of the privilege table
+               local privileges = {}
+               for priv, _ in pairs(minetest.auth_table[name].privileges) do
+                       privileges[priv] = true
+               end
+               -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
                if minetest.is_singleplayer() then
-                       return {
-                               password = "",
-                               privileges = minetest.registered_privileges
-                       }
-               else
-                       if minetest.auth_table[name] and name == minetest.setting_get("name") then
-                               return {
-                                       password = minetest.auth_table[name].password,
-                                       privileges = minetest.registered_privileges
-                               }
-                       else
-                               return minetest.auth_table[name]
+                       for priv, def in pairs(minetest.registered_privileges) do
+                               if def.give_to_singleplayer then
+                                       privileges[priv] = true
+                               end
+                       end
+               -- For the admin, give everything
+               elseif name == minetest.setting_get("name") then
+                       for priv, def in pairs(minetest.registered_privileges) do
+                               privileges[priv] = true
                        end
                end
+               -- All done
+               return {
+                       password = minetest.auth_table[name].password,
+                       privileges = privileges,
+               }
        end,
        create_auth = function(name, password)
                assert(type(name) == "string")
@@ -1275,6 +1317,7 @@ minetest.builtin_auth_handler = {
                        minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
                end
                minetest.auth_table[name].privileges = privileges
+               minetest.notify_authentication_modified(name)
                save_auth_file()
        end
 }
index a03d9a9e1b127ed76a3ff5480646558c95edac0e..3ba59565b7ff32de30a33fe8082f51118aed79e3 100644 (file)
@@ -506,7 +506,12 @@ minetest.register_on_respawnplayer(func(ObjectRef))
 ^ currently called _before_ repositioning of player occurs
 minetest.register_on_chat_message(func(name, message))
 minetest.register_chatcommand(cmd, chatcommand definition)
-minetest.register_privilege(name, description)
+minetest.register_privilege(name, definition)
+^ definition: "description text"
+^ definition: {
+      description = "description text",
+      give_to_singleplayer = boolean, -- default: true
+  }
 minetest.register_authentication_handler(handler)
 ^ See minetest.builtin_auth_handler in builtin.lua for reference
 
@@ -516,13 +521,20 @@ minetest.setting_getbool(name) -> boolean value or nil
 minetest.add_to_creative_inventory(itemstring)
 
 Authentication:
+minetest.notify_authentication_modified(name)
+^ Should be called by the authentication handler if privileges change.
+^ To report everybody, set name=nil.
 minetest.get_password_hash(name, raw_password)
-minetest.set_player_password(name, password_hash)
+^ Convert a name-password pair to a password hash that minetest can use
 minetest.string_to_privs(str) -> {priv1=true,...}
 minetest.privs_to_string(privs) -> "priv1,priv2,..."
+^ Convert between two privilege representations
+minetest.set_player_password(name, password_hash)
 minetest.set_player_privs(name, {priv1=true,...})
 minetest.get_player_privs(name) -> {priv1=true,...}
+^ These call the authentication handler
 minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs
+^ A quickhand for checking privileges
 
 Chat:
 minetest.chat_send_all(text)
index 0c7fc988c9263fee1c0931fbf3cf3690d97dedcc..56da7bd071835e3812eff5df2bbfe2147b48b311 100644 (file)
@@ -220,6 +220,7 @@ endif()
 set(minetest_SRCS
        ${common_SRCS}
        ${sound_SRCS}
+       localplayer.cpp
        sky.cpp
        clientmap.cpp
        content_cso.cpp
index ba05329e58c651be2b88196abf766c067761d15a..290776bf3d2da197a60ee5398b0c40d29872558c 100644 (file)
@@ -351,7 +351,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
        if ((hypot(speed.X, speed.Z) > BS) &&
                (player->touching_ground) &&
                (g_settings->getBool("view_bobbing") == true) &&
-               (g_settings->getBool("free_move") == false))
+               (g_settings->getBool("free_move") == false ||
+                               !m_gamedef->checkLocalPrivilege("fly")))
        {
                // Start animation
                m_view_bobbing_state = 1;
index 7f1b39c33a2b3367206e7f815c07d50cdefb8c4c..de3c636988aa0d49f5f37eb75120b25748bf639f 100644 (file)
@@ -1674,6 +1674,21 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        m_sound->stopSound(client_id);
                }
        }
+       else if(command == TOCLIENT_PRIVILEGES)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+               
+               m_privileges.clear();
+               infostream<<"Client: Privileges updated: ";
+               u16 num_privileges = readU16(is);
+               for(u16 i=0; i<num_privileges; i++){
+                       std::string priv = deSerializeString(is);
+                       m_privileges.insert(priv);
+                       infostream<<priv<<" ";
+               }
+               infostream<<std::endl;
+       }
        else
        {
                infostream<<"Client: Ignoring unknown command "
index 302dd80059c2a71d2a8e1c4e594482f315b50015..72ab70abd246baed500a710c3a9bf10b0bd1c65b 100644 (file)
@@ -20,8 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef CLIENT_HEADER
 #define CLIENT_HEADER
 
-#ifndef SERVER
-
 #include "connection.h"
 #include "environment.h"
 #include "common_irrlicht.h"
@@ -35,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "inventorymanager.h"
 #include "filesys.h"
 #include "filecache.h"
+#include "localplayer.h"
 
 struct MeshMakeData;
 class MapBlockMesh;
@@ -262,14 +261,8 @@ public:
 
        u16 getHP();
 
-       float getAvgRtt()
-       {
-               try{
-                       return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
-               } catch(con::PeerNotFoundException){
-                       return 1337;
-               }
-       }
+       bool checkPrivilege(const std::string &priv)
+       { return (m_privileges.count(priv) != 0); }
 
        bool getChatMessage(std::wstring &message);
        void typeChatMessage(const std::wstring& message);
@@ -312,6 +305,8 @@ public:
        virtual u16 allocateUnknownNodeId(const std::string &name);
        virtual ISoundManager* getSoundManager();
        virtual MtEventManager* getEventManager();
+       virtual bool checkLocalPrivilege(const std::string &priv)
+       { return checkPrivilege(priv); }
 
 private:
        
@@ -392,9 +387,10 @@ private:
        std::map<int, s32> m_sounds_client_to_server;
        // And relations to objects
        std::map<int, u16> m_sounds_to_objects;
-};
 
-#endif // !SERVER
+       // Privileges
+       std::set<std::string> m_privileges;
+};
 
 #endif // !CLIENT_HEADER
 
index def4570e1ecf62e91e7684956f51034375364c5c..461c13cd269e8ecf1c84d8346f30a804e2d8a187 100644 (file)
@@ -50,9 +50,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        PROTOCOL_VERSION 9:
                ContentFeatures and NodeDefManager use a different serialization
                    format; better for future version cross-compatibility
+               Many things
+       PROTOCOL_VERSION 10:
+               TOCLIENT_PRIVILEGES
+               Version raised to force 'fly' and 'fast' privileges into effect.
 */
 
-#define PROTOCOL_VERSION 9
+#define PROTOCOL_VERSION 10
 
 #define PROTOCOL_ID 0x4f457403
 
@@ -291,6 +295,15 @@ enum ToClientCommand
                u16 command
                s32 sound_id
        */
+
+       TOCLIENT_PRIVILEGES = 0x41,
+       /*
+               u16 command
+               u16 number of privileges
+               for each privilege
+                       u16 len
+                       u8[len] privilege
+       */
 };
 
 enum ToServerCommand
index 627d3c049e6692fa57efa704d78616040e752ab2..2a9c8a91a50c8b46e46429718b793c9d55d707d7 100644 (file)
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "content_cso.h"
 #include "sound.h"
 #include "nodedef.h"
+#include "localplayer.h"
 class Settings;
 struct ToolCapabilities;
 
index 001b53a40f659e22fd77c34a4c160066c14b6c9f..e1f5bb3fbbd9320925b115b983ac9877c93dba9c 100644 (file)
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "gamedef.h"
 #ifndef SERVER
 #include "clientmap.h"
+#include "localplayer.h"
 #endif
 #include "daynightratio.h"
 
@@ -1888,7 +1889,8 @@ void ClientEnvironment::step(float dtime)
        stepTimeOfDay(dtime);
 
        // Get some settings
-       bool free_move = g_settings->getBool("free_move");
+       bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
+       bool free_move = fly_allowed && g_settings->getBool("free_move");
 
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
index 941f286d91abcccf577b32078f0f6cc274b6e86f..a0f6c0d8ca4a96f93ae7e1b6c1a8da59271e089a 100644 (file)
@@ -1636,6 +1636,8 @@ void the_game(
                                g_settings->set("free_move","true");
                                statustext = L"free_move enabled";
                                statustext_time = 0;
+                               if(!client.checkPrivilege("fly"))
+                                       statustext += L" (note: no 'fly' privilege)";
                        }
                }
                else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
@@ -1651,6 +1653,8 @@ void the_game(
                                g_settings->set("fast_move","true");
                                statustext = L"fast_move enabled";
                                statustext_time = 0;
+                               if(!client.checkPrivilege("fast"))
+                                       statustext += L" (note: no 'fast' privilege)";
                        }
                }
                else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
index 3b5ad5caeeadfc60f5788ca4c0b77d035f27db41..b378a1d6d8675987848db0feeb69c8bda81ef6b9 100644 (file)
@@ -50,10 +50,15 @@ public:
        
        // Used for keeping track of names/ids of unknown nodes
        virtual u16 allocateUnknownNodeId(const std::string &name)=0;
-
+       
+       // Only usable on the client
        virtual ISoundManager* getSoundManager()=0;
        virtual MtEventManager* getEventManager()=0;
        
+       // Used on the client
+       virtual bool checkLocalPrivilege(const std::string &priv)
+       { return false; }
+       
        // Shorthands
        IItemDefManager* idef(){return getItemDefManager();}
        INodeDefManager* ndef(){return getNodeDefManager();}
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
new file mode 100644 (file)
index 0000000..bbe447a
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+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 General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "localplayer.h"
+#include "main.h" // For g_settings
+#include "event.h"
+#include "collision.h"
+#include "gamedef.h"
+#include "nodedef.h"
+#include "settings.h"
+#include "map.h"
+
+/*
+       LocalPlayer
+*/
+
+LocalPlayer::LocalPlayer(IGameDef *gamedef):
+       Player(gamedef),
+       m_sneak_node(32767,32767,32767),
+       m_sneak_node_exists(false)
+{
+       // Initialize hp to 0, so that no hearts will be shown if server
+       // doesn't support health points
+       hp = 0;
+}
+
+LocalPlayer::~LocalPlayer()
+{
+}
+
+void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
+               core::list<CollisionInfo> *collision_info)
+{
+       INodeDefManager *nodemgr = m_gamedef->ndef();
+
+       v3f position = getPosition();
+       v3f oldpos = position;
+       v3s16 oldpos_i = floatToInt(oldpos, BS);
+
+       v3f old_speed = m_speed;
+
+       /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
+                       <<oldpos_i.Z<<")"<<std::endl;*/
+
+       /*
+               Calculate new position
+       */
+       position += m_speed * dtime;
+       
+       // 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");
+       if(free_move)
+       {
+               setPosition(position);
+               return;
+       }
+
+       /*
+               Collision detection
+       */
+       
+       // Player position in nodes
+       v3s16 pos_i = floatToInt(position, BS);
+       
+       /*
+               Check if player is in water (the oscillating value)
+       */
+       try{
+               // If in water, the threshold of coming out is at higher y
+               if(in_water)
+               {
+                       v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
+                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+               }
+               // If not in water, the threshold of going in is at lower y
+               else
+               {
+                       v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
+                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+               }
+       }
+       catch(InvalidPositionException &e)
+       {
+               in_water = false;
+       }
+
+       /*
+               Check if player is in water (the stable value)
+       */
+       try{
+               v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
+               in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+       }
+       catch(InvalidPositionException &e)
+       {
+               in_water_stable = false;
+       }
+
+       /*
+               Check if player is climbing
+       */
+
+       try {
+               v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
+               v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
+               is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
+               nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
+       }
+       catch(InvalidPositionException &e)
+       {
+               is_climbing = false;
+       }
+
+       /*
+               Collision uncertainty radius
+               Make it a bit larger than the maximum distance of movement
+       */
+       //f32 d = pos_max_d * 1.1;
+       // A fairly large value in here makes moving smoother
+       f32 d = 0.15*BS;
+
+       // This should always apply, otherwise there are glitches
+       assert(d > pos_max_d);
+
+       float player_radius = BS*0.30;
+       float player_height = BS*1.55;
+       
+       // Maximum distance over border for sneaking
+       f32 sneak_max = BS*0.4;
+
+       /*
+               If sneaking, player has larger collision radius to keep from
+               falling
+       */
+       /*if(control.sneak)
+               player_radius = sneak_max + d*1.1;*/
+       
+       /*
+               If sneaking, keep in range from the last walked node and don't
+               fall off from it
+       */
+       if(control.sneak && m_sneak_node_exists)
+       {
+               f32 maxd = 0.5*BS + sneak_max;
+               v3f lwn_f = intToFloat(m_sneak_node, BS);
+               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)
+               {
+                       position.Y = min_y;
+
+                       //v3f old_speed = m_speed;
+
+                       if(m_speed.Y < 0)
+                               m_speed.Y = 0;
+
+                       /*if(collision_info)
+                       {
+                               // Report fall collision
+                               if(old_speed.Y < m_speed.Y - 0.1)
+                               {
+                                       CollisionInfo info;
+                                       info.t = COLLISION_FALL;
+                                       info.speed = m_speed.Y - old_speed.Y;
+                                       collision_info->push_back(info);
+                               }
+                       }*/
+               }
+       }
+
+       /*
+               Calculate player collision box (new and old)
+       */
+       core::aabbox3d<f32> playerbox(
+               position.X - player_radius,
+               position.Y - 0.0,
+               position.Z - player_radius,
+               position.X + player_radius,
+               position.Y + player_height,
+               position.Z + player_radius
+       );
+       core::aabbox3d<f32> playerbox_old(
+               oldpos.X - player_radius,
+               oldpos.Y - 0.0,
+               oldpos.Z - player_radius,
+               oldpos.X + player_radius,
+               oldpos.Y + player_height,
+               oldpos.Z + player_radius
+       );
+
+       /*
+               If the player's feet touch the topside of any node, this is
+               set to true.
+
+               Player is allowed to jump when this is true.
+       */
+       bool touching_ground_was = touching_ground;
+       touching_ground = false;
+
+       /*std::cout<<"Checking collisions for ("
+                       <<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
+                       <<") -> ("
+                       <<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
+                       <<"):"<<std::endl;*/
+       
+       bool standing_on_unloaded = false;
+       
+       /*
+               Go through every node around the player
+       */
+       for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
+       for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
+       for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
+       {
+               bool is_unloaded = false;
+               try{
+                       // Player collides into walkable nodes
+                       if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
+                               continue;
+               }
+               catch(InvalidPositionException &e)
+               {
+                       is_unloaded = true;
+                       // Doing nothing here will block the player from
+                       // walking over map borders
+               }
+
+               core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
+               
+               /*
+                       See if the player is touching ground.
+
+                       Player touches ground if player's minimum Y is near node's
+                       maximum Y and player's X-Z-area overlaps with the node's
+                       X-Z-area.
+
+                       Use 0.15*BS so that it is easier to get on a node.
+               */
+               if(
+                               //fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
+                               fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
+                               && nodebox.MaxEdge.X-d > playerbox.MinEdge.X
+                               && nodebox.MinEdge.X+d < playerbox.MaxEdge.X
+                               && nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
+                               && nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
+               ){
+                       touching_ground = true;
+                       if(is_unloaded)
+                               standing_on_unloaded = true;
+               }
+               
+               // If player doesn't intersect with node, ignore node.
+               if(playerbox.intersectsWithBox(nodebox) == false)
+                       continue;
+               
+               /*
+                       Go through every axis
+               */
+               v3f dirs[3] = {
+                       v3f(0,0,1), // back-front
+                       v3f(0,1,0), // top-bottom
+                       v3f(1,0,0), // right-left
+               };
+               for(u16 i=0; i<3; i++)
+               {
+                       /*
+                               Calculate values along the axis
+                       */
+                       f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
+                       f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
+                       f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
+                       f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
+                       f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
+                       f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
+                       
+                       /*
+                               Check collision for the axis.
+                               Collision happens when player is going through a surface.
+                       */
+                       /*f32 neg_d = d;
+                       f32 pos_d = d;
+                       // Make it easier to get on top of a node
+                       if(i == 1)
+                               neg_d = 0.15*BS;
+                       bool negative_axis_collides =
+                               (nodemax > playermin && nodemax <= playermin_old + neg_d
+                                       && m_speed.dotProduct(dirs[i]) < 0);
+                       bool positive_axis_collides =
+                               (nodemin < playermax && nodemin >= playermax_old - pos_d
+                                       && m_speed.dotProduct(dirs[i]) > 0);*/
+                       bool negative_axis_collides =
+                               (nodemax > playermin && nodemax <= playermin_old + d
+                                       && m_speed.dotProduct(dirs[i]) < 0);
+                       bool positive_axis_collides =
+                               (nodemin < playermax && nodemin >= playermax_old - d
+                                       && m_speed.dotProduct(dirs[i]) > 0);
+                       bool main_axis_collides =
+                                       negative_axis_collides || positive_axis_collides;
+                       
+                       /*
+                               Check overlap of player and node in other axes
+                       */
+                       bool other_axes_overlap = true;
+                       for(u16 j=0; j<3; j++)
+                       {
+                               if(j == i)
+                                       continue;
+                               f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
+                               f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
+                               f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
+                               f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
+                               if(!(nodemax - d > playermin && nodemin + d < playermax))
+                               {
+                                       other_axes_overlap = false;
+                                       break;
+                               }
+                       }
+                       
+                       /*
+                               If this is a collision, revert the position in the main
+                               direction.
+                       */
+                       if(other_axes_overlap && main_axis_collides)
+                       {
+                               //v3f old_speed = m_speed;
+
+                               m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
+                               position -= position.dotProduct(dirs[i]) * dirs[i];
+                               position += oldpos.dotProduct(dirs[i]) * dirs[i];
+                               
+                               /*if(collision_info)
+                               {
+                                       // Report fall collision
+                                       if(old_speed.Y < m_speed.Y - 0.1)
+                                       {
+                                               CollisionInfo info;
+                                               info.t = COLLISION_FALL;
+                                               info.speed = m_speed.Y - old_speed.Y;
+                                               collision_info->push_back(info);
+                                       }
+                               }*/
+                       }
+               
+               }
+       } // xyz
+
+       /*
+               Check the nodes under the player to see from which node the
+               player is sneaking from, if any.
+       */
+       {
+               v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
+               v2f player_p2df(position.X, position.Z);
+               f32 min_distance_f = 100000.0*BS;
+               // If already seeking from some node, compare to it.
+               /*if(m_sneak_node_exists)
+               {
+                       v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
+                       v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
+                       f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
+                       f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
+                       // Ignore if player is not on the same level (likely dropped)
+                       if(d_vert_f < 0.15*BS)
+                               min_distance_f = d_horiz_f;
+               }*/
+               v3s16 new_sneak_node = m_sneak_node;
+               for(s16 x=-1; x<=1; x++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = pos_i_bottom + v3s16(x,0,z);
+                       v3f pf = intToFloat(p, BS);
+                       v2f node_p2df(pf.X, pf.Z);
+                       f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
+                       f32 max_axis_distance_f = MYMAX(
+                                       fabs(player_p2df.X-node_p2df.X),
+                                       fabs(player_p2df.Y-node_p2df.Y));
+                                       
+                       if(distance_f > min_distance_f ||
+                                       max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
+                               continue;
+
+                       try{
+                               // The node to be sneaked on has to be walkable
+                               if(nodemgr->get(map.getNode(p)).walkable == false)
+                                       continue;
+                               // And the node above it has to be nonwalkable
+                               if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
+                                       continue;
+                       }
+                       catch(InvalidPositionException &e)
+                       {
+                               continue;
+                       }
+
+                       min_distance_f = distance_f;
+                       new_sneak_node = p;
+               }
+               
+               bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
+               
+               if(control.sneak && m_sneak_node_exists)
+               {
+                       if(sneak_node_found)
+                               m_sneak_node = new_sneak_node;
+               }
+               else
+               {
+                       m_sneak_node = new_sneak_node;
+                       m_sneak_node_exists = sneak_node_found;
+               }
+
+               /*
+                       If sneaking, the player's collision box can be in air, so
+                       this has to be set explicitly
+               */
+               if(sneak_node_found && control.sneak)
+                       touching_ground = true;
+       }
+       
+       /*
+               Set new position
+       */
+       setPosition(position);
+       
+       /*
+               Report collisions
+       */
+       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;
+                       collision_info->push_back(info);
+               }
+       }
+
+       if(!touching_ground_was && touching_ground){
+               MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
+               m_gamedef->event()->put(e);
+       }
+
+       {
+               camera_barely_in_ceiling = false;
+               v3s16 camera_np = floatToInt(getEyePosition(), BS);
+               MapNode n = map.getNodeNoEx(camera_np);
+               if(n.getContent() != CONTENT_IGNORE){
+                       if(nodemgr->get(n).walkable){
+                               camera_barely_in_ceiling = true;
+                       }
+               }
+       }
+}
+
+void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
+{
+       move(dtime, map, pos_max_d, NULL);
+}
+
+void LocalPlayer::applyControl(float dtime)
+{
+       // Clear stuff
+       swimming_up = false;
+
+       // Random constants
+       f32 walk_acceleration = 4.0 * BS;
+       f32 walkspeed_max = 4.0 * BS;
+       
+       setPitch(control.pitch);
+       setYaw(control.yaw);
+       
+       v3f move_direction = v3f(0,0,1);
+       move_direction.rotateXZBy(getYaw());
+       
+       v3f speed = v3f(0,0,0);
+       
+       bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
+       bool fast_allowed = m_gamedef->checkLocalPrivilege("fly");
+
+       bool free_move = fly_allowed && g_settings->getBool("free_move");
+       bool fast_move = fast_allowed && g_settings->getBool("fast_move");
+       bool continuous_forward = g_settings->getBool("continuous_forward");
+
+       if(free_move || is_climbing)
+       {
+               v3f speed = getSpeed();
+               speed.Y = 0;
+               setSpeed(speed);
+       }
+
+       // Whether superspeed mode is used or not
+       bool superspeed = false;
+       
+       // If free movement and fast movement, always move fast
+       if(free_move && fast_move)
+               superspeed = true;
+       
+       // Auxiliary button 1 (E)
+       if(control.aux1)
+       {
+               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
+               {
+                       // If not free movement but fast is allowed, aux1 is
+                       // "Turbo button"
+                       if(fast_move)
+                               superspeed = true;
+               }
+       }
+
+       if(continuous_forward)
+               speed += move_direction;
+
+       if(control.up)
+       {
+               if(continuous_forward)
+                       superspeed = true;
+               else
+                       speed += move_direction;
+       }
+       if(control.down)
+       {
+               speed -= move_direction;
+       }
+       if(control.left)
+       {
+               speed += move_direction.crossProduct(v3f(0,1,0));
+       }
+       if(control.right)
+       {
+               speed += move_direction.crossProduct(v3f(0,-1,0));
+       }
+       if(control.jump)
+       {
+               if(free_move)
+               {
+                       v3f speed = getSpeed();
+                       if(fast_move)
+                               speed.Y = 20*BS;
+                       else
+                               speed.Y = walkspeed_max;
+                       setSpeed(speed);
+               }
+               else if(touching_ground)
+               {
+                       /*
+                               NOTE: The d value in move() affects jump height by
+                               raising the height at which the jump speed is kept
+                               at its starting value
+                       */
+                       v3f speed = getSpeed();
+                       if(speed.Y >= -0.5*BS)
+                       {
+                               speed.Y = 6.5*BS;
+                               setSpeed(speed);
+                               
+                               MtEvent *e = new SimpleTriggerEvent("PlayerJump");
+                               m_gamedef->event()->put(e);
+                       }
+               }
+               // Use the oscillating value for getting out of water
+               // (so that the player doesn't fly on the surface)
+               else if(in_water)
+               {
+                       v3f speed = getSpeed();
+                       speed.Y = 1.5*BS;
+                       setSpeed(speed);
+                       swimming_up = true;
+               }
+               else if(is_climbing)
+               {
+                       v3f speed = getSpeed();
+                       speed.Y = 3*BS;
+                       setSpeed(speed);
+               }
+       }
+
+       // The speed of the player (Y is ignored)
+       if(superspeed)
+               speed = speed.normalize() * walkspeed_max * 5.0;
+       else if(control.sneak)
+               speed = speed.normalize() * walkspeed_max / 3.0;
+       else
+               speed = speed.normalize() * walkspeed_max;
+       
+       f32 inc = walk_acceleration * BS * dtime;
+       
+       // Faster acceleration if fast and free movement
+       if(free_move && fast_move)
+               inc = walk_acceleration * BS * dtime * 10;
+       
+       // Accelerate to target speed with maximum increment
+       accelerate(speed, inc);
+}
+
+v3s16 LocalPlayer::getStandingNodePos()
+{
+       if(m_sneak_node_exists)
+               return m_sneak_node;
+       return floatToInt(getPosition(), BS);
+}
+
diff --git a/src/localplayer.h b/src/localplayer.h
new file mode 100644 (file)
index 0000000..473a80f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+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 General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef LOCALPLAYER_HEADER
+#define LOCALPLAYER_HEADER
+
+#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:
+       LocalPlayer(IGameDef *gamedef);
+       virtual ~LocalPlayer();
+
+       bool isLocal() const
+       {
+               return true;
+       }
+       
+       void move(f32 dtime, Map &map, f32 pos_max_d,
+                       core::list<CollisionInfo> *collision_info);
+       void move(f32 dtime, Map &map, f32 pos_max_d);
+
+       void applyControl(float dtime);
+
+       v3s16 getStandingNodePos();
+       
+       PlayerControl control;
+
+private:
+       // This is used for determining the sneaking range
+       v3s16 m_sneak_node;
+       // Whether the player is allowed to sneak
+       bool m_sneak_node_exists;
+};
+
+#endif
+
index 6e8b8c1d44a5a865ce5031e8e847cad9c7697f5a..e7824afbce47dec37aaa89f7fdeca704c3f3f65c 100644 (file)
@@ -18,20 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "player.h"
-#include "map.h"
-#include "connection.h"
 #include "constants.h"
 #include "utility.h"
-#ifndef SERVER
-#include <ITextSceneNode.h>
-#endif
-#include "main.h" // For g_settings
-#include "settings.h"
-#include "nodedef.h"
-#include "collision.h"
-#include "environment.h"
 #include "gamedef.h"
-#include "event.h"
+#include "connection.h" // PEER_ID_INEXISTENT
+#include "settings.h"
 #include "content_sao.h"
 
 Player::Player(IGameDef *gamedef):
@@ -171,615 +162,6 @@ void Player::deSerialize(std::istream &is)
        }
 }
 
-#ifndef SERVER
-/*
-       LocalPlayer
-*/
-
-LocalPlayer::LocalPlayer(IGameDef *gamedef):
-       Player(gamedef),
-       m_sneak_node(32767,32767,32767),
-       m_sneak_node_exists(false)
-{
-       // Initialize hp to 0, so that no hearts will be shown if server
-       // doesn't support health points
-       hp = 0;
-}
-
-LocalPlayer::~LocalPlayer()
-{
-}
-
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
-               core::list<CollisionInfo> *collision_info)
-{
-       INodeDefManager *nodemgr = m_gamedef->ndef();
-
-       v3f position = getPosition();
-       v3f oldpos = position;
-       v3s16 oldpos_i = floatToInt(oldpos, BS);
-
-       v3f old_speed = m_speed;
-
-       /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
-                       <<oldpos_i.Z<<")"<<std::endl;*/
-
-       /*
-               Calculate new position
-       */
-       position += m_speed * dtime;
-       
-       // Skip collision detection if a special movement mode is used
-       bool free_move = g_settings->getBool("free_move");
-       if(free_move)
-       {
-               setPosition(position);
-               return;
-       }
-
-       /*
-               Collision detection
-       */
-       
-       // Player position in nodes
-       v3s16 pos_i = floatToInt(position, BS);
-       
-       /*
-               Check if player is in water (the oscillating value)
-       */
-       try{
-               // If in water, the threshold of coming out is at higher y
-               if(in_water)
-               {
-                       v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
-                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-               }
-               // If not in water, the threshold of going in is at lower y
-               else
-               {
-                       v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
-                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-               }
-       }
-       catch(InvalidPositionException &e)
-       {
-               in_water = false;
-       }
-
-       /*
-               Check if player is in water (the stable value)
-       */
-       try{
-               v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
-               in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-       }
-       catch(InvalidPositionException &e)
-       {
-               in_water_stable = false;
-       }
-
-       /*
-               Check if player is climbing
-       */
-
-       try {
-               v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
-               v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
-               is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
-               nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
-       }
-       catch(InvalidPositionException &e)
-       {
-               is_climbing = false;
-       }
-
-       /*
-               Collision uncertainty radius
-               Make it a bit larger than the maximum distance of movement
-       */
-       //f32 d = pos_max_d * 1.1;
-       // A fairly large value in here makes moving smoother
-       f32 d = 0.15*BS;
-
-       // This should always apply, otherwise there are glitches
-       assert(d > pos_max_d);
-
-       float player_radius = BS*0.30;
-       float player_height = BS*1.55;
-       
-       // Maximum distance over border for sneaking
-       f32 sneak_max = BS*0.4;
-
-       /*
-               If sneaking, player has larger collision radius to keep from
-               falling
-       */
-       /*if(control.sneak)
-               player_radius = sneak_max + d*1.1;*/
-       
-       /*
-               If sneaking, keep in range from the last walked node and don't
-               fall off from it
-       */
-       if(control.sneak && m_sneak_node_exists)
-       {
-               f32 maxd = 0.5*BS + sneak_max;
-               v3f lwn_f = intToFloat(m_sneak_node, BS);
-               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)
-               {
-                       position.Y = min_y;
-
-                       //v3f old_speed = m_speed;
-
-                       if(m_speed.Y < 0)
-                               m_speed.Y = 0;
-
-                       /*if(collision_info)
-                       {
-                               // Report fall collision
-                               if(old_speed.Y < m_speed.Y - 0.1)
-                               {
-                                       CollisionInfo info;
-                                       info.t = COLLISION_FALL;
-                                       info.speed = m_speed.Y - old_speed.Y;
-                                       collision_info->push_back(info);
-                               }
-                       }*/
-               }
-       }
-
-       /*
-               Calculate player collision box (new and old)
-       */
-       core::aabbox3d<f32> playerbox(
-               position.X - player_radius,
-               position.Y - 0.0,
-               position.Z - player_radius,
-               position.X + player_radius,
-               position.Y + player_height,
-               position.Z + player_radius
-       );
-       core::aabbox3d<f32> playerbox_old(
-               oldpos.X - player_radius,
-               oldpos.Y - 0.0,
-               oldpos.Z - player_radius,
-               oldpos.X + player_radius,
-               oldpos.Y + player_height,
-               oldpos.Z + player_radius
-       );
-
-       /*
-               If the player's feet touch the topside of any node, this is
-               set to true.
-
-               Player is allowed to jump when this is true.
-       */
-       bool touching_ground_was = touching_ground;
-       touching_ground = false;
-
-       /*std::cout<<"Checking collisions for ("
-                       <<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
-                       <<") -> ("
-                       <<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
-                       <<"):"<<std::endl;*/
-       
-       bool standing_on_unloaded = false;
-       
-       /*
-               Go through every node around the player
-       */
-       for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
-       for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
-       for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
-       {
-               bool is_unloaded = false;
-               try{
-                       // Player collides into walkable nodes
-                       if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
-                               continue;
-               }
-               catch(InvalidPositionException &e)
-               {
-                       is_unloaded = true;
-                       // Doing nothing here will block the player from
-                       // walking over map borders
-               }
-
-               core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
-               
-               /*
-                       See if the player is touching ground.
-
-                       Player touches ground if player's minimum Y is near node's
-                       maximum Y and player's X-Z-area overlaps with the node's
-                       X-Z-area.
-
-                       Use 0.15*BS so that it is easier to get on a node.
-               */
-               if(
-                               //fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
-                               fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
-                               && nodebox.MaxEdge.X-d > playerbox.MinEdge.X
-                               && nodebox.MinEdge.X+d < playerbox.MaxEdge.X
-                               && nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
-                               && nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
-               ){
-                       touching_ground = true;
-                       if(is_unloaded)
-                               standing_on_unloaded = true;
-               }
-               
-               // If player doesn't intersect with node, ignore node.
-               if(playerbox.intersectsWithBox(nodebox) == false)
-                       continue;
-               
-               /*
-                       Go through every axis
-               */
-               v3f dirs[3] = {
-                       v3f(0,0,1), // back-front
-                       v3f(0,1,0), // top-bottom
-                       v3f(1,0,0), // right-left
-               };
-               for(u16 i=0; i<3; i++)
-               {
-                       /*
-                               Calculate values along the axis
-                       */
-                       f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
-                       f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
-                       f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
-                       f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
-                       f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
-                       f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
-                       
-                       /*
-                               Check collision for the axis.
-                               Collision happens when player is going through a surface.
-                       */
-                       /*f32 neg_d = d;
-                       f32 pos_d = d;
-                       // Make it easier to get on top of a node
-                       if(i == 1)
-                               neg_d = 0.15*BS;
-                       bool negative_axis_collides =
-                               (nodemax > playermin && nodemax <= playermin_old + neg_d
-                                       && m_speed.dotProduct(dirs[i]) < 0);
-                       bool positive_axis_collides =
-                               (nodemin < playermax && nodemin >= playermax_old - pos_d
-                                       && m_speed.dotProduct(dirs[i]) > 0);*/
-                       bool negative_axis_collides =
-                               (nodemax > playermin && nodemax <= playermin_old + d
-                                       && m_speed.dotProduct(dirs[i]) < 0);
-                       bool positive_axis_collides =
-                               (nodemin < playermax && nodemin >= playermax_old - d
-                                       && m_speed.dotProduct(dirs[i]) > 0);
-                       bool main_axis_collides =
-                                       negative_axis_collides || positive_axis_collides;
-                       
-                       /*
-                               Check overlap of player and node in other axes
-                       */
-                       bool other_axes_overlap = true;
-                       for(u16 j=0; j<3; j++)
-                       {
-                               if(j == i)
-                                       continue;
-                               f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
-                               f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
-                               f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
-                               f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
-                               if(!(nodemax - d > playermin && nodemin + d < playermax))
-                               {
-                                       other_axes_overlap = false;
-                                       break;
-                               }
-                       }
-                       
-                       /*
-                               If this is a collision, revert the position in the main
-                               direction.
-                       */
-                       if(other_axes_overlap && main_axis_collides)
-                       {
-                               //v3f old_speed = m_speed;
-
-                               m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
-                               position -= position.dotProduct(dirs[i]) * dirs[i];
-                               position += oldpos.dotProduct(dirs[i]) * dirs[i];
-                               
-                               /*if(collision_info)
-                               {
-                                       // Report fall collision
-                                       if(old_speed.Y < m_speed.Y - 0.1)
-                                       {
-                                               CollisionInfo info;
-                                               info.t = COLLISION_FALL;
-                                               info.speed = m_speed.Y - old_speed.Y;
-                                               collision_info->push_back(info);
-                                       }
-                               }*/
-                       }
-               
-               }
-       } // xyz
-
-       /*
-               Check the nodes under the player to see from which node the
-               player is sneaking from, if any.
-       */
-       {
-               v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
-               v2f player_p2df(position.X, position.Z);
-               f32 min_distance_f = 100000.0*BS;
-               // If already seeking from some node, compare to it.
-               /*if(m_sneak_node_exists)
-               {
-                       v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
-                       v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
-                       f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
-                       f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
-                       // Ignore if player is not on the same level (likely dropped)
-                       if(d_vert_f < 0.15*BS)
-                               min_distance_f = d_horiz_f;
-               }*/
-               v3s16 new_sneak_node = m_sneak_node;
-               for(s16 x=-1; x<=1; x++)
-               for(s16 z=-1; z<=1; z++)
-               {
-                       v3s16 p = pos_i_bottom + v3s16(x,0,z);
-                       v3f pf = intToFloat(p, BS);
-                       v2f node_p2df(pf.X, pf.Z);
-                       f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
-                       f32 max_axis_distance_f = MYMAX(
-                                       fabs(player_p2df.X-node_p2df.X),
-                                       fabs(player_p2df.Y-node_p2df.Y));
-                                       
-                       if(distance_f > min_distance_f ||
-                                       max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
-                               continue;
-
-                       try{
-                               // The node to be sneaked on has to be walkable
-                               if(nodemgr->get(map.getNode(p)).walkable == false)
-                                       continue;
-                               // And the node above it has to be nonwalkable
-                               if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
-                                       continue;
-                       }
-                       catch(InvalidPositionException &e)
-                       {
-                               continue;
-                       }
-
-                       min_distance_f = distance_f;
-                       new_sneak_node = p;
-               }
-               
-               bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
-               
-               if(control.sneak && m_sneak_node_exists)
-               {
-                       if(sneak_node_found)
-                               m_sneak_node = new_sneak_node;
-               }
-               else
-               {
-                       m_sneak_node = new_sneak_node;
-                       m_sneak_node_exists = sneak_node_found;
-               }
-
-               /*
-                       If sneaking, the player's collision box can be in air, so
-                       this has to be set explicitly
-               */
-               if(sneak_node_found && control.sneak)
-                       touching_ground = true;
-       }
-       
-       /*
-               Set new position
-       */
-       setPosition(position);
-       
-       /*
-               Report collisions
-       */
-       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;
-                       collision_info->push_back(info);
-               }
-       }
-
-       if(!touching_ground_was && touching_ground){
-               MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
-               m_gamedef->event()->put(e);
-       }
-
-       {
-               camera_barely_in_ceiling = false;
-               v3s16 camera_np = floatToInt(getEyePosition(), BS);
-               MapNode n = map.getNodeNoEx(camera_np);
-               if(n.getContent() != CONTENT_IGNORE){
-                       if(nodemgr->get(n).walkable){
-                               camera_barely_in_ceiling = true;
-                       }
-               }
-       }
-}
-
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
-{
-       move(dtime, map, pos_max_d, NULL);
-}
-
-void LocalPlayer::applyControl(float dtime)
-{
-       // Clear stuff
-       swimming_up = false;
-
-       // Random constants
-       f32 walk_acceleration = 4.0 * BS;
-       f32 walkspeed_max = 4.0 * BS;
-       
-       setPitch(control.pitch);
-       setYaw(control.yaw);
-       
-       v3f move_direction = v3f(0,0,1);
-       move_direction.rotateXZBy(getYaw());
-       
-       v3f speed = v3f(0,0,0);
-
-       bool free_move = g_settings->getBool("free_move");
-       bool fast_move = g_settings->getBool("fast_move");
-       bool continuous_forward = g_settings->getBool("continuous_forward");
-
-       if(free_move || is_climbing)
-       {
-               v3f speed = getSpeed();
-               speed.Y = 0;
-               setSpeed(speed);
-       }
-
-       // Whether superspeed mode is used or not
-       bool superspeed = false;
-       
-       // If free movement and fast movement, always move fast
-       if(free_move && fast_move)
-               superspeed = true;
-       
-       // Auxiliary button 1 (E)
-       if(control.aux1)
-       {
-               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
-               {
-                       // If not free movement but fast is allowed, aux1 is
-                       // "Turbo button"
-                       if(fast_move)
-                               superspeed = true;
-               }
-       }
-
-       if(continuous_forward)
-               speed += move_direction;
-
-       if(control.up)
-       {
-               if(continuous_forward)
-                       superspeed = true;
-               else
-                       speed += move_direction;
-       }
-       if(control.down)
-       {
-               speed -= move_direction;
-       }
-       if(control.left)
-       {
-               speed += move_direction.crossProduct(v3f(0,1,0));
-       }
-       if(control.right)
-       {
-               speed += move_direction.crossProduct(v3f(0,-1,0));
-       }
-       if(control.jump)
-       {
-               if(free_move)
-               {
-                       v3f speed = getSpeed();
-                       if(fast_move)
-                               speed.Y = 20*BS;
-                       else
-                               speed.Y = walkspeed_max;
-                       setSpeed(speed);
-               }
-               else if(touching_ground)
-               {
-                       /*
-                               NOTE: The d value in move() affects jump height by
-                               raising the height at which the jump speed is kept
-                               at its starting value
-                       */
-                       v3f speed = getSpeed();
-                       if(speed.Y >= -0.5*BS)
-                       {
-                               speed.Y = 6.5*BS;
-                               setSpeed(speed);
-                               
-                               MtEvent *e = new SimpleTriggerEvent("PlayerJump");
-                               m_gamedef->event()->put(e);
-                       }
-               }
-               // Use the oscillating value for getting out of water
-               // (so that the player doesn't fly on the surface)
-               else if(in_water)
-               {
-                       v3f speed = getSpeed();
-                       speed.Y = 1.5*BS;
-                       setSpeed(speed);
-                       swimming_up = true;
-               }
-               else if(is_climbing)
-               {
-                       v3f speed = getSpeed();
-                       speed.Y = 3*BS;
-                       setSpeed(speed);
-               }
-       }
-
-       // The speed of the player (Y is ignored)
-       if(superspeed)
-               speed = speed.normalize() * walkspeed_max * 5.0;
-       else if(control.sneak)
-               speed = speed.normalize() * walkspeed_max / 3.0;
-       else
-               speed = speed.normalize() * walkspeed_max;
-       
-       f32 inc = walk_acceleration * BS * dtime;
-       
-       // Faster acceleration if fast and free movement
-       if(free_move && fast_move)
-               inc = walk_acceleration * BS * dtime * 10;
-       
-       // Accelerate to target speed with maximum increment
-       accelerate(speed, inc);
-}
-
-v3s16 LocalPlayer::getStandingNodePos()
-{
-       if(m_sneak_node_exists)
-               return m_sneak_node;
-       return floatToInt(getPosition(), BS);
-}
-
-#endif
-
 /*
        RemotePlayer
 */
index 9bbdda15a90c0f1d5e6aac20974f10d5c6a57dcb..6108af3db8786cae8638e434a259d19ee8468032 100644 (file)
@@ -20,14 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef PLAYER_HEADER
 #define PLAYER_HEADER
 
-#include "common_irrlicht.h"
+#include "irrlichttypes.h"
 #include "inventory.h"
 
 #define PLAYERNAME_SIZE 20
 
 #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
 
-
 class Map;
 class IGameDef;
 struct CollisionInfo;
@@ -167,83 +166,6 @@ protected:
        v3f m_position;
 };
 
-#ifndef SERVER
-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:
-       LocalPlayer(IGameDef *gamedef);
-       virtual ~LocalPlayer();
-
-       bool isLocal() const
-       {
-               return true;
-       }
-       
-       void move(f32 dtime, Map &map, f32 pos_max_d,
-                       core::list<CollisionInfo> *collision_info);
-       void move(f32 dtime, Map &map, f32 pos_max_d);
-
-       void applyControl(float dtime);
-
-       v3s16 getStandingNodePos();
-       
-       PlayerControl control;
-
-private:
-       // This is used for determining the sneaking range
-       v3s16 m_sneak_node;
-       // Whether the player is allowed to sneak
-       bool m_sneak_node_exists;
-};
-#endif // !SERVER
-
 /*
        Player on the server
 */
index a9f54627d45834f09d17e76795216653582229f2..a86c56f9c98016ec613b3803c8be431c2e284e9a 100644 (file)
@@ -3985,6 +3985,16 @@ static int l_get_password_hash(lua_State *L)
        return 1;
 }
 
+// notify_authentication_modified(name)
+static int l_notify_authentication_modified(lua_State *L)
+{
+       std::string name = "";
+       if(lua_isstring(L, 1))
+               name = lua_tostring(L, 1);
+       get_server(L)->reportPrivsModified(name);
+       return 0;
+}
+
 static const struct luaL_Reg minetest_f [] = {
        {"debug", l_debug},
        {"log", l_log},
@@ -4006,6 +4016,7 @@ static const struct luaL_Reg minetest_f [] = {
        {"sound_stop", l_sound_stop},
        {"is_singleplayer", l_is_singleplayer},
        {"get_password_hash", l_get_password_hash},
+       {"notify_authentication_modified", l_notify_authentication_modified},
        {NULL, NULL}
 };
 
index 08813f7fa41a83780a0ab57412090130074eb978..d6357e41607f8fb61e2f935398737dfd3034cf72 100644 (file)
@@ -2194,13 +2194,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send node definitions
                SendNodeDef(m_con, peer_id, m_nodedef);
                
-               // Send texture announcement
+               // Send media announcement
                sendMediaAnnouncement(peer_id);
                
-               // Send player info to all players
-               //SendPlayerInfos();
-
-               // Send inventory to player
+               // Send privileges
+               SendPlayerPrivileges(peer_id);
+               
+               // Send inventory
                UpdateCrafting(peer_id);
                SendInventory(peer_id);
                
@@ -3544,6 +3544,28 @@ void Server::SendMovePlayer(u16 peer_id)
        m_con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendPlayerPrivileges(u16 peer_id)
+{
+       Player *player = m_env->getPlayer(peer_id);
+       assert(player);
+       std::set<std::string> privs;
+       scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
+       
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_PRIVILEGES);
+       writeU16(os, privs.size());
+       for(std::set<std::string>::const_iterator i = privs.begin();
+                       i != privs.end(); i++){
+               os<<serializeString(*i);
+       }
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
 s32 Server::playSound(const SimpleSoundSpec &spec,
                const ServerSoundParams &params)
 {
@@ -4286,6 +4308,23 @@ bool Server::checkPriv(const std::string &name, const std::string &priv)
        return (privs.count(priv) != 0);
 }
 
+void Server::reportPrivsModified(const std::string &name)
+{
+       if(name == ""){
+               for(core::map<u16, RemoteClient*>::Iterator
+                               i = m_clients.getIterator();
+                               i.atEnd() == false; i++){
+                       RemoteClient *client = i.getNode()->getValue();
+                       SendPlayerPrivileges(client->peer_id);
+               }
+       } else {
+               Player *player = m_env->getPlayer(name.c_str());
+               if(!player)
+                       return;
+               SendPlayerPrivileges(player->peer_id);
+       }
+}
+
 // Saves g_settings to configpath given at initialization
 void Server::saveConfig()
 {
index 4b04044e1284b823749e61de70f737fdb1fbb742..abe466b16f957e042810ff0f7d62aa03d4475f69 100644 (file)
@@ -502,6 +502,7 @@ public:
        // Envlock + conlock
        std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
        bool checkPriv(const std::string &name, const std::string &priv);
+       void reportPrivsModified(const std::string &name=""); // ""=all
 
        // Saves g_settings to configpath given at initialization
        void saveConfig();
@@ -592,11 +593,12 @@ private:
        */
 
        // Envlock and conlock should be locked when calling these
-       void SendMovePlayer(u16 peer_id);
        void SendInventory(u16 peer_id);
        void SendChatMessage(u16 peer_id, const std::wstring &message);
        void BroadcastChatMessage(const std::wstring &message);
        void SendPlayerHP(u16 peer_id);
+       void SendMovePlayer(u16 peer_id);
+       void SendPlayerPrivileges(u16 peer_id);
        /*
                Send a node removal/addition event to all clients except ignore_id.
                Additionally, if far_players!=NULL, players further away than