// 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) {
+ if (m_simple_singleplayer_mode && !m_clients.getClientIDs().empty()) {
infostream << "Server: Not allowing another client (" << addr_s <<
") to connect in simple singleplayer mode" << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_SINGLEPLAYER);
Compose auth methods for answer
*/
std::string encpwd; // encrypted Password field for the user
- bool has_auth = m_script->getAuth(playername, &encpwd, NULL);
+ bool has_auth = m_script->getAuth(playername, &encpwd, nullptr);
u32 auth_mechs = 0;
client->chosen_mech = AUTH_MECHANISM_NONE;
{
session_t peer_id = pkt->getPeerId();
- PlayerSAO* playersao = StageTwoClientInit(peer_id);
-
- if (playersao == NULL) {
- errorstream << "TOSERVER_CLIENT_READY stage 2 client init failed "
- "peer_id=" << peer_id << std::endl;
- DisconnectPeer(peer_id);
- return;
- }
-
-
- if (pkt->getSize() < 8) {
- errorstream << "TOSERVER_CLIENT_READY client sent inconsistent data, "
- "disconnecting peer_id: " << peer_id << std::endl;
- DisconnectPeer(peer_id);
- return;
- }
-
+ // decode all information first
u8 major_ver, minor_ver, patch_ver, reserved;
+ u16 formspec_ver = 1; // v1 for clients older than 5.1.0-dev
std::string full_ver;
+
*pkt >> major_ver >> minor_ver >> patch_ver >> reserved >> full_ver;
+ if (pkt->getRemainingBytes() >= 2)
+ *pkt >> formspec_ver;
m_clients.setClientVersion(peer_id, major_ver, minor_ver, patch_ver,
full_ver);
- if (pkt->getRemainingBytes() >= 2)
- *pkt >> playersao->getPlayer()->formspec_version;
-
- const std::vector<std::string> &players = m_clients.getPlayerNames();
- NetworkPacket list_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, peer_id);
- list_pkt << (u8) PLAYER_LIST_INIT << (u16) players.size();
- for (const std::string &player: players) {
- list_pkt << player;
+ // Emerge player
+ PlayerSAO* playersao = StageTwoClientInit(peer_id);
+ if (!playersao) {
+ errorstream << "Server: stage 2 client init failed "
+ "peer_id=" << peer_id << std::endl;
+ DisconnectPeer(peer_id);
+ return;
}
- m_clients.send(peer_id, 0, &list_pkt, true);
- NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
- // (u16) 1 + std::string represents a pseudo vector serialization representation
- notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(playersao->getPlayer()->getName());
- m_clients.sendToAll(¬ice_pkt);
+ playersao->getPlayer()->formspec_version = formspec_ver;
m_clients.event(peer_id, CSE_SetClientReady);
+ // Send player list to this client
+ {
+ const std::vector<std::string> &players = m_clients.getPlayerNames();
+ NetworkPacket list_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, peer_id);
+ list_pkt << (u8) PLAYER_LIST_INIT << (u16) players.size();
+ for (const auto &player : players)
+ list_pkt << player;
+ Send(peer_id, &list_pkt);
+ }
+
s64 last_login;
m_script->getAuth(playersao->getPlayer()->getName(), nullptr, nullptr, &last_login);
m_script->on_joinplayer(playersao, last_login);
// Send shutdown timer if shutdown has been scheduled
- if (m_shutdown_state.isTimerRunning()) {
+ if (m_shutdown_state.isTimerRunning())
SendChatMessage(peer_id, m_shutdown_state.getShutdownTimerMessage());
- }
}
void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
("GOTBLOCKS length is too short");
}
- m_clients.lock();
+ ClientInterface::AutoLock lock(m_clients);
RemoteClient *client = m_clients.lockedGetClientNoEx(pkt->getPeerId());
for (u16 i = 0; i < count; i++) {
*pkt >> p;
client->GotBlock(p);
}
- m_clients.unlock();
}
void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
f32 yaw = (f32)f32yaw / 100.0f;
u32 keyPressed = 0;
- // default behavior (in case an old client doesn't send these)
f32 fov = 0;
u8 wanted_range = 0;
playersao->setFov(fov);
playersao->setWantedRange(wanted_range);
- player->keyPressed = keyPressed;
- player->control.jump = (keyPressed & (0x1 << 4));
- player->control.aux1 = (keyPressed & (0x1 << 5));
- player->control.sneak = (keyPressed & (0x1 << 6));
- player->control.dig = (keyPressed & (0x1 << 7));
- player->control.place = (keyPressed & (0x1 << 8));
- player->control.zoom = (keyPressed & (0x1 << 9));
+ player->control.unpackKeysPressed(keyPressed);
if (playersao->checkMovementCheat()) {
// Call callbacks
<< std::endl;
PlayerHPChangeReason reason(PlayerHPChangeReason::FALL);
- playersao->setHP((s32)playersao->getHP() - (s32)damage, reason, false);
- SendPlayerHPOrDie(playersao, reason); // correct client side prediction
+ playersao->setHP((s32)playersao->getHP() - (s32)damage, reason, true);
}
}
session_t peer_id = pkt->getPeerId();
RemoteClient *client = getClient(peer_id, CS_Invalid);
ClientState cstate = client->getState();
+ const std::string playername = client->getName();
- std::string playername = client->getName();
-
- std::string salt;
- std::string verification_key;
+ std::string salt, verification_key;
std::string addr_s = getPeerAddress(peer_id).serializeString();
u8 is_empty;
verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s
<< ", with is_empty=" << (is_empty == 1) << std::endl;
+ const bool empty_disallowed = !isSingleplayer() && is_empty == 1 &&
+ g_settings->getBool("disallow_empty_password");
+
// Either this packet is sent because the user is new or to change the password
if (cstate == CS_HelloSent) {
if (!client->isMechAllowed(AUTH_MECHANISM_FIRST_SRP)) {
return;
}
- if (!isSingleplayer() &&
- g_settings->getBool("disallow_empty_password") &&
- is_empty == 1) {
+ if (empty_disallowed) {
actionstream << "Server: " << playername
<< " supplied empty password from " << addr_s << std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_EMPTY_PASSWORD);
}
std::string initial_ver_key;
-
initial_ver_key = encode_srp_verifier(verification_key, salt);
+
+ // It is possible for multiple connections to get this far with the same
+ // player name. In the end only one player with a given name will be emerged
+ // (see Server::StateTwoClientInit) but we still have to be careful here.
+ if (m_script->getAuth(playername, nullptr, nullptr)) {
+ // Another client beat us to it
+ actionstream << "Server: Client from " << addr_s
+ << " tried to register " << playername << " a second time."
+ << std::endl;
+ DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
+ return;
+ }
m_script->createAuth(playername, initial_ver_key);
m_script->on_authplayer(playername, addr_s, true);
return;
}
m_clients.event(peer_id, CSE_SudoLeave);
+
+ if (empty_disallowed) {
+ actionstream << "Server: " << playername
+ << " supplied empty password" << std::endl;
+ SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+ L"Changing to an empty password is not allowed."));
+ return;
+ }
+
std::string pw_db_field = encode_srp_verifier(verification_key, salt);
bool success = m_script->setPassword(playername, pw_db_field);
if (success) {
RemoteClient *client = getClient(peer_id, CS_Invalid);
ClientState cstate = client->getState();
- bool wantSudo = (cstate == CS_Active);
-
if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
actionstream << "Server: got SRP _A packet in wrong state " << cstate <<
" from " << getPeerAddress(peer_id).serializeString() <<
return;
}
+ const bool wantSudo = (cstate == CS_Active);
+
if (client->chosen_mech != AUTH_MECHANISM_NONE) {
actionstream << "Server: got SRP _A packet, while auth is already "
"going on with mech " << client->chosen_mech << " from " <<
client->chosen_mech = chosen;
- std::string salt;
- std::string verifier;
+ std::string salt, verifier;
if (based_on == 0) {
<< std::endl;
if (wantSudo) {
DenySudoAccess(peer_id);
+ client->resetChosenMech();
return;
}
session_t peer_id = pkt->getPeerId();
RemoteClient *client = getClient(peer_id, CS_Invalid);
ClientState cstate = client->getState();
- std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString();
- std::string playername = client->getName();
+ const std::string addr_s = client->getAddress().serializeString();
+ const std::string playername = client->getName();
- bool wantSudo = (cstate == CS_Active);
+ const bool wantSudo = (cstate == CS_Active);
verbosestream << "Server: Received TOSERVER_SRP_BYTES_M." << std::endl;
<< " tried to change their password, but supplied wrong"
<< " (SRP) password for authentication." << std::endl;
DenySudoAccess(peer_id);
+ client->resetChosenMech();
return;
}
if (client->create_player_on_auth_success) {
m_script->createAuth(playername, client->enc_pwd);
- std::string checkpwd; // not used, but needed for passing something
- if (!m_script->getAuth(playername, &checkpwd, NULL)) {
+ if (!m_script->getAuth(playername, nullptr, nullptr)) {
errorstream << "Server: " << playername <<
" cannot be authenticated (auth handler does not work?)" <<
std::endl;