예제 #1
0
void Client::sendChangePassword(const std::string &oldpassword,
        const std::string &newpassword)
{
	LocalPlayer *player = m_env.getLocalPlayer();
	if (player == NULL)
		return;

	std::string playername = player->getName();
	if (m_proto_ver >= 25) {
		// get into sudo mode and then send new password to server
		m_password = oldpassword;
		m_new_password = newpassword;
		startAuth(choseAuthMech(m_sudo_auth_methods));
	} else {
		std::string oldpwd = translate_password(playername, oldpassword);
		std::string newpwd = translate_password(playername, newpassword);

		NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);

		for (u8 i = 0; i < PASSWORD_SIZE; i++) {
			pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
		}

		for (u8 i = 0; i < PASSWORD_SIZE; i++) {
			pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
		}
		Send(&pkt);
	}
}
예제 #2
0
// check_password_entry(name, entry, password)
int ModApiUtil::l_check_password_entry(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string name = luaL_checkstring(L, 1);
	std::string entry = luaL_checkstring(L, 2);
	std::string password = luaL_checkstring(L, 3);

	if (base64_is_valid(entry)) {
		std::string hash = translate_password(name, password);
		lua_pushboolean(L, hash == entry);
		return 1;
	}

	std::string salt;
	std::string verifier;

	if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) {
		// invalid format
		warningstream << "Invalid password format for " << name << std::endl;
		lua_pushboolean(L, false);
		return 1;
	}
	std::string gen_verifier = generate_srp_verifier(name, password, salt);

	lua_pushboolean(L, gen_verifier == verifier);
	return 1;
}
예제 #3
0
// get_password_hash(name, raw_password)
int ModApiUtil::l_get_password_hash(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string name = luaL_checkstring(L, 1);
	std::string raw_password = luaL_checkstring(L, 2);
	std::string hash = translate_password(name, raw_password);
	lua_pushstring(L, hash.c_str());
	return 1;
}
void Client::sendChangePassword(const std::string &oldpassword,
								const std::string &newpassword)
{
	Player *player = m_env.getLocalPlayer();
	if(player == NULL)
		return;

	std::string playername = player->getName();
	std::string oldpwd = translate_password(playername, oldpassword);
	std::string newpwd = translate_password(playername, newpassword);

	MSGPACK_PACKET_INIT(TOSERVER_CHANGE_PASSWORD, 2);
	PACK(TOSERVER_CHANGE_PASSWORD_OLD, oldpwd);
	PACK(TOSERVER_CHANGE_PASSWORD_NEW, newpwd);

	// Send as reliable
	Send(0, buffer, true);
}
예제 #5
0
파일: client.cpp 프로젝트: EXio4/minetest
void Client::startAuth(AuthMechanism chosen_auth_mechanism)
{
	m_chosen_auth_mech = chosen_auth_mechanism;

	switch (chosen_auth_mechanism) {
		case AUTH_MECHANISM_FIRST_SRP: {
			// send srp verifier to server
			std::string verifier;
			std::string salt;
			generate_srp_verifier_and_salt(getPlayerName(), m_password,
				&verifier, &salt);

			NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
			resp_pkt << salt << verifier << (u8)((m_password.empty()) ? 1 : 0);

			Send(&resp_pkt);
			break;
		}
		case AUTH_MECHANISM_SRP:
		case AUTH_MECHANISM_LEGACY_PASSWORD: {
			u8 based_on = 1;

			if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
				m_password = translate_password(getPlayerName(), m_password);
				based_on = 0;
			}

			std::string playername_u = lowercase(getPlayerName());
			m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
				getPlayerName().c_str(), playername_u.c_str(),
				(const unsigned char *) m_password.c_str(),
				m_password.length(), NULL, NULL);
			char *bytes_A = 0;
			size_t len_A = 0;
			SRP_Result res = srp_user_start_authentication(
				(struct SRPUser *) m_auth_data, NULL, NULL, 0,
				(unsigned char **) &bytes_A, &len_A);
			FATAL_ERROR_IF(res != SRP_OK, "Creating local SRP user failed.");

			NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
			resp_pkt << std::string(bytes_A, len_A) << based_on;
			Send(&resp_pkt);
			break;
		}
		case AUTH_MECHANISM_NONE:
			break; // not handled in this method
	}
}
예제 #6
0
void Client::step(float dtime)
{
	DSTACK(FUNCTION_NAME);

	// Limit a bit
	if(dtime > 2.0)
		dtime = 2.0;

	if(m_ignore_damage_timer > dtime)
		m_ignore_damage_timer -= dtime;
	else
		m_ignore_damage_timer = 0.0;

	m_animation_time += dtime;
	if(m_animation_time > 60.0)
		m_animation_time -= 60.0;

	m_time_of_day_update_timer += dtime;

	ReceiveAll();

	/*
		Packet counter
	*/
	{
		float &counter = m_packetcounter_timer;
		counter -= dtime;
		if(counter <= 0.0)
		{
			counter = 20.0;

			infostream << "Client packetcounter (" << m_packetcounter_timer
					<< "):"<<std::endl;
			m_packetcounter.print(infostream);
			m_packetcounter.clear();
		}
	}

	// UGLY hack to fix 2 second startup delay caused by non existent
	// server client startup synchronization in local server or singleplayer mode
	static bool initial_step = true;
	if (initial_step) {
		initial_step = false;
	}
	else if(m_state == LC_Created) {
		float &counter = m_connection_reinit_timer;
		counter -= dtime;
		if(counter <= 0.0) {
			counter = 2.0;

			LocalPlayer *myplayer = m_env.getLocalPlayer();
			FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment.");

			u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
				CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;

			if (proto_version_min < 25) {
				// Send TOSERVER_INIT_LEGACY
				// [0] u16 TOSERVER_INIT_LEGACY
				// [2] u8 SER_FMT_VER_HIGHEST_READ
				// [3] u8[20] player_name
				// [23] u8[28] password (new in some version)
				// [51] u16 minimum supported network protocol version (added sometime)
				// [53] u16 maximum supported network protocol version (added later than the previous one)

				char pName[PLAYERNAME_SIZE];
				char pPassword[PASSWORD_SIZE];
				memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
				memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));

				std::string hashed_password = translate_password(myplayer->getName(), m_password);
				snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
				snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());

				sendLegacyInit(pName, pPassword);
			}
			if (CLIENT_PROTOCOL_VERSION_MAX >= 25)
				sendInit(myplayer->getName());
		}

		// Not connected, return
		return;
	}

	/*
		Do stuff if connected
	*/

	/*
		Run Map's timers and unload unused data
	*/
	const float map_timer_and_unload_dtime = 5.25;
	if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) {
		ScopeProfiler sp(g_profiler, "Client: map timer and unload");
		std::vector<v3s16> deleted_blocks;
		m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
			g_settings->getFloat("client_unload_unused_data_timeout"),
			g_settings->getS32("client_mapblock_limit"),
			&deleted_blocks);

		/*
			Send info to server
			NOTE: This loop is intentionally iterated the way it is.
		*/

		std::vector<v3s16>::iterator i = deleted_blocks.begin();
		std::vector<v3s16> sendlist;
		for(;;) {
			if(sendlist.size() == 255 || i == deleted_blocks.end()) {
				if(sendlist.empty())
					break;
				/*
					[0] u16 command
					[2] u8 count
					[3] v3s16 pos_0
					[3+6] v3s16 pos_1
					...
				*/

				sendDeletedBlocks(sendlist);

				if(i == deleted_blocks.end())
					break;

				sendlist.clear();
			}

			sendlist.push_back(*i);
			++i;
		}
	}

	/*
		Handle environment
	*/
	// Control local player (0ms)
	LocalPlayer *player = m_env.getLocalPlayer();
	assert(player != NULL);
	player->applyControl(dtime);

	// Step environment
	m_env.step(dtime);

	/*
		Get events
	*/
	for(;;) {
		ClientEnvEvent event = m_env.getClientEvent();
		if(event.type == CEE_NONE) {
			break;
		}
		else if(event.type == CEE_PLAYER_DAMAGE) {
			if(m_ignore_damage_timer <= 0) {
				u8 damage = event.player_damage.amount;

				if(event.player_damage.send_to_server)
					sendDamage(damage);

				// Add to ClientEvent queue
				ClientEvent event;
				event.type = CE_PLAYER_DAMAGE;
				event.player_damage.amount = damage;
				m_client_event_queue.push(event);
			}
		}
		else if(event.type == CEE_PLAYER_BREATH) {
				u16 breath = event.player_breath.amount;
				sendBreath(breath);
		}
	}

	/*
		Print some info
	*/
	float &counter = m_avg_rtt_timer;
	counter += dtime;
	if(counter >= 10) {
		counter = 0.0;
		// connectedAndInitialized() is true, peer exists.
		float avg_rtt = getRTT();
		infostream << "Client: avg_rtt=" << avg_rtt << std::endl;
	}

	/*
		Send player position to server
	*/
	{
		float &counter = m_playerpos_send_timer;
		counter += dtime;
		if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
		{
			counter = 0.0;
			sendPlayerPos();
		}
	}

	/*
		Replace updated meshes
	*/
	{
		int num_processed_meshes = 0;
		while (!m_mesh_update_thread.m_queue_out.empty())
		{
			num_processed_meshes++;

			MinimapMapblock *minimap_mapblock = NULL;
			bool do_mapper_update = true;

			MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
			MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
			if (block) {
				// Delete the old mesh
				if (block->mesh != NULL) {
					delete block->mesh;
					block->mesh = NULL;
				}

				if (r.mesh) {
					minimap_mapblock = r.mesh->moveMinimapMapblock();
					if (minimap_mapblock == NULL)
						do_mapper_update = false;
				}

				if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
					delete r.mesh;
				} else {
					// Replace with the new mesh
					block->mesh = r.mesh;
				}
			} else {
				delete r.mesh;
			}

			if (do_mapper_update)
				m_mapper->addBlock(r.p, minimap_mapblock);

			if (r.ack_block_to_server) {
				/*
					Acknowledge block
					[0] u8 count
					[1] v3s16 pos_0
				*/
				sendGotBlocks(r.p);
			}
		}

		if (num_processed_meshes > 0)
			g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
	}

	/*
		Load fetched media
	*/
	if (m_media_downloader && m_media_downloader->isStarted()) {
		m_media_downloader->step(this);
		if (m_media_downloader->isDone()) {
			received_media();
			delete m_media_downloader;
			m_media_downloader = NULL;
		}
	}

	/*
		If the server didn't update the inventory in a while, revert
		the local inventory (so the player notices the lag problem
		and knows something is wrong).
	*/
	if(m_inventory_from_server)
	{
		float interval = 10.0;
		float count_before = floor(m_inventory_from_server_age / interval);

		m_inventory_from_server_age += dtime;

		float count_after = floor(m_inventory_from_server_age / interval);

		if(count_after != count_before)
		{
			// Do this every <interval> seconds after TOCLIENT_INVENTORY
			// Reset the locally changed inventory to the authoritative inventory
			LocalPlayer *player = m_env.getLocalPlayer();
			player->inventory = *m_inventory_from_server;
			m_inventory_updated = true;
		}
	}

	/*
		Update positions of sounds attached to objects
	*/
	{
		for(UNORDERED_MAP<int, u16>::iterator i = m_sounds_to_objects.begin();
				i != m_sounds_to_objects.end(); ++i) {
			int client_id = i->first;
			u16 object_id = i->second;
			ClientActiveObject *cao = m_env.getActiveObject(object_id);
			if(!cao)
				continue;
			v3f pos = cao->getPosition();
			m_sound->updateSoundPosition(client_id, pos);
		}
	}

	/*
		Handle removed remotely initiated sounds
	*/
	m_removed_sounds_check_timer += dtime;
	if(m_removed_sounds_check_timer >= 2.32) {
		m_removed_sounds_check_timer = 0;
		// Find removed sounds and clear references to them
		std::vector<s32> removed_server_ids;
		for(UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.begin();
				i != m_sounds_server_to_client.end();) {
			s32 server_id = i->first;
			int client_id = i->second;
			++i;
			if(!m_sound->soundExists(client_id)) {
				m_sounds_server_to_client.erase(server_id);
				m_sounds_client_to_server.erase(client_id);
				m_sounds_to_objects.erase(client_id);
				removed_server_ids.push_back(server_id);
			}
		}

		// Sync to server
		if(!removed_server_ids.empty()) {
			sendRemovedSounds(removed_server_ids);
		}
	}

	// Write server map
	if (m_localdb && m_localdb_save_interval.step(dtime,
			m_cache_save_interval)) {
		m_localdb->endSave();
		m_localdb->beginSave();
	}
}
예제 #7
0
void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) {
	const auto peer_id = pkt->getPeerId();
	auto & packet = *(pkt->packet);

	RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);

	std::string addr_s;
	try {
		addr_s = getPeerAddress(pkt->getPeerId()).serializeString();
	} catch (std::exception &e) {
		/*
		 * no peer for this packet found
		 * most common reason is peer timeout, e.g. peer didn't
		 * respond for some time, your server was overloaded or
		 * things like that.
		 */
		infostream << "Server::ProcessData(): Canceling: peer "
		           << pkt->getPeerId() << " not found : " << e.what() << std::endl;
		return;
	}

	// If net_proto_version is set, this client has already been handled
	if(client->getState() > CS_Created) {
		verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
		              << addr_s << " (peer_id=" << peer_id << ")" << std::endl;
		return;
	}

	verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
	              << peer_id << ")" << std::endl;

	// 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.getClientIDs().size() > 1) {
		infostream << "Server: Not allowing another client (" << addr_s
		           << ") to connect in simple singleplayer mode" << std::endl;
		DenyAccess(peer_id, "Running in simple singleplayer mode.");
		return;
	}

	// First byte after command is maximum supported
	// serialization version
	u8 client_max;
	packet[TOSERVER_INIT_LEGACY_FMT].convert(client_max);
	u8 our_max = SER_FMT_VER_HIGHEST_READ;
	// Use the highest version supported by both
	int deployed = std::min(client_max, our_max);
	// If it's lower than the lowest supported, give up.
	if (deployed < SER_FMT_VER_LOWEST_READ)
		deployed = SER_FMT_VER_INVALID;

	if (deployed == SER_FMT_VER_INVALID) {
		actionstream << "Server: A mismatched client tried to connect from "
		             << addr_s << std::endl;
		infostream << "Server: Cannot negotiate serialization version with "
		           << addr_s << std::endl;
		DenyAccess(peer_id, std::string(
		               "Your client's version is not supported.\n"
		               "Server version is ")
		           + (g_version_string) + "."
		          );
		return;
	}

	client->setPendingSerializationVersion(deployed);

	/*
		Read and check network protocol version
	*/

	u16 min_net_proto_version = 0;
	packet[TOSERVER_INIT_LEGACY_PROTOCOL_VERSION_MIN].convert(min_net_proto_version);
	u16 max_net_proto_version = min_net_proto_version;
	packet[TOSERVER_INIT_LEGACY_PROTOCOL_VERSION_MAX].convert(max_net_proto_version);

	packet.convert_safe(TOSERVER_INIT_LEGACY_PROTOCOL_VERSION_FM, client->net_proto_version_fm);

	// Start with client's maximum version
	u16 net_proto_version = max_net_proto_version;

	// Figure out a working version if it is possible at all
	if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
	        min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
		// If maximum is larger than our maximum, go with our maximum
		if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
			net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
		// Else go with client's maximum
		else
			net_proto_version = max_net_proto_version;
	}

	verbosestream << "Server: " << addr_s << ": Protocol version: min: "
	              << min_net_proto_version << ", max: " << max_net_proto_version
	              << ", chosen: " << net_proto_version << std::endl;

	client->net_proto_version = net_proto_version;

	if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
	        net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
		actionstream << "Server: A mismatched client tried to connect from "
		             << addr_s << std::endl;
		DenyAccess(peer_id, std::string(
		               "Your client's version is not supported.\n"
		               "Server version is ")
		           + (g_version_string) + ",\n"
		           + "server's PROTOCOL_VERSION is "
		           + itos(SERVER_PROTOCOL_VERSION_MIN)
		           + "..."
		           + itos(SERVER_PROTOCOL_VERSION_MAX)
		           + ", client's PROTOCOL_VERSION is "
		           + itos(min_net_proto_version)
		           + "..."
		           + itos(max_net_proto_version)
		          );
		return;
	}

	if(g_settings->getBool("strict_protocol_version_checking")) {
		if(net_proto_version != LATEST_PROTOCOL_VERSION) {
			actionstream << "Server: A mismatched (strict) client tried to "
			             << "connect from " << addr_s << std::endl;
			DenyAccess(peer_id, std::string(
			               "Your client's version is not supported.\n"
			               "Server version is ")
			           + (g_version_string) + ",\n"
			           + "server's PROTOCOL_VERSION (strict) is "
			           + itos(LATEST_PROTOCOL_VERSION)
			           + ", client's PROTOCOL_VERSION is "
			           + itos(min_net_proto_version)
			           + "..."
			           + itos(max_net_proto_version)
			          );
			return;
		}
	}

	/*
		Set up player
	*/

	// Get player name
	std::string playername;
	packet[TOSERVER_INIT_LEGACY_NAME].convert(playername);

	if(playername.empty()) {
		actionstream << "Server: Player with an empty name "
		             << "tried to connect from " << addr_s << std::endl;
		DenyAccess(peer_id, "Empty name");
		return;
	}

	if(!g_settings->getBool("enable_any_name") && string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
		actionstream << "Server: Player with an invalid name [" << playername
		             << "] tried to connect from " << addr_s << std::endl;
		DenyAccess(peer_id, "Name contains unallowed characters");
		return;
	}

	if(!isSingleplayer() && playername == "singleplayer") {
		actionstream << "Server: Player with the name \"singleplayer\" "
		             << "tried to connect from " << addr_s << std::endl;
		DenyAccess(peer_id, "Name is not allowed");
		return;
	}

	{
		std::string reason;
		if(m_script->on_prejoinplayer(playername, addr_s, &reason)) {
			actionstream << "Server: Player with the name \"" << playername << "\" "
			             << "tried to connect from " << addr_s << " "
			             << "but it was disallowed for the following reason: "
			             << reason << std::endl;
			DenyAccess(peer_id, reason);
			return;
		}
	}

	infostream << "Server: New connection: \"" << playername << "\" from "
	           << addr_s << " (peer_id=" << peer_id << ")" << std::endl;

	// Get password
	std::string given_password;
	packet[TOSERVER_INIT_LEGACY_PASSWORD].convert(given_password);

	if(!base64_is_valid(given_password.c_str())) {
		actionstream << "Server: " << playername
		             << " supplied invalid password hash" << std::endl;
		DenyAccess(peer_id, "Invalid password hash");
		return;
	}

	// Enforce user limit.
	// Don't enforce for users that have some admin right
	if(m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
	        !checkPriv(playername, "server") &&
	        !checkPriv(playername, "ban") &&
	        !checkPriv(playername, "privs") &&
	        !checkPriv(playername, "password") &&
	        playername != g_settings->get("name")) {
		actionstream << "Server: " << playername << " tried to join, but there"
		             << " are already max_users="
		             << g_settings->getU16("max_users") << " players." << std::endl;
		DenyAccess(peer_id, "Too many users.");
		return;
	}

	std::string checkpwd; // Password hash to check against
	bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);

	// If no authentication info exists for user, create it
	if(!has_auth) {
		if(!isSingleplayer() &&
		        g_settings->getBool("disallow_empty_password") &&
		        given_password == "") {
			actionstream << "Server: " << playername
			             << " supplied empty password" << std::endl;
			DenyAccess(peer_id, "Empty passwords are "
			           "disallowed. Set a password and try again.");
			return;
		}
		std::string raw_default_password = g_settings->get("default_password");
		std::string initial_password =
		    translate_password(playername, raw_default_password);

		// If default_password is empty, allow any initial password
		if (raw_default_password.length() == 0)
			initial_password = given_password;

		m_script->createAuth(playername, initial_password);
	}

	has_auth = m_script->getAuth(playername, &checkpwd, NULL);

	if(!has_auth) {
		actionstream << "Server: " << playername << " cannot be authenticated"
		             << " (auth handler does not work?)" << std::endl;
		DenyAccess(peer_id, "Not allowed to login");
		return;
	}

	if(given_password != checkpwd) {
		actionstream << "Server: " << playername << " supplied wrong password"
		             << " at " << addr_s
		             << std::endl;
		DenyAccess(peer_id, "Wrong password");
		return;
	}

	RemotePlayer *player =
	    static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));

	if (player && player->peer_id != 0) {

		if (given_password.size()) {
			actionstream << "Server: " << playername << " rejoining" << std::endl;
			DenyAccessVerCompliant(player->peer_id, player->protocol_version, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
			player->getPlayerSAO()->removingFromEnvironment();
			m_env->removePlayer(player);
			player = nullptr;
		} else {
			errorstream << "Server: " << playername << ": Failed to emerge player" << " (player allocated to an another client)" << std::endl;
			DenyAccess(peer_id, "Another client is connected with this "
			           "name. If your client closed unexpectedly, try again in "
			           "a minute.");
		}
	}

	m_clients.setPlayerName(peer_id, playername);

	/*
		Answer with a TOCLIENT_INIT
	*/
	{
		MSGPACK_PACKET_INIT(TOCLIENT_INIT_LEGACY, 6);
		PACK(TOCLIENT_INIT_DEPLOYED, deployed);
		PACK(TOCLIENT_INIT_SEED, m_env->getServerMap().getSeed());
		PACK(TOCLIENT_INIT_STEP, g_settings->getFloat("dedicated_server_step"));

		//if (player) //todo : remake me
		//	PACK(TOCLIENT_INIT_POS, player->getPosition());

		Settings params;
		m_emerge->params.save(params);
		PACK(TOCLIENT_INIT_MAP_PARAMS, params);

		PACK(TOCLIENT_INIT_PROTOCOL_VERSION_FM, SERVER_PROTOCOL_VERSION_FM);

		PACK(TOCLIENT_INIT_WEATHER, g_settings->getBool("weather"));

		// Send as reliable
		m_clients.send(peer_id, 0, buffer, true);
		m_clients.event(peer_id, CSE_InitLegacy);
	}

	return;
}