Improved server commands and added player permissions.
authorCiaran Gultnieks <ciaran@ciarang.com>
Mon, 16 May 2011 09:41:19 +0000 (10:41 +0100)
committerCiaran Gultnieks <ciaran@ciarang.com>
Mon, 16 May 2011 09:41:19 +0000 (10:41 +0100)
--HG--
extra : rebase_source : 178fe08f10b7de3ebaba088bd24faad795114216

src/CMakeLists.txt
src/player.cpp
src/player.h
src/server.cpp
src/server.h
src/servercommand.cpp [new file with mode: 0644]
src/servercommand.h [new file with mode: 0644]
src/utility.h

index 8e6f54303b76106f91362ddeabc45bd68a803eff..42260b3ae439d3fc402e4471eef2be3eadf7898e 100644 (file)
@@ -69,6 +69,7 @@ set(common_SRCS
        connection.cpp
        environment.cpp
        server.cpp
+       servercommand.cpp
        socket.cpp
        mapblock.cpp
        mapsector.cpp
index 64780de75d7c2402d185b06b1e9362a0d57a4dbf..a6ddeee64464dc6b50f07c3ebdbd26a6bfd847cd 100644 (file)
@@ -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);
 }
index f70b52fe737453a6741a8ca339de8f2f8148da27..778bb54b312534b04eeb6cda872722377f061322 100644 (file)
@@ -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;
+       }
+
 };
 
 /*
index b5a38aa06b6ec34f946337bd920bc9f1a09eecb8..f77b4f3c641894aedc8457d329746f0c9c839ff3 100644 (file)
@@ -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
                {
index 4603f98ed5eed3b242c10d27ab8f2053af4d5928..d8b47aef9f96396471731bfb73a9107ab2295f5a 100644 (file)
@@ -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 (file)
index 0000000..fa841a1
--- /dev/null
@@ -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 (file)
index 0000000..01efcae
--- /dev/null
@@ -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
+
+
index 12d732bea8d03ffca666787b8730ffc5efe860c3..326ebf16125f002ffe95a63b19dab05925332648 100644 (file)
@@ -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;