forked from Mirrorlandia_minetest/minetest
Add 'fly' and 'fast' privileges and the underlying privileges-to-client system
This commit is contained in:
parent
96ee73f790
commit
52122c342d
@ -870,8 +870,24 @@ end
|
|||||||
--
|
--
|
||||||
|
|
||||||
minetest.registered_privileges = {}
|
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
|
end
|
||||||
|
|
||||||
minetest.register_privilege("interact", "Can interact with things and modify the world")
|
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("ban", "Can ban and unban players")
|
||||||
minetest.register_privilege("give", "Can use /give and /giveme")
|
minetest.register_privilege("give", "Can use /give and /giveme")
|
||||||
minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
|
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
|
-- Chat commands
|
||||||
@ -929,8 +953,8 @@ minetest.register_chatcommand("help", {
|
|||||||
end
|
end
|
||||||
elseif param == "privs" then
|
elseif param == "privs" then
|
||||||
minetest.chat_send_player(name, "Available privileges:")
|
minetest.chat_send_player(name, "Available privileges:")
|
||||||
for priv, desc in pairs(minetest.registered_privileges) do
|
for priv, def in pairs(minetest.registered_privileges) do
|
||||||
minetest.chat_send_player(name, priv..": "..desc)
|
minetest.chat_send_player(name, priv..": "..def.description)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local cmd = param
|
local cmd = param
|
||||||
@ -1004,7 +1028,7 @@ minetest.register_chatcommand("revoke", {
|
|||||||
local revokeprivs = minetest.string_to_privs(revokeprivstr)
|
local revokeprivs = minetest.string_to_privs(revokeprivstr)
|
||||||
local privs = minetest.get_player_privs(revokename)
|
local privs = minetest.get_player_privs(revokename)
|
||||||
for priv, _ in pairs(revokeprivs) do
|
for priv, _ in pairs(revokeprivs) do
|
||||||
table.remove(privs, priv)
|
privs[priv] = nil
|
||||||
end
|
end
|
||||||
minetest.set_player_privs(revokename, privs)
|
minetest.set_player_privs(revokename, privs)
|
||||||
end,
|
end,
|
||||||
@ -1200,6 +1224,7 @@ local function read_auth_file()
|
|||||||
end
|
end
|
||||||
io.close(file)
|
io.close(file)
|
||||||
minetest.auth_table = newtable
|
minetest.auth_table = newtable
|
||||||
|
minetest.notify_authentication_modified()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function save_auth_file()
|
local function save_auth_file()
|
||||||
@ -1228,24 +1253,41 @@ read_auth_file()
|
|||||||
minetest.builtin_auth_handler = {
|
minetest.builtin_auth_handler = {
|
||||||
get_auth = function(name)
|
get_auth = function(name)
|
||||||
assert(type(name) == "string")
|
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
|
if not minetest.auth_table[name] then
|
||||||
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
|
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
|
||||||
end
|
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
|
if minetest.is_singleplayer() then
|
||||||
return {
|
for priv, def in pairs(minetest.registered_privileges) do
|
||||||
password = "",
|
if def.give_to_singleplayer then
|
||||||
privileges = minetest.registered_privileges
|
privileges[priv] = true
|
||||||
}
|
end
|
||||||
else
|
end
|
||||||
if minetest.auth_table[name] and name == minetest.setting_get("name") then
|
-- For the admin, give everything
|
||||||
return {
|
elseif name == minetest.setting_get("name") then
|
||||||
password = minetest.auth_table[name].password,
|
for priv, def in pairs(minetest.registered_privileges) do
|
||||||
privileges = minetest.registered_privileges
|
privileges[priv] = true
|
||||||
}
|
|
||||||
else
|
|
||||||
return minetest.auth_table[name]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- All done
|
||||||
|
return {
|
||||||
|
password = minetest.auth_table[name].password,
|
||||||
|
privileges = privileges,
|
||||||
|
}
|
||||||
end,
|
end,
|
||||||
create_auth = function(name, password)
|
create_auth = function(name, password)
|
||||||
assert(type(name) == "string")
|
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")))
|
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
|
||||||
end
|
end
|
||||||
minetest.auth_table[name].privileges = privileges
|
minetest.auth_table[name].privileges = privileges
|
||||||
|
minetest.notify_authentication_modified(name)
|
||||||
save_auth_file()
|
save_auth_file()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
@ -506,7 +506,12 @@ minetest.register_on_respawnplayer(func(ObjectRef))
|
|||||||
^ currently called _before_ repositioning of player occurs
|
^ currently called _before_ repositioning of player occurs
|
||||||
minetest.register_on_chat_message(func(name, message))
|
minetest.register_on_chat_message(func(name, message))
|
||||||
minetest.register_chatcommand(cmd, chatcommand definition)
|
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)
|
minetest.register_authentication_handler(handler)
|
||||||
^ See minetest.builtin_auth_handler in builtin.lua for reference
|
^ 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)
|
minetest.add_to_creative_inventory(itemstring)
|
||||||
|
|
||||||
Authentication:
|
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.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.string_to_privs(str) -> {priv1=true,...}
|
||||||
minetest.privs_to_string(privs) -> "priv1,priv2,..."
|
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.set_player_privs(name, {priv1=true,...})
|
||||||
minetest.get_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
|
minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs
|
||||||
|
^ A quickhand for checking privileges
|
||||||
|
|
||||||
Chat:
|
Chat:
|
||||||
minetest.chat_send_all(text)
|
minetest.chat_send_all(text)
|
||||||
|
@ -220,6 +220,7 @@ endif()
|
|||||||
set(minetest_SRCS
|
set(minetest_SRCS
|
||||||
${common_SRCS}
|
${common_SRCS}
|
||||||
${sound_SRCS}
|
${sound_SRCS}
|
||||||
|
localplayer.cpp
|
||||||
sky.cpp
|
sky.cpp
|
||||||
clientmap.cpp
|
clientmap.cpp
|
||||||
content_cso.cpp
|
content_cso.cpp
|
||||||
|
@ -351,7 +351,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
|
|||||||
if ((hypot(speed.X, speed.Z) > BS) &&
|
if ((hypot(speed.X, speed.Z) > BS) &&
|
||||||
(player->touching_ground) &&
|
(player->touching_ground) &&
|
||||||
(g_settings->getBool("view_bobbing") == true) &&
|
(g_settings->getBool("view_bobbing") == true) &&
|
||||||
(g_settings->getBool("free_move") == false))
|
(g_settings->getBool("free_move") == false ||
|
||||||
|
!m_gamedef->checkLocalPrivilege("fly")))
|
||||||
{
|
{
|
||||||
// Start animation
|
// Start animation
|
||||||
m_view_bobbing_state = 1;
|
m_view_bobbing_state = 1;
|
||||||
|
@ -1674,6 +1674,21 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
|||||||
m_sound->stopSound(client_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
|
else
|
||||||
{
|
{
|
||||||
infostream<<"Client: Ignoring unknown command "
|
infostream<<"Client: Ignoring unknown command "
|
||||||
|
20
src/client.h
20
src/client.h
@ -20,8 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#ifndef CLIENT_HEADER
|
#ifndef CLIENT_HEADER
|
||||||
#define CLIENT_HEADER
|
#define CLIENT_HEADER
|
||||||
|
|
||||||
#ifndef SERVER
|
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
#include "common_irrlicht.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 "inventorymanager.h"
|
||||||
#include "filesys.h"
|
#include "filesys.h"
|
||||||
#include "filecache.h"
|
#include "filecache.h"
|
||||||
|
#include "localplayer.h"
|
||||||
|
|
||||||
struct MeshMakeData;
|
struct MeshMakeData;
|
||||||
class MapBlockMesh;
|
class MapBlockMesh;
|
||||||
@ -262,14 +261,8 @@ public:
|
|||||||
|
|
||||||
u16 getHP();
|
u16 getHP();
|
||||||
|
|
||||||
float getAvgRtt()
|
bool checkPrivilege(const std::string &priv)
|
||||||
{
|
{ return (m_privileges.count(priv) != 0); }
|
||||||
try{
|
|
||||||
return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
|
|
||||||
} catch(con::PeerNotFoundException){
|
|
||||||
return 1337;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getChatMessage(std::wstring &message);
|
bool getChatMessage(std::wstring &message);
|
||||||
void typeChatMessage(const std::wstring& message);
|
void typeChatMessage(const std::wstring& message);
|
||||||
@ -312,6 +305,8 @@ public:
|
|||||||
virtual u16 allocateUnknownNodeId(const std::string &name);
|
virtual u16 allocateUnknownNodeId(const std::string &name);
|
||||||
virtual ISoundManager* getSoundManager();
|
virtual ISoundManager* getSoundManager();
|
||||||
virtual MtEventManager* getEventManager();
|
virtual MtEventManager* getEventManager();
|
||||||
|
virtual bool checkLocalPrivilege(const std::string &priv)
|
||||||
|
{ return checkPrivilege(priv); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -392,9 +387,10 @@ private:
|
|||||||
std::map<int, s32> m_sounds_client_to_server;
|
std::map<int, s32> m_sounds_client_to_server;
|
||||||
// And relations to objects
|
// And relations to objects
|
||||||
std::map<int, u16> m_sounds_to_objects;
|
std::map<int, u16> m_sounds_to_objects;
|
||||||
};
|
|
||||||
|
|
||||||
#endif // !SERVER
|
// Privileges
|
||||||
|
std::set<std::string> m_privileges;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // !CLIENT_HEADER
|
#endif // !CLIENT_HEADER
|
||||||
|
|
||||||
|
@ -50,9 +50,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
PROTOCOL_VERSION 9:
|
PROTOCOL_VERSION 9:
|
||||||
ContentFeatures and NodeDefManager use a different serialization
|
ContentFeatures and NodeDefManager use a different serialization
|
||||||
format; better for future version cross-compatibility
|
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
|
#define PROTOCOL_ID 0x4f457403
|
||||||
|
|
||||||
@ -291,6 +295,15 @@ enum ToClientCommand
|
|||||||
u16 command
|
u16 command
|
||||||
s32 sound_id
|
s32 sound_id
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
TOCLIENT_PRIVILEGES = 0x41,
|
||||||
|
/*
|
||||||
|
u16 command
|
||||||
|
u16 number of privileges
|
||||||
|
for each privilege
|
||||||
|
u16 len
|
||||||
|
u8[len] privilege
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ToServerCommand
|
enum ToServerCommand
|
||||||
|
@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "content_cso.h"
|
#include "content_cso.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
|
#include "localplayer.h"
|
||||||
class Settings;
|
class Settings;
|
||||||
struct ToolCapabilities;
|
struct ToolCapabilities;
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
#ifndef SERVER
|
#ifndef SERVER
|
||||||
#include "clientmap.h"
|
#include "clientmap.h"
|
||||||
|
#include "localplayer.h"
|
||||||
#endif
|
#endif
|
||||||
#include "daynightratio.h"
|
#include "daynightratio.h"
|
||||||
|
|
||||||
@ -1888,7 +1889,8 @@ void ClientEnvironment::step(float dtime)
|
|||||||
stepTimeOfDay(dtime);
|
stepTimeOfDay(dtime);
|
||||||
|
|
||||||
// Get some settings
|
// 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
|
// Get local player
|
||||||
LocalPlayer *lplayer = getLocalPlayer();
|
LocalPlayer *lplayer = getLocalPlayer();
|
||||||
|
@ -1636,6 +1636,8 @@ void the_game(
|
|||||||
g_settings->set("free_move","true");
|
g_settings->set("free_move","true");
|
||||||
statustext = L"free_move enabled";
|
statustext = L"free_move enabled";
|
||||||
statustext_time = 0;
|
statustext_time = 0;
|
||||||
|
if(!client.checkPrivilege("fly"))
|
||||||
|
statustext += L" (note: no 'fly' privilege)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
|
else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
|
||||||
@ -1651,6 +1653,8 @@ void the_game(
|
|||||||
g_settings->set("fast_move","true");
|
g_settings->set("fast_move","true");
|
||||||
statustext = L"fast_move enabled";
|
statustext = L"fast_move enabled";
|
||||||
statustext_time = 0;
|
statustext_time = 0;
|
||||||
|
if(!client.checkPrivilege("fast"))
|
||||||
|
statustext += L" (note: no 'fast' privilege)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
|
else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
|
||||||
|
@ -51,9 +51,14 @@ public:
|
|||||||
// Used for keeping track of names/ids of unknown nodes
|
// Used for keeping track of names/ids of unknown nodes
|
||||||
virtual u16 allocateUnknownNodeId(const std::string &name)=0;
|
virtual u16 allocateUnknownNodeId(const std::string &name)=0;
|
||||||
|
|
||||||
|
// Only usable on the client
|
||||||
virtual ISoundManager* getSoundManager()=0;
|
virtual ISoundManager* getSoundManager()=0;
|
||||||
virtual MtEventManager* getEventManager()=0;
|
virtual MtEventManager* getEventManager()=0;
|
||||||
|
|
||||||
|
// Used on the client
|
||||||
|
virtual bool checkLocalPrivilege(const std::string &priv)
|
||||||
|
{ return false; }
|
||||||
|
|
||||||
// Shorthands
|
// Shorthands
|
||||||
IItemDefManager* idef(){return getItemDefManager();}
|
IItemDefManager* idef(){return getItemDefManager();}
|
||||||
INodeDefManager* ndef(){return getNodeDefManager();}
|
INodeDefManager* ndef(){return getNodeDefManager();}
|
||||||
|
638
src/localplayer.cpp
Normal file
638
src/localplayer.cpp
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
101
src/localplayer.h
Normal file
101
src/localplayer.h
Normal file
@ -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
|
||||||
|
|
622
src/player.cpp
622
src/player.cpp
@ -18,20 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "map.h"
|
|
||||||
#include "connection.h"
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "utility.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 "gamedef.h"
|
||||||
#include "event.h"
|
#include "connection.h" // PEER_ID_INEXISTENT
|
||||||
|
#include "settings.h"
|
||||||
#include "content_sao.h"
|
#include "content_sao.h"
|
||||||
|
|
||||||
Player::Player(IGameDef *gamedef):
|
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
|
RemotePlayer
|
||||||
*/
|
*/
|
||||||
|
80
src/player.h
80
src/player.h
@ -20,14 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#ifndef PLAYER_HEADER
|
#ifndef PLAYER_HEADER
|
||||||
#define PLAYER_HEADER
|
#define PLAYER_HEADER
|
||||||
|
|
||||||
#include "common_irrlicht.h"
|
#include "irrlichttypes.h"
|
||||||
#include "inventory.h"
|
#include "inventory.h"
|
||||||
|
|
||||||
#define PLAYERNAME_SIZE 20
|
#define PLAYERNAME_SIZE 20
|
||||||
|
|
||||||
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
|
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
|
||||||
|
|
||||||
|
|
||||||
class Map;
|
class Map;
|
||||||
class IGameDef;
|
class IGameDef;
|
||||||
struct CollisionInfo;
|
struct CollisionInfo;
|
||||||
@ -167,83 +166,6 @@ protected:
|
|||||||
v3f m_position;
|
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
|
Player on the server
|
||||||
*/
|
*/
|
||||||
|
@ -3985,6 +3985,16 @@ static int l_get_password_hash(lua_State *L)
|
|||||||
return 1;
|
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 [] = {
|
static const struct luaL_Reg minetest_f [] = {
|
||||||
{"debug", l_debug},
|
{"debug", l_debug},
|
||||||
{"log", l_log},
|
{"log", l_log},
|
||||||
@ -4006,6 +4016,7 @@ static const struct luaL_Reg minetest_f [] = {
|
|||||||
{"sound_stop", l_sound_stop},
|
{"sound_stop", l_sound_stop},
|
||||||
{"is_singleplayer", l_is_singleplayer},
|
{"is_singleplayer", l_is_singleplayer},
|
||||||
{"get_password_hash", l_get_password_hash},
|
{"get_password_hash", l_get_password_hash},
|
||||||
|
{"notify_authentication_modified", l_notify_authentication_modified},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2194,13 +2194,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
// Send node definitions
|
// Send node definitions
|
||||||
SendNodeDef(m_con, peer_id, m_nodedef);
|
SendNodeDef(m_con, peer_id, m_nodedef);
|
||||||
|
|
||||||
// Send texture announcement
|
// Send media announcement
|
||||||
sendMediaAnnouncement(peer_id);
|
sendMediaAnnouncement(peer_id);
|
||||||
|
|
||||||
// Send player info to all players
|
// Send privileges
|
||||||
//SendPlayerInfos();
|
SendPlayerPrivileges(peer_id);
|
||||||
|
|
||||||
// Send inventory to player
|
// Send inventory
|
||||||
UpdateCrafting(peer_id);
|
UpdateCrafting(peer_id);
|
||||||
SendInventory(peer_id);
|
SendInventory(peer_id);
|
||||||
|
|
||||||
@ -3544,6 +3544,28 @@ void Server::SendMovePlayer(u16 peer_id)
|
|||||||
m_con.Send(peer_id, 0, data, true);
|
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,
|
s32 Server::playSound(const SimpleSoundSpec &spec,
|
||||||
const ServerSoundParams ¶ms)
|
const ServerSoundParams ¶ms)
|
||||||
{
|
{
|
||||||
@ -4286,6 +4308,23 @@ bool Server::checkPriv(const std::string &name, const std::string &priv)
|
|||||||
return (privs.count(priv) != 0);
|
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
|
// Saves g_settings to configpath given at initialization
|
||||||
void Server::saveConfig()
|
void Server::saveConfig()
|
||||||
{
|
{
|
||||||
|
@ -502,6 +502,7 @@ public:
|
|||||||
// Envlock + conlock
|
// Envlock + conlock
|
||||||
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
|
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
|
||||||
bool checkPriv(const std::string &name, const std::string &priv);
|
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
|
// Saves g_settings to configpath given at initialization
|
||||||
void saveConfig();
|
void saveConfig();
|
||||||
@ -592,11 +593,12 @@ private:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Envlock and conlock should be locked when calling these
|
// Envlock and conlock should be locked when calling these
|
||||||
void SendMovePlayer(u16 peer_id);
|
|
||||||
void SendInventory(u16 peer_id);
|
void SendInventory(u16 peer_id);
|
||||||
void SendChatMessage(u16 peer_id, const std::wstring &message);
|
void SendChatMessage(u16 peer_id, const std::wstring &message);
|
||||||
void BroadcastChatMessage(const std::wstring &message);
|
void BroadcastChatMessage(const std::wstring &message);
|
||||||
void SendPlayerHP(u16 peer_id);
|
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.
|
Send a node removal/addition event to all clients except ignore_id.
|
||||||
Additionally, if far_players!=NULL, players further away than
|
Additionally, if far_players!=NULL, players further away than
|
||||||
|
Loading…
Reference in New Issue
Block a user