void Client::addNode(v3s16 p, MapNode n)
{
	//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

	TimeTaker timer1("Client::addNode()");

	core::map<v3s16, MapBlock*> modified_blocks;

	try
	{
		//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
		std::string st = std::string("");
		m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
	}
	catch(InvalidPositionException &e)
	{}

	//TimeTaker timer2("Client::addNode(): updateMeshes");

	for(core::map<v3s16, MapBlock * >::Iterator
			i = modified_blocks.getIterator();
			i.atEnd() == false; i++)
	{
		v3s16 p = i.getNode()->getKey();
		//m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
		addUpdateMeshTaskWithEdge(p);
	}
}
Beispiel #2
0
void Client::removeNode(v3s16 p)
{
	std::map<v3s16, MapBlock*> modified_blocks;

	try {
		m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
	}
	catch(InvalidPositionException &e) {
	}

	for (const auto &modified_block : modified_blocks) {
		addUpdateMeshTaskWithEdge(modified_block.first, false, true);
	}
}
Beispiel #3
0
void Client::removeNode(v3s16 p)
{
	std::map<v3s16, MapBlock*> modified_blocks;

	try {
		m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
	}
	catch(InvalidPositionException &e) {
	}

	for(std::map<v3s16, MapBlock *>::iterator
			i = modified_blocks.begin();
			i != modified_blocks.end(); ++i) {
		addUpdateMeshTaskWithEdge(i->first, false, true);
	}
}
Beispiel #4
0
void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
{
	//TimeTaker timer1("Client::addNode()");

	std::map<v3s16, MapBlock*> modified_blocks;

	try {
		//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
		m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
	}
	catch(InvalidPositionException &e) {
	}

	for (const auto &modified_block : modified_blocks) {
		addUpdateMeshTaskWithEdge(modified_block.first, false, true);
	}
}
Beispiel #5
0
void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
{
	//TimeTaker timer1("Client::addNode()");

	std::map<v3s16, MapBlock*> modified_blocks;

	try {
		//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
		m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
	}
	catch(InvalidPositionException &e) {
	}

	for(std::map<v3s16, MapBlock *>::iterator
			i = modified_blocks.begin();
			i != modified_blocks.end(); ++i) {
		addUpdateMeshTaskWithEdge(i->first, false, true);
	}
}
void Client::removeNode(v3s16 p)
{
	//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

	core::map<v3s16, MapBlock*> modified_blocks;

	try
	{
		//TimeTaker t("removeNodeAndUpdate", m_device);
		m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
	}
	catch(InvalidPositionException &e)
	{
	}

	for(core::map<v3s16, MapBlock * >::Iterator
			i = modified_blocks.getIterator();
			i.atEnd() == false; i++)
	{
		v3s16 p = i.getNode()->getKey();
		//m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
		addUpdateMeshTaskWithEdge(p);
	}
}
/*
	sender_peer_id given to this shall be quaranteed to be a valid peer
*/
void Client::ProcessData(NetworkPacket *pkt) {
	DSTACK(__FUNCTION_NAME);

	ScopeProfiler sp(g_profiler, "Client::ProcessData");

	auto datasize = pkt->getSize();
	auto sender_peer_id = pkt->getPeerId();

	int command;
	MsgpackPacket packet;
	msgpack::unpacked msg;

	if (!con::parse_msgpack_packet(pkt->getString(0), datasize, &packet, &command, &msg)) {
		// invalid packet
		return;
	}

	//infostream<<"Client: received command="<<command<<std::endl;
	m_packetcounter.add((u16)command);

	/*
		If this check is removed, be sure to change the queue
		system to know the ids
	*/
	if(sender_peer_id != PEER_ID_SERVER)
	{
		infostream<<"Client::ProcessData(): Discarding data not "
				"coming from server: peer_id="<<sender_peer_id
				<<std::endl;
		return;
	}

	u8 ser_version = m_server_ser_ver;

	//infostream<<"Client received command="<<(int)command<<std::endl;

	if(command == TOCLIENT_INIT)
	{
		u8 deployed;
		packet[TOCLIENT_INIT_DEPLOYED].convert(&deployed);

		infostream<<"Client: TOCLIENT_INIT received with "
				"deployed="<<((int)deployed&0xff)<<std::endl;

		if(!ser_ver_supported(deployed))
		{
			infostream<<"Client: TOCLIENT_INIT: Server sent "
					<<"unsupported ser_fmt_ver"<<std::endl;
			return;
		}

		m_server_ser_ver = deployed;

		// Set player position
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		packet[TOCLIENT_INIT_SEED].convert(&m_map_seed);
		infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;

		packet[TOCLIENT_INIT_STEP].convert(&m_recommended_send_interval);
		infostream<<"Client: received recommended send interval "
				<<m_recommended_send_interval<<std::endl;

		// TOCLIENT_INIT_POS

		if (m_localserver) {
			Settings settings;
			packet[TOCLIENT_INIT_MAP_PARAMS].convert(&settings);
			m_localserver->getEmergeManager()->params.load(settings);
		}

		// Reply to server
		MSGPACK_PACKET_INIT(TOSERVER_INIT2, 0);
		m_con.Send(PEER_ID_SERVER, 1, buffer, true);

		m_state = LC_Init;

		return;
	}

	if(command == TOCLIENT_ACCESS_DENIED_LEGACY)
	{
		// The server didn't like our password. Note, this needs
		// to be processed even if the serialisation format has
		// not been agreed yet, the same as TOCLIENT_INIT.
		m_access_denied = true;
		packet[TOCLIENT_ACCESS_DENIED_CUSTOM_STRING].convert(&m_access_denied_reason);
		return;
	}

	if(ser_version == SER_FMT_VER_INVALID)
	{
		infostream<<"Client: Server serialization"
				" format invalid or not initialized."
				" Skipping incoming command="<<command<<std::endl;
		return;
	}

	/*
	  Handle runtime commands
	*/
	// there's no sane reason why we shouldn't have a player and
	// almost everyone needs a player reference
	Player *player = m_env.getLocalPlayer();
	if(!player)
		return;

	if(command == TOCLIENT_REMOVENODE)
	{
		v3s16 p = packet[TOCLIENT_REMOVENODE_POS].as<v3s16>();
		removeNode(p, 2); //use light from top node
	}
	else if(command == TOCLIENT_ADDNODE)
	{
		v3s16 p = packet[TOCLIENT_ADDNODE_POS].as<v3s16>();
		MapNode n = packet[TOCLIENT_ADDNODE_NODE].as<MapNode>();
		bool remove_metadata = packet[TOCLIENT_ADDNODE_REMOVE_METADATA].as<bool>();

		addNode(p, n, remove_metadata, 2); //fast add
	}
	else if(command == TOCLIENT_BLOCKDATA)
	{
		v3s16 p = packet[TOCLIENT_BLOCKDATA_POS].as<v3s16>();
		s8 step = 1;
		packet[TOCLIENT_BLOCKDATA_STEP].convert(&step);
		if (step == 1) {

		std::istringstream istr(packet[TOCLIENT_BLOCKDATA_DATA].as<std::string>(), std::ios_base::binary);

		MapBlock *block;

		block = m_env.getMap().getBlockNoCreateNoEx(p);
		bool new_block = !block;
		if (new_block)
			block = new MapBlock(&m_env.getMap(), p, this);

		block->deSerialize(istr, ser_version, false);
		s32 h; // for convert to atomic
		packet[TOCLIENT_BLOCKDATA_HEAT].convert(&h);
		block->heat = h;
		packet[TOCLIENT_BLOCKDATA_HUMIDITY].convert(&h);
		block->humidity = h;


		if (packet.count(TOCLIENT_BLOCKDATA_CONTENT_ONLY))
			block->content_only = packet[TOCLIENT_BLOCKDATA_CONTENT_ONLY].as<content_t>();


		if (m_localserver != NULL) {
			m_localserver->getMap().saveBlock(block);
		}

		if (new_block)
			if (!m_env.getMap().insertBlock(block))
				delete block;

		/*
			//Add it to mesh update queue and set it to be acknowledged after update.
		*/
		//infostream<<"Adding mesh update task for received block "<<p<<std::endl;
		updateMeshTimestampWithEdge(p);
		if (block->content_only != CONTENT_IGNORE && block->content_only != CONTENT_AIR) {
			if (getNodeBlockPos(floatToInt(m_env.getLocalPlayer()->getPosition(), BS)).getDistanceFrom(p) <= 1)
				addUpdateMeshTaskWithEdge(p);
		}

/*
#if !defined(NDEBUG)
		if (m_env.getClientMap().m_block_boundary.size() > 150)
			m_env.getClientMap().m_block_boundary.clear();
		m_env.getClientMap().m_block_boundary[p] = block;
#endif
*/

		}//step

	}
	else if(command == TOCLIENT_INVENTORY)
	{
		std::string datastring = packet[TOCLIENT_INVENTORY_DATA].as<std::string>();
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		player->inventory.deSerialize(is);

		m_inventory_updated = true;

		delete m_inventory_from_server;
		m_inventory_from_server = new Inventory(player->inventory);
		m_inventory_from_server_age = 0.0;
	}
	else if(command == TOCLIENT_TIME_OF_DAY)
	{
		u16 time_of_day = packet[TOCLIENT_TIME_OF_DAY_TIME].as<u16>();
		time_of_day = time_of_day % 24000;
		f32 time_speed = packet[TOCLIENT_TIME_OF_DAY_TIME_SPEED].as<f32>();

		// Update environment
		m_env.setTimeOfDay(time_of_day);
		m_env.setTimeOfDaySpeed(time_speed);
		m_time_of_day_set = true;

		u32 dr = m_env.getDayNightRatio();
		verbosestream<<"Client: time_of_day="<<time_of_day
				<<" time_speed="<<time_speed
				<<" dr="<<dr<<std::endl;
	}
	else if(command == TOCLIENT_CHAT_MESSAGE)
	{
		std::string message = packet[TOCLIENT_CHAT_MESSAGE_DATA].as<std::string>();
		m_chat_queue.push(message);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
	{
		std::vector<u16> removed_objects;
		packet[TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD_REMOVE].convert(&removed_objects);
		for (size_t i = 0; i < removed_objects.size(); ++i)
			m_env.removeActiveObject(removed_objects[i]);

		std::vector<ActiveObjectAddData> added_objects;
		packet[TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD_ADD].convert(&added_objects);
		for (size_t i = 0; i < added_objects.size(); ++i)
			m_env.addActiveObject(added_objects[i].id, added_objects[i].type, added_objects[i].data);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
	{
		ActiveObjectMessages messages;
		packet[TOCLIENT_ACTIVE_OBJECT_MESSAGES_MESSAGES].convert(&messages);
		for (size_t i = 0; i < messages.size(); ++i)
			m_env.processActiveObjectMessage(messages[i].first, messages[i].second);
	}
	else if(command == TOCLIENT_MOVEMENT)
	{
		Player *player = m_env.getLocalPlayer();
		packet[TOCLIENT_MOVEMENT_ACCELERATION_DEFAULT].convert(&player->movement_acceleration_default);
		packet[TOCLIENT_MOVEMENT_ACCELERATION_AIR].convert(&player->movement_acceleration_air);
		packet[TOCLIENT_MOVEMENT_ACCELERATION_FAST].convert(&player->movement_acceleration_fast);
		packet[TOCLIENT_MOVEMENT_SPEED_WALK].convert(&player->movement_speed_walk);
		packet[TOCLIENT_MOVEMENT_SPEED_CROUCH].convert(&player->movement_speed_crouch);
		packet[TOCLIENT_MOVEMENT_SPEED_FAST].convert(&player->movement_speed_fast);
		packet[TOCLIENT_MOVEMENT_SPEED_CLIMB].convert(&player->movement_speed_climb);
		packet[TOCLIENT_MOVEMENT_SPEED_JUMP].convert(&player->movement_speed_jump);
		packet[TOCLIENT_MOVEMENT_LIQUID_FLUIDITY].convert(&player->movement_liquid_fluidity);
		packet[TOCLIENT_MOVEMENT_LIQUID_FLUIDITY_SMOOTH].convert(&player->movement_liquid_fluidity_smooth);
		packet[TOCLIENT_MOVEMENT_LIQUID_SINK].convert(&player->movement_liquid_sink);
		packet[TOCLIENT_MOVEMENT_GRAVITY].convert(&player->movement_gravity);
	}
	else if(command == TOCLIENT_HP)
	{
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		u8 oldhp = player->hp;
		u8 hp = packet[TOCLIENT_HP_HP].as<u8>();
		player->hp = hp;

		if(hp < oldhp)
		{
			// Add to ClientEvent queue
			ClientEvent event;
			event.type = CE_PLAYER_DAMAGE;
			event.player_damage.amount = oldhp - hp;
			m_client_event_queue.push(event);
		}
	}
	else if(command == TOCLIENT_BREATH)
	{
		Player *player = m_env.getLocalPlayer();
		player->setBreath(packet[TOCLIENT_BREATH_BREATH].as<u16>()) ;
	}
	else if(command == TOCLIENT_MOVE_PLAYER)
	{
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		v3f pos = packet[TOCLIENT_MOVE_PLAYER_POS].as<v3f>();
		f32 pitch = packet[TOCLIENT_MOVE_PLAYER_PITCH].as<f32>();
		f32 yaw = packet[TOCLIENT_MOVE_PLAYER_YAW].as<f32>();
		player->setPosition(pos);

		infostream<<"Client got TOCLIENT_MOVE_PLAYER"
				<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
				<<" pitch="<<pitch
				<<" yaw="<<yaw
				<<std::endl;

		/*
			Add to ClientEvent queue.
			This has to be sent to the main program because otherwise
			it would just force the pitch and yaw values to whatever
			the camera points to.
		*/
		ClientEvent event;
		event.type = CE_PLAYER_FORCE_MOVE;
		event.player_force_move.pitch = pitch;
		event.player_force_move.yaw = yaw;
		m_client_event_queue.push(event);

		// Ignore damage for a few seconds, so that the player doesn't
		// get damage from falling on ground
		m_ignore_damage_timer = 3.0;
	}
	else if(command == TOCLIENT_DEATHSCREEN)
	{
		bool set_camera_point_target = packet[TOCLIENT_DEATHSCREEN_SET_CAMERA].as<bool>();
		v3f camera_point_target = packet[TOCLIENT_DEATHSCREEN_CAMERA_POINT].as<v3f>();

		ClientEvent event;
		event.type = CE_DEATHSCREEN;
		event.deathscreen.set_camera_point_target = set_camera_point_target;
		event.deathscreen.camera_point_target_x = camera_point_target.X;
		event.deathscreen.camera_point_target_y = camera_point_target.Y;
		event.deathscreen.camera_point_target_z = camera_point_target.Z;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_ANNOUNCE_MEDIA)
	{
		if (m_media_downloader == NULL ||
				m_media_downloader->isStarted()) {
			const char *problem = m_media_downloader ?
				"we already saw another announcement" :
				"all media has been received already";
			errorstream<<"Client: Received media announcement but "
				<<problem<<"!"
				<<std::endl;
			return;
		}

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		MediaAnnounceList announce_list;
		packet[TOCLIENT_ANNOUNCE_MEDIA_LIST].convert(&announce_list);
		for (size_t i = 0; i < announce_list.size(); ++i)
			m_media_downloader->addFile(announce_list[i].first, base64_decode(announce_list[i].second));

		std::vector<std::string> remote_media;
		std::string remote_media_string = packet[TOCLIENT_ANNOUNCE_MEDIA_REMOTE_SERVER].as<std::string>();
		Strfnd sf(remote_media_string);
		while(!sf.atend()) {
			std::string baseurl = trim(sf.next(","));
			if(baseurl != "")
				m_media_downloader->addRemoteServer(baseurl);
		}

		m_media_downloader->step(this);
	}
	else if(command == TOCLIENT_MEDIA)
	{
		MediaData media_data;
		packet[TOCLIENT_MEDIA_MEDIA].convert(&media_data);

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		for(size_t i = 0; i < media_data.size(); ++i)
			m_media_downloader->conventionalTransferDone(
					media_data[i].first, media_data[i].second, this);
	}
	else if(command == TOCLIENT_NODEDEF)
	{
		infostream<<"Client: Received node definitions: packet size: "
				<<datasize<<std::endl;

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		packet[TOCLIENT_NODEDEF_DEFINITIONS].convert(m_nodedef);
		m_nodedef_received = true;
	}
	else if(command == TOCLIENT_ITEMDEF)
	{
		infostream<<"Client: Received item definitions: packet size: "
				<<datasize<<std::endl;

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		packet[TOCLIENT_ITEMDEF_DEFINITIONS].convert(m_itemdef);
		m_itemdef_received = true;
	}
	else if(command == TOCLIENT_PLAY_SOUND)
	{
		s32 server_id = packet[TOCLIENT_PLAY_SOUND_ID].as<s32>();
		std::string name = packet[TOCLIENT_PLAY_SOUND_NAME].as<std::string>();
		float gain = packet[TOCLIENT_PLAY_SOUND_GAIN].as<f32>();
		int type = packet[TOCLIENT_PLAY_SOUND_TYPE].as<u8>(); // 0=local, 1=positional, 2=object
		v3f pos = packet[TOCLIENT_PLAY_SOUND_POS].as<v3f>();
		u16 object_id = packet[TOCLIENT_PLAY_SOUND_OBJECT_ID].as<u16>();
		bool loop = packet[TOCLIENT_PLAY_SOUND_LOOP].as<bool>();
		// Start playing
		int client_id = -1;
		switch(type){
		case 0: // local
			client_id = m_sound->playSound(name, loop, gain);
			break;
		case 1: // positional
			client_id = m_sound->playSoundAt(name, loop, gain, pos);
			break;
		case 2: { // object
			ClientActiveObject *cao = m_env.getActiveObject(object_id);
			if(cao)
				pos = cao->getPosition();
			client_id = m_sound->playSoundAt(name, loop, gain, pos);
			// TODO: Set up sound to move with object
			break; }
		default:
			break;
		}
		if(client_id != -1){
			m_sounds_server_to_client[server_id] = client_id;
			m_sounds_client_to_server[client_id] = server_id;
			if(object_id != 0)
				m_sounds_to_objects[client_id] = object_id;
		}
	}
	else if(command == TOCLIENT_STOP_SOUND)
	{
		s32 server_id = packet[TOCLIENT_STOP_SOUND_ID].as<s32>();
		std::map<s32, int>::iterator i =
				m_sounds_server_to_client.find(server_id);
		if(i != m_sounds_server_to_client.end()){
			int client_id = i->second;
			m_sound->stopSound(client_id);
		}
	}
	else if(command == TOCLIENT_PRIVILEGES)
	{
		packet[TOCLIENT_PRIVILEGES_PRIVILEGES].convert(&m_privileges);
	}
	else if(command == TOCLIENT_INVENTORY_FORMSPEC)
	{
		// Store formspec in LocalPlayer
		player->inventory_formspec = packet[TOCLIENT_INVENTORY_FORMSPEC_DATA].as<std::string>();
	}
	else if(command == TOCLIENT_DETACHED_INVENTORY)
	{
		std::string name = packet[TOCLIENT_DETACHED_INVENTORY_NAME].as<std::string>();
		std::string datastring = packet[TOCLIENT_DETACHED_INVENTORY_DATA].as<std::string>();
		std::istringstream is(datastring, std::ios_base::binary);

		infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;

		Inventory *inv = NULL;
		if(m_detached_inventories.count(name) > 0)
			inv = m_detached_inventories[name];
		else{
			inv = new Inventory(m_itemdef);
			m_detached_inventories[name] = inv;
		}
		inv->deSerialize(is);
	}
	else if(command == TOCLIENT_SHOW_FORMSPEC)
	{
		std::string formspec = packet[TOCLIENT_SHOW_FORMSPEC_DATA].as<std::string>();
		std::string formname = packet[TOCLIENT_SHOW_FORMSPEC_NAME].as<std::string>();

		ClientEvent event;
		event.type = CE_SHOW_FORMSPEC;
		// pointer is required as event is a struct only!
		// adding a std:string to a struct isn't possible
		event.show_formspec.formspec = new std::string(formspec);
		event.show_formspec.formname = new std::string(formname);
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_SPAWN_PARTICLE)
	{
		v3f pos = packet[TOCLIENT_SPAWN_PARTICLE_POS].as<v3f>();
		v3f vel = packet[TOCLIENT_SPAWN_PARTICLE_VELOCITY].as<v3f>();
		v3f acc = packet[TOCLIENT_SPAWN_PARTICLE_ACCELERATION].as<v3f>();
		float expirationtime = packet[TOCLIENT_SPAWN_PARTICLE_EXPIRATIONTIME].as<float>();
		float size = packet[TOCLIENT_SPAWN_PARTICLE_SIZE].as<float>();
		bool collisiondetection = packet[TOCLIENT_SPAWN_PARTICLE_COLLISIONDETECTION].as<bool>();
		std::string texture = packet[TOCLIENT_SPAWN_PARTICLE_TEXTURE].as<std::string>();
		bool vertical = packet[TOCLIENT_SPAWN_PARTICLE_VERTICAL].as<bool>();

		ClientEvent event;
		event.type = CE_SPAWN_PARTICLE;
		event.spawn_particle.pos = new v3f (pos);
		event.spawn_particle.vel = new v3f (vel);
		event.spawn_particle.acc = new v3f (acc);

		event.spawn_particle.expirationtime = expirationtime;
		event.spawn_particle.size = size;
		event.spawn_particle.collisiondetection =
				collisiondetection;
		event.spawn_particle.vertical = vertical;
		event.spawn_particle.texture = new std::string(texture);

		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
	{
		u16 amount;
		float spawntime, minexptime, maxexptime, minsize, maxsize;
		v3f minpos, maxpos, minvel, maxvel, minacc, maxacc;
		bool collisiondetection, vertical;
		u32 id;
		std::string texture;

		packet[TOCLIENT_ADD_PARTICLESPAWNER_AMOUNT].convert(&amount);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_SPAWNTIME].convert(&spawntime);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINPOS].convert(&minpos);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXPOS].convert(&maxpos);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINVEL].convert(&minvel);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXVEL].convert(&maxvel);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINACC].convert(&minacc);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXACC].convert(&maxacc);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINEXPTIME].convert(&minexptime);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXEXPTIME].convert(&maxexptime);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINSIZE].convert(&minsize);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXSIZE].convert(&maxsize);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_COLLISIONDETECTION].convert(&collisiondetection);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_TEXTURE].convert(&texture);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_VERTICAL].convert(&vertical);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_ID].convert(&id);

		ClientEvent event;
		event.type = CE_ADD_PARTICLESPAWNER;
		event.add_particlespawner.amount = amount;
		event.add_particlespawner.spawntime = spawntime;

		event.add_particlespawner.minpos = new v3f (minpos);
		event.add_particlespawner.maxpos = new v3f (maxpos);
		event.add_particlespawner.minvel = new v3f (minvel);
		event.add_particlespawner.maxvel = new v3f (maxvel);
		event.add_particlespawner.minacc = new v3f (minacc);
		event.add_particlespawner.maxacc = new v3f (maxacc);

		event.add_particlespawner.minexptime = minexptime;
		event.add_particlespawner.maxexptime = maxexptime;
		event.add_particlespawner.minsize = minsize;
		event.add_particlespawner.maxsize = maxsize;
		event.add_particlespawner.collisiondetection = collisiondetection;
		event.add_particlespawner.vertical = vertical;
		event.add_particlespawner.texture = new std::string(texture);
		event.add_particlespawner.id = id;

		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
	{
		u32 id = packet[TOCLIENT_DELETE_PARTICLESPAWNER_ID].as<u32>();

		ClientEvent event;
		event.type = CE_DELETE_PARTICLESPAWNER;
		event.delete_particlespawner.id = id;

		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUDADD)
	{
		//std::string datastring((char *)&data[2], datasize - 2);
		//std::istringstream is(datastring, std::ios_base::binary);

		u32 id, number, item, dir;
		u8 type;
		v2f pos, scale, align, offset;
		std::string name, text;
		v3f world_pos;
		v2s32 size;

		packet[TOCLIENT_HUDADD_ID].convert(&id);
		packet[TOCLIENT_HUDADD_TYPE].convert(&type);
		packet[TOCLIENT_HUDADD_POS].convert(&pos);
		packet[TOCLIENT_HUDADD_NAME].convert(&name);
		packet[TOCLIENT_HUDADD_SCALE].convert(&scale);
		packet[TOCLIENT_HUDADD_TEXT].convert(&text);
		packet[TOCLIENT_HUDADD_NUMBER].convert(&number);
		packet[TOCLIENT_HUDADD_ITEM].convert(&item);
		packet[TOCLIENT_HUDADD_DIR].convert(&dir);
		packet[TOCLIENT_HUDADD_ALIGN].convert(&align);
		packet[TOCLIENT_HUDADD_OFFSET].convert(&offset);
		packet[TOCLIENT_HUDADD_WORLD_POS].convert(&world_pos);
		packet[TOCLIENT_HUDADD_SIZE].convert(&size);

		ClientEvent event;
		event.type = CE_HUDADD;
		event.hudadd.id     = id;
		event.hudadd.type   = type;
		event.hudadd.pos    = new v2f(pos);
		event.hudadd.name   = new std::string(name);
		event.hudadd.scale  = new v2f(scale);
		event.hudadd.text   = new std::string(text);
		event.hudadd.number = number;
		event.hudadd.item   = item;
		event.hudadd.dir    = dir;
		event.hudadd.align  = new v2f(align);
		event.hudadd.offset = new v2f(offset);
		event.hudadd.world_pos = new v3f(world_pos);
		event.hudadd.size      = new v2s32(size);
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUDRM)
	{
		u32 id = packet[TOCLIENT_HUDRM_ID].as<u32>();

		ClientEvent event;
		event.type = CE_HUDRM;
		event.hudrm.id = id;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUDCHANGE)
	{
		std::string sdata;
		v2f v2fdata;
		v3f v3fdata;
		v2s32 v2s32data;
		u32 intdata = 0;

		u32 id = packet[TOCLIENT_HUDCHANGE_ID].as<u32>();
		u8 stat = packet[TOCLIENT_HUDCHANGE_STAT].as<int>();

		if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
				stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
			packet[TOCLIENT_HUDCHANGE_V2F].convert(&v2fdata);
		else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
			packet[TOCLIENT_HUDCHANGE_STRING].convert(&sdata);
		else if (stat == HUD_STAT_WORLD_POS)
			packet[TOCLIENT_HUDCHANGE_V3F].convert(&v3fdata);
		else if (stat == HUD_STAT_SIZE)
			packet[TOCLIENT_HUDCHANGE_V2S32].convert(&v2s32data);
		else
			packet[TOCLIENT_HUDCHANGE_U32].convert(&intdata);

		ClientEvent event;
		event.type = CE_HUDCHANGE;
		event.hudchange.id      = id;
		event.hudchange.stat    = (HudElementStat)stat;
		event.hudchange.v2fdata = new v2f(v2fdata);
		event.hudchange.v3fdata = new v3f(v3fdata);
		event.hudchange.sdata   = new std::string(sdata);
		event.hudchange.data    = intdata;
		event.hudchange.v2s32data = new v2s32(v2s32data);
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUD_SET_FLAGS)
	{
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		u32 flags = packet[TOCLIENT_HUD_SET_FLAGS_FLAGS].as<u32>();
		u32 mask = packet[TOCLIENT_HUD_SET_FLAGS_MASK].as<u32>();

		player->hud_flags &= ~mask;
		player->hud_flags |= flags;
	}
	else if(command == TOCLIENT_HUD_SET_PARAM)
	{
		u16 param = packet[TOCLIENT_HUD_SET_PARAM_ID].as<u16>();
		std::string value = packet[TOCLIENT_HUD_SET_PARAM_VALUE].as<std::string>();

		if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
			s32 hotbar_itemcount = readS32((u8*) value.c_str());
			if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
				player->hud_hotbar_itemcount = hotbar_itemcount;
		} else if (param == HUD_PARAM_HOTBAR_IMAGE) {
			((LocalPlayer *) player)->hotbar_image = value;
		} else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
			((LocalPlayer *) player)->hotbar_selected_image = value;
		}
	}
/*
	else if(command == TOCLIENT_ANIMATIONS)
	{
		LocalPlayer *player = m_env.getLocalPlayer();
		packet[TOCLIENT_ANIMATIONS_DEFAULT_START].convert(&player->animation_default_start);
		packet[TOCLIENT_ANIMATIONS_DEFAULT_STOP].convert(&player->animation_default_stop);
		packet[TOCLIENT_ANIMATIONS_WALK_START].convert(&player->animation_walk_start);
		packet[TOCLIENT_ANIMATIONS_WALK_STOP].convert(&player->animation_walk_stop);
		packet[TOCLIENT_ANIMATIONS_DIG_START].convert(&player->animation_dig_start);
		packet[TOCLIENT_ANIMATIONS_DIG_STOP].convert(&player->animation_dig_stop);
		packet[TOCLIENT_ANIMATIONS_WD_START].convert(&player->animation_wd_start);
		packet[TOCLIENT_ANIMATIONS_WD_STOP].convert(&player->animation_wd_stop);
	}
*/
	else if(command == TOCLIENT_SET_SKY)
	{
		video::SColor *bgcolor = new video::SColor(packet[TOCLIENT_SET_SKY_COLOR].as<video::SColor>());
		std::string *type = new std::string(packet[TOCLIENT_SET_SKY_TYPE].as<std::string>());
		std::vector<std::string> *params = new std::vector<std::string>;
		packet[TOCLIENT_SET_SKY_PARAMS].convert(params);

		ClientEvent event;
		event.type            = CE_SET_SKY;
		event.set_sky.bgcolor = bgcolor;
		event.set_sky.type    = type;
		event.set_sky.params  = params;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
	{
		bool do_override;
		float day_night_ratio_f;
		packet[TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO_DO].convert(&do_override);
		packet[TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO_VALUE].convert(&day_night_ratio_f);

		ClientEvent event;
		event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
		event.override_day_night_ratio.do_override = do_override;
		event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
	{
		LocalPlayer *player = m_env.getLocalPlayer();
		if(!player)
			return;

		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_IDLE].convert(&player->local_animations[0]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_WALK].convert(&player->local_animations[1]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_DIG].convert(&player->local_animations[2]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_WALKDIG].convert(&player->local_animations[3]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_FRAME_SPEED].convert(&player->local_animation_speed);
	}
	else if(command == TOCLIENT_EYE_OFFSET)
	{
		LocalPlayer *player = m_env.getLocalPlayer();
		if(!player)
			return;

		packet[TOCLIENT_EYE_OFFSET_FIRST].convert(&player->eye_offset_first);
		packet[TOCLIENT_EYE_OFFSET_THIRD].convert(&player->eye_offset_third);
	}
	else
	{
		infostream<<"Client: Ignoring unknown command "
				<<command<<std::endl;
	}
}
/*
	sender_peer_id given to this shall be quaranteed to be a valid peer
*/
void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
{
	DSTACK(__FUNCTION_NAME);

	// Ignore packets that don't even fit a command
	if(datasize < 2)
	{
		m_packetcounter.add(60000);
		return;
	}

	ToClientCommand command = (ToClientCommand)readU16(&data[0]);

	//infostream<<"Client: received command="<<command<<std::endl;
	m_packetcounter.add((u16)command);

	/*
		If this check is removed, be sure to change the queue
		system to know the ids
	*/
	if(sender_peer_id != PEER_ID_SERVER)
	{
		infostream<<"Client::ProcessData(): Discarding data not "
				"coming from server: peer_id="<<sender_peer_id
				<<std::endl;
		return;
	}

	u8 ser_version = m_server_ser_ver;

	//infostream<<"Client received command="<<(int)command<<std::endl;

	if(command == TOCLIENT_INIT)
	{
		if(datasize < 3)
			return;

		u8 deployed = data[2];

		infostream<<"Client: TOCLIENT_INIT received with "
				"deployed="<<((int)deployed&0xff)<<std::endl;

		if(deployed < SER_FMT_VER_LOWEST
				|| deployed > SER_FMT_VER_HIGHEST)
		{
			infostream<<"Client: TOCLIENT_INIT: Server sent "
					<<"unsupported ser_fmt_ver"<<std::endl;
			return;
		}

		m_server_ser_ver = deployed;

		// Get player position
		v3s16 playerpos_s16(0, BS*2+BS*20, 0);
		if(datasize >= 2+1+6)
			playerpos_s16 = readV3S16(&data[2+1]);
		v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			// Set player position
			Player *player = m_env.getLocalPlayer();
			assert(player != NULL);
			player->setPosition(playerpos_f);
		}

		if(datasize >= 2+1+6+8)
		{
			// Get map seed
			m_map_seed = readU64(&data[2+1+6]);
			infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
		}

		// Reply to server
		u32 replysize = 2;
		SharedBuffer<u8> reply(replysize);
		writeU16(&reply[0], TOSERVER_INIT2);
		// Send as reliable
		m_con.Send(PEER_ID_SERVER, 1, reply, true);

		return;
	}

	if(command == TOCLIENT_ACCESS_DENIED)
	{
		// The server didn't like our password. Note, this needs
		// to be processed even if the serialisation format has
		// not been agreed yet, the same as TOCLIENT_INIT.
		m_access_denied = true;
		m_access_denied_reason = L"Unknown";
		if(datasize >= 4)
		{
			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);
			m_access_denied_reason = deSerializeWideString(is);
		}
		return;
	}

	if(ser_version == SER_FMT_VER_INVALID)
	{
		infostream<<"Client: Server serialization"
				" format invalid or not initialized."
				" Skipping incoming command="<<command<<std::endl;
		return;
	}

	// Just here to avoid putting the two if's together when
	// making some copypasta
	{}

	if(command == TOCLIENT_REMOVENODE)
	{
		if(datasize < 8)
			return;
		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		//TimeTaker t1("TOCLIENT_REMOVENODE");

		// This will clear the cracking animation after digging
		((ClientMap&)m_env.getMap()).clearTempMod(p);

		removeNode(p);
	}
	else if(command == TOCLIENT_ADDNODE)
	{
		if(datasize < 8 + MapNode::serializedLength(ser_version))
			return;

		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		//TimeTaker t1("TOCLIENT_ADDNODE");

		MapNode n;
		n.deSerialize(&data[8], ser_version);

		addNode(p, n);
	}
	else if(command == TOCLIENT_BLOCKDATA)
	{
		// Ignore too small packet
		if(datasize < 8)
			return;

		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		/*infostream<<"Client: Thread: BLOCKDATA for ("
				<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
		/*infostream<<"Client: Thread: BLOCKDATA for ("
				<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/

		std::string datastring((char*)&data[8], datasize-8);
		std::istringstream istr(datastring, std::ios_base::binary);

		MapSector *sector;
		MapBlock *block;

		v2s16 p2d(p.X, p.Z);
		sector = m_env.getMap().emergeSector(p2d);

		assert(sector->getPos() == p2d);

		//TimeTaker timer("MapBlock deSerialize");
		// 0ms

		block = sector->getBlockNoCreateNoEx(p.Y);
		if(block)
		{
			/*
				Update an existing block
			*/
			//infostream<<"Updating"<<std::endl;
			block->deSerialize(istr, ser_version);
		}
		else
		{
			/*
				Create a new block
			*/
			//infostream<<"Creating new"<<std::endl;
			block = new MapBlock(&m_env.getMap(), p);
			block->deSerialize(istr, ser_version);
			sector->insertBlock(block);

			//DEBUG
			/*NodeMod mod;
			mod.type = NODEMOD_CHANGECONTENT;
			mod.param = CONTENT_MESE;
			block->setTempMod(v3s16(8,10,8), mod);
			block->setTempMod(v3s16(8,9,8), mod);
			block->setTempMod(v3s16(8,8,8), mod);
			block->setTempMod(v3s16(8,7,8), mod);
			block->setTempMod(v3s16(8,6,8), mod);*/
		}

#if 0
		/*
			Acknowledge block
		*/
		/*
			[0] u16 command
			[2] u8 count
			[3] v3s16 pos_0
			[3+6] v3s16 pos_1
			...
		*/
		u32 replysize = 2+1+6;
		SharedBuffer<u8> reply(replysize);
		writeU16(&reply[0], TOSERVER_GOTBLOCKS);
		reply[2] = 1;
		writeV3S16(&reply[3], p);
		// Send as reliable
		m_con.Send(PEER_ID_SERVER, 1, reply, true);
#endif

		/*
			Update Mesh of this block and blocks at x-, y- and z-.
			Environment should not be locked as it interlocks with the
			main thread, from which is will want to retrieve textures.
		*/

		//m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
		/*
			Add it to mesh update queue and set it to be acknowledged after update.
		*/
		//infostream<<"Adding mesh update task for received block"<<std::endl;
		addUpdateMeshTaskWithEdge(p, true);
	}
	else if(command == TOCLIENT_PLAYERPOS)
	{
		infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
				<<std::endl;
		/*u16 our_peer_id;
		{
			//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
			our_peer_id = m_con.GetPeerID();
		}
		// Cancel if we don't have a peer id
		if(our_peer_id == PEER_ID_INEXISTENT){
			infostream<<"TOCLIENT_PLAYERPOS cancelled: "
					"we have no peer id"
					<<std::endl;
			return;
		}*/

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			u32 player_size = 2+12+12+4+4;

			u32 player_count = (datasize-2) / player_size;
			u32 start = 2;
			for(u32 i=0; i<player_count; i++)
			{
				u16 peer_id = readU16(&data[start]);

				Player *player = m_env.getPlayer(peer_id);

				// Skip if player doesn't exist
				if(player == NULL)
				{
					start += player_size;
					continue;
				}

				// Skip if player is local player
				if(player->isLocal())
				{
					start += player_size;
					continue;
				}

				v3s32 ps = readV3S32(&data[start+2]);
				v3s32 ss = readV3S32(&data[start+2+12]);
				s32 pitch_i = readS32(&data[start+2+12+12]);
				s32 yaw_i = readS32(&data[start+2+12+12+4]);
				/*infostream<<"Client: got "
						<<"pitch_i="<<pitch_i
						<<" yaw_i="<<yaw_i<<std::endl;*/
				f32 pitch = (f32)pitch_i / 100.0;
				f32 yaw = (f32)yaw_i / 100.0;
				v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
				v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
				player->setPosition(position);
				player->setSpeed(speed);
				player->setPitch(pitch);
				player->setYaw(yaw);

				/*infostream<<"Client: player "<<peer_id
						<<" pitch="<<pitch
						<<" yaw="<<yaw<<std::endl;*/

				start += player_size;
			}
		} //envlock
	}
	else if(command == TOCLIENT_PLAYERINFO)
	{
		u16 our_peer_id;
		{
			//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
			our_peer_id = m_con.GetPeerID();
		}
		// Cancel if we don't have a peer id
		if(our_peer_id == PEER_ID_INEXISTENT){
			infostream<<"TOCLIENT_PLAYERINFO cancelled: "
					"we have no peer id"
					<<std::endl;
			return;
		}

		//infostream<<"Client: Server reports players:"<<std::endl;

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			u32 item_size = 2+PLAYERNAME_SIZE;
			u32 player_count = (datasize-2) / item_size;
			u32 start = 2;
			// peer_ids
			core::list<u16> players_alive;
			for(u32 i=0; i<player_count; i++)
			{
				// Make sure the name ends in '\0'
				data[start+2+20-1] = 0;

				u16 peer_id = readU16(&data[start]);

				players_alive.push_back(peer_id);

				/*infostream<<"peer_id="<<peer_id
						<<" name="<<((char*)&data[start+2])<<std::endl;*/

				// Don't update the info of the local player
				if(peer_id == our_peer_id)
				{
					start += item_size;
					continue;
				}

				Player *player = m_env.getPlayer(peer_id);

				// Create a player if it doesn't exist
				if(player == NULL)
				{
					player = new RemotePlayer(
							m_device->getSceneManager()->getRootSceneNode(),
							m_device,
							-1);
					player->peer_id = peer_id;
					m_env.addPlayer(player);
					infostream<<"Client: Adding new player "
							<<peer_id<<std::endl;
				}

				player->updateName((char*)&data[start+2]);

				start += item_size;
			}

			/*
				Remove those players from the environment that
				weren't listed by the server.
			*/
			//infostream<<"Removing dead players"<<std::endl;
			core::list<Player*> players = m_env.getPlayers();
			core::list<Player*>::Iterator ip;
			for(ip=players.begin(); ip!=players.end(); ip++)
			{
				// Ingore local player
				if((*ip)->isLocal())
					continue;

				// Warn about a special case
				if((*ip)->peer_id == 0)
				{
					infostream<<"Client: Removing "
							"dead player with id=0"<<std::endl;
				}

				bool is_alive = false;
				core::list<u16>::Iterator i;
				for(i=players_alive.begin(); i!=players_alive.end(); i++)
				{
					if((*ip)->peer_id == *i)
					{
						is_alive = true;
						break;
					}
				}
				/*infostream<<"peer_id="<<((*ip)->peer_id)
						<<" is_alive="<<is_alive<<std::endl;*/
				if(is_alive)
					continue;
				infostream<<"Removing dead player "<<(*ip)->peer_id
						<<std::endl;
				m_env.removePlayer((*ip)->peer_id);
			}
		} //envlock
	}
	else if(command == TOCLIENT_SECTORMETA)
	{
		infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
#if 0
		/*
			[0] u16 command
			[2] u8 sector count
			[3...] v2s16 pos + sector metadata
		*/
		if(datasize < 3)
			return;

		//infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);

			u8 buf[4];

			is.read((char*)buf, 1);
			u16 sector_count = readU8(buf);

			//infostream<<"sector_count="<<sector_count<<std::endl;

			for(u16 i=0; i<sector_count; i++)
			{
				// Read position
				is.read((char*)buf, 4);
				v2s16 pos = readV2S16(buf);
				/*infostream<<"Client: deserializing sector at "
						<<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
				// Create sector
				assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
				((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
			}
		} //envlock
#endif
	}
	else if(command == TOCLIENT_INVENTORY)
	{
		if(datasize < 3)
			return;

		//TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);

		{ //envlock
			//TimeTaker t2("mutex locking", m_device);
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
			//t2.stop();

			//TimeTaker t3("istringstream init", m_device);
			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);
			//t3.stop();

			//m_env.printPlayers(infostream);

			//TimeTaker t4("player get", m_device);
			Player *player = m_env.getLocalPlayer();
			assert(player != NULL);
			//t4.stop();

			//TimeTaker t1("inventory.deSerialize()", m_device);
			player->inventory.deSerialize(is);
			//t1.stop();

			m_inventory_updated = true;

			//infostream<<"Client got player inventory:"<<std::endl;
			//player->inventory.print(infostream);
		}
	}
	//DEBUG
	else if(command == TOCLIENT_OBJECTDATA)
	{
		// Strip command word and create a stringstream
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		u8 buf[12];

		/*
			Read players
		*/

		is.read((char*)buf, 2);
		u16 playercount = readU16(buf);

		for(u16 i=0; i<playercount; i++)
		{
			is.read((char*)buf, 2);
			u16 peer_id = readU16(buf);
			is.read((char*)buf, 12);
			v3s32 p_i = readV3S32(buf);
			is.read((char*)buf, 12);
			v3s32 s_i = readV3S32(buf);
			is.read((char*)buf, 4);
			s32 pitch_i = readS32(buf);
			is.read((char*)buf, 4);
			s32 yaw_i = readS32(buf);

			Player *player = m_env.getPlayer(peer_id);

			// Skip if player doesn't exist
			if(player == NULL)
			{
				continue;
			}

			// Skip if player is local player
			if(player->isLocal())
			{
				continue;
			}

			f32 pitch = (f32)pitch_i / 100.0;
			f32 yaw = (f32)yaw_i / 100.0;
			v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
			v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);

			player->setPosition(position);
			player->setSpeed(speed);
			player->setPitch(pitch);
			player->setYaw(yaw);
		}

		/*
			Read block objects
			NOTE: Deprecated stuff
		*/

		// Read active block count
		u16 blockcount = readU16(is);
		if(blockcount != 0){
			infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
					"not supported"<<std::endl;
			return;
		}
	}
	else if(command == TOCLIENT_TIME_OF_DAY)
	{
		if(datasize < 4)
			return;

		u16 time_of_day = readU16(&data[2]);
		time_of_day = time_of_day % 24000;
		//infostream<<"Client: time_of_day="<<time_of_day<<std::endl;

		/*
			time_of_day:
			0 = midnight
			12000 = midday
		*/
		{
			m_env.setTimeOfDay(time_of_day);

			u32 dr = m_env.getDayNightRatio();

			infostream<<"Client: time_of_day="<<time_of_day
					<<", dr="<<dr
					<<std::endl;
		}

	}
	else if(command == TOCLIENT_CHAT_MESSAGE)
	{
		/*
			u16 command
			u16 length
			wstring message
		*/
		u8 buf[6];
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		// Read stuff
		is.read((char*)buf, 2);
		u16 len = readU16(buf);

		std::wstring message;
		for(u16 i=0; i<len; i++)
		{
			is.read((char*)buf, 2);
			message += (wchar_t)readU16(buf);
		}

		/*infostream<<"Client received chat message: "
				<<wide_to_narrow(message)<<std::endl;*/

		m_chat_queue.push_back(message);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
	{
		//if(g_settings->getBool("enable_experimental"))
		{
			/*
				u16 command
				u16 count of removed objects
				for all removed objects {
					u16 id
				}
				u16 count of added objects
				for all added objects {
					u16 id
					u8 type
					u32 initialization data length
					string initialization data
				}
			*/

			char buf[6];
			// Get all data except the command number
			std::string datastring((char*)&data[2], datasize-2);
			// Throw them in an istringstream
			std::istringstream is(datastring, std::ios_base::binary);

			// Read stuff

			// Read removed objects
			is.read(buf, 2);
			u16 removed_count = readU16((u8*)buf);
			for(u16 i=0; i<removed_count; i++)
			{
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				// Remove it
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.removeActiveObject(id);
				}
			}

			// Read added objects
			is.read(buf, 2);
			u16 added_count = readU16((u8*)buf);
			for(u16 i=0; i<added_count; i++)
			{
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				is.read(buf, 1);
				u8 type = readU8((u8*)buf);
				std::string data = deSerializeLongString(is);
				// Add it
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.addActiveObject(id, type, data);
				}
			}
		}
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
	{
		//if(g_settings->getBool("enable_experimental"))
		{
			/*
				u16 command
				for all objects
				{
					u16 id
					u16 message length
					string message
				}
			*/
			char buf[6];
			// Get all data except the command number
			std::string datastring((char*)&data[2], datasize-2);
			// Throw them in an istringstream
			std::istringstream is(datastring, std::ios_base::binary);

			while(is.eof() == false)
			{
				// Read stuff
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				if(is.eof())
					break;
				is.read(buf, 2);
				u16 message_size = readU16((u8*)buf);
				std::string message;
				message.reserve(message_size);
				for(u16 i=0; i<message_size; i++)
				{
					is.read(buf, 1);
					message.append(buf, 1);
				}
				// Pass on to the environment
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.processActiveObjectMessage(id, message);
				}
			}
		}
	}
	else if(command == TOCLIENT_HP)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		assert(player != NULL);
		u8 hp = readU8(is);
		player->hp = hp;
	}
	else if(command == TOCLIENT_MOVE_PLAYER)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		assert(player != NULL);
		v3f pos = readV3F1000(is);
		f32 pitch = readF1000(is);
		f32 yaw = readF1000(is);
		player->setPosition(pos);
		/*player->setPitch(pitch);
		player->setYaw(yaw);*/

		infostream<<"Client got TOCLIENT_MOVE_PLAYER"
				<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
				<<" pitch="<<pitch
				<<" yaw="<<yaw
				<<std::endl;

		/*
			Add to ClientEvent queue.
			This has to be sent to the main program because otherwise
			it would just force the pitch and yaw values to whatever
			the camera points to.
		*/
		ClientEvent event;
		event.type = CE_PLAYER_FORCE_MOVE;
		event.player_force_move.pitch = pitch;
		event.player_force_move.yaw = yaw;
		m_client_event_queue.push_back(event);

		// Ignore damage for a few seconds, so that the player doesn't
		// get damage from falling on ground
		m_ignore_damage_timer = 3.0;
	}
	else if(command == TOCLIENT_PLAYERITEM)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		u16 count = readU16(is);

		for (u16 i = 0; i < count; ++i) {
			u16 peer_id = readU16(is);
			Player *player = m_env.getPlayer(peer_id);

			if (player == NULL)
			{
				infostream<<"Client: ignoring player item "
					<< deSerializeString(is)
					<< " for non-existing peer id " << peer_id
					<< std::endl;
				continue;
			} else if (player->isLocal()) {
				infostream<<"Client: ignoring player item "
					<< deSerializeString(is)
					<< " for local player" << std::endl;
				continue;
			} else {
				InventoryList *inv = player->inventory.getList("main");
				std::string itemstring(deSerializeString(is));
				if (itemstring.empty()) {
					inv->deleteItem(0);
					infostream
						<<"Client: empty player item for peer "
						<< peer_id << std::endl;
				} else {
					std::istringstream iss(itemstring);
					delete inv->changeItem(0, InventoryItem::deSerialize(iss));
					infostream<<"Client: player item for peer " << peer_id << ": ";
					player->getWieldItem()->serialize(infostream);
					infostream<<std::endl;
				}
			}
		}
	}
	else if(command == TOCLIENT_DEATHSCREEN)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		bool set_camera_point_target = readU8(is);
		v3f camera_point_target = readV3F1000(is);

		ClientEvent event;
		event.type = CE_DEATHSCREEN;
		event.deathscreen.set_camera_point_target = set_camera_point_target;
		event.deathscreen.camera_point_target_x = camera_point_target.X;
		event.deathscreen.camera_point_target_y = camera_point_target.Y;
		event.deathscreen.camera_point_target_z = camera_point_target.Z;
		m_client_event_queue.push_back(event);
	}
	else
	{
		infostream<<"Client: Ignoring unknown command "
				<<command<<std::endl;
	}
}