Skip to content
Snippets Groups Projects
server.cpp 126 KiB
Newer Older
	if(!initializeWorld(m_path_world, m_gamespec.id))
		throw ServerError("Failed to initialize world");
Perttu Ahola's avatar
Perttu Ahola committed

	// Lock environment
	JMutexAutoLock envlock(m_env_mutex);
	JMutexAutoLock conlock(m_con_mutex);

Perttu Ahola's avatar
Perttu Ahola committed
	// Initialize scripting
	
	infostream<<"Server: Initializing Lua"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
	m_lua = script_init();
	assert(m_lua);
	// Export API
	scriptapi_export(m_lua, this);
Perttu Ahola's avatar
Perttu Ahola committed
	// Load and run builtin.lua
	infostream<<"Server: Loading builtin.lua [\""
			<<builtinpath<<"\"]"<<std::endl;
	bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
Perttu Ahola's avatar
Perttu Ahola committed
	if(!success){
		errorstream<<"Server: Failed to load and run "
				<<builtinpath<<std::endl;
		throw ModError("Failed to load and run "+builtinpath);
Perttu Ahola's avatar
Perttu Ahola committed
	}
	// Find mods in mod search paths
	m_mods = getMods(m_modspaths);
	// Print 'em
	infostream<<"Server: Loading mods: ";
	for(core::list<ModSpec>::Iterator i = m_mods.begin();
			i != m_mods.end(); i++){
		const ModSpec &mod = *i;
		infostream<<mod.name<<" ";
	}
	infostream<<std::endl;
	// Load and run "mod" scripts
	for(core::list<ModSpec>::Iterator i = m_mods.begin();
			i != m_mods.end(); i++){
		const ModSpec &mod = *i;
Perttu Ahola's avatar
Perttu Ahola committed
		std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
		infostream<<"  ["<<padStringRight(mod.name, 12)<<"] [\""
				<<scriptpath<<"\"]"<<std::endl;
		bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
Perttu Ahola's avatar
Perttu Ahola committed
		if(!success){
			errorstream<<"Server: Failed to load and run "
					<<scriptpath<<std::endl;
			throw ModError("Failed to load and run "+scriptpath);
Perttu Ahola's avatar
Perttu Ahola committed
	
	// Read Textures and calculate sha1 sums
	fillMediaCache();
	// Apply item aliases in the node definition manager
	m_nodedef->updateAliases(m_itemdef);

Perttu Ahola's avatar
Perttu Ahola committed
	// Initialize Environment
	
	m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
Perttu Ahola's avatar
Perttu Ahola committed
	// Give environment reference to scripting api
	scriptapi_add_environment(m_lua, m_env);
	
	// Register us to receive map edit events
Perttu Ahola's avatar
Perttu Ahola committed
	m_env->getMap().addEventReceiver(this);
	// If file exists, load environment metadata
	if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
		infostream<<"Server: Loading environment metadata"<<std::endl;
		m_env->loadMeta(m_path_world);
	infostream<<"Server: Loading players"<<std::endl;
	m_env->deSerializePlayers(m_path_world);

	/*
		Add some test ActiveBlockModifiers to environment
	*/
	add_legacy_abms(m_env, m_nodedef);
Perttu Ahola's avatar
Perttu Ahola committed
}

Server::~Server()
{
	infostream<<"Server destructing"<<std::endl;
	/*
		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;

			try{
				SendChatMessage(client->peer_id, line);
			}
			catch(con::PeerNotFoundException &e)
			{}
	
	{
		JMutexAutoLock envlock(m_env_mutex);
		infostream<<"Server: Saving players"<<std::endl;
		m_env->serializePlayers(m_path_world);
		/*
			Save environment metadata
		*/
		infostream<<"Server: Saving environment metadata"<<std::endl;
		m_env->saveMeta(m_path_world);
Perttu Ahola's avatar
Perttu Ahola committed
	stop();
Perttu Ahola's avatar
Perttu Ahola committed
	{
		JMutexAutoLock clientslock(m_con_mutex);

		for(core::map<u16, RemoteClient*>::Iterator
			i = m_clients.getIterator();
			i.atEnd() == false; i++)
Perttu Ahola's avatar
Perttu Ahola committed
		{
			/*// Delete player
			// NOTE: These are removed by env destructor
			{
				u16 peer_id = i.getNode()->getKey();
				JMutexAutoLock envlock(m_env_mutex);
Perttu Ahola's avatar
Perttu Ahola committed
				m_env->removePlayer(peer_id);
			}*/
			
			// Delete client
			delete i.getNode()->getValue();
		}
Perttu Ahola's avatar
Perttu Ahola committed
	}
	
	// Delete things in the reverse order of creation
Perttu Ahola's avatar
Perttu Ahola committed
	delete m_env;
	delete m_rollback;
	delete m_craftdef;
Perttu Ahola's avatar
Perttu Ahola committed
	
	// Deinitialize scripting
	infostream<<"Server: Deinitializing scripting"<<std::endl;
	script_deinit(m_lua);
Perttu Ahola's avatar
Perttu Ahola committed

	// Delete detached inventories
	{
		for(std::map<std::string, Inventory*>::iterator
				i = m_detached_inventories.begin();
				i != m_detached_inventories.end(); i++){
			delete i->second;
		}
	}
Perttu Ahola's avatar
Perttu Ahola committed
}

void Server::start(unsigned short port)
{
	DSTACK(__FUNCTION_NAME);
	infostream<<"Starting server on port "<<port<<"..."<<std::endl;

Perttu Ahola's avatar
Perttu Ahola committed
	// Stop thread if already running
	m_thread.stop();
	
	// Initialize connection
Perttu Ahola's avatar
Perttu Ahola committed
	m_con.Serve(port);

	// Start thread
	m_thread.setRun(true);
	m_thread.Start();
	
	// ASCII art for the win!
	actionstream
	<<"        .__               __                   __   "<<std::endl
	<<"  _____ |__| ____   _____/  |_  ____   _______/  |_ "<<std::endl
	<<" /     \\|  |/    \\_/ __ \\   __\\/ __ \\ /  ___/\\   __\\"<<std::endl
	<<"|  Y Y  \\  |   |  \\  ___/|  | \\  ___/ \\___ \\  |  |  "<<std::endl
	<<"|__|_|  /__|___|  /\\___  >__|  \\___  >____  > |__|  "<<std::endl
	<<"      \\/        \\/     \\/          \\/     \\/        "<<std::endl;
	actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
	actionstream<<"Server for gameid=\""<<m_gamespec.id
			<<"\" listening on port "<<port<<"."<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
}

void Server::stop()
{
	DSTACK(__FUNCTION_NAME);
	infostream<<"Server: Stopping and waiting threads"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
	// Stop threads (set run=false first so both start stopping)
	m_thread.setRun(false);
	m_emergethread.setRun(false);
	m_thread.stop();
	m_emergethread.stop();
	
	infostream<<"Server: Threads stopped"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
}

void Server::step(float dtime)
{
	DSTACK(__FUNCTION_NAME);
	// Limit a bit
	if(dtime > 2.0)
		dtime = 2.0;
	{
		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);
	}
Perttu Ahola's avatar
Perttu Ahola committed
}

void Server::AsyncRunStep()
{
	DSTACK(__FUNCTION_NAME);
	g_profiler->add("Server::AsyncRunStep (num)", 1);
	
Perttu Ahola's avatar
Perttu Ahola committed
	float dtime;
	{
		JMutexAutoLock lock1(m_step_dtime_mutex);
		dtime = m_step_dtime;
	}
	
	{
		// Send blocks to clients
		SendBlocks(dtime);
	}
	
	if(dtime < 0.001)
		return;
	
	g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
	//infostream<<"Server steps "<<dtime<<std::endl;
	//infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
	{
		JMutexAutoLock lock1(m_step_dtime_mutex);
		m_step_dtime -= dtime;
	{
		// Process connection's timeouts
		JMutexAutoLock lock2(m_con_mutex);
		ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
		m_con.RunTimeouts(dtime);
	}
	
	{
		// This has to be called so that the client list gets synced
		// with the peer list of the connection
		handlePeerChanges();
	}

		Update time of day and overall game time
		JMutexAutoLock envlock(m_env_mutex);

		m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));

		/*
			Send to clients at constant intervals
		*/

		m_time_of_day_send_timer -= dtime;
		if(m_time_of_day_send_timer < 0.0)
		{
			m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");

			//JMutexAutoLock envlock(m_env_mutex);
			JMutexAutoLock conlock(m_con_mutex);

			for(core::map<u16, RemoteClient*>::Iterator
				i = m_clients.getIterator();
				i.atEnd() == false; i++)
			{
				RemoteClient *client = i.getNode()->getValue();
				SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
						m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
				// Send as reliable
				m_con.Send(client->peer_id, 0, data, true);
			}
		}
	}
Perttu Ahola's avatar
Perttu Ahola committed
	{
		JMutexAutoLock lock(m_env_mutex);
		// Step environment
		ScopeProfiler sp(g_profiler, "SEnv step");
		ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
Perttu Ahola's avatar
Perttu Ahola committed
		m_env->step(dtime);
Perttu Ahola's avatar
Perttu Ahola committed
	}
	const float map_timer_and_unload_dtime = 2.92;
	if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
	{
		JMutexAutoLock lock(m_env_mutex);
		// Run Map's timers and unload unused data
		ScopeProfiler sp(g_profiler, "Server: map timer and unload");
Perttu Ahola's avatar
Perttu Ahola committed
		m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
				g_settings->getFloat("server_unload_unused_data_timeout"));
Perttu Ahola's avatar
Perttu Ahola committed
	
	/*
		Do background stuff
	*/
	{
		JMutexAutoLock lock(m_env_mutex);
		JMutexAutoLock lock2(m_con_mutex);

		ScopeProfiler sp(g_profiler, "Server: handle players");

		for(core::map<u16, RemoteClient*>::Iterator
			i = m_clients.getIterator();
			i.atEnd() == false; i++)
		{
			RemoteClient *client = i.getNode()->getValue();
			PlayerSAO *playersao = getPlayerSAO(client->peer_id);
				Handle player HPs (die if hp=0)
			if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
				DiePlayer(client->peer_id);
			/*
				Send player inventories and HPs if necessary
			*/
				SendMovePlayer(client->peer_id);
			if(playersao->m_inventory_not_sent){
				UpdateCrafting(client->peer_id);
				SendInventory(client->peer_id);
			if(playersao->m_hp_not_sent){
				SendPlayerHP(client->peer_id);
	/* Transform liquids */
	m_liquid_transform_timer += dtime;
	if(m_liquid_transform_timer >= 1.00)
		m_liquid_transform_timer -= 1.00;
		
		JMutexAutoLock lock(m_env_mutex);
		ScopeProfiler sp(g_profiler, "Server: liquid transform");
		core::map<v3s16, MapBlock*> modified_blocks;
Perttu Ahola's avatar
Perttu Ahola committed
		m_env->getMap().transformLiquids(modified_blocks);
#if 0		
		/*
			Update lighting
		*/
		core::map<v3s16, MapBlock*> lighting_modified_blocks;
Perttu Ahola's avatar
Perttu Ahola committed
		ServerMap &map = ((ServerMap&)m_env->getMap());
		map.updateLighting(modified_blocks, lighting_modified_blocks);
		
		// Add blocks modified by lighting to modified_blocks
		for(core::map<v3s16, MapBlock*>::Iterator
				i = lighting_modified_blocks.getIterator();
				i.atEnd() == false; i++)
		{
			MapBlock *block = i.getNode()->getValue();
			modified_blocks.insert(block->getPos(), block);
		}
#endif
		/*
			Set the modified blocks unsent for all the clients
		*/
		
		JMutexAutoLock lock2(m_con_mutex);

		for(core::map<u16, RemoteClient*>::Iterator
				i = m_clients.getIterator();
				i.atEnd() == false; i++)
		{
			RemoteClient *client = i.getNode()->getValue();
			
			if(modified_blocks.size() > 0)
			{
				// Remove block from sent history
				client->SetBlocksNotSent(modified_blocks);
			}
		}
Perttu Ahola's avatar
Perttu Ahola committed
	// Periodically print some info
	{
		float &counter = m_print_info_timer;
Perttu Ahola's avatar
Perttu Ahola committed
		counter += dtime;
		if(counter >= 30.0)
		{
			counter = 0.0;

			JMutexAutoLock lock2(m_con_mutex);
			
			if(m_clients.size() != 0)
				infostream<<"Players:"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
			for(core::map<u16, RemoteClient*>::Iterator
				i = m_clients.getIterator();
				i.atEnd() == false; i++)
			{
				//u16 peer_id = i.getNode()->getKey();
				RemoteClient *client = i.getNode()->getValue();
Perttu Ahola's avatar
Perttu Ahola committed
				Player *player = m_env->getPlayer(client->peer_id);
Perttu Ahola's avatar
Perttu Ahola committed
				if(player==NULL)
					continue;
				infostream<<"* "<<player->getName()<<"\t";
				client->PrintInfo(infostream);
	//if(g_settings->getBool("enable_experimental"))
		Check added and deleted active objects
		//infostream<<"Server: Checking added and deleted active objects"<<std::endl;
		JMutexAutoLock envlock(m_env_mutex);
		JMutexAutoLock conlock(m_con_mutex);
		ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
		// Radius inside which objects are active
Perttu Ahola's avatar
Perttu Ahola committed
		s16 radius = g_settings->getS16("active_object_send_range_blocks");
		radius *= MAP_BLOCKSIZE;
Perttu Ahola's avatar
Perttu Ahola committed

		for(core::map<u16, RemoteClient*>::Iterator
			i = m_clients.getIterator();
			i.atEnd() == false; i++)
		{
			RemoteClient *client = i.getNode()->getValue();

			// If definitions and textures have not been sent, don't
			// send objects either
			if(!client->definitions_sent)
				continue;

Perttu Ahola's avatar
Perttu Ahola committed
			Player *player = m_env->getPlayer(client->peer_id);
			if(player==NULL)
				// This can happen if the client timeouts somehow
				/*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
						<<client->peer_id
						<<" has no associated player"<<std::endl;*/
			v3s16 pos = floatToInt(player->getPosition(), BS);

			core::map<u16, bool> removed_objects;
			core::map<u16, bool> added_objects;
Perttu Ahola's avatar
Perttu Ahola committed
			m_env->getRemovedActiveObjects(pos, radius,
					client->m_known_objects, removed_objects);
Perttu Ahola's avatar
Perttu Ahola committed
			m_env->getAddedActiveObjects(pos, radius,
					client->m_known_objects, added_objects);
			
			// Ignore if nothing happened
			if(removed_objects.size() == 0 && added_objects.size() == 0)
				//infostream<<"active objects: none changed"<<std::endl;
			
			std::string data_buffer;
			char buf[4];
			
			// Handle removed objects
			writeU16((u8*)buf, removed_objects.size());
			data_buffer.append(buf, 2);
			for(core::map<u16, bool>::Iterator
					i = removed_objects.getIterator();
					i.atEnd()==false; i++)
			{
				// Get object
				u16 id = i.getNode()->getKey();
Perttu Ahola's avatar
Perttu Ahola committed
				ServerActiveObject* obj = m_env->getActiveObject(id);
				// Add to data buffer for sending
				writeU16((u8*)buf, i.getNode()->getKey());
				data_buffer.append(buf, 2);
				
				// Remove from known objects
				client->m_known_objects.remove(i.getNode()->getKey());
				if(obj && obj->m_known_by_count > 0)
					obj->m_known_by_count--;
			}
			// Handle added objects
			writeU16((u8*)buf, added_objects.size());
			data_buffer.append(buf, 2);
			for(core::map<u16, bool>::Iterator
					i = added_objects.getIterator();
					i.atEnd()==false; i++)
				// Get object
				u16 id = i.getNode()->getKey();
Perttu Ahola's avatar
Perttu Ahola committed
				ServerActiveObject* obj = m_env->getActiveObject(id);
				
				// Get object type
				u8 type = ACTIVEOBJECT_TYPE_INVALID;
				if(obj == NULL)
					infostream<<"WARNING: "<<__FUNCTION_NAME
							<<": NULL object"<<std::endl;
				else

				// Add to data buffer for sending
				writeU16((u8*)buf, id);
				data_buffer.append(buf, 2);
				writeU8((u8*)buf, type);
				data_buffer.append(buf, 1);
				
				if(obj)
					data_buffer.append(serializeLongString(
							obj->getClientInitializationData()));
				else
					data_buffer.append(serializeLongString(""));
				// Add to known objects
				client->m_known_objects.insert(i.getNode()->getKey(), false);

				if(obj)
					obj->m_known_by_count++;
			// Send packet
			SharedBuffer<u8> reply(2 + data_buffer.size());
			writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
			memcpy((char*)&reply[2], data_buffer.c_str(),
					data_buffer.size());
			// Send as reliable
			m_con.Send(client->peer_id, 0, reply, true);
			verbosestream<<"Server: Sent object remove/add: "
					<<removed_objects.size()<<" removed, "
					<<added_objects.size()<<" added, "
					<<"packet size is "<<reply.getSize()<<std::endl;
		}
Perttu Ahola's avatar
Perttu Ahola committed

#if 0
		/*
			Collect a list of all the objects known by the clients
			and report it back to the environment.
		*/

		core::map<u16, bool> all_known_objects;

		for(core::map<u16, RemoteClient*>::Iterator
			i = m_clients.getIterator();
			i.atEnd() == false; i++)
		{
			RemoteClient *client = i.getNode()->getValue();
			// Go through all known objects of client
			for(core::map<u16, bool>::Iterator
					i = client->m_known_objects.getIterator();
					i.atEnd()==false; i++)
			{
				u16 id = i.getNode()->getKey();
				all_known_objects[id] = true;
			}
		}
		
Perttu Ahola's avatar
Perttu Ahola committed
		m_env->setKnownActiveObjects(whatever);
Perttu Ahola's avatar
Perttu Ahola committed
#endif

	/*
		Send object messages
	*/
	{
		JMutexAutoLock envlock(m_env_mutex);
		JMutexAutoLock conlock(m_con_mutex);

		ScopeProfiler sp(g_profiler, "Server: sending object messages");
		// Key = object id
		// Value = data sent by object
		core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;

		// Get active object messages from environment
		for(;;)
		{
Perttu Ahola's avatar
Perttu Ahola committed
			ActiveObjectMessage aom = m_env->getActiveObjectMessage();
			if(aom.id == 0)
				break;
			
			core::list<ActiveObjectMessage>* message_list = NULL;
			core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
			n = buffered_messages.find(aom.id);
			if(n == NULL)
				message_list = new core::list<ActiveObjectMessage>;
				buffered_messages.insert(aom.id, message_list);
				message_list = n->getValue();
			message_list->push_back(aom);
		}
		
		// Route data to every client
		for(core::map<u16, RemoteClient*>::Iterator
			i = m_clients.getIterator();
			i.atEnd()==false; i++)
		{
			RemoteClient *client = i.getNode()->getValue();
			std::string reliable_data;
			std::string unreliable_data;
			// Go through all objects in message buffer
			for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
					j = buffered_messages.getIterator();
					j.atEnd()==false; j++)
				// If object is not known by client, skip it
				u16 id = j.getNode()->getKey();
				if(client->m_known_objects.find(id) == NULL)
					continue;
				// Get message list of object
				core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
				// Go through every message
				for(core::list<ActiveObjectMessage>::Iterator
						k = list->begin(); k != list->end(); k++)
				{
					// Compose the full new data with header
					ActiveObjectMessage aom = *k;
					std::string new_data;
					// Add object id
					char buf[2];
					writeU16((u8*)&buf[0], aom.id);
					new_data.append(buf, 2);
					new_data += serializeString(aom.datastring);
					// Add data to buffer
					if(aom.reliable)
						reliable_data += new_data;
					else
						unreliable_data += new_data;
				}
				reliable_data and unreliable_data are now ready.
				Send them.
			if(reliable_data.size() > 0)
				SharedBuffer<u8> reply(2 + reliable_data.size());
				writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
				memcpy((char*)&reply[2], reliable_data.c_str(),
						reliable_data.size());
				// Send as reliable
				m_con.Send(client->peer_id, 0, reply, true);
			if(unreliable_data.size() > 0)
				SharedBuffer<u8> reply(2 + unreliable_data.size());
				writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
				memcpy((char*)&reply[2], unreliable_data.c_str(),
						unreliable_data.size());
				// Send as unreliable
				m_con.Send(client->peer_id, 0, reply, false);

			/*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
				infostream<<"Server: Size of object message data: "
						<<"reliable: "<<reliable_data.size()
						<<", unreliable: "<<unreliable_data.size()
						<<std::endl;
		}

		// Clear buffered_messages
		for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
				i = buffered_messages.getIterator();
				i.atEnd()==false; i++)
		{
			delete i.getNode()->getValue();
Perttu Ahola's avatar
Perttu Ahola committed
		}
Perttu Ahola's avatar
Perttu Ahola committed

	} // enable_experimental

	/*
		Send queued-for-sending map edit events.
	*/
	{
		// We will be accessing the environment and the connection
		JMutexAutoLock lock(m_env_mutex);
		JMutexAutoLock conlock(m_con_mutex);

		//u32 count = 0;

		// Single change sending is disabled if queue size is not small
		bool disable_single_change_sending = false;
		if(m_unsent_map_edit_queue.size() >= 4)
			disable_single_change_sending = true;

		int event_count = m_unsent_map_edit_queue.size();

		// We'll log the amount of each
		Profiler prof;

		while(m_unsent_map_edit_queue.size() != 0)
		{
			MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
			
			// Players far away from the change are stored here.
			// Instead of sending the changes, MapBlocks are set not sent
			// for them.
			core::list<u16> far_players;

			if(event->type == MEET_ADDNODE)
			{
				//infostream<<"Server: MEET_ADDNODE"<<std::endl;
				prof.add("MEET_ADDNODE", 1);
				if(disable_single_change_sending)
					sendAddNode(event->p, event->n, event->already_known_by_peer,
							&far_players, 5);
				else
					sendAddNode(event->p, event->n, event->already_known_by_peer,
							&far_players, 30);
			}
			else if(event->type == MEET_REMOVENODE)
			{
				//infostream<<"Server: MEET_REMOVENODE"<<std::endl;
				prof.add("MEET_REMOVENODE", 1);
				if(disable_single_change_sending)
					sendRemoveNode(event->p, event->already_known_by_peer,
							&far_players, 5);
				else
					sendRemoveNode(event->p, event->already_known_by_peer,
							&far_players, 30);
			else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
			{
				infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
				prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
			else if(event->type == MEET_OTHER)
			{
				infostream<<"Server: MEET_OTHER"<<std::endl;
				prof.add("MEET_OTHER", 1);
Perttu Ahola's avatar
Perttu Ahola committed
				for(core::map<v3s16, bool>::Iterator
						i = event->modified_blocks.getIterator();
						i.atEnd()==false; i++)
				{
					v3s16 p = i.getNode()->getKey();
					setBlockNotSent(p);
				}
				prof.add("unknown", 1);
				infostream<<"WARNING: Server: Unknown MapEditEvent "
						<<((u32)event->type)<<std::endl;
			}
			if(far_players.size() > 0)
				// Convert list format to that wanted by SetBlocksNotSent
				core::map<v3s16, MapBlock*> modified_blocks2;
				for(core::map<v3s16, bool>::Iterator
						i = event->modified_blocks.getIterator();
						i.atEnd()==false; i++)
				{
					v3s16 p = i.getNode()->getKey();
					modified_blocks2.insert(p,
Perttu Ahola's avatar
Perttu Ahola committed
							m_env->getMap().getBlockNoCreateNoEx(p));
				for(core::list<u16>::Iterator
						i = far_players.begin();
						i != far_players.end(); i++)
				{
					u16 peer_id = *i;
					RemoteClient *client = getClient(peer_id);
					if(client==NULL)
						continue;
					client->SetBlocksNotSent(modified_blocks2);
				}
			/*// Don't send too many at a time
Perttu Ahola's avatar
Perttu Ahola committed
			if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
		if(event_count >= 5){
			infostream<<"Server: MapEditEvents:"<<std::endl;
			prof.print(infostream);
		} else if(event_count != 0){
			verbosestream<<"Server: MapEditEvents:"<<std::endl;
			prof.print(verbosestream);
Perttu Ahola's avatar
Perttu Ahola committed
	/*
		Trigger emergethread (it somehow gets to a non-triggered but
		bysy state sometimes)
	*/
		float &counter = m_emergethread_trigger_timer;
		counter += dtime;
		if(counter >= 2.0)
		{
			counter = 0.0;
			
			m_emergethread.trigger();

			// Update m_enable_rollback_recording here too
			m_enable_rollback_recording =
					g_settings->getBool("enable_rollback_recording");
Perttu Ahola's avatar
Perttu Ahola committed

	// Save map, players and auth stuff
Perttu Ahola's avatar
Perttu Ahola committed
	{
		float &counter = m_savemap_timer;
Perttu Ahola's avatar
Perttu Ahola committed
		counter += dtime;
		if(counter >= g_settings->getFloat("server_map_save_interval"))
Perttu Ahola's avatar
Perttu Ahola committed
		{
			counter = 0.0;
Perttu Ahola's avatar
Perttu Ahola committed

			ScopeProfiler sp(g_profiler, "Server: saving stuff");
Constantin Wenger's avatar
Constantin Wenger committed
			if(m_banmanager.isModified())
				m_banmanager.save();
			// Save changed parts of map
			m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
			m_env->serializePlayers(m_path_world);
			
			// Save environment metadata
			m_env->saveMeta(m_path_world);
Perttu Ahola's avatar
Perttu Ahola committed
		}
	}
}

void Server::Receive()
{
	DSTACK(__FUNCTION_NAME);
Perttu Ahola's avatar
Perttu Ahola committed
	u16 peer_id;
	u32 datasize;
	try{
		{
			JMutexAutoLock conlock(m_con_mutex);
			datasize = m_con.Receive(peer_id, data);
Perttu Ahola's avatar
Perttu Ahola committed
		}

		// This has to be called so that the client list gets synced
		// with the peer list of the connection
		handlePeerChanges();

Perttu Ahola's avatar
Perttu Ahola committed
		ProcessData(*data, datasize, peer_id);
	}
	catch(con::InvalidIncomingDataException &e)
	{
Perttu Ahola's avatar
Perttu Ahola committed
				"InvalidIncomingDataException: what()="
				<<e.what()<<std::endl;
	}
	catch(con::PeerNotFoundException &e)
	{
		//NOTE: This is not needed anymore
		
		// The peer has been disconnected.
		// Find the associated player and remove it.

		/*JMutexAutoLock envlock(m_env_mutex);

		infostream<<"ServerThread: peer_id="<<peer_id
Perttu Ahola's avatar
Perttu Ahola committed
				<<" has apparently closed connection. "
				<<"Removing player."<<std::endl;

Perttu Ahola's avatar
Perttu Ahola committed
		m_env->removePlayer(peer_id);*/
Perttu Ahola's avatar
Perttu Ahola committed
	}
}

void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
	DSTACK(__FUNCTION_NAME);
	// Environment is locked first.
	JMutexAutoLock envlock(m_env_mutex);
	JMutexAutoLock conlock(m_con_mutex);
	
	ScopeProfiler sp(g_profiler, "Server::ProcessData");
	
Perttu Ahola's avatar
Perttu Ahola committed
	try{
		Address address = m_con.GetPeerAddress(peer_id);
		std::string addr_s = 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(addr_s)));
Perttu Ahola's avatar
Perttu Ahola committed
	}
	catch(con::PeerNotFoundException &e)
	{
		infostream<<"Server::ProcessData(): Cancelling: peer "
Perttu Ahola's avatar
Perttu Ahola committed
				<<peer_id<<" not found"<<std::endl;
		return;
	}
	
	std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
	u8 peer_ser_ver = getClient(peer_id)->serialization_version;
Perttu Ahola's avatar
Perttu Ahola committed

	try
	{

	if(datasize < 2)
		return;

	ToServerCommand command = (ToServerCommand)readU16(&data[0]);
	
	if(command == TOSERVER_INIT)
	{
		// [0] u16 TOSERVER_INIT
		// [2] u8 SER_FMT_VER_HIGHEST
		// [3] u8[20] player_name