Ctrl+C handling on POSIX, some commands for server and other tweaking

This commit is contained in:
Perttu Ahola 2011-02-15 16:11:24 +02:00
parent be7391c2b1
commit d065bae323
13 changed files with 384 additions and 111 deletions

@ -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

@ -31,6 +31,8 @@
#map-dir = /home/palle/custom_map
#operator_name =
#plants_amount = 1.0
#ravines_amount = 1.0
#coal_amount = 1.0

@ -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

@ -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)

@ -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;
}

@ -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);

@ -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 <signal.h>
bool g_killed = false;
void sigint_handler(int sig)
{
if(g_killed == false)
{
dstream<<DTIME<<"sigint_handler(): "
<<"Ctrl-C pressed, shutting down."<<std::endl;
g_killed = true;
}
else
{
(void)signal(SIGINT, SIG_DFL);
}
}
void signal_handler_init(void)
{
dstream<<"signal_handler_init()"<<std::endl;
(void)signal(SIGINT, sigint_handler);
}
#else // _WIN32
void signal_handler_init(void)
{
// No-op
}
#endif
bool * signal_handler_killstatus(void)
{
return &g_killed;
}
/*
Path mangler
*/
std::string path_data = "../data";
std::string path_userdata = "../";

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PORTING_HEADER
#include <string>
// 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.
*/

@ -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();
@ -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<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();
}
}
}
@ -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<<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;
// Send message
SendChatMessage(peer_id, os.str());
}
SendChatMessage(peer_id, getStatusString());
// Send information about joining in chat
{
@ -2461,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)
{
// 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<<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
{
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;
SendChatMessage(client->peer_id, line);
/*
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;
// 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<PlayerInfo> 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<PrioritySortedBlockTransfer> 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<<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();
@ -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<<std::endl;
std::cout<<DTIME<<std::endl;
std::cout<<"========================"<<std::endl;
std::cout<<"Running dedicated server"<<std::endl;
std::cout<<"========================"<<std::endl;
@ -3472,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)

@ -390,15 +390,11 @@ public:
void Receive();
void ProcessData(u8 *data, u32 datasize, u16 peer_id);
/*void Send(u16 peer_id, u16 channelnum,
SharedBuffer<u8> 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<v2s16> ps, u8 ver);
//
core::list<PlayerInfo> 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<double> m_uptime;
enum PeerChangeType
{
PEER_ADDED,
@ -508,14 +512,18 @@ private:
std::string m_mapsavedir;
MutexedVariable<bool> 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

@ -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)

@ -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);
}
};

@ -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<std::string, std::string>::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<std::string> &dst,
core::map<std::string, bool> &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<std::string, std::string>::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<std::string, std::string> m_settings;
core::map<std::string, std::string> m_defaults;
// All methods that access m_settings/m_defaults directly should lock this.
JMutex m_mutex;
};
/*