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
#map-dir = /home/palle/custom_map
+#operator_name =
+
#plants_amount = 1.0
#ravines_amount = 1.0
#coal_amount = 1.0
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 )
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
{\r
if(event.EventType==EET_KEY_INPUT_EVENT)\r
{\r
- if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)\r
+ if(event.KeyInput.PressedDown)\r
{\r
- quitMenu();\r
- return true;\r
+ if(event.KeyInput.Key==KEY_ESCAPE)\r
+ {\r
+ quitMenu();\r
+ return true;\r
+ }\r
+ else if(event.KeyInput.Key==KEY_RETURN)\r
+ {\r
+ quitMenu();\r
+ return true;\r
+ }\r
}\r
}\r
if(event.EventType==EET_GUI_EVENT)\r
# maybe done\r
* not done\r
\r
-=== Stuff to do before release\r
+=== Fixmes\r
* Make server find the spawning place from the real map data, not from\r
the heightmap\r
- But the changing borders of chunk have to be avoided, because\r
placement and transfer\r
* only_from_disk might not work anymore - check and fix it.\r
* Check the fixmes in the list above\r
-* FIXME: Sneaking doesn't switch sneak node when moving sideways\r
+* When sending blocks to the client, the server takes way too much\r
+ CPU time (20-30% for single player), find out what it is doing.\r
+ - Make a simple profiler\r
\r
=== Making it more portable\r
* Some MSVC: std::sto* are defined without a namespace and collide\r
with the ones in utility.h\r
-* On Kray's machine, the new find_library(XXF86VM_LIBRARY, Xxf86vm)\r
- line doesn't find the library.\r
\r
-=== Stuff to do after release\r
+=== Features\r
* Make an "environment metafile" to store at least time of day\r
* Move digging property stuff from material.{h,cpp} to mapnode.cpp...\r
- Or maybe move content_features to material.{h,cpp}?\r
}\r
void gotText(std::wstring text)\r
{\r
+ // Discard empty line\r
+ if(text == L"")\r
+ return;\r
+ \r
+ // Parse command (server command starts with "/#")\r
+ if(text[0] == L'/' && text[1] != L'#')\r
+ {\r
+ std::wstring reply = L"Local: ";\r
+\r
+ reply += L"Local commands not yet supported. "\r
+ "Server prefix is \"/#\".";\r
+ \r
+ m_client->addChatMessage(reply);\r
+ return;\r
+ }\r
+\r
+ // Send to others\r
m_client->sendChatMessage(text);\r
+ // Show locally\r
m_client->addChatMessage(text);\r
}\r
\r
\r
DSTACK(__FUNCTION_NAME);\r
\r
+ porting::signal_handler_init();\r
+ bool &kill = *porting::signal_handler_killstatus();\r
+ \r
porting::initializePaths();\r
// Create user data directory\r
fs::CreateDir(porting::path_userdata);\r
server.start(port);\r
\r
// Run server\r
- dedicated_server_loop(server);\r
+ dedicated_server_loop(server, kill);\r
\r
return 0;\r
}\r
// 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;
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);
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 = "../";
#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"
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.
*/
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();
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;
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();
+ }
}
}
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
{
// 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
return list;
}
+
void Server::peerAdded(con::Peer *peer)
{
DSTACK(__FUNCTION_NAME);
JMutexAutoLock envlock(m_env_mutex);
+ //TimeTaker timer("Server::SendBlocks");
+
core::array<PrioritySortedBlockTransfer> queue;
s32 total_sending = 0;
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();
}
}
-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;
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)
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()
else
return 1000;
}
-
+
+ bool getShutdownRequested()
+ {
+ return m_shutdown_requested.get();
+ }
+
private:
// Virtual methods from con::PeerHandler.
// 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
float m_time_of_day_send_timer;
MutexedVariable<double> m_uptime;
-
+
enum PeerChangeType
{
PEER_ADDED,
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
std::ostream *dout_client_ptr = &dstream;
std::ostream *derr_client_ptr = &dstream;
-
/*
gettime.h implementation
*/
DSTACK(__FUNCTION_NAME);
+ porting::signal_handler_init();
+ bool &kill = *porting::signal_handler_killstatus();
+
porting::initializePaths();
initializeMaterialProperties();
srand(time(0));
mysrand(time(0));
+ // Initialize stuff
+
+ init_mapnode();
+ init_mineral();
+
/*
Run unit tests
*/
run_tests();
}
- // Initialize stuff
-
- init_mapnode();
- init_mineral();
-
/*
Check parameters
*/
// 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)
// Transparency
n.d = CONTENT_AIR;
assert(n.light_propagates() == true);
- n.d = 0;
+ n.d = CONTENT_STONE;
assert(n.light_propagates() == false);
}
};
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++)
bool parseConfigLine(const std::string &line)
{
+ JMutexAutoLock lock(m_mutex);
+
std::string trimmedline = trim(line);
// Ignore comments
core::list<std::string> &dst,
core::map<std::string, bool> &updated)
{
+ JMutexAutoLock lock(m_mutex);
+
if(is.eof())
return false;
}
}
+ JMutexAutoLock lock(m_mutex);
+
// Write stuff back
{
std::ofstream os(filename);
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)
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;
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;
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;
Settings & operator=(Settings &other)
{
+ JMutexAutoLock lock(m_mutex);
+ JMutexAutoLock lock2(other.m_mutex);
+
if(&other == this)
return *this;
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;
};
/*