diff options
Diffstat (limited to 'util/travis/script.sh')
0 files changed, 0 insertions, 0 deletions
/*
Minetest
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "server.h"
#include "log.h"
#include "base64.h"
#include "content_abm.h"
#include "content_sao.h"
#include "emerge.h"
#include "main.h"
#include "nodedef.h"
#include "player.h"
#include "rollback_interface.h"
#include "scripting_game.h"
#include "settings.h"
#include "tool.h"
#include "version.h"
#include "network/networkprotocol.h"
#include "network/serveropcodes.h"
#include "util/pointedthing.h"
#include "util/serialize.h"
void Server::handleCommand_Deprecated(NetworkPacket* pkt)
{
infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
<< " not supported anymore" << std::endl;
}
void Server::handleCommand_Init(NetworkPacket* pkt)
{
// [0] u8 SER_FMT_VER_HIGHEST_READ
// [1] u8[20] player_name
// [21] u8[28] password <--- can be sent without this, from old versions
if (pkt->getSize() < 1+PLAYERNAME_SIZE)
return;
RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
std::string addr_s;
try {
Address address = getPeerAddress(pkt->getPeerId());
addr_s = address.serializeString();
}
catch (con::PeerNotFoundException &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(): Cancelling: peer "
<< pkt->getPeerId() << " not found" << 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=" << pkt->getPeerId() << ")" << std::endl;
return;
}
verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
<< pkt->getPeerId() << ")" << 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(pkt->getPeerId(), L"Running in simple singleplayer mode.");
return;
}
// First byte after command is maximum supported
// serialization version
u8 client_max;
*pkt >> 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)
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(pkt->getPeerId(), std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(minetest_version_simple) + L"."
);
return;
}
client->setPendingSerializationVersion(deployed);
/*
Read and check network protocol version
*/
u16 min_net_proto_version = 0;
if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2)
min_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE);
// Use same version as minimum and maximum if maximum version field
// doesn't exist (backwards compatibility)
u16 max_net_proto_version = min_net_proto_version;
if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2)
max_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2);
// 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(pkt->getPeerId(), std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(minetest_version_simple) + L",\n"
+ L"server's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
+ L"..."
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
+ L", client's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(min_net_proto_version))
+ L"..."
+ narrow_to_wide(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(pkt->getPeerId(), std::wstring(
L"Your client's version is not supported.\n"
L"Server version is ")
+ narrow_to_wide(minetest_version_simple) + L",\n"
+ L"server's PROTOCOL_VERSION (strict) is "
+ narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
+ L", client's PROTOCOL_VERSION is "
+ narrow_to_wide(itos(min_net_proto_version))
+ L"..."
+ narrow_to_wide(itos(max_net_proto_version))
);
return;
}
}
/*
Set up player
*/
char playername[PLAYERNAME_SIZE];
unsigned int playername_length = 0;
for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
playername[playername_length] = pkt->getChar(1+playername_length);
if (pkt->getChar(1+playername_length) == 0)
break;
}
if (playername_length == PLAYERNAME_SIZE) {
actionstream << "Server: Player with name exceeding max length "
<< "tried to connect from " << addr_s << std::endl;
DenyAccess(pkt->getPeerId(), L"Name too long");
return;
}
if (playername[0]=='\0') {
actionstream << "Server: Player with an empty name "
<< "tried to connect from " << addr_s << std::endl;
DenyAccess(pkt->getPeerId(), L"Empty name");
return;
}
if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
actionstream << "Server: Player with an invalid name "
<< "tried to connect from " << addr_s << std::endl;
DenyAccess(pkt->getPeerId(), L"Name contains unallowed characters");
return;
}
if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
actionstream << "Server: Player with the name \"singleplayer\" "
<< "tried to connect from " << addr_s << std::endl;
DenyAccess(pkt->getPeerId(), L"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(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
return;
}
}
infostream<<"Server: New connection: \""<<playername<<"\" from "
<<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
// Get password
char given_password[PASSWORD_SIZE];
if (pkt->getSize() < 1 + PLAYERNAME_SIZE + PASSWORD_SIZE) {
// old version - assume blank password
given_password[0] = 0;
}
else {
for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
given_password[i] = pkt->getChar(21 + i);
}
given_password[PASSWORD_SIZE - 1] = 0;
}
if (!base64_is_valid(given_password)) {
actionstream << "Server: " << playername
<< " supplied invalid password hash" << std::endl;
DenyAccess(pkt->getPeerId(), L"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(pkt->getPeerId(), L"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") &&
std::string(given_password) == "") {
actionstream << "Server: " << playername
<< " supplied empty password" << std::endl;
DenyAccess(pkt->getPeerId(), L"Empty passwords are "
L"disallowed. Set a password and try again.");
return;
}
std::wstring raw_default_password =
narrow_to_wide(g_settings->get("default_password"));
std::string initial_password =
translatePassword(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(pkt->getPeerId(), L"Not allowed to login");
return;
}
if (given_password != checkpwd) {
actionstream << "Server: " << playername << " supplied wrong password"
<< std::endl;
DenyAccess(pkt->getPeerId(), L"Wrong password");
return;
}
RemotePlayer *player =
static_cast<RemotePlayer*>(m_env->getPlayer(playername));
if (player && player->peer_id != 0) {
errorstream << "Server: " << playername << ": Failed to emerge player"
<< " (player allocated to an another client)" << std::endl;
DenyAccess(pkt->getPeerId(), L"Another client is connected with this "
L"name. If your client closed unexpectedly, try again in "
L"a minute.");
}
m_clients.setPlayerName(pkt->getPeerId(), playername);
/*
Answer with a TOCLIENT_INIT
*/
NetworkPacket* resp_pkt = new NetworkPacket(TOCLIENT_INIT, 1 + 6 + 8 + 4,
pkt->getPeerId());
*resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
<< (u64) m_env->getServerMap().getSeed()
<< g_settings->getFloat("dedicated_server_step");
Send(resp_pkt);
m_clients.event(pkt->getPeerId(), CSE_Init);
}
void Server::handleCommand_Init2(NetworkPacket* pkt)
{
verbosestream << "Server: Got TOSERVER_INIT2 from "
<< pkt->getPeerId() << std::endl;
m_clients.event(pkt->getPeerId(), CSE_GotInit2);
u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
///// begin compatibility code
PlayerSAO* playersao = NULL;
if (protocol_version <= 22) {
playersao = StageTwoClientInit(pkt->getPeerId());
if (playersao == NULL) {
errorstream
<< "TOSERVER_INIT2 stage 2 client init failed for peer "
<< pkt->getPeerId() << std::endl;
return;
}
}
///// end compatibility code
/*
Send some initialization data
*/
infostream << "Server: Sending content to "
<< getPlayerName(pkt->getPeerId()) << std::endl;
// Send player movement settings
SendMovement(pkt->getPeerId());
// Send item definitions
SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
// Send node definitions
SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
// Send media announcement
sendMediaAnnouncement(pkt->getPeerId());
// Send detached inventories
sendDetachedInventories(pkt->getPeerId());
// Send time of day
u16 time = m_env->getTimeOfDay();
float time_speed = g_settings->getFloat("time_speed");
SendTimeOfDay(pkt->getPeerId(), time, time_speed);
///// begin compatibility code
if (protocol_version <= 22) {
m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
m_script->on_joinplayer(playersao);
}
///// end compatibility code
// Warnings about protocol version can be issued here
if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
}
}
void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
{
std::list<std::string> tosend;
u16 numfiles;
*pkt >> numfiles;
infostream << "Sending " << numfiles << " files to "
<< getPlayerName(pkt->getPeerId()) << std::endl;
verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
for (u16 i = 0; i < numfiles; i++) {
std::string name;
*pkt >> name;
tosend.push_back(name);
verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
<< name << std::endl;
}
sendRequestedMedia(pkt->getPeerId(), tosend);
}
void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
{
}
void Server::handleCommand_ClientReady(NetworkPacket* pkt)
{
u16 peer_id = pkt->getPeerId();
u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
// clients <= protocol version 22 did not send ready message,
// they're already initialized
if (peer_proto_ver <= 22) {
infostream << "Client sent message not expected by a "
<< "client using protocol version <= 22,"
<< "disconnecing peer_id: " << peer_id << std::endl;
m_con.DisconnectPeer(peer_id);
return;
}
PlayerSAO* playersao = StageTwoClientInit(peer_id);
if (playersao == NULL) {
errorstream
<< "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
<< peer_id << std::endl;
m_con.DisconnectPeer(peer_id);
return;
}
if (pkt->getSize() < 8) {
errorstream
<< "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
<< peer_id << std::endl;
m_con.DisconnectPeer(peer_id);
return;
}
u8 major_ver, minor_ver, patch_ver;
*pkt >> major_ver >> minor_ver >> patch_ver;
m_clients.setClientVersion(
peer_id, major_ver, minor_ver, patch_ver,
std::string(pkt->getString(6),(u16) pkt->getU8(4)));
m_clients.event(peer_id, CSE_SetClientReady);
m_script->on_joinplayer(playersao);
}
void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
{
if (pkt->getSize() < 1)
return;
/*
[0] u16 command
[2] u8 count
[3] v3s16 pos_0
[3+6] v3s16 pos_1
...
*/
u8 count;
*pkt >> count;
RemoteClient *client = getClient(pkt->getPeerId());
for (u16 i = 0; i < count; i++) {
if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
throw con::InvalidIncomingDataException
("GOTBLOCKS length is too short");
v3s16 p;
*pkt >> p;
client->GotBlock(p);
}
}
void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
{
if (pkt->getSize() < 12 + 12 + 4 + 4)
return;
v3s32 ps, ss;
s32 f32pitch, f32yaw;
*pkt >> ps;
*pkt >> ss;
*pkt >> f32pitch;
*pkt >> f32yaw;
f32 pitch = (f32)f32pitch / 100.0;
f32 yaw = (f32)f32yaw / 100.0;
u32 keyPressed = 0;
if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
*pkt >> keyPressed;
v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
pitch = wrapDegrees(pitch);
yaw = wrapDegrees(yaw);
Player *player = m_env->getPlayer(pkt->getPeerId());
if (player == NULL) {
errorstream << "Server::ProcessData(): Cancelling: "
"No player for peer_id=" << pkt->getPeerId()
<< " disconnecting peer!" << std::endl;
m_con.DisconnectPeer(pkt->getPeerId());
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream << "Server::ProcessData(): Cancelling: "
"No player object for peer_id=" << pkt->getPeerId()
<< " disconnecting peer!" << std::endl;
m_con.DisconnectPeer(pkt->getPeerId());
return;
}
player->setPosition(position);
player->setSpeed(speed);
player->setPitch(pitch);
player->setYaw(yaw);
player->keyPressed = keyPressed;
player->control.up = (keyPressed & 1);
player->control.down = (keyPressed & 2);
player->control.left = (keyPressed & 4);
player->control.right = (keyPressed & 8);
player->control.jump = (keyPressed & 16);
player->control.aux1 = (keyPressed & 32);
player->control.sneak = (keyPressed & 64);
player->control.LMB = (keyPressed & 128);
player->control.RMB = (keyPressed & 256);
bool cheated = playersao->checkMovementCheat();
if (cheated) {
// Call callbacks
m_script->on_cheat(playersao, "moved_too_fast");
}
}
void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
{
if (pkt->getSize() < 1)
return;
/*
[0] u16 command
[2] u8 count
[3] v3s16 pos_0
[3+6] v3s16 pos_1
...
*/
u8 count;
*pkt >> count;
RemoteClient *client = getClient(pkt->getPeerId());
for (u16 i = 0; i < count; i++) {
if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
throw con::InvalidIncomingDataException
("DELETEDBLOCKS length is too short");
v3s16 p;
*pkt >> p;
client->SetBlockNotSent(p);
}
}
void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
{
Player *player = m_env->getPlayer(pkt->getPeerId());
if (player == NULL) {
errorstream << "Server::ProcessData(): Cancelling: "
"No player for peer_id=" << pkt->getPeerId()
<< " disconnecting peer!" << std::endl;
m_con.DisconnectPeer(pkt->getPeerId());
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream << "Server::ProcessData(): Cancelling: "
"No player object for peer_id=" << pkt->getPeerId()
<< " disconnecting peer!" << std::endl;
m_con.DisconnectPeer(pkt->getPeerId());
return;
}
// Strip command and create a stream
std::string datastring(pkt->getString(0), pkt->getSize());
verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
<< std::endl;
std::istringstream is(datastring, std::ios_base::binary);
// Create an action
InventoryAction *a = InventoryAction::deSerialize(is);
if (a == NULL) {
infostream << "TOSERVER_INVENTORY_ACTION: "
<< "InventoryAction::deSerialize() returned NULL"
<< std::endl;
return;
}
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:")+player->getName());
/*
Note: Always set inventory not sent, to repair cases
where the client made a bad prediction.
*/
/*
Handle restrictions and special cases of the move action
*/
if (a->getType() == IACTION_MOVE) {
IMoveAction *ma = (IMoveAction*)a;
ma->from_inv.applyCurrentPlayer(player->getName());
ma->to_inv.applyCurrentPlayer(player->getName());
setInventoryModified(ma->from_inv);
setInventoryModified(ma->to_inv);
bool from_inv_is_current_player =
(ma->from_inv.type == InventoryLocation::PLAYER) &&
(ma->from_inv.name == player->getName());
bool to_inv_is_current_player =
(ma->to_inv.type == InventoryLocation::PLAYER) &&
(ma->to_inv.name == player->getName());
/*
Disable moving items out of craftpreview
*/
if (ma->from_list == "craftpreview") {
infostream << "Ignoring IMoveAction from "
<< (ma->from_inv.dump()) << ":" << ma->from_list
<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
<< " because src is " << ma->from_list << std::endl;
delete a;
return;
}
/*
Disable moving items into craftresult and craftpreview
*/
if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
infostream << "Ignoring IMoveAction from "
<< (ma->from_inv.dump()) << ":" << ma->from_list
<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
<< " because dst is " << ma->to_list << std::endl;
delete a;
return;
}
// Disallow moving items in elsewhere than player's inventory
// if not allowed to interact
if (!checkPriv(player->getName(), "interact") &&
(!from_inv_is_current_player ||
!to_inv_is_current_player)) {
infostream << "Cannot move outside of player's inventory: "
<< "No interact privilege" << std::endl;
delete a;
return;
}
}
/*
Handle restrictions and special cases of the drop action
*/
else if (a->getType() == IACTION_DROP) {
IDropAction *da = (IDropAction*)a;
da->from_inv.applyCurrentPlayer(player->getName());
setInventoryModified(da->from_inv);
/*
Disable dropping items out of craftpreview
*/
if (da->from_list == "craftpreview") {
infostream << "Ignoring IDropAction from "