diff options
author | Ciaran Gultnieks <ciaran@ciarang.com> | 2011-05-16 10:41:19 +0100 |
---|---|---|
committer | Ciaran Gultnieks <ciaran@ciarang.com> | 2011-05-16 10:41:19 +0100 |
commit | 248d7c8469f8cb37406ea0ce56d0945e38334cfb (patch) | |
tree | df8c2ad03a674f8d7c08d6af01ddb3f2a7e7b689 | |
parent | dadac0e79f96b112947a50f37b39ad9677e870d0 (diff) | |
download | minetest-248d7c8469f8cb37406ea0ce56d0945e38334cfb.tar.gz minetest-248d7c8469f8cb37406ea0ce56d0945e38334cfb.tar.bz2 minetest-248d7c8469f8cb37406ea0ce56d0945e38334cfb.zip |
Improved server commands and added player permissions.
--HG--
extra : rebase_source : 178fe08f10b7de3ebaba088bd24faad795114216
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/player.cpp | 9 | ||||
-rw-r--r-- | src/player.h | 72 | ||||
-rw-r--r-- | src/server.cpp | 91 | ||||
-rw-r--r-- | src/server.h | 23 | ||||
-rw-r--r-- | src/servercommand.cpp | 233 | ||||
-rw-r--r-- | src/servercommand.h | 76 | ||||
-rw-r--r-- | src/utility.h | 19 |
8 files changed, 454 insertions, 70 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e6f54303..42260b3ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,6 +69,7 @@ set(common_SRCS connection.cpp environment.cpp server.cpp + servercommand.cpp socket.cpp mapblock.cpp mapsector.cpp diff --git a/src/player.cpp b/src/player.cpp index 64780de75..a6ddeee64 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -34,7 +34,8 @@ Player::Player(): m_pitch(0), m_yaw(0), m_speed(0,0,0), - m_position(0,0,0) + m_position(0,0,0), + privs(PRIV_DEFAULT) { updateName("<not set>"); resetInventory(); @@ -100,6 +101,7 @@ void Player::serialize(std::ostream &os) args.setV3F("position", m_position); args.setBool("craftresult_is_preview", craftresult_is_preview); args.setS32("hp", hp); + args.setU64("privs", privs); args.writeLines(os); @@ -141,6 +143,11 @@ void Player::deSerialize(std::istream &is) }catch(SettingNotFoundException &e){ hp = 20; } + try{ + privs = args.getU64("privs"); + }catch(SettingNotFoundException &e){ + privs = PRIV_DEFAULT; + } inventory.deSerialize(is); } diff --git a/src/player.h b/src/player.h index f70b52fe7..778bb54b3 100644 --- a/src/player.h +++ b/src/player.h @@ -28,11 +28,29 @@ with this program; if not, write to the Free Software Foundation, Inc., #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 Player { public: + + Player(); virtual ~Player(); @@ -123,6 +141,9 @@ public: u16 hp; + // Player's privileges - a bitmaps of PRIV_xxxx. + u64 privs; + u16 peer_id; protected: @@ -131,6 +152,57 @@ protected: f32 m_yaw; v3f m_speed; 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; + } + }; /* diff --git a/src/server.cpp b/src/server.cpp index b5a38aa06..f77b4f3c6 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "materials.h" #include "mineral.h" #include "config.h" +#include "servercommand.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) @@ -1994,6 +1995,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 13) return; + if((player->privs & PRIV_BUILD) == 0) + return; + /* [0] u16 command [2] u8 button (0=left, 1=right) @@ -2075,6 +2079,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 7) return; + if((player->privs & PRIV_BUILD) == 0) + return; + /* length: 7 [0] u16 command @@ -2167,6 +2174,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { if(datasize < 17) return; + if((player->privs & PRIV_BUILD) == 0) + return; /* length: 17 [0] u16 command @@ -2615,6 +2624,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) #endif else if(command == TOSERVER_SIGNTEXT) { + if((player->privs & PRIV_BUILD) == 0) + return; /* u16 command v3s16 blockpos @@ -2672,6 +2683,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_SIGNNODETEXT) { + if((player->privs & PRIV_BUILD) == 0) + return; /* u16 command v3s16 p @@ -2853,71 +2866,19 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) line += L"Server: "; message = message.substr(commandprefix.size()); - // Get player name as narrow string - std::string name_s = player->getName(); - // Convert message to narrow string - std::string message_s = wide_to_narrow(message); - // Operator is the single name defined in config. - std::string operator_name = g_settings.get("name"); - bool is_operator = (operator_name != "" && - wide_to_narrow(name) == operator_name); - bool valid_command = false; - if(message_s == "help") - { - line += L"-!- Available commands: "; - 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; - } + + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + this, + &m_env, + player + ); + + line += ServerCommand::processCommand(ctx); + send_to_sender = ctx->flags & 1; + send_to_others = ctx->flags & 2; + delete ctx; + } else { diff --git a/src/server.h b/src/server.h index 4603f98ed..d8b47aef9 100644 --- a/src/server.h +++ b/src/server.h @@ -387,6 +387,12 @@ public: 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() { return m_shutdown_requested.get(); @@ -405,6 +411,19 @@ public: Inventory* getInventory(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: // Virtual methods from con::PeerHandler. @@ -429,7 +448,6 @@ private: void SendChatMessage(u16 peer_id, const std::wstring &message); void BroadcastChatMessage(const std::wstring &message); void SendPlayerHP(Player *player); - void SendMovePlayer(Player *player); /* Send a node removal/addition event to all clients except ignore_id. Additionally, if far_players!=NULL, players further away than @@ -455,9 +473,6 @@ private: // When called, connection mutex should be locked RemoteClient* getClient(u16 peer_id); - // Connection must be locked when called - std::wstring getStatusString(); - /* Get a player from memory or creates one. If player is already connected, return NULL diff --git a/src/servercommand.cpp b/src/servercommand.cpp new file mode 100644 index 000000000..fa841a1bb --- /dev/null +++ b/src/servercommand.cpp @@ -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."; +} + diff --git a/src/servercommand.h b/src/servercommand.h new file mode 100644 index 000000000..01efcae06 --- /dev/null +++ b/src/servercommand.h @@ -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 + + diff --git a/src/utility.h b/src/utility.h index 12d732bea..326ebf161 100644 --- a/src/utility.h +++ b/src/utility.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <fstream> #include <string> #include <sstream> +#include <vector> #include <jthread.h> #include <jmutex.h> #include <jmutexautolock.h> @@ -731,6 +732,19 @@ inline std::string wide_to_narrow(const std::wstring& wcs) 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. wraps degrees to the range of -360...360 @@ -791,6 +805,11 @@ inline s32 stoi(std::string s) 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) { float f; |