From d065bae323838734556de2693b6b004c98c95092 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 15 Feb 2011 16:11:24 +0200 Subject: [PATCH] Ctrl+C handling on POSIX, some commands for server and other tweaking --- doc/changelog.txt | 2 + minetest.conf.example | 2 + src/CMakeLists.txt | 6 + src/guiPauseMenu.cpp | 14 ++- src/main.cpp | 33 ++++- src/mapblock.cpp | 12 +- src/porting.cpp | 47 +++++++ src/porting.h | 11 +- src/server.cpp | 282 +++++++++++++++++++++++++++++++----------- src/server.h | 32 +++-- src/servermain.cpp | 18 +-- src/test.cpp | 2 +- src/utility.h | 34 ++++- 13 files changed, 384 insertions(+), 111 deletions(-) diff --git a/doc/changelog.txt b/doc/changelog.txt index 424f98bcf..a8722f1c9 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,5 +1,7 @@ Minetest-c55 changelog ---------------------- +This should contain all the major changes. +For minor stuff, refer to the commit log of the repository. 2011-02-14: - Created changelog.txt diff --git a/minetest.conf.example b/minetest.conf.example index 264d77e5a..37c93a5a2 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -31,6 +31,8 @@ #map-dir = /home/palle/custom_map +#operator_name = + #plants_amount = 1.0 #ravines_amount = 1.0 #coal_amount = 1.0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7fefc0238..6d9601c65 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,8 @@ if(RUN_IN_PLACE) add_definitions ( -DRUN_IN_PLACE ) endif(RUN_IN_PLACE) +set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++") + # Use cmake_config.h add_definitions ( -DUSE_CMAKE_CONFIG_H ) @@ -161,6 +163,10 @@ else() set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${WARNING_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops") set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall") + + if(USE_GPROF) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg") + endif() if(BUILD_SERVER) set_target_properties(minetestserver PROPERTIES diff --git a/src/guiPauseMenu.cpp b/src/guiPauseMenu.cpp index b6f913d6b..2d42fdb77 100644 --- a/src/guiPauseMenu.cpp +++ b/src/guiPauseMenu.cpp @@ -174,10 +174,18 @@ bool GUIPauseMenu::OnEvent(const SEvent& event) { if(event.EventType==EET_KEY_INPUT_EVENT) { - if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown) + if(event.KeyInput.PressedDown) { - quitMenu(); - return true; + if(event.KeyInput.Key==KEY_ESCAPE) + { + quitMenu(); + return true; + } + else if(event.KeyInput.Key==KEY_RETURN) + { + quitMenu(); + return true; + } } } if(event.EventType==EET_GUI_EVENT) diff --git a/src/main.cpp b/src/main.cpp index bcc8dc446..452030a24 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -268,7 +268,7 @@ Doing now (most important at the top): # maybe done * not done -=== Stuff to do before release +=== Fixmes * Make server find the spawning place from the real map data, not from the heightmap - But the changing borders of chunk have to be avoided, because @@ -277,15 +277,15 @@ Doing now (most important at the top): placement and transfer * only_from_disk might not work anymore - check and fix it. * Check the fixmes in the list above -* FIXME: Sneaking doesn't switch sneak node when moving sideways +* When sending blocks to the client, the server takes way too much + CPU time (20-30% for single player), find out what it is doing. + - Make a simple profiler === Making it more portable * Some MSVC: std::sto* are defined without a namespace and collide with the ones in utility.h -* On Kray's machine, the new find_library(XXF86VM_LIBRARY, Xxf86vm) - line doesn't find the library. -=== Stuff to do after release +=== Features * Make an "environment metafile" to store at least time of day * Move digging property stuff from material.{h,cpp} to mapnode.cpp... - Or maybe move content_features to material.{h,cpp}? @@ -567,7 +567,25 @@ struct TextDestChat : public TextDest } void gotText(std::wstring text) { + // Discard empty line + if(text == L"") + return; + + // Parse command (server command starts with "/#") + if(text[0] == L'/' && text[1] != L'#') + { + std::wstring reply = L"Local: "; + + reply += L"Local commands not yet supported. " + "Server prefix is \"/#\"."; + + m_client->addChatMessage(reply); + return; + } + + // Send to others m_client->sendChatMessage(text); + // Show locally m_client->addChatMessage(text); } @@ -1546,6 +1564,9 @@ int main(int argc, char *argv[]) DSTACK(__FUNCTION_NAME); + porting::signal_handler_init(); + bool &kill = *porting::signal_handler_killstatus(); + porting::initializePaths(); // Create user data directory fs::CreateDir(porting::path_userdata); @@ -1681,7 +1702,7 @@ int main(int argc, char *argv[]) server.start(port); // Run server - dedicated_server_loop(server); + dedicated_server_loop(server, kill); return 0; } diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 422d3b531..1dbbe5c4e 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -1771,11 +1771,11 @@ void MapBlock::serialize(std::ostream &os, u8 version) // First byte u8 flags = 0; if(is_underground) - flags |= 1; + flags |= 0x01; if(m_day_night_differs) - flags |= 2; + flags |= 0x02; if(m_lighting_expired) - flags |= 3; + flags |= 0x04; os.write((char*)&flags, 1); u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; @@ -1895,9 +1895,9 @@ void MapBlock::deSerialize(std::istream &is, u8 version) u8 flags; is.read((char*)&flags, 1); - is_underground = (flags & 1) ? true : false; - m_day_night_differs = (flags & 2) ? true : false; - m_lighting_expired = (flags & 3) ? true : false; + is_underground = (flags & 0x01) ? true : false; + m_day_night_differs = (flags & 0x02) ? true : false; + m_lighting_expired = (flags & 0x04) ? true : false; // Uncompress data std::ostringstream os(std::ios_base::binary); diff --git a/src/porting.cpp b/src/porting.cpp index 592636336..f92b291ac 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -29,6 +29,53 @@ with this program; if not, write to the Free Software Foundation, Inc., namespace porting { +/* + Signal handler (grabs Ctrl-C on POSIX systems) +*/ + +#if !defined(_WIN32) // POSIX + #include + +bool g_killed = false; + +void sigint_handler(int sig) +{ + if(g_killed == false) + { + dstream< -// Included for u64 and such +// Included for u32 and such #include "common_irrlicht.h" #include "debug.h" #include "constants.h" @@ -47,6 +47,15 @@ with this program; if not, write to the Free Software Foundation, Inc., namespace porting { +/* + Signal handler (grabs Ctrl-C on POSIX systems) +*/ + +void signal_handler_init(void); +// Returns a pointer to a bool. +// When the bool is true, program should quit. +bool * signal_handler_killstatus(void); + /* Path of static data directory. */ diff --git a/src/server.cpp b/src/server.cpp index dc72661ff..31ebfacbb 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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()"<Receive(); @@ -967,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; @@ -987,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::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::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::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(); + } } } @@ -1586,39 +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<::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<isSavingEnabled() == false) - os<<" WARNING: Map saving is disabled."<getName()); - - std::wstring line = std::wstring(L"<")+name+L"> "+message; - dstream<<"CHAT: "<::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) { - // 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"Server: "; - // Don't send if it's the same one - if(peer_id == client->peer_id) - continue; + 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< "; + line += message; + send_to_others = true; + } + + if(line != L"") + { + dstream<<"CHAT: "<peer_id, line); + /* + Send the message to clients + */ + for(core::map::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; + + // 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 @@ -2580,6 +2676,7 @@ core::list Server::getPlayerInfo() return list; } + void Server::peerAdded(con::Peer *peer) { DSTACK(__FUNCTION_NAME); @@ -3020,6 +3117,8 @@ void Server::SendBlocks(float dtime) JMutexAutoLock envlock(m_env_mutex); + //TimeTaker timer("Server::SendBlocks"); + core::array queue; s32 total_sending = 0; @@ -3087,6 +3186,39 @@ RemoteClient* Server::getClient(u16 peer_id) return n->getValue(); } +std::wstring Server::getStatusString() +{ + std::wostringstream os(std::ios_base::binary); + os<::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<isSavingEnabled() == false) + os<<" WARNING: Map saving is disabled."<resetInventory(); @@ -3455,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< data, bool reliable);*/ - - // Environment and Connection must be locked when called + // Environment and Connection must be locked when called void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver); - // Environment and Connection must be locked when called - //void SendSectorMeta(u16 peer_id, core::list ps, u8 ver); - + // + core::list getPlayerInfo(); u32 getDayNightRatio() @@ -412,7 +408,12 @@ public: else return 1000; } - + + bool getShutdownRequested() + { + return m_shutdown_requested.get(); + } + private: // Virtual methods from con::PeerHandler. @@ -432,7 +433,10 @@ 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 @@ -490,7 +494,7 @@ private: float m_time_of_day_send_timer; MutexedVariable m_uptime; - + enum PeerChangeType { PEER_ADDED, @@ -508,14 +512,18 @@ private: std::string m_mapsavedir; + MutexedVariable m_shutdown_requested; + friend class EmergeThread; friend class RemoteClient; }; /* - Runs a simple dedicated server loop + Runs a simple dedicated server loop. + + Shuts down when run is set to false. */ -void dedicated_server_loop(Server &server); +void dedicated_server_loop(Server &server, bool &run); #endif diff --git a/src/servermain.cpp b/src/servermain.cpp index 1c301d4f5..254b1f28a 100644 --- a/src/servermain.cpp +++ b/src/servermain.cpp @@ -98,7 +98,6 @@ std::ostream *derr_server_ptr = &dstream; std::ostream *dout_client_ptr = &dstream; std::ostream *derr_client_ptr = &dstream; - /* gettime.h implementation */ @@ -129,6 +128,9 @@ int main(int argc, char *argv[]) DSTACK(__FUNCTION_NAME); + porting::signal_handler_init(); + bool &kill = *porting::signal_handler_killstatus(); + porting::initializePaths(); initializeMaterialProperties(); @@ -251,6 +253,11 @@ int main(int argc, char *argv[]) srand(time(0)); mysrand(time(0)); + // Initialize stuff + + init_mapnode(); + init_mineral(); + /* Run unit tests */ @@ -260,11 +267,6 @@ int main(int argc, char *argv[]) run_tests(); } - // Initialize stuff - - init_mapnode(); - init_mineral(); - /* Check parameters */ @@ -308,9 +310,9 @@ int main(int argc, char *argv[]) // Create server Server server(map_dir.c_str()); server.start(port); - + // Run server - dedicated_server_loop(server); + dedicated_server_loop(server, kill); } //try catch(con::PeerNotFoundException &e) diff --git a/src/test.cpp b/src/test.cpp index e5b22a978..1de902787 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -192,7 +192,7 @@ struct TestMapNode // Transparency n.d = CONTENT_AIR; assert(n.light_propagates() == true); - n.d = 0; + n.d = CONTENT_STONE; assert(n.light_propagates() == false); } }; diff --git a/src/utility.h b/src/utility.h index 46277cd42..8c81aba91 100644 --- a/src/utility.h +++ b/src/utility.h @@ -802,9 +802,15 @@ struct ValueSpec class Settings { public: + Settings() + { + m_mutex.Init(); + } void writeLines(std::ostream &os) { + JMutexAutoLock lock(m_mutex); + for(core::map::Iterator i = m_settings.getIterator(); i.atEnd() == false; i++) @@ -817,6 +823,8 @@ public: bool parseConfigLine(const std::string &line) { + JMutexAutoLock lock(m_mutex); + std::string trimmedline = trim(line); // Ignore comments @@ -899,6 +907,8 @@ public: core::list &dst, core::map &updated) { + JMutexAutoLock lock(m_mutex); + if(is.eof()) return false; @@ -981,6 +991,8 @@ public: } } + JMutexAutoLock lock(m_mutex); + // Write stuff back { std::ofstream os(filename); @@ -1087,21 +1099,29 @@ public: void set(std::string name, std::string value) { + JMutexAutoLock lock(m_mutex); + m_settings[name] = value; } void setDefault(std::string name, std::string value) { + JMutexAutoLock lock(m_mutex); + m_defaults[name] = value; } bool exists(std::string name) { + JMutexAutoLock lock(m_mutex); + return (m_settings.find(name) || m_defaults.find(name)); } std::string get(std::string name) { + JMutexAutoLock lock(m_mutex); + core::map::Node *n; n = m_settings.find(name); if(n == NULL) @@ -1139,7 +1159,7 @@ public: bool getBoolAsk(std::string name, std::string question, bool def) { // If it is in settings - if(m_settings.find(name)) + if(exists(name)) return getBool(name); std::string s; @@ -1167,7 +1187,7 @@ public: u16 getU16Ask(std::string name, std::string question, u16 def) { // If it is in settings - if(m_settings.find(name)) + if(exists(name)) return getU16(name); std::string s; @@ -1238,12 +1258,17 @@ public: void clear() { + JMutexAutoLock lock(m_mutex); + m_settings.clear(); m_defaults.clear(); } Settings & operator+=(Settings &other) { + JMutexAutoLock lock(m_mutex); + JMutexAutoLock lock2(other.m_mutex); + if(&other == this) return *this; @@ -1267,6 +1292,9 @@ public: Settings & operator=(Settings &other) { + JMutexAutoLock lock(m_mutex); + JMutexAutoLock lock2(other.m_mutex); + if(&other == this) return *this; @@ -1279,6 +1307,8 @@ public: private: core::map m_settings; core::map m_defaults; + // All methods that access m_settings/m_defaults directly should lock this. + JMutex m_mutex; }; /*