]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/server.cpp
Ctrl+C handling on POSIX, some commands for server and other tweaking
[dragonfireclient.git] / src / server.cpp
index 71a094e140e8d2ed5b736c3fdae310558b106a2c..31ebfacbb5d888294f6c09cb65351e38116f5866 100644 (file)
@@ -46,7 +46,12 @@ void * ServerThread::Thread()
        while(getRun())
        {
                try{
-                       m_server->AsyncRunStep();
+                       //TimeTaker timer("AsyncRunStep() + Receive()");
+
+                       {
+                               //TimeTaker timer("AsyncRunStep()");
+                               m_server->AsyncRunStep();
+                       }
                
                        //dout_server<<"Running m_server->Receive()"<<std::endl;
                        m_server->Receive();
@@ -166,37 +171,6 @@ void * EmergeThread::Thread()
                                                only_from_disk,
                                                changed_blocks,
                                                lighting_invalidated_blocks);
-
-#if 0
-                               /*
-                                       EXPERIMENTAL: Create a few other blocks too
-                               */
-                               
-                               map.emergeBlock(
-                                               p + v3s16(0,1,0),
-                                               only_from_disk,
-                                               changed_blocks,
-                                               lighting_invalidated_blocks);
-
-                               map.emergeBlock(
-                                               p + v3s16(0,-1,0),
-                                               only_from_disk,
-                                               changed_blocks,
-                                               lighting_invalidated_blocks);
-#if 0
-                               map.emergeBlock(
-                                               p + v3s16(0,2,0),
-                                               only_from_disk,
-                                               changed_blocks,
-                                               lighting_invalidated_blocks);
-
-                               map.emergeBlock(
-                                               p + v3s16(0,-2,0),
-                                               only_from_disk,
-                                               changed_blocks,
-                                               lighting_invalidated_blocks);
-#endif
-#endif
                        }
 
                        // If it is a dummy, block was not found on disk
@@ -962,8 +936,8 @@ void PlayerInfo::PrintLine(std::ostream *s)
 {
        (*s)<<id<<": ";
        (*s)<<"\""<<name<<"\" ("
-                       <<position.X<<","<<position.Y
-                       <<","<<position.Z<<") ";
+                       <<(position.X/10)<<","<<(position.Y/10)
+                       <<","<<(position.Z/10)<<") ";
        address.print(s);
        (*s)<<" avg_rtt="<<avg_rtt;
        (*s)<<std::endl;
@@ -998,7 +972,8 @@ Server::Server(
        m_time_counter(0),
        m_time_of_day_send_timer(0),
        m_uptime(0),
-       m_mapsavedir(mapsavedir)
+       m_mapsavedir(mapsavedir),
+       m_shutdown_requested(false)
 {
        //m_flowwater_timer = 0.0;
        m_liquid_transform_timer = 0.0;
@@ -1018,28 +993,62 @@ Server::Server(
 
 Server::~Server()
 {
-       // Save players
+       /*
+               Send shutdown message
+       */
+       {
+               JMutexAutoLock conlock(m_con_mutex);
+               
+               std::wstring line = L"*** Server shutting down";
+
+               /*
+                       Send the message to clients
+               */
+               for(core::map<u16, RemoteClient*>::Iterator
+                       i = m_clients.getIterator();
+                       i.atEnd() == false; i++)
+               {
+                       // Get client and check that it is valid
+                       RemoteClient *client = i.getNode()->getValue();
+                       assert(client->peer_id == i.getNode()->getKey());
+                       if(client->serialization_version == SER_FMT_VER_INVALID)
+                               continue;
+
+                       SendChatMessage(client->peer_id, line);
+               }
+       }
+
+       /*
+               Save players
+       */
        m_env.serializePlayers(m_mapsavedir);
        
-       // Stop threads
+       /*
+               Stop threads
+       */
        stop();
-
-       JMutexAutoLock clientslock(m_con_mutex);
-
-       for(core::map<u16, RemoteClient*>::Iterator
-               i = m_clients.getIterator();
-               i.atEnd() == false; i++)
+       
+       /*
+               Delete clients
+       */
        {
-               /*// Delete player
-               // NOTE: These are removed by env destructor
+               JMutexAutoLock clientslock(m_con_mutex);
+
+               for(core::map<u16, RemoteClient*>::Iterator
+                       i = m_clients.getIterator();
+                       i.atEnd() == false; i++)
                {
-                       u16 peer_id = i.getNode()->getKey();
-                       JMutexAutoLock envlock(m_env_mutex);
-                       m_env.removePlayer(peer_id);
-               }*/
-               
-               // Delete client
-               delete i.getNode()->getValue();
+                       /*// Delete player
+                       // NOTE: These are removed by env destructor
+                       {
+                               u16 peer_id = i.getNode()->getKey();
+                               JMutexAutoLock envlock(m_env_mutex);
+                               m_env.removePlayer(peer_id);
+                       }*/
+                       
+                       // Delete client
+                       delete i.getNode()->getValue();
+               }
        }
 }
 
@@ -1395,20 +1404,23 @@ void Server::AsyncRunStep()
 
                        JMutexAutoLock lock(m_env_mutex);
 
-                       // Save only changed parts
-                       m_env.getMap().save(true);
-
-                       // Delete unused sectors
-                       u32 deleted_count = m_env.getMap().deleteUnusedSectors(
-                                       g_settings.getFloat("server_unload_unused_sectors_timeout"));
-                       if(deleted_count > 0)
+                       if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
                        {
-                               dout_server<<"Server: Unloaded "<<deleted_count
-                                               <<" sectors from memory"<<std::endl;
-                       }
+                               // Save only changed parts
+                               m_env.getMap().save(true);
 
-                       // Save players
-                       m_env.serializePlayers(m_mapsavedir);
+                               // Delete unused sectors
+                               u32 deleted_count = m_env.getMap().deleteUnusedSectors(
+                                               g_settings.getFloat("server_unload_unused_sectors_timeout"));
+                               if(deleted_count > 0)
+                               {
+                                       dout_server<<"Server: Unloaded "<<deleted_count
+                                                       <<" sectors from memory"<<std::endl;
+                               }
+
+                               // Save players
+                               m_env.serializePlayers(m_mapsavedir);
+                       }
                }
        }
 }
@@ -1614,37 +1626,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        m_time_of_day.get());
                        m_con.Send(peer->id, 0, data, true);
                }
-
+               
                // Send information about server to player in chat
-               {
-                       std::wostringstream os(std::ios_base::binary);
-                       os<<L"# Server: ";
-                       // Uptime
-                       os<<L"uptime="<<m_uptime.get();
-                       // Information about clients
-                       os<<L", clients={";
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
-                       {
-                               // Get client and check that it is valid
-                               RemoteClient *client = i.getNode()->getValue();
-                               assert(client->peer_id == i.getNode()->getKey());
-                               if(client->serialization_version == SER_FMT_VER_INVALID)
-                                       continue;
-                               // Get player
-                               Player *player = m_env.getPlayer(client->peer_id);
-                               // Get name of player
-                               std::wstring name = L"unknown";
-                               if(player != NULL)
-                                       name = narrow_to_wide(player->getName());
-                               // Add name to information string
-                               os<<name<<L",";
-                       }
-                       os<<L"}";
-                       // Send message
-                       SendChatMessage(peer_id, os.str());
-               }
+               SendChatMessage(peer_id, getStatusString());
                
                // Send information about joining in chat
                {
@@ -1849,6 +1833,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        0: start digging
                        1: place block
                        2: stop digging (all parameters ignored)
+                       3: digging completed
                */
                u8 action = readU8(&data[2]);
                v3s16 p_under;
@@ -1991,23 +1976,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                }
 
                                /*
-                                       Add digged item to inventory
+                                       Add dug item to inventory
                                */
 
                                InventoryItem *item = NULL;
 
                                if(mineral != MINERAL_NONE)
                                        item = getDiggedMineralItem(mineral);
-
+                               
+                               // If not mineral
                                if(item == NULL)
-                                       item = new MaterialItem(material, 1);
-
-                               player->inventory.addItem("main", item);
+                               {
+                                       std::string &dug_s = content_features(material).dug_item;
+                                       if(dug_s != "")
+                                       {
+                                               std::istringstream is(dug_s, std::ios::binary);
+                                               item = InventoryItem::deSerialize(is);
+                                       }
+                               }
+                               
+                               if(item != NULL)
+                               {
+                                       // Add a item to inventory
+                                       player->inventory.addItem("main", item);
 
-                               /*
-                                       Send inventory
-                               */
-                               SendInventory(player->peer_id);
+                                       // Send inventory
+                                       SendInventory(player->peer_id);
+                               }
                        }
 
                        /*
@@ -2476,29 +2471,115 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                // Get player name of this client
                std::wstring name = narrow_to_wide(player->getName());
-
-               std::wstring line = std::wstring(L"<")+name+L"> "+message;
                
-               dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
-
-               /*
-                       Send the message to all other clients
-               */
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++)
+               // Line to send to players
+               std::wstring line;
+               // Whether to send to the player that sent the line
+               bool send_to_sender = false;
+               // Whether to send to other players
+               bool send_to_others = false;
+               
+               // Parse commands
+               std::wstring commandprefix = L"/#";
+               if(message.substr(0, commandprefix.size()) == commandprefix)
+               {
+                       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 ";
+                               }
+                               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;
+                               }
+                       }
+                       
+                       if(valid_command == false)
+                       {
+                               line += L"-!- Invalid command: " + message;
+                               send_to_sender = true;
+                       }
+               }
+               else
                {
-                       // Get client and check that it is valid
-                       RemoteClient *client = i.getNode()->getValue();
-                       assert(client->peer_id == i.getNode()->getKey());
-                       if(client->serialization_version == SER_FMT_VER_INVALID)
-                               continue;
+                       line += L"<";
+                       /*if(is_operator)
+                               line += L"@";*/
+                       line += name;
+                       line += L"> ";
+                       line += message;
+                       send_to_others = true;
+               }
+               
+               if(line != L"")
+               {
+                       dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
 
-                       // Don't send if it's the same one
-                       if(peer_id == client->peer_id)
-                               continue;
+                       /*
+                               Send the message to clients
+                       */
+                       for(core::map<u16, RemoteClient*>::Iterator
+                               i = m_clients.getIterator();
+                               i.atEnd() == false; i++)
+                       {
+                               // Get client and check that it is valid
+                               RemoteClient *client = i.getNode()->getValue();
+                               assert(client->peer_id == i.getNode()->getKey());
+                               if(client->serialization_version == SER_FMT_VER_INVALID)
+                                       continue;
 
-                       SendChatMessage(client->peer_id, line);
+                               // Filter recipient
+                               bool sender_selected = (peer_id == client->peer_id);
+                               if(sender_selected == true && send_to_sender == false)
+                                       continue;
+                               if(sender_selected == false && send_to_others == false)
+                                       continue;
+
+                               SendChatMessage(client->peer_id, line);
+                       }
                }
        }
        else
@@ -2595,6 +2676,7 @@ core::list<PlayerInfo> Server::getPlayerInfo()
        return list;
 }
 
+
 void Server::peerAdded(con::Peer *peer)
 {
        DSTACK(__FUNCTION_NAME);
@@ -2668,6 +2750,7 @@ void Server::SendPlayerInfos()
                                "peer_id="<<player->peer_id<<std::endl;*/
                
                writeU16(&data[start], player->peer_id);
+               memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
                snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
                start += 2+PLAYERNAME_SIZE;
        }
@@ -3034,6 +3117,8 @@ void Server::SendBlocks(float dtime)
 
        JMutexAutoLock envlock(m_env_mutex);
 
+       //TimeTaker timer("Server::SendBlocks");
+
        core::array<PrioritySortedBlockTransfer> queue;
 
        s32 total_sending = 0;
@@ -3101,6 +3186,39 @@ RemoteClient* Server::getClient(u16 peer_id)
        return n->getValue();
 }
 
+std::wstring Server::getStatusString()
+{
+       std::wostringstream os(std::ios_base::binary);
+       os<<L"# Server: ";
+       // Uptime
+       os<<L"uptime="<<m_uptime.get();
+       // Information about clients
+       os<<L", clients={";
+       for(core::map<u16, RemoteClient*>::Iterator
+               i = m_clients.getIterator();
+               i.atEnd() == false; i++)
+       {
+               // Get client and check that it is valid
+               RemoteClient *client = i.getNode()->getValue();
+               assert(client->peer_id == i.getNode()->getKey());
+               if(client->serialization_version == SER_FMT_VER_INVALID)
+                       continue;
+               // Get player
+               Player *player = m_env.getPlayer(client->peer_id);
+               // Get name of player
+               std::wstring name = L"unknown";
+               if(player != NULL)
+                       name = narrow_to_wide(player->getName());
+               // Add name to information string
+               os<<name<<L",";
+       }
+       os<<L"}";
+       if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
+               os<<" WARNING: Map saving is disabled."<<std::endl;
+       return os.str();
+}
+
+
 void setCreativeInventory(Player *player)
 {
        player->resetInventory();
@@ -3204,7 +3322,7 @@ Player *Server::emergePlayer(const char *name, const char *password,
 #if 1
                player->setPosition(intToFloat(v3s16(
                                0,
-                               40, //64,
+                               45, //64,
                                0
                )));
 #endif
@@ -3469,11 +3587,11 @@ void Server::handlePeerChanges()
        }
 }
 
-void dedicated_server_loop(Server &server)
+void dedicated_server_loop(Server &server, bool &kill)
 {
        DSTACK(__FUNCTION_NAME);
        
-       std::cout<<std::endl;
+       std::cout<<DTIME<<std::endl;
        std::cout<<"========================"<<std::endl;
        std::cout<<"Running dedicated server"<<std::endl;
        std::cout<<"========================"<<std::endl;
@@ -3486,6 +3604,12 @@ void dedicated_server_loop(Server &server)
                sleep_ms(30);
                server.step(0.030);
 
+               if(server.getShutdownRequested() || kill)
+               {
+                       std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
+                       break;
+               }
+
                static int counter = 0;
                counter--;
                if(counter <= 0)