From 52122c342d335a2561ace87c9d8deaa16a136604 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 31 Mar 2012 16:23:26 +0300 Subject: [PATCH] Add 'fly' and 'fast' privileges and the underlying privileges-to-client system --- builtin/builtin.lua | 77 ++++-- doc/lua_api.txt | 16 +- src/CMakeLists.txt | 1 + src/camera.cpp | 3 +- src/client.cpp | 15 ++ src/client.h | 20 +- src/clientserver.h | 15 +- src/content_cao.cpp | 1 + src/environment.cpp | 4 +- src/game.cpp | 4 + src/gamedef.h | 7 +- src/localplayer.cpp | 638 ++++++++++++++++++++++++++++++++++++++++++++ src/localplayer.h | 101 +++++++ src/player.cpp | 622 +----------------------------------------- src/player.h | 80 +----- src/scriptapi.cpp | 11 + src/server.cpp | 49 +++- src/server.h | 4 +- 18 files changed, 928 insertions(+), 740 deletions(-) create mode 100644 src/localplayer.cpp create mode 100644 src/localplayer.h diff --git a/builtin/builtin.lua b/builtin/builtin.lua index d6782a49a..35501787c 100644 --- a/builtin/builtin.lua +++ b/builtin/builtin.lua @@ -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 } diff --git a/doc/lua_api.txt b/doc/lua_api.txt index a03d9a9e1..3ba59565b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c7fc988c..56da7bd07 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -220,6 +220,7 @@ endif() set(minetest_SRCS ${common_SRCS} ${sound_SRCS} + localplayer.cpp sky.cpp clientmap.cpp content_cso.cpp diff --git a/src/camera.cpp b/src/camera.cpp index ba05329e5..290776bf3 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -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; diff --git a/src/client.cpp b/src/client.cpp index 7f1b39c33..de3c63698 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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 m_sounds_client_to_server; // And relations to objects std::map m_sounds_to_objects; -}; -#endif // !SERVER + // Privileges + std::set m_privileges; +}; #endif // !CLIENT_HEADER diff --git a/src/clientserver.h b/src/clientserver.h index def4570e1..461c13cd2 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -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 diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 627d3c049..2a9c8a91a 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -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; diff --git a/src/environment.cpp b/src/environment.cpp index 001b53a40..e1f5bb3fb 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -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(); diff --git a/src/game.cpp b/src/game.cpp index 941f286d9..a0f6c0d8c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -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"))) diff --git a/src/gamedef.h b/src/gamedef.h index 3b5ad5cae..b378a1d6d 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -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 index 000000000..bbe447a6c --- /dev/null +++ b/src/localplayer.cpp @@ -0,0 +1,638 @@ +/* +Minetest-c55 +Copyright (C) 2010-2012 celeron55, Perttu Ahola + +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 *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=("<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 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 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 (" + < (" + <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 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 index 000000000..473a80f5e --- /dev/null +++ b/src/localplayer.h @@ -0,0 +1,101 @@ +/* +Minetest-c55 +Copyright (C) 2010-2012 celeron55, Perttu Ahola + +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 *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 + diff --git a/src/player.cpp b/src/player.cpp index 6e8b8c1d4..e7824afbc 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -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 -#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 *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=("<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 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 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 (" - < (" - <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 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 */ diff --git a/src/player.h b/src/player.h index 9bbdda15a..6108af3db 100644 --- a/src/player.h +++ b/src/player.h @@ -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 *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 */ diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index a9f54627d..a86c56f9c 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -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} }; diff --git a/src/server.cpp b/src/server.cpp index 08813f7fa..d6357e416 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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 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::const_iterator i = privs.begin(); + i != privs.end(); i++){ + os< 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 ¶ms) { @@ -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::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() { diff --git a/src/server.h b/src/server.h index 4b04044e1..abe466b16 100644 --- a/src/server.h +++ b/src/server.h @@ -502,6 +502,7 @@ public: // Envlock + conlock std::set 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