Improved server commands and added player permissions.

--HG--
extra : rebase_source : 178fe08f10b7de3ebaba088bd24faad795114216
This commit is contained in:
Ciaran Gultnieks 2011-05-16 10:41:19 +01:00
parent dadac0e79f
commit 248d7c8469
8 changed files with 454 additions and 70 deletions

@ -69,6 +69,7 @@ set(common_SRCS
connection.cpp connection.cpp
environment.cpp environment.cpp
server.cpp server.cpp
servercommand.cpp
socket.cpp socket.cpp
mapblock.cpp mapblock.cpp
mapsector.cpp mapsector.cpp

@ -34,7 +34,8 @@ Player::Player():
m_pitch(0), m_pitch(0),
m_yaw(0), m_yaw(0),
m_speed(0,0,0), m_speed(0,0,0),
m_position(0,0,0) m_position(0,0,0),
privs(PRIV_DEFAULT)
{ {
updateName("<not set>"); updateName("<not set>");
resetInventory(); resetInventory();
@ -100,6 +101,7 @@ void Player::serialize(std::ostream &os)
args.setV3F("position", m_position); args.setV3F("position", m_position);
args.setBool("craftresult_is_preview", craftresult_is_preview); args.setBool("craftresult_is_preview", craftresult_is_preview);
args.setS32("hp", hp); args.setS32("hp", hp);
args.setU64("privs", privs);
args.writeLines(os); args.writeLines(os);
@ -141,6 +143,11 @@ void Player::deSerialize(std::istream &is)
}catch(SettingNotFoundException &e){ }catch(SettingNotFoundException &e){
hp = 20; hp = 20;
} }
try{
privs = args.getU64("privs");
}catch(SettingNotFoundException &e){
privs = PRIV_DEFAULT;
}
inventory.deSerialize(is); inventory.deSerialize(is);
} }

@ -28,11 +28,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.," #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_BUILD = 1; // Can build - i.e. modify the world
// (not enforced yet)
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn ,settings)
const u64 PRIV_DEFAULT = PRIV_BUILD;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
class Map; class Map;
class Player class Player
{ {
public: public:
Player(); Player();
virtual ~Player(); virtual ~Player();
@ -123,6 +141,9 @@ public:
u16 hp; u16 hp;
// Player's privileges - a bitmaps of PRIV_xxxx.
u64 privs;
u16 peer_id; u16 peer_id;
protected: protected:
@ -131,6 +152,57 @@ protected:
f32 m_yaw; f32 m_yaw;
v3f m_speed; v3f m_speed;
v3f m_position; v3f m_position;
public:
// Converst a prvileges value into a human-readable string,
// with each component separated by a comma.
static std::wstring privsToString(u64 privs)
{
std::wostringstream os(std::ios_base::binary);
if(privs & PRIV_BUILD)
os<<L"build,";
if(privs & PRIV_TELEPORT)
os<<L"teleport,";
if(privs & PRIV_SETTIME)
os<<L"settime,";
if(privs & PRIV_PRIVS)
os<<L"privs,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::wstring tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
static u64 stringToPrivs(std::wstring str)
{
u64 privs=0;
std::vector<std::wstring> pr;
pr=str_split(str, ',');
for(std::vector<std::wstring>::iterator i = pr.begin();
i != pr.end(); ++i)
{
if(*i == L"build")
privs |= PRIV_BUILD;
else if(*i == L"teleport")
privs |= PRIV_TELEPORT;
else if(*i == L"settime")
privs |= PRIV_SETTIME;
else if(*i == L"privs")
privs |= PRIV_PRIVS;
else
return PRIV_INVALID;
}
return privs;
}
}; };
/* /*

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "materials.h" #include "materials.h"
#include "mineral.h" #include "mineral.h"
#include "config.h" #include "config.h"
#include "servercommand.h"
#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
@ -1994,6 +1995,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(datasize < 13) if(datasize < 13)
return; return;
if((player->privs & PRIV_BUILD) == 0)
return;
/* /*
[0] u16 command [0] u16 command
[2] u8 button (0=left, 1=right) [2] u8 button (0=left, 1=right)
@ -2075,6 +2079,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(datasize < 7) if(datasize < 7)
return; return;
if((player->privs & PRIV_BUILD) == 0)
return;
/* /*
length: 7 length: 7
[0] u16 command [0] u16 command
@ -2167,6 +2174,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{ {
if(datasize < 17) if(datasize < 17)
return; return;
if((player->privs & PRIV_BUILD) == 0)
return;
/* /*
length: 17 length: 17
[0] u16 command [0] u16 command
@ -2615,6 +2624,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
#endif #endif
else if(command == TOSERVER_SIGNTEXT) else if(command == TOSERVER_SIGNTEXT)
{ {
if((player->privs & PRIV_BUILD) == 0)
return;
/* /*
u16 command u16 command
v3s16 blockpos v3s16 blockpos
@ -2672,6 +2683,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
else if(command == TOSERVER_SIGNNODETEXT) else if(command == TOSERVER_SIGNNODETEXT)
{ {
if((player->privs & PRIV_BUILD) == 0)
return;
/* /*
u16 command u16 command
v3s16 p v3s16 p
@ -2853,71 +2866,19 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
line += L"Server: "; line += L"Server: ";
message = message.substr(commandprefix.size()); message = message.substr(commandprefix.size());
// Get player name as narrow string
std::string name_s = player->getName(); ServerCommandContext *ctx = new ServerCommandContext(
// Convert message to narrow string str_split(message, L' '),
std::string message_s = wide_to_narrow(message); this,
// Operator is the single name defined in config. &m_env,
std::string operator_name = g_settings.get("name"); player
bool is_operator = (operator_name != "" && );
wide_to_narrow(name) == operator_name);
bool valid_command = false; line += ServerCommand::processCommand(ctx);
if(message_s == "help") send_to_sender = ctx->flags & 1;
{ send_to_others = ctx->flags & 2;
line += L"-!- Available commands: "; delete ctx;
line += L"status ";
if(is_operator)
{
line += L"shutdown setting time ";
}
else
{
}
send_to_sender = true;
valid_command = true;
}
else if(message_s == "status")
{
line = getStatusString();
send_to_sender = true;
valid_command = true;
}
else if(is_operator)
{
if(message_s == "shutdown")
{
dstream<<DTIME<<" Server: Operator requested shutdown."
<<std::endl;
m_shutdown_requested.set(true);
line += L"*** Server shutting down (operator request)";
send_to_sender = true;
valid_command = true;
}
else if(message_s.substr(0,8) == "setting ")
{
std::string confline = message_s.substr(8);
g_settings.parseConfigLine(confline);
line += L"-!- Setting changed.";
send_to_sender = true;
valid_command = true;
}
else if(message_s.substr(0,5) == "time ")
{
u32 time = stoi(message_s.substr(5));
m_time_of_day.set(time);
m_time_of_day_send_timer = 0;
line += L"-!- time_of_day changed.";
send_to_sender = true;
valid_command = true;
}
}
if(valid_command == false)
{
line += L"-!- Invalid command: " + message;
send_to_sender = true;
}
} }
else else
{ {

@ -387,6 +387,12 @@ public:
return time_to_daynight_ratio(m_time_of_day.get()); return time_to_daynight_ratio(m_time_of_day.get());
} }
void setTimeOfDay(u32 time)
{
m_time_of_day.set(time);
m_time_of_day_send_timer = 0;
}
bool getShutdownRequested() bool getShutdownRequested()
{ {
return m_shutdown_requested.get(); return m_shutdown_requested.get();
@ -405,6 +411,19 @@ public:
Inventory* getInventory(InventoryContext *c, std::string id); Inventory* getInventory(InventoryContext *c, std::string id);
void inventoryModified(InventoryContext *c, std::string id); void inventoryModified(InventoryContext *c, std::string id);
// Connection must be locked when called
std::wstring getStatusString();
void requestShutdown(void)
{
m_shutdown_requested.set(true);
}
// Envlock and conlock should be locked when calling this
void SendMovePlayer(Player *player);
private: private:
// Virtual methods from con::PeerHandler. // Virtual methods from con::PeerHandler.
@ -429,7 +448,6 @@ private:
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(Player *player); void SendPlayerHP(Player *player);
void SendMovePlayer(Player *player);
/* /*
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
@ -455,9 +473,6 @@ private:
// When called, connection mutex should be locked // When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id); RemoteClient* getClient(u16 peer_id);
// Connection must be locked when called
std::wstring getStatusString();
/* /*
Get a player from memory or creates one. Get a player from memory or creates one.
If player is already connected, return NULL If player is already connected, return NULL

233
src/servercommand.cpp Normal file

@ -0,0 +1,233 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.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 "servercommand.h"
#include "utility.h"
// Process a command sent from a client. The environment and connection
// should be locked when this is called.
// Returns a response message, to be dealt with according to the flags set
// in the context.
std::wstring ServerCommand::processCommand(ServerCommandContext *ctx)
{
std::wostringstream os(std::ios_base::binary);
ctx->flags = 1; // Default, unless we change it.
u64 privs = ctx->player->privs;
if(ctx->parms.size() == 0 || ctx->parms[0] == L"help")
{
os<<L"-!- Available commands: ";
os<<L"status privs ";
if(privs & PRIV_SERVER)
os<<L"shutdown setting ";
if(privs & PRIV_SETTIME)
os<<L" time";
if(privs & PRIV_TELEPORT)
os<<L" teleport";
if(privs & PRIV_PRIVS)
os<<L" grant revoke";
}
else if(ctx->parms[0] == L"status")
{
cmd_status(os, ctx);
}
else if(ctx->parms[0] == L"privs")
{
cmd_privs(os, ctx);
}
else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke")
{
cmd_grantrevoke(os, ctx);
}
else if(ctx->parms[0] == L"time")
{
cmd_time(os, ctx);
}
else if(ctx->parms[0] == L"shutdown")
{
cmd_shutdown(os, ctx);
}
else if(ctx->parms[0] == L"setting")
{
cmd_setting(os, ctx);
}
else if(ctx->parms[0] == L"teleport")
{
cmd_teleport(os, ctx);
}
else
{
os<<L"-!- Invalid command: " + ctx->parms[0];
}
return os.str();
}
void ServerCommand::cmd_status(std::wostringstream &os,
ServerCommandContext *ctx)
{
os<<ctx->server->getStatusString();
}
void ServerCommand::cmd_privs(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() == 1)
{
os<<L"-!- " + Player::privsToString(ctx->player->privs);
return;
}
if((ctx->player->privs & PRIV_PRIVS) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(tp == NULL)
{
os<<L"-!- No such player";
return;
}
os<<L"-!- " + Player::privsToString(tp->privs);
}
void ServerCommand::cmd_grantrevoke(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() != 3)
{
os<<L"-!- Missing parameter";
return;
}
if((ctx->player->privs & PRIV_PRIVS) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
u64 newprivs = Player::stringToPrivs(ctx->parms[2]);
if(newprivs == PRIV_INVALID)
{
os<<L"-!- Invalid privileges specified";
return;
}
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(tp == NULL)
{
os<<L"-!- No such player";
return;
}
if(ctx->parms[0] == L"grant")
tp->privs |= newprivs;
else
tp->privs &= ~newprivs;
os<<L"-!- Privileges change to ";
os<<Player::privsToString(tp->privs);
}
void ServerCommand::cmd_time(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() != 2)
{
os<<L"-!- Missing parameter";
return;
}
if((ctx->player->privs & PRIV_SETTIME) ==0)
{
os<<L"-!- You don't have permission to do that";
return;
}
u32 time = stoi(wide_to_narrow(ctx->parms[1]));
ctx->server->setTimeOfDay(time);
os<<L"-!- time_of_day changed.";
}
void ServerCommand::cmd_shutdown(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->player->privs & PRIV_SERVER) ==0)
{
os<<L"-!- You don't have permission to do that";
return;
}
dstream<<DTIME<<" Server: Operator requested shutdown."
<<std::endl;
ctx->server->requestShutdown();
os<<L"*** Server shutting down (operator request)";
ctx->flags |= 2;
}
void ServerCommand::cmd_setting(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->player->privs & PRIV_SERVER) ==0)
{
os<<L"-!- You don't have permission to do that";
return;
}
std::string confline = wide_to_narrow(ctx->parms[1] + L" = " + ctx->parms[2]);
g_settings.parseConfigLine(confline);
os<< L"-!- Setting changed.";
}
void ServerCommand::cmd_teleport(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->player->privs & PRIV_TELEPORT) ==0)
{
os<<L"-!- You don't have permission to do that";
return;
}
if(ctx->parms.size() != 2)
{
os<<L"-!- Missing parameter";
return;
}
std::vector<std::wstring> coords = str_split(ctx->parms[1], L',');
if(coords.size() != 3)
{
os<<L"-!- You can only specify coordinates currently";
return;
}
v3f dest(stoi(coords[0])*10, stoi(coords[1])*10, stoi(coords[2])*10);
ctx->player->setPosition(dest);
ctx->server->SendMovePlayer(ctx->player);
os<< L"-!- Teleported.";
}

76
src/servercommand.h Normal file

@ -0,0 +1,76 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.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 SERVERCOMMAND_HEADER
#define SERVERCOMMAND_HEADER
#include <vector>
#include <sstream>
#include "common_irrlicht.h"
#include "player.h"
#include "server.h"
struct ServerCommandContext
{
std::vector<std::wstring> parms;
Server* server;
ServerEnvironment *env;
Player* player;
u32 flags;
ServerCommandContext(
std::vector<std::wstring> parms,
Server* server,
ServerEnvironment *env,
Player* player)
: parms(parms), server(server), env(env), player(player)
{
}
};
class ServerCommand
{
public:
static std::wstring processCommand(ServerCommandContext *ctx);
private:
static void cmd_status(std::wostringstream &os,
ServerCommandContext *ctx);
static void cmd_privs(std::wostringstream &os,
ServerCommandContext *ctx);
static void cmd_grantrevoke(std::wostringstream &os,
ServerCommandContext *ctx);
static void cmd_time(std::wostringstream &os,
ServerCommandContext *ctx);
static void cmd_shutdown(std::wostringstream &os,
ServerCommandContext *ctx);
static void cmd_setting(std::wostringstream &os,
ServerCommandContext *ctx);
static void cmd_teleport(std::wostringstream &os,
ServerCommandContext *ctx);
};
#endif

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <vector>
#include <jthread.h> #include <jthread.h>
#include <jmutex.h> #include <jmutex.h>
#include <jmutexautolock.h> #include <jmutexautolock.h>
@ -731,6 +732,19 @@ inline std::string wide_to_narrow(const std::wstring& wcs)
return *mbs; return *mbs;
} }
// Split a string using the given delimiter. Returns a vector containing
// the component parts.
inline std::vector<std::wstring> str_split(const std::wstring &str, wchar_t delimiter)
{
std::vector<std::wstring> parts;
std::wstringstream sstr(str);
std::wstring part;
while(std::getline(sstr, part, delimiter))
parts.push_back(part);
return parts;
}
/* /*
See test.cpp for example cases. See test.cpp for example cases.
wraps degrees to the range of -360...360 wraps degrees to the range of -360...360
@ -791,6 +805,11 @@ inline s32 stoi(std::string s)
return atoi(s.c_str()); return atoi(s.c_str());
} }
inline s32 stoi(std::wstring s)
{
return atoi(wide_to_narrow(s).c_str());
}
inline float stof(std::string s) inline float stof(std::string s)
{ {
float f; float f;