#include "sha1.h"
#include "base64.h"
#include "tool.h"
+#include "utility_string.h"
+#include "sound.h" // dummySoundManager
+#include "event_manager.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
{
infostream<<"Server: PeerNotFoundException"<<std::endl;
}
+ catch(con::ConnectionBindFailed &e)
+ {
+ m_server->setAsyncFatalError(e.what());
+ }
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
(*s)<<std::endl;
}
-static std::string padStringRight(std::string s, size_t len)
-{
- if(len > s.size())
- s.insert(s.end(), len - s.size(), ' ');
- return s;
-}
-
/*
Server
*/
Server::Server(
- std::string path_world,
- std::string path_config,
- std::string gamename
+ const std::string &path_world,
+ const std::string &path_config,
+ const SubgameSpec &gamespec,
+ bool simple_singleplayer_mode
):
- m_gamename(gamename),
m_path_world(path_world),
m_path_config(path_config),
+ m_gamespec(gamespec),
+ m_simple_singleplayer_mode(simple_singleplayer_mode),
+ m_async_fatal_error(""),
m_env(NULL),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_authmanager(path_world+DIR_DELIM+"auth.txt"),
m_itemdef(createItemDefManager()),
m_nodedef(createNodeDefManager()),
m_craftdef(createCraftDefManager()),
+ m_event(new EventManager()),
m_thread(this),
m_emergethread(this),
- m_time_counter(0),
m_time_of_day_send_timer(0),
m_uptime(0),
m_shutdown_requested(false),
m_step_dtime_mutex.Init();
m_step_dtime = 0.0;
- // Figure out some paths
- // share/server
- m_path_share = porting::path_share + DIR_DELIM + "server";
- // game
- m_path_game = porting::path_user + DIR_DELIM + "server" + DIR_DELIM
- + "games" + DIR_DELIM + m_gamename;
- bool user_game = true; // Game is in user's directory
- if(!fs::PathExists(m_path_game)){
- m_path_game = m_path_share + DIR_DELIM + "games" + DIR_DELIM
- + m_gamename;
- user_game = false;
- }
- if(!fs::PathExists(m_path_game)){
- throw ServerError("Could not find game files for game \""
- +gamename+"\"");
- }
- // addons
- if(!user_game)
- m_path_addons.insert(m_path_share + DIR_DELIM + "addons"
- + DIR_DELIM + m_gamename);
- m_path_addons.insert(porting::path_user + DIR_DELIM + "server"
- + DIR_DELIM + "addons" + DIR_DELIM + m_gamename);
-
- infostream<<"Server created for gamename=\""<<gamename<<"\""<<std::endl;
- infostream<<"- path_world = "<<m_path_world<<std::endl;
- infostream<<"- path_config = "<<m_path_config<<std::endl;
- infostream<<"- path_game = "<<m_path_game<<std::endl;
- for(std::set<std::string>::const_iterator i = m_path_addons.begin();
- i != m_path_addons.end(); i++)
- infostream<<"- path_addons+= "<<(*i)<<std::endl;
-
- // Path to builtin.lua
- std::string builtinpath = m_path_share + DIR_DELIM + "builtin.lua";
+ if(path_world == "")
+ throw ServerError("Supplied empty world path");
+
+ if(!gamespec.isValid())
+ throw ServerError("Supplied invalid gamespec");
+
+ infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
+ if(m_simple_singleplayer_mode)
+ infostream<<" in simple singleplayer mode"<<std::endl;
+ else
+ infostream<<std::endl;
+ infostream<<"- world: "<<m_path_world<<std::endl;
+ infostream<<"- config: "<<m_path_config<<std::endl;
+ infostream<<"- game: "<<m_gamespec.path<<std::endl;
- // Add default global mod search path
- m_modspaths.push_front(m_path_game + DIR_DELIM "mods");
// Add world mod search path
m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
// Add addon mod search path
- for(std::set<std::string>::const_iterator i = m_path_addons.begin();
- i != m_path_addons.end(); i++){
- m_modspaths.push_front((*i) + DIR_DELIM + "mods");
- }
+ for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
+ i != m_gamespec.mods_paths.end(); i++)
+ m_modspaths.push_front((*i));
// Print out mod search paths
for(core::list<std::string>::Iterator i = m_modspaths.begin();
i != m_modspaths.end(); i++){
std::string modspath = *i;
- infostream<<"- modspath += "<<modspath<<std::endl;
+ infostream<<"- mods: "<<modspath<<std::endl;
}
+ // Path to builtin.lua
+ std::string builtinpath = porting::path_share + DIR_DELIM + "builtin"
+ + DIR_DELIM + "builtin.lua";
+
+ // Create world if it doesn't exist
+ if(!initializeWorld(m_path_world, m_gamespec.id))
+ throw ServerError("Failed to initialize world");
+
// Lock environment
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
delete i.getNode()->getValue();
}
}
-
- // Delete Environment
+
+ // Delete things in the reverse order of creation
delete m_env;
-
+ delete m_event;
delete m_itemdef;
delete m_nodedef;
delete m_craftdef;
void Server::start(unsigned short port)
{
DSTACK(__FUNCTION_NAME);
+ infostream<<"Starting server on port "<<port<<"..."<<std::endl;
+
// Stop thread if already running
m_thread.stop();
<<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
<<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
<<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
- actionstream<<"Server listening on port "<<port<<"."<<std::endl;
+ actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
+ actionstream<<"Server for gameid=\""<<m_gamespec.id
+ <<"\" listening on port "<<port<<"."<<std::endl;
}
void Server::stop()
JMutexAutoLock lock(m_step_dtime_mutex);
m_step_dtime += dtime;
}
+ // Throw if fatal error occurred in thread
+ std::string async_err = m_async_fatal_error.get();
+ if(async_err != ""){
+ throw ServerError(async_err);
+ }
}
void Server::AsyncRunStep()
}
/*
- Update m_time_of_day and overall game time
+ Update time of day and overall game time
*/
{
JMutexAutoLock envlock(m_env_mutex);
- m_time_counter += dtime;
- f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
- u32 units = (u32)(m_time_counter*speed);
- m_time_counter -= (f32)units / speed;
-
- m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
-
- //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
+ m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
/*
Send to clients at constant intervals
//Player *player = m_env->getPlayer(client->peer_id);
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
- m_env->getTimeOfDay());
+ m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
// Send as reliable
m_con.Send(client->peer_id, 0, data, true);
}
try{
Address address = m_con.GetPeerAddress(peer_id);
+ std::string addr_s = address.serializeString();
// drop player if is ip is banned
- if(m_banmanager.isIpBanned(address.serializeString())){
+ if(m_banmanager.isIpBanned(addr_s)){
+ infostream<<"Server: A banned client tried to connect from "
+ <<addr_s<<"; banned name was "
+ <<m_banmanager.getBanName(addr_s)<<std::endl;
+ // This actually doesn't seem to transfer to the client
SendAccessDenied(m_con, peer_id,
L"Your ip is banned. Banned name was "
- +narrow_to_wide(m_banmanager.getBanName(
- address.serializeString())));
+ +narrow_to_wide(m_banmanager.getBanName(addr_s)));
m_con.DeletePeer(peer_id);
return;
}
SendAccessDenied(m_con, peer_id, L"Invalid password");
return;
}
+
+ // Do not allow multiple players in simple singleplayer mode.
+ // This isn't a perfect way to do it, but will suffice for now.
+ if(m_simple_singleplayer_mode && m_clients.size() > 1){
+ infostream<<"Server: Not allowing another client to connect in"
+ <<" simple singleplayer mode"<<std::endl;
+ SendAccessDenied(m_con, peer_id,
+ L"Running in simple singleplayer mode.");
+ return;
+ }
// Enforce user limit.
// Don't enforce for users that have some admin right
// Send time of day
{
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
- m_env->getTimeOfDay());
+ m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
m_con.Send(peer_id, 0, data, true);
}
- // Send information about server to player in chat
- SendChatMessage(peer_id, getStatusString());
-
- // Send information about joining in chat
+ // Note things in chat if not in simple singleplayer mode
+ if(!m_simple_singleplayer_mode)
{
- std::wstring name = L"unknown";
- Player *player = m_env->getPlayer(peer_id);
- if(player != NULL)
- name = narrow_to_wide(player->getName());
+ // Send information about server to player in chat
+ SendChatMessage(peer_id, getStatusString());
- std::wstring message;
- message += L"*** ";
- message += name;
- message += L" joined game";
- BroadcastChatMessage(message);
+ // Send information about joining in chat
+ {
+ std::wstring name = L"unknown";
+ Player *player = m_env->getPlayer(peer_id);
+ if(player != NULL)
+ name = narrow_to_wide(player->getName());
+
+ std::wstring message;
+ message += L"*** ";
+ message += name;
+ message += L" joined game";
+ BroadcastChatMessage(message);
+ }
}
// Warnings about protocol version can be issued here
<<action<<std::endl;
}
}
+ else if(command == TOSERVER_REMOVED_SOUNDS)
+ {
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ int num = readU16(is);
+ for(int k=0; k<num; k++){
+ s32 id = readS32(is);
+ std::map<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(id);
+ if(i == m_playing_sounds.end())
+ continue;
+ ServerPlayingSound &psound = i->second;
+ psound.clients.erase(peer_id);
+ if(psound.clients.size() == 0)
+ m_playing_sounds.erase(i++);
+ }
+ }
else
{
infostream<<"Server::ProcessData(): Ignoring "
m_con.Send(player->peer_id, 0, data, true);
}
+s32 Server::playSound(const SimpleSoundSpec &spec,
+ const ServerSoundParams ¶ms)
+{
+ // Find out initial position of sound
+ bool pos_exists = false;
+ v3f pos = params.getPos(m_env, &pos_exists);
+ // If position is not found while it should be, cancel sound
+ if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
+ return -1;
+ // Filter destination clients
+ std::set<RemoteClient*> dst_clients;
+ if(params.to_player != "")
+ {
+ Player *player = m_env->getPlayer(params.to_player.c_str());
+ if(!player){
+ infostream<<"Server::playSound: Player \""<<params.to_player
+ <<"\" not found"<<std::endl;
+ return -1;
+ }
+ if(player->peer_id == PEER_ID_INEXISTENT){
+ infostream<<"Server::playSound: Player \""<<params.to_player
+ <<"\" not connected"<<std::endl;
+ return -1;
+ }
+ RemoteClient *client = getClient(player->peer_id);
+ dst_clients.insert(client);
+ }
+ else
+ {
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator(); i.atEnd() == false; i++)
+ {
+ RemoteClient *client = i.getNode()->getValue();
+ Player *player = m_env->getPlayer(client->peer_id);
+ if(!player)
+ continue;
+ if(pos_exists){
+ if(player->getPosition().getDistanceFrom(pos) >
+ params.max_hear_distance)
+ continue;
+ }
+ dst_clients.insert(client);
+ }
+ }
+ if(dst_clients.size() == 0)
+ return -1;
+ // Create the sound
+ s32 id = m_next_sound_id++;
+ // The sound will exist as a reference in m_playing_sounds
+ m_playing_sounds[id] = ServerPlayingSound();
+ ServerPlayingSound &psound = m_playing_sounds[id];
+ psound.params = params;
+ for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
+ i != dst_clients.end(); i++)
+ psound.clients.insert((*i)->peer_id);
+ // Create packet
+ std::ostringstream os(std::ios_base::binary);
+ writeU16(os, TOCLIENT_PLAY_SOUND);
+ writeS32(os, id);
+ os<<serializeString(spec.name);
+ writeF1000(os, spec.gain * params.gain);
+ writeU8(os, params.type);
+ writeV3F1000(os, pos);
+ writeU16(os, params.object);
+ writeU8(os, params.loop);
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send
+ for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
+ i != dst_clients.end(); i++){
+ // Send as reliable
+ m_con.Send((*i)->peer_id, 0, data, true);
+ }
+ return id;
+}
+void Server::stopSound(s32 handle)
+{
+ // Get sound reference
+ std::map<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(handle);
+ if(i == m_playing_sounds.end())
+ return;
+ ServerPlayingSound &psound = i->second;
+ // Create packet
+ std::ostringstream os(std::ios_base::binary);
+ writeU16(os, TOCLIENT_STOP_SOUND);
+ writeS32(os, handle);
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send
+ for(std::set<u16>::iterator i = psound.clients.begin();
+ i != psound.clients.end(); i++){
+ // Send as reliable
+ m_con.Send(*i, 0, data, true);
+ }
+ // Remove sound reference
+ m_playing_sounds.erase(i);
+}
+
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
core::list<u16> *far_players, float far_d_nodes)
{
return os.str();
}
+u64 Server::getPlayerAuthPrivs(const std::string &name)
+{
+ try{
+ return m_authmanager.getPrivs(name);
+ }
+ catch(AuthNotFoundException &e)
+ {
+ dstream<<"WARNING: Auth not found for "<<name<<std::endl;
+ return 0;
+ }
+}
+
+void Server::setPlayerAuthPrivs(const std::string &name, u64 privs)
+{
+ try{
+ return m_authmanager.setPrivs(name, privs);
+ }
+ catch(AuthNotFoundException &e)
+ {
+ dstream<<"WARNING: Auth not found for "<<name<<std::endl;
+ }
+}
+
+u64 Server::getPlayerEffectivePrivs(const std::string &name)
+{
+ // Local player gets all privileges regardless of
+ // what's set on their account.
+ if(m_simple_singleplayer_mode)
+ return PRIV_ALL;
+ if(name == g_settings->get("name"))
+ return PRIV_ALL;
+ return getPlayerAuthPrivs(name);
+}
+
void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
{
// Add player to auth manager
{
return m_nodedef->allocateDummy(name);
}
+ISoundManager* Server::getSoundManager()
+{
+ return &dummySoundManager;
+}
+MtEventManager* Server::getEventManager()
+{
+ return m_event;
+}
IWritableItemDefManager* Server::getWritableItemDefManager()
{
obj->m_known_by_count--;
}
+ /*
+ Clear references to playing sounds
+ */
+ for(std::map<s32, ServerPlayingSound>::iterator
+ i = m_playing_sounds.begin();
+ i != m_playing_sounds.end();)
+ {
+ ServerPlayingSound &psound = i->second;
+ psound.clients.erase(c.peer_id);
+ if(psound.clients.size() == 0)
+ m_playing_sounds.erase(i++);
+ else
+ i++;
+ }
+
ServerRemotePlayer* player =
static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
if(player==NULL)
return 0;
std::string playername = player->getName();
- // Local player gets all privileges regardless of
- // what's set on their account.
- if(g_settings->get("name") == playername)
- {
- return PRIV_ALL;
- }
- else
- {
- return getPlayerAuthPrivs(playername);
- }
+ return getPlayerEffectivePrivs(playername);
}
void dedicated_server_loop(Server &server, bool &kill)