]> git.lizzy.rs Git - dragonfireclient.git/blob - src/network/packethandlers/server.cpp
Move sha1.hpp and base64.hpp to util/
[dragonfireclient.git] / src / network / packethandlers / server.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "server.h"
21 #include "log.h"
22
23 #include "content_abm.h"
24 #include "content_sao.h"
25 #include "emerge.h"
26 #include "main.h"
27 #include "nodedef.h"
28 #include "player.h"
29 #include "rollback_interface.h"
30 #include "scripting_game.h"
31 #include "settings.h"
32 #include "tool.h"
33 #include "version.h"
34 #include "network/networkprotocol.h"
35 #include "network/serveropcodes.h"
36 #include "util/base64.h"
37 #include "util/pointedthing.h"
38 #include "util/serialize.h"
39
40 void Server::handleCommand_Deprecated(NetworkPacket* pkt)
41 {
42         infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
43                 << " not supported anymore" << std::endl;
44 }
45
46 void Server::handleCommand_Init(NetworkPacket* pkt)
47 {
48         // [0] u8 SER_FMT_VER_HIGHEST_READ
49         // [1] u8[20] player_name
50         // [21] u8[28] password <--- can be sent without this, from old versions
51
52         if (pkt->getSize() < 1+PLAYERNAME_SIZE)
53                 return;
54
55         RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
56
57         std::string addr_s;
58         try {
59                 Address address = getPeerAddress(pkt->getPeerId());
60                 addr_s = address.serializeString();
61         }
62         catch (con::PeerNotFoundException &e) {
63                 /*
64                  * no peer for this packet found
65                  * most common reason is peer timeout, e.g. peer didn't
66                  * respond for some time, your server was overloaded or
67                  * things like that.
68                  */
69                 infostream << "Server::ProcessData(): Cancelling: peer "
70                                 << pkt->getPeerId() << " not found" << std::endl;
71                 return;
72         }
73
74         // If net_proto_version is set, this client has already been handled
75         if (client->getState() > CS_Created) {
76                 verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
77                                 << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
78                 return;
79         }
80
81         verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
82                         << pkt->getPeerId() << ")" << std::endl;
83
84         // Do not allow multiple players in simple singleplayer mode.
85         // This isn't a perfect way to do it, but will suffice for now
86         if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
87                 infostream << "Server: Not allowing another client (" << addr_s
88                                 << ") to connect in simple singleplayer mode" << std::endl;
89                 DenyAccess(pkt->getPeerId(), L"Running in simple singleplayer mode.");
90                 return;
91         }
92
93         // First byte after command is maximum supported
94         // serialization version
95         u8 client_max;
96
97         *pkt >> client_max;
98
99         u8 our_max = SER_FMT_VER_HIGHEST_READ;
100         // Use the highest version supported by both
101         int deployed = std::min(client_max, our_max);
102         // If it's lower than the lowest supported, give up.
103         if (deployed < SER_FMT_VER_LOWEST)
104                 deployed = SER_FMT_VER_INVALID;
105
106         if (deployed == SER_FMT_VER_INVALID) {
107                 actionstream << "Server: A mismatched client tried to connect from "
108                                 << addr_s << std::endl;
109                 infostream<<"Server: Cannot negotiate serialization version with "
110                                 << addr_s << std::endl;
111                 DenyAccess(pkt->getPeerId(), std::wstring(
112                                 L"Your client's version is not supported.\n"
113                                 L"Server version is ")
114                                 + narrow_to_wide(minetest_version_simple) + L"."
115                 );
116                 return;
117         }
118
119         client->setPendingSerializationVersion(deployed);
120
121         /*
122                 Read and check network protocol version
123         */
124
125         u16 min_net_proto_version = 0;
126         if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2)
127                 min_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE);
128
129         // Use same version as minimum and maximum if maximum version field
130         // doesn't exist (backwards compatibility)
131         u16 max_net_proto_version = min_net_proto_version;
132         if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2)
133                 max_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2);
134
135         // Start with client's maximum version
136         u16 net_proto_version = max_net_proto_version;
137
138         // Figure out a working version if it is possible at all
139         if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
140                         min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
141                 // If maximum is larger than our maximum, go with our maximum
142                 if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
143                         net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
144                 // Else go with client's maximum
145                 else
146                         net_proto_version = max_net_proto_version;
147         }
148
149         verbosestream << "Server: " << addr_s << ": Protocol version: min: "
150                         << min_net_proto_version << ", max: " << max_net_proto_version
151                         << ", chosen: " << net_proto_version << std::endl;
152
153         client->net_proto_version = net_proto_version;
154
155         if (net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
156                         net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
157                 actionstream << "Server: A mismatched client tried to connect from "
158                                 << addr_s << std::endl;
159                 DenyAccess(pkt->getPeerId(), std::wstring(
160                                 L"Your client's version is not supported.\n"
161                                 L"Server version is ")
162                                 + narrow_to_wide(minetest_version_simple) + L",\n"
163                                 + L"server's PROTOCOL_VERSION is "
164                                 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
165                                 + L"..."
166                                 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
167                                 + L", client's PROTOCOL_VERSION is "
168                                 + narrow_to_wide(itos(min_net_proto_version))
169                                 + L"..."
170                                 + narrow_to_wide(itos(max_net_proto_version))
171                 );
172                 return;
173         }
174
175         if (g_settings->getBool("strict_protocol_version_checking")) {
176                 if (net_proto_version != LATEST_PROTOCOL_VERSION) {
177                         actionstream << "Server: A mismatched (strict) client tried to "
178                                         << "connect from " << addr_s << std::endl;
179                         DenyAccess(pkt->getPeerId(), std::wstring(
180                                         L"Your client's version is not supported.\n"
181                                         L"Server version is ")
182                                         + narrow_to_wide(minetest_version_simple) + L",\n"
183                                         + L"server's PROTOCOL_VERSION (strict) is "
184                                         + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
185                                         + L", client's PROTOCOL_VERSION is "
186                                         + narrow_to_wide(itos(min_net_proto_version))
187                                         + L"..."
188                                         + narrow_to_wide(itos(max_net_proto_version))
189                         );
190                         return;
191                 }
192         }
193
194         /*
195                 Set up player
196         */
197         char playername[PLAYERNAME_SIZE];
198         unsigned int playername_length = 0;
199         for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
200                 playername[playername_length] = pkt->getChar(1+playername_length);
201                 if (pkt->getChar(1+playername_length) == 0)
202                         break;
203         }
204
205         if (playername_length == PLAYERNAME_SIZE) {
206                 actionstream << "Server: Player with name exceeding max length "
207                                 << "tried to connect from " << addr_s << std::endl;
208                 DenyAccess(pkt->getPeerId(), L"Name too long");
209                 return;
210         }
211
212
213         if (playername[0]=='\0') {
214                 actionstream << "Server: Player with an empty name "
215                                 << "tried to connect from " << addr_s << std::endl;
216                 DenyAccess(pkt->getPeerId(), L"Empty name");
217                 return;
218         }
219
220         if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
221                 actionstream << "Server: Player with an invalid name "
222                                 << "tried to connect from " << addr_s << std::endl;
223                 DenyAccess(pkt->getPeerId(), L"Name contains unallowed characters");
224                 return;
225         }
226
227         if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
228                 actionstream << "Server: Player with the name \"singleplayer\" "
229                                 << "tried to connect from " << addr_s << std::endl;
230                 DenyAccess(pkt->getPeerId(), L"Name is not allowed");
231                 return;
232         }
233
234         {
235                 std::string reason;
236                 if (m_script->on_prejoinplayer(playername, addr_s, reason)) {
237                         actionstream << "Server: Player with the name \"" << playername << "\" "
238                                         << "tried to connect from " << addr_s << " "
239                                         << "but it was disallowed for the following reason: "
240                                         << reason << std::endl;
241                         DenyAccess(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
242                         return;
243                 }
244         }
245
246         infostream<<"Server: New connection: \""<<playername<<"\" from "
247                         <<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
248
249         // Get password
250         char given_password[PASSWORD_SIZE];
251         if (pkt->getSize() < 1 + PLAYERNAME_SIZE + PASSWORD_SIZE) {
252                 // old version - assume blank password
253                 given_password[0] = 0;
254         }
255         else {
256                 for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
257                         given_password[i] = pkt->getChar(21 + i);
258                 }
259                 given_password[PASSWORD_SIZE - 1] = 0;
260         }
261
262         if (!base64_is_valid(given_password)) {
263                 actionstream << "Server: " << playername
264                                 << " supplied invalid password hash" << std::endl;
265                 DenyAccess(pkt->getPeerId(), L"Invalid password hash");
266                 return;
267         }
268
269         // Enforce user limit.
270         // Don't enforce for users that have some admin right
271         if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
272                         !checkPriv(playername, "server") &&
273                         !checkPriv(playername, "ban") &&
274                         !checkPriv(playername, "privs") &&
275                         !checkPriv(playername, "password") &&
276                         playername != g_settings->get("name")) {
277                 actionstream << "Server: " << playername << " tried to join, but there"
278                                 << " are already max_users="
279                                 << g_settings->getU16("max_users") << " players." << std::endl;
280                 DenyAccess(pkt->getPeerId(), L"Too many users.");
281                 return;
282         }
283
284         std::string checkpwd; // Password hash to check against
285         bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
286
287         // If no authentication info exists for user, create it
288         if (!has_auth) {
289                 if (!isSingleplayer() &&
290                                 g_settings->getBool("disallow_empty_password") &&
291                                 std::string(given_password) == "") {
292                         actionstream << "Server: " << playername
293                                         << " supplied empty password" << std::endl;
294                         DenyAccess(pkt->getPeerId(), L"Empty passwords are "
295                                         L"disallowed. Set a password and try again.");
296                         return;
297                 }
298                 std::wstring raw_default_password =
299                         narrow_to_wide(g_settings->get("default_password"));
300                 std::string initial_password =
301                         translatePassword(playername, raw_default_password);
302
303                 // If default_password is empty, allow any initial password
304                 if (raw_default_password.length() == 0)
305                         initial_password = given_password;
306
307                 m_script->createAuth(playername, initial_password);
308         }
309
310         has_auth = m_script->getAuth(playername, &checkpwd, NULL);
311
312         if (!has_auth) {
313                 actionstream << "Server: " << playername << " cannot be authenticated"
314                                 << " (auth handler does not work?)" << std::endl;
315                 DenyAccess(pkt->getPeerId(), L"Not allowed to login");
316                 return;
317         }
318
319         if (given_password != checkpwd) {
320                 actionstream << "Server: " << playername << " supplied wrong password"
321                                 << std::endl;
322                 DenyAccess(pkt->getPeerId(), L"Wrong password");
323                 return;
324         }
325
326         RemotePlayer *player =
327                         static_cast<RemotePlayer*>(m_env->getPlayer(playername));
328
329         if (player && player->peer_id != 0) {
330                 errorstream << "Server: " << playername << ": Failed to emerge player"
331                                 << " (player allocated to an another client)" << std::endl;
332                 DenyAccess(pkt->getPeerId(), L"Another client is connected with this "
333                                 L"name. If your client closed unexpectedly, try again in "
334                                 L"a minute.");
335         }
336
337         m_clients.setPlayerName(pkt->getPeerId(), playername);
338
339         /*
340                 Answer with a TOCLIENT_INIT
341         */
342
343         NetworkPacket* resp_pkt = new NetworkPacket(TOCLIENT_INIT, 1 + 6 + 8 + 4,
344                         pkt->getPeerId());
345
346         *resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
347                         << (u64) m_env->getServerMap().getSeed()
348                         << g_settings->getFloat("dedicated_server_step");
349
350         Send(resp_pkt);
351         m_clients.event(pkt->getPeerId(), CSE_Init);
352 }
353
354 void Server::handleCommand_Init2(NetworkPacket* pkt)
355 {
356         verbosestream << "Server: Got TOSERVER_INIT2 from "
357                         << pkt->getPeerId() << std::endl;
358
359         m_clients.event(pkt->getPeerId(), CSE_GotInit2);
360         u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
361
362
363         ///// begin compatibility code
364         PlayerSAO* playersao = NULL;
365         if (protocol_version <= 22) {
366                 playersao = StageTwoClientInit(pkt->getPeerId());
367
368                 if (playersao == NULL) {
369                         errorstream
370                                 << "TOSERVER_INIT2 stage 2 client init failed for peer "
371                                 << pkt->getPeerId() << std::endl;
372                         return;
373                 }
374         }
375         ///// end compatibility code
376
377         /*
378                 Send some initialization data
379         */
380
381         infostream << "Server: Sending content to "
382                         << getPlayerName(pkt->getPeerId()) << std::endl;
383
384         // Send player movement settings
385         SendMovement(pkt->getPeerId());
386
387         // Send item definitions
388         SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
389
390         // Send node definitions
391         SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
392
393         m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
394
395         // Send media announcement
396         sendMediaAnnouncement(pkt->getPeerId());
397
398         // Send detached inventories
399         sendDetachedInventories(pkt->getPeerId());
400
401         // Send time of day
402         u16 time = m_env->getTimeOfDay();
403         float time_speed = g_settings->getFloat("time_speed");
404         SendTimeOfDay(pkt->getPeerId(), time, time_speed);
405
406         ///// begin compatibility code
407         if (protocol_version <= 22) {
408                 m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
409                 m_script->on_joinplayer(playersao);
410         }
411         ///// end compatibility code
412
413         // Warnings about protocol version can be issued here
414         if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
415                 SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
416                                 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
417         }
418 }
419
420 void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
421 {
422         std::list<std::string> tosend;
423         u16 numfiles;
424
425         *pkt >> numfiles;
426
427         infostream << "Sending " << numfiles << " files to "
428                         << getPlayerName(pkt->getPeerId()) << std::endl;
429         verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
430
431         for (u16 i = 0; i < numfiles; i++) {
432                 std::string name;
433
434                 *pkt >> name;
435
436                 tosend.push_back(name);
437                 verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
438                                 << name << std::endl;
439         }
440
441         sendRequestedMedia(pkt->getPeerId(), tosend);
442 }
443
444 void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
445 {
446 }
447
448 void Server::handleCommand_ClientReady(NetworkPacket* pkt)
449 {
450         u16 peer_id = pkt->getPeerId();
451         u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
452
453         // clients <= protocol version 22 did not send ready message,
454         // they're already initialized
455         if (peer_proto_ver <= 22) {
456                 infostream << "Client sent message not expected by a "
457                         << "client using protocol version <= 22,"
458                         << "disconnecing peer_id: " << peer_id << std::endl;
459                 m_con.DisconnectPeer(peer_id);
460                 return;
461         }
462
463         PlayerSAO* playersao = StageTwoClientInit(peer_id);
464
465         if (playersao == NULL) {
466                 errorstream
467                         << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
468                         << peer_id << std::endl;
469                 m_con.DisconnectPeer(peer_id);
470                 return;
471         }
472
473
474         if (pkt->getSize() < 8) {
475                 errorstream
476                         << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
477                         << peer_id << std::endl;
478                 m_con.DisconnectPeer(peer_id);
479                 return;
480         }
481
482         u8 major_ver, minor_ver, patch_ver;
483         *pkt >> major_ver >> minor_ver >> patch_ver;
484
485         m_clients.setClientVersion(
486                         peer_id, major_ver, minor_ver, patch_ver,
487                         std::string(pkt->getString(6),(u16) pkt->getU8(4)));
488
489         m_clients.event(peer_id, CSE_SetClientReady);
490         m_script->on_joinplayer(playersao);
491 }
492
493 void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
494 {
495         if (pkt->getSize() < 1)
496                 return;
497
498         /*
499                 [0] u16 command
500                 [2] u8 count
501                 [3] v3s16 pos_0
502                 [3+6] v3s16 pos_1
503                 ...
504         */
505
506         u8 count;
507         *pkt >> count;
508
509         RemoteClient *client = getClient(pkt->getPeerId());
510
511         for (u16 i = 0; i < count; i++) {
512                 if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
513                         throw con::InvalidIncomingDataException
514                                 ("GOTBLOCKS length is too short");
515                 v3s16 p;
516
517                 *pkt >> p;
518
519                 client->GotBlock(p);
520         }
521 }
522
523 void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
524 {
525         if (pkt->getSize() < 12 + 12 + 4 + 4)
526                 return;
527
528         v3s32 ps, ss;
529         s32 f32pitch, f32yaw;
530
531         *pkt >> ps;
532         *pkt >> ss;
533         *pkt >> f32pitch;
534         *pkt >> f32yaw;
535
536         f32 pitch = (f32)f32pitch / 100.0;
537         f32 yaw = (f32)f32yaw / 100.0;
538         u32 keyPressed = 0;
539
540         if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
541                 *pkt >> keyPressed;
542
543         v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
544         v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
545
546         pitch = wrapDegrees(pitch);
547         yaw = wrapDegrees(yaw);
548
549         Player *player = m_env->getPlayer(pkt->getPeerId());
550         if (player == NULL) {
551                 errorstream << "Server::ProcessData(): Cancelling: "
552                                 "No player for peer_id=" << pkt->getPeerId()
553                                 << " disconnecting peer!" << std::endl;
554                 m_con.DisconnectPeer(pkt->getPeerId());
555                 return;
556         }
557
558         PlayerSAO *playersao = player->getPlayerSAO();
559         if (playersao == NULL) {
560                 errorstream << "Server::ProcessData(): Cancelling: "
561                                 "No player object for peer_id=" << pkt->getPeerId()
562                                 << " disconnecting peer!" << std::endl;
563                 m_con.DisconnectPeer(pkt->getPeerId());
564                 return;
565         }
566
567         player->setPosition(position);
568         player->setSpeed(speed);
569         player->setPitch(pitch);
570         player->setYaw(yaw);
571         player->keyPressed = keyPressed;
572         player->control.up = (keyPressed & 1);
573         player->control.down = (keyPressed & 2);
574         player->control.left = (keyPressed & 4);
575         player->control.right = (keyPressed & 8);
576         player->control.jump = (keyPressed & 16);
577         player->control.aux1 = (keyPressed & 32);
578         player->control.sneak = (keyPressed & 64);
579         player->control.LMB = (keyPressed & 128);
580         player->control.RMB = (keyPressed & 256);
581
582         bool cheated = playersao->checkMovementCheat();
583         if (cheated) {
584                 // Call callbacks
585                 m_script->on_cheat(playersao, "moved_too_fast");
586         }
587 }
588
589 void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
590 {
591         if (pkt->getSize() < 1)
592                 return;
593
594         /*
595                 [0] u16 command
596                 [2] u8 count
597                 [3] v3s16 pos_0
598                 [3+6] v3s16 pos_1
599                 ...
600         */
601
602         u8 count;
603         *pkt >> count;
604
605         RemoteClient *client = getClient(pkt->getPeerId());
606
607         for (u16 i = 0; i < count; i++) {
608                 if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
609                         throw con::InvalidIncomingDataException
610                                 ("DELETEDBLOCKS length is too short");
611                 v3s16 p;
612                 *pkt >> p;
613
614                 client->SetBlockNotSent(p);
615         }
616 }
617
618 void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
619 {
620         Player *player = m_env->getPlayer(pkt->getPeerId());
621         if (player == NULL) {
622                 errorstream << "Server::ProcessData(): Cancelling: "
623                                 "No player for peer_id=" << pkt->getPeerId()
624                                 << " disconnecting peer!" << std::endl;
625                 m_con.DisconnectPeer(pkt->getPeerId());
626                 return;
627         }
628
629         PlayerSAO *playersao = player->getPlayerSAO();
630         if (playersao == NULL) {
631                 errorstream << "Server::ProcessData(): Cancelling: "
632                                 "No player object for peer_id=" << pkt->getPeerId()
633                                 << " disconnecting peer!" << std::endl;
634                 m_con.DisconnectPeer(pkt->getPeerId());
635                 return;
636         }
637
638         // Strip command and create a stream
639         std::string datastring(pkt->getString(0), pkt->getSize());
640         verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
641                 << std::endl;
642         std::istringstream is(datastring, std::ios_base::binary);
643         // Create an action
644         InventoryAction *a = InventoryAction::deSerialize(is);
645         if (a == NULL) {
646                 infostream << "TOSERVER_INVENTORY_ACTION: "
647                                 << "InventoryAction::deSerialize() returned NULL"
648                                 << std::endl;
649                 return;
650         }
651
652         // If something goes wrong, this player is to blame
653         RollbackScopeActor rollback_scope(m_rollback,
654                         std::string("player:")+player->getName());
655
656         /*
657                 Note: Always set inventory not sent, to repair cases
658                 where the client made a bad prediction.
659         */
660
661         /*
662                 Handle restrictions and special cases of the move action
663         */
664         if (a->getType() == IACTION_MOVE) {
665                 IMoveAction *ma = (IMoveAction*)a;
666
667                 ma->from_inv.applyCurrentPlayer(player->getName());
668                 ma->to_inv.applyCurrentPlayer(player->getName());
669
670                 setInventoryModified(ma->from_inv);
671                 setInventoryModified(ma->to_inv);
672
673                 bool from_inv_is_current_player =
674                         (ma->from_inv.type == InventoryLocation::PLAYER) &&
675                         (ma->from_inv.name == player->getName());
676
677                 bool to_inv_is_current_player =
678                         (ma->to_inv.type == InventoryLocation::PLAYER) &&
679                         (ma->to_inv.name == player->getName());
680
681                 /*
682                         Disable moving items out of craftpreview
683                 */
684                 if (ma->from_list == "craftpreview") {
685                         infostream << "Ignoring IMoveAction from "
686                                         << (ma->from_inv.dump()) << ":" << ma->from_list
687                                         << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
688                                         << " because src is " << ma->from_list << std::endl;
689                         delete a;
690                         return;
691                 }
692
693                 /*
694                         Disable moving items into craftresult and craftpreview
695                 */
696                 if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
697                         infostream << "Ignoring IMoveAction from "
698                                         << (ma->from_inv.dump()) << ":" << ma->from_list
699                                         << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
700                                         << " because dst is " << ma->to_list << std::endl;
701                         delete a;
702                         return;
703                 }
704
705                 // Disallow moving items in elsewhere than player's inventory
706                 // if not allowed to interact
707                 if (!checkPriv(player->getName(), "interact") &&
708                                 (!from_inv_is_current_player ||
709                                 !to_inv_is_current_player)) {
710                         infostream << "Cannot move outside of player's inventory: "
711                                         << "No interact privilege" << std::endl;
712                         delete a;
713                         return;
714                 }
715         }
716         /*
717                 Handle restrictions and special cases of the drop action
718         */
719         else if (a->getType() == IACTION_DROP) {
720                 IDropAction *da = (IDropAction*)a;
721
722                 da->from_inv.applyCurrentPlayer(player->getName());
723
724                 setInventoryModified(da->from_inv);
725
726                 /*
727                         Disable dropping items out of craftpreview
728                 */
729                 if (da->from_list == "craftpreview") {
730                         infostream << "Ignoring IDropAction from "
731                                         << (da->from_inv.dump()) << ":" << da->from_list
732                                         << " because src is " << da->from_list << std::endl;
733                         delete a;
734                         return;
735                 }
736
737                 // Disallow dropping items if not allowed to interact
738                 if (!checkPriv(player->getName(), "interact")) {
739                         delete a;
740                         return;
741                 }
742         }
743         /*
744                 Handle restrictions and special cases of the craft action
745         */
746         else if (a->getType() == IACTION_CRAFT) {
747                 ICraftAction *ca = (ICraftAction*)a;
748
749                 ca->craft_inv.applyCurrentPlayer(player->getName());
750
751                 setInventoryModified(ca->craft_inv);
752
753                 //bool craft_inv_is_current_player =
754                 //      (ca->craft_inv.type == InventoryLocation::PLAYER) &&
755                 //      (ca->craft_inv.name == player->getName());
756
757                 // Disallow crafting if not allowed to interact
758                 if (!checkPriv(player->getName(), "interact")) {
759                         infostream << "Cannot craft: "
760                                         << "No interact privilege" << std::endl;
761                         delete a;
762                         return;
763                 }
764         }
765
766         // Do the action
767         a->apply(this, playersao, this);
768         // Eat the action
769         delete a;
770 }
771
772 void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
773 {
774         /*
775                 u16 command
776                 u16 length
777                 wstring message
778         */
779         u16 len;
780         *pkt >> len;
781
782         std::wstring message;
783         for (u16 i = 0; i < len; i++) {
784                 u16 tmp_wchar;
785                 *pkt >> tmp_wchar;
786
787                 message += (wchar_t)tmp_wchar;
788         }
789
790         Player *player = m_env->getPlayer(pkt->getPeerId());
791         if (player == NULL) {
792                 errorstream << "Server::ProcessData(): Cancelling: "
793                                 "No player for peer_id=" << pkt->getPeerId()
794                                 << " disconnecting peer!" << std::endl;
795                 m_con.DisconnectPeer(pkt->getPeerId());
796                 return;
797         }
798
799         // If something goes wrong, this player is to blame
800         RollbackScopeActor rollback_scope(m_rollback,
801                         std::string("player:")+player->getName());
802
803         // Get player name of this client
804         std::wstring name = narrow_to_wide(player->getName());
805
806         // Run script hook
807         bool ate = m_script->on_chat_message(player->getName(),
808                         wide_to_narrow(message));
809         // If script ate the message, don't proceed
810         if (ate)
811                 return;
812
813         // Line to send to players
814         std::wstring line;
815         // Whether to send to the player that sent the line
816         bool send_to_sender_only = false;
817
818         // Commands are implemented in Lua, so only catch invalid
819         // commands that were not "eaten" and send an error back
820         if (message[0] == L'/') {
821                 message = message.substr(1);
822                 send_to_sender_only = true;
823                 if (message.length() == 0)
824                         line += L"-!- Empty command";
825                 else
826                         line += L"-!- Invalid command: " + str_split(message, L' ')[0];
827         }
828         else {
829                 if (checkPriv(player->getName(), "shout")) {
830                         line += L"<";
831                         line += name;
832                         line += L"> ";
833                         line += message;
834                 } else {
835                         line += L"-!- You don't have permission to shout.";
836                         send_to_sender_only = true;
837                 }
838         }
839
840         if (line != L"")
841         {
842                 /*
843                         Send the message to sender
844                 */
845                 if (send_to_sender_only) {
846                         SendChatMessage(pkt->getPeerId(), line);
847                 }
848                 /*
849                         Send the message to others
850                 */
851                 else {
852                         actionstream << "CHAT: " << wide_to_narrow(line)<<std::endl;
853
854                         std::list<u16> clients = m_clients.getClientIDs();
855
856                         for (std::list<u16>::iterator
857                                 i = clients.begin();
858                                 i != clients.end(); ++i) {
859                                 if (*i != pkt->getPeerId())
860                                         SendChatMessage(*i, line);
861                         }
862                 }
863         }
864 }
865
866 void Server::handleCommand_Damage(NetworkPacket* pkt)
867 {
868         u8 damage;
869
870         *pkt >> damage;
871
872         Player *player = m_env->getPlayer(pkt->getPeerId());
873         if (player == NULL) {
874                 errorstream << "Server::ProcessData(): Cancelling: "
875                                 "No player for peer_id=" << pkt->getPeerId()
876                                 << " disconnecting peer!" << std::endl;
877                 m_con.DisconnectPeer(pkt->getPeerId());
878                 return;
879         }
880
881         PlayerSAO *playersao = player->getPlayerSAO();
882         if (playersao == NULL) {
883                 errorstream << "Server::ProcessData(): Cancelling: "
884                                 "No player object for peer_id=" << pkt->getPeerId()
885                                 << " disconnecting peer!" << std::endl;
886                 m_con.DisconnectPeer(pkt->getPeerId());
887                 return;
888         }
889
890         if (g_settings->getBool("enable_damage")) {
891                 actionstream << player->getName() << " damaged by "
892                                 << (int)damage << " hp at " << PP(player->getPosition() / BS)
893                                 << std::endl;
894
895                 playersao->setHP(playersao->getHP() - damage);
896
897                 if (playersao->getHP() == 0 && playersao->m_hp_not_sent)
898                         DiePlayer(pkt->getPeerId());
899
900                 if (playersao->m_hp_not_sent)
901                         SendPlayerHP(pkt->getPeerId());
902         }
903 }
904
905 void Server::handleCommand_Breath(NetworkPacket* pkt)
906 {
907         u16 breath;
908
909         *pkt >> breath;
910
911         Player *player = m_env->getPlayer(pkt->getPeerId());
912         if (player == NULL) {
913                 errorstream << "Server::ProcessData(): Cancelling: "
914                                 "No player for peer_id=" << pkt->getPeerId()
915                                 << " disconnecting peer!" << std::endl;
916                 m_con.DisconnectPeer(pkt->getPeerId());
917                 return;
918         }
919
920         PlayerSAO *playersao = player->getPlayerSAO();
921         if (playersao == NULL) {
922                 errorstream << "Server::ProcessData(): Cancelling: "
923                                 "No player object for peer_id=" << pkt->getPeerId()
924                                 << " disconnecting peer!" << std::endl;
925                 m_con.DisconnectPeer(pkt->getPeerId());
926                 return;
927         }
928
929         playersao->setBreath(breath);
930         m_script->player_event(playersao,"breath_changed");
931 }
932
933 void Server::handleCommand_Password(NetworkPacket* pkt)
934 {
935         /*
936                 [0] u16 TOSERVER_PASSWORD
937                 [2] u8[28] old password
938                 [30] u8[28] new password
939         */
940
941         errorstream << "PAssword packet size: " << pkt->getSize() << " size required: " << PASSWORD_SIZE * 2 << std::endl;
942         if (pkt->getSize() != PASSWORD_SIZE * 2)
943                 return;
944
945         std::string oldpwd;
946         std::string newpwd;
947
948         for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
949                 char c = pkt->getChar(i);
950                 if (c == 0)
951                         break;
952                 oldpwd += c;
953         }
954
955         for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
956                 char c = pkt->getChar(PASSWORD_SIZE + i);
957                 if (c == 0)
958                         break;
959                 newpwd += c;
960         }
961
962         Player *player = m_env->getPlayer(pkt->getPeerId());
963         if (player == NULL) {
964                 errorstream << "Server::ProcessData(): Cancelling: "
965                                 "No player for peer_id=" << pkt->getPeerId()
966                                 << " disconnecting peer!" << std::endl;
967                 m_con.DisconnectPeer(pkt->getPeerId());
968                 return;
969         }
970
971         if (!base64_is_valid(newpwd)) {
972                 infostream<<"Server: " << player->getName() <<
973                                 " supplied invalid password hash" << std::endl;
974                 // Wrong old password supplied!!
975                 SendChatMessage(pkt->getPeerId(), L"Invalid new password hash supplied. Password NOT changed.");
976                 return;
977         }
978
979         infostream << "Server: Client requests a password change from "
980                         << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl;
981
982         std::string playername = player->getName();
983
984         std::string checkpwd;
985         m_script->getAuth(playername, &checkpwd, NULL);
986
987         if (oldpwd != checkpwd) {
988                 infostream << "Server: invalid old password" << std::endl;
989                 // Wrong old password supplied!!
990                 SendChatMessage(pkt->getPeerId(), L"Invalid old password supplied. Password NOT changed.");
991                 return;
992         }
993
994         bool success = m_script->setPassword(playername, newpwd);
995         if (success) {
996                 actionstream << player->getName() << " changes password" << std::endl;
997                 SendChatMessage(pkt->getPeerId(), L"Password change successful.");
998         } else {
999                 actionstream << player->getName() << " tries to change password but "
1000                                 << "it fails" << std::endl;
1001                 SendChatMessage(pkt->getPeerId(), L"Password change failed or inavailable.");
1002         }
1003 }
1004
1005 void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
1006 {
1007         if (pkt->getSize() < 2)
1008                 return;
1009
1010         Player *player = m_env->getPlayer(pkt->getPeerId());
1011         if (player == NULL) {
1012                 errorstream << "Server::ProcessData(): Cancelling: "
1013                                 "No player for peer_id=" << pkt->getPeerId()
1014                                 << " disconnecting peer!" << std::endl;
1015                 m_con.DisconnectPeer(pkt->getPeerId());
1016                 return;
1017         }
1018
1019         PlayerSAO *playersao = player->getPlayerSAO();
1020         if (playersao == NULL) {
1021                 errorstream << "Server::ProcessData(): Cancelling: "
1022                                 "No player object for peer_id=" << pkt->getPeerId()
1023                                 << " disconnecting peer!" << std::endl;
1024                 m_con.DisconnectPeer(pkt->getPeerId());
1025                 return;
1026         }
1027
1028         u16 item;
1029
1030         *pkt >> item;
1031
1032         playersao->setWieldIndex(item);
1033 }
1034
1035 void Server::handleCommand_Respawn(NetworkPacket* pkt)
1036 {
1037         Player *player = m_env->getPlayer(pkt->getPeerId());
1038         if (player == NULL) {
1039                 errorstream << "Server::ProcessData(): Cancelling: "
1040                                 "No player for peer_id=" << pkt->getPeerId()
1041                                 << " disconnecting peer!" << std::endl;
1042                 m_con.DisconnectPeer(pkt->getPeerId());
1043                 return;
1044         }
1045
1046         if (player->hp != 0 || !g_settings->getBool("enable_damage"))
1047                 return;
1048
1049         RespawnPlayer(pkt->getPeerId());
1050
1051         actionstream<<player->getName()<<" respawns at "
1052                         <<PP(player->getPosition()/BS)<<std::endl;
1053
1054         // ActiveObject is added to environment in AsyncRunStep after
1055         // the previous addition has been succesfully removed
1056 }
1057
1058 void Server::handleCommand_Interact(NetworkPacket* pkt)
1059 {
1060         std::string datastring(pkt->getString(0), pkt->getSize());
1061         std::istringstream is(datastring, std::ios_base::binary);
1062
1063         /*
1064                 [0] u16 command
1065                 [2] u8 action
1066                 [3] u16 item
1067                 [5] u32 length of the next item
1068                 [9] serialized PointedThing
1069                 actions:
1070                 0: start digging (from undersurface) or use
1071                 1: stop digging (all parameters ignored)
1072                 2: digging completed
1073                 3: place block or item (to abovesurface)
1074                 4: use item
1075         */
1076         u8 action = readU8(is);
1077         u16 item_i = readU16(is);
1078         std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1079         PointedThing pointed;
1080         pointed.deSerialize(tmp_is);
1081
1082         verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
1083                         << item_i << ", pointed=" << pointed.dump() << std::endl;
1084
1085         Player *player = m_env->getPlayer(pkt->getPeerId());
1086         if (player == NULL) {
1087                 errorstream << "Server::ProcessData(): Cancelling: "
1088                                 "No player for peer_id=" << pkt->getPeerId()
1089                                 << " disconnecting peer!" << std::endl;
1090                 m_con.DisconnectPeer(pkt->getPeerId());
1091                 return;
1092         }
1093
1094         PlayerSAO *playersao = player->getPlayerSAO();
1095         if (playersao == NULL) {
1096                 errorstream << "Server::ProcessData(): Cancelling: "
1097                                 "No player object for peer_id=" << pkt->getPeerId()
1098                                 << " disconnecting peer!" << std::endl;
1099                 m_con.DisconnectPeer(pkt->getPeerId());
1100                 return;
1101         }
1102
1103         if (player->hp == 0) {
1104                 verbosestream << "TOSERVER_INTERACT: " << player->getName()
1105                         << " tried to interact, but is dead!" << std::endl;
1106                 return;
1107         }
1108
1109         v3f player_pos = playersao->getLastGoodPosition();
1110
1111         // Update wielded item
1112         playersao->setWieldIndex(item_i);
1113
1114         // Get pointed to node (undefined if not POINTEDTYPE_NODE)
1115         v3s16 p_under = pointed.node_undersurface;
1116         v3s16 p_above = pointed.node_abovesurface;
1117
1118         // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
1119         ServerActiveObject *pointed_object = NULL;
1120         if (pointed.type == POINTEDTHING_OBJECT) {
1121                 pointed_object = m_env->getActiveObject(pointed.object_id);
1122                 if (pointed_object == NULL) {
1123                         verbosestream << "TOSERVER_INTERACT: "
1124                                 "pointed object is NULL" << std::endl;
1125                         return;
1126                 }
1127
1128         }
1129
1130         v3f pointed_pos_under = player_pos;
1131         v3f pointed_pos_above = player_pos;
1132         if (pointed.type == POINTEDTHING_NODE) {
1133                 pointed_pos_under = intToFloat(p_under, BS);
1134                 pointed_pos_above = intToFloat(p_above, BS);
1135         }
1136         else if (pointed.type == POINTEDTHING_OBJECT) {
1137                 pointed_pos_under = pointed_object->getBasePosition();
1138                 pointed_pos_above = pointed_pos_under;
1139         }
1140
1141         /*
1142                 Check that target is reasonably close
1143                 (only when digging or placing things)
1144         */
1145         if (action == 0 || action == 2 || action == 3) {
1146                 float d = player_pos.getDistanceFrom(pointed_pos_under);
1147                 float max_d = BS * 14; // Just some large enough value
1148                 if (d > max_d) {
1149                         actionstream << "Player " << player->getName()
1150                                         << " tried to access " << pointed.dump()
1151                                         << " from too far: "
1152                                         << "d=" << d <<", max_d=" << max_d
1153                                         << ". ignoring." << std::endl;
1154                         // Re-send block to revert change on client-side
1155                         RemoteClient *client = getClient(pkt->getPeerId());
1156                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1157                         client->SetBlockNotSent(blockpos);
1158                         // Call callbacks
1159                         m_script->on_cheat(playersao, "interacted_too_far");
1160                         // Do nothing else
1161                         return;
1162                 }
1163         }
1164
1165         /*
1166                 Make sure the player is allowed to do it
1167         */
1168         if (!checkPriv(player->getName(), "interact")) {
1169                 actionstream<<player->getName()<<" attempted to interact with "
1170                                 <<pointed.dump()<<" without 'interact' privilege"
1171                                 <<std::endl;
1172                 // Re-send block to revert change on client-side
1173                 RemoteClient *client = getClient(pkt->getPeerId());
1174                 // Digging completed -> under
1175                 if (action == 2) {
1176                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1177                         client->SetBlockNotSent(blockpos);
1178                 }
1179                 // Placement -> above
1180                 if (action == 3) {
1181                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
1182                         client->SetBlockNotSent(blockpos);
1183                 }
1184                 return;
1185         }
1186
1187         /*
1188                 If something goes wrong, this player is to blame
1189         */
1190         RollbackScopeActor rollback_scope(m_rollback,
1191                         std::string("player:")+player->getName());
1192
1193         /*
1194                 0: start digging or punch object
1195         */
1196         if (action == 0) {
1197                 if (pointed.type == POINTEDTHING_NODE) {
1198                         /*
1199                                 NOTE: This can be used in the future to check if
1200                                 somebody is cheating, by checking the timing.
1201                         */
1202                         MapNode n(CONTENT_IGNORE);
1203                         bool pos_ok;
1204                         n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
1205                         if (pos_ok)
1206                                 n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
1207
1208                         if (!pos_ok) {
1209                                 infostream << "Server: Not punching: Node not found."
1210                                                 << " Adding block to emerge queue."
1211                                                 << std::endl;
1212                                 m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
1213                         }
1214
1215                         if (n.getContent() != CONTENT_IGNORE)
1216                                 m_script->node_on_punch(p_under, n, playersao, pointed);
1217                         // Cheat prevention
1218                         playersao->noCheatDigStart(p_under);
1219                 }
1220                 else if (pointed.type == POINTEDTHING_OBJECT) {
1221                         // Skip if object has been removed
1222                         if (pointed_object->m_removed)
1223                                 return;
1224
1225                         actionstream<<player->getName()<<" punches object "
1226                                         <<pointed.object_id<<": "
1227                                         <<pointed_object->getDescription()<<std::endl;
1228
1229                         ItemStack punchitem = playersao->getWieldedItem();
1230                         ToolCapabilities toolcap =
1231                                         punchitem.getToolCapabilities(m_itemdef);
1232                         v3f dir = (pointed_object->getBasePosition() -
1233                                         (player->getPosition() + player->getEyeOffset())
1234                                                 ).normalize();
1235                         float time_from_last_punch =
1236                                 playersao->resetTimeFromLastPunch();
1237                         pointed_object->punch(dir, &toolcap, playersao,
1238                                         time_from_last_punch);
1239                 }
1240
1241         } // action == 0
1242
1243         /*
1244                 1: stop digging
1245         */
1246         else if (action == 1) {
1247         } // action == 1
1248
1249         /*
1250                 2: Digging completed
1251         */
1252         else if (action == 2) {
1253                 // Only digging of nodes
1254                 if (pointed.type == POINTEDTHING_NODE) {
1255                         bool pos_ok;
1256                         MapNode n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
1257                         if (!pos_ok) {
1258                                 infostream << "Server: Not finishing digging: Node not found."
1259                                                    << " Adding block to emerge queue."
1260                                                    << std::endl;
1261                                 m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
1262                         }
1263
1264                         /* Cheat prevention */
1265                         bool is_valid_dig = true;
1266                         if (!isSingleplayer() && !g_settings->getBool("disable_anticheat")) {
1267                                 v3s16 nocheat_p = playersao->getNoCheatDigPos();
1268                                 float nocheat_t = playersao->getNoCheatDigTime();
1269                                 playersao->noCheatDigEnd();
1270                                 // If player didn't start digging this, ignore dig
1271                                 if (nocheat_p != p_under) {
1272                                         infostream << "Server: NoCheat: " << player->getName()
1273                                                         << " started digging "
1274                                                         << PP(nocheat_p) << " and completed digging "
1275                                                         << PP(p_under) << "; not digging." << std::endl;
1276                                         is_valid_dig = false;
1277                                         // Call callbacks
1278                                         m_script->on_cheat(playersao, "finished_unknown_dig");
1279                                 }
1280                                 // Get player's wielded item
1281                                 ItemStack playeritem;
1282                                 InventoryList *mlist = playersao->getInventory()->getList("main");
1283                                 if (mlist != NULL)
1284                                         playeritem = mlist->getItem(playersao->getWieldIndex());
1285                                 ToolCapabilities playeritem_toolcap =
1286                                                 playeritem.getToolCapabilities(m_itemdef);
1287                                 // Get diggability and expected digging time
1288                                 DigParams params = getDigParams(m_nodedef->get(n).groups,
1289                                                 &playeritem_toolcap);
1290                                 // If can't dig, try hand
1291                                 if (!params.diggable) {
1292                                         const ItemDefinition &hand = m_itemdef->get("");
1293                                         const ToolCapabilities *tp = hand.tool_capabilities;
1294                                         if (tp)
1295                                                 params = getDigParams(m_nodedef->get(n).groups, tp);
1296                                 }
1297                                 // If can't dig, ignore dig
1298                                 if (!params.diggable) {
1299                                         infostream << "Server: NoCheat: " << player->getName()
1300                                                         << " completed digging " << PP(p_under)
1301                                                         << ", which is not diggable with tool. not digging."
1302                                                         << std::endl;
1303                                         is_valid_dig = false;
1304                                         // Call callbacks
1305                                         m_script->on_cheat(playersao, "dug_unbreakable");
1306                                 }
1307                                 // Check digging time
1308                                 // If already invalidated, we don't have to
1309                                 if (!is_valid_dig) {
1310                                         // Well not our problem then
1311                                 }
1312                                 // Clean and long dig
1313                                 else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
1314                                         // All is good, but grab time from pool; don't care if
1315                                         // it's actually available
1316                                         playersao->getDigPool().grab(params.time);
1317                                 }
1318                                 // Short or laggy dig
1319                                 // Try getting the time from pool
1320                                 else if (playersao->getDigPool().grab(params.time)) {
1321                                         // All is good
1322                                 }
1323                                 // Dig not possible
1324                                 else {
1325                                         infostream << "Server: NoCheat: " << player->getName()
1326                                                         << " completed digging " << PP(p_under)
1327                                                         << "too fast; not digging." << std::endl;
1328                                         is_valid_dig = false;
1329                                         // Call callbacks
1330                                         m_script->on_cheat(playersao, "dug_too_fast");
1331                                 }
1332                         }
1333
1334                         /* Actually dig node */
1335
1336                         if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
1337                                 m_script->node_on_dig(p_under, n, playersao);
1338
1339                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1340                         RemoteClient *client = getClient(pkt->getPeerId());
1341                         // Send unusual result (that is, node not being removed)
1342                         if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) {
1343                                 // Re-send block to revert change on client-side
1344                                 client->SetBlockNotSent(blockpos);
1345                         }
1346                         else {
1347                                 client->ResendBlockIfOnWire(blockpos);
1348                         }
1349                 }
1350         } // action == 2
1351
1352         /*
1353                 3: place block or right-click object
1354         */
1355         else if (action == 3) {
1356                 ItemStack item = playersao->getWieldedItem();
1357
1358                 // Reset build time counter
1359                 if (pointed.type == POINTEDTHING_NODE &&
1360                                 item.getDefinition(m_itemdef).type == ITEM_NODE)
1361                         getClient(pkt->getPeerId())->m_time_from_building = 0.0;
1362
1363                 if (pointed.type == POINTEDTHING_OBJECT) {
1364                         // Right click object
1365
1366                         // Skip if object has been removed
1367                         if (pointed_object->m_removed)
1368                                 return;
1369
1370                         actionstream << player->getName() << " right-clicks object "
1371                                         << pointed.object_id << ": "
1372                                         << pointed_object->getDescription() << std::endl;
1373
1374                         // Do stuff
1375                         pointed_object->rightClick(playersao);
1376                 }
1377                 else if (m_script->item_OnPlace(
1378                                 item, playersao, pointed)) {
1379                         // Placement was handled in lua
1380
1381                         // Apply returned ItemStack
1382                         playersao->setWieldedItem(item);
1383                 }
1384
1385                 // If item has node placement prediction, always send the
1386                 // blocks to make sure the client knows what exactly happened
1387                 RemoteClient *client = getClient(pkt->getPeerId());
1388                 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
1389                 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1390                 if (item.getDefinition(m_itemdef).node_placement_prediction != "") {
1391                         client->SetBlockNotSent(blockpos);
1392                         if (blockpos2 != blockpos) {
1393                                 client->SetBlockNotSent(blockpos2);
1394                         }
1395                 }
1396                 else {
1397                         client->ResendBlockIfOnWire(blockpos);
1398                         if (blockpos2 != blockpos) {
1399                                 client->ResendBlockIfOnWire(blockpos2);
1400                         }
1401                 }
1402         } // action == 3
1403
1404         /*
1405                 4: use
1406         */
1407         else if (action == 4) {
1408                 ItemStack item = playersao->getWieldedItem();
1409
1410                 actionstream << player->getName() << " uses " << item.name
1411                                 << ", pointing at " << pointed.dump() << std::endl;
1412
1413                 if (m_script->item_OnUse(
1414                                 item, playersao, pointed)) {
1415                         // Apply returned ItemStack
1416                         playersao->setWieldedItem(item);
1417                 }
1418
1419         } // action == 4
1420
1421
1422         /*
1423                 Catch invalid actions
1424         */
1425         else {
1426                 infostream << "WARNING: Server: Invalid action "
1427                                 << action << std::endl;
1428         }
1429 }
1430
1431 void Server::handleCommand_RemovedSounds(NetworkPacket* pkt)
1432 {
1433         u16 num;
1434         *pkt >> num;
1435         for (u16 k = 0; k < num; k++) {
1436                 s32 id;
1437
1438                 *pkt >> id;
1439
1440                 std::map<s32, ServerPlayingSound>::iterator i =
1441                         m_playing_sounds.find(id);
1442
1443                 if (i == m_playing_sounds.end())
1444                         continue;
1445
1446                 ServerPlayingSound &psound = i->second;
1447                 psound.clients.erase(pkt->getPeerId());
1448                 if (psound.clients.empty())
1449                         m_playing_sounds.erase(i++);
1450         }
1451 }
1452
1453 void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
1454 {
1455         v3s16 p;
1456         std::string formname;
1457         u16 num;
1458
1459         *pkt >> p >> formname >> num;
1460
1461         std::map<std::string, std::string> fields;
1462         for (u16 k = 0; k < num; k++) {
1463                 std::string fieldname;
1464                 *pkt >> fieldname;
1465                 fields[fieldname] = pkt->readLongString();
1466         }
1467
1468         Player *player = m_env->getPlayer(pkt->getPeerId());
1469         if (player == NULL) {
1470                 errorstream << "Server::ProcessData(): Cancelling: "
1471                                 "No player for peer_id=" << pkt->getPeerId()
1472                                 << " disconnecting peer!" << std::endl;
1473                 m_con.DisconnectPeer(pkt->getPeerId());
1474                 return;
1475         }
1476
1477         PlayerSAO *playersao = player->getPlayerSAO();
1478         if (playersao == NULL) {
1479                 errorstream << "Server::ProcessData(): Cancelling: "
1480                                 "No player object for peer_id=" << pkt->getPeerId()
1481                                 << " disconnecting peer!"  << std::endl;
1482                 m_con.DisconnectPeer(pkt->getPeerId());
1483                 return;
1484         }
1485
1486         // If something goes wrong, this player is to blame
1487         RollbackScopeActor rollback_scope(m_rollback,
1488                         std::string("player:")+player->getName());
1489
1490         // Check the target node for rollback data; leave others unnoticed
1491         RollbackNode rn_old(&m_env->getMap(), p, this);
1492
1493         m_script->node_on_receive_fields(p, formname, fields, playersao);
1494
1495         // Report rollback data
1496         RollbackNode rn_new(&m_env->getMap(), p, this);
1497         if (rollback() && rn_new != rn_old) {
1498                 RollbackAction action;
1499                 action.setSetNode(p, rn_old, rn_new);
1500                 rollback()->reportAction(action);
1501         }
1502 }
1503
1504 void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
1505 {
1506         std::string formname;
1507         u16 num;
1508
1509         *pkt >> formname >> num;
1510
1511         std::map<std::string, std::string> fields;
1512         for (u16 k = 0; k < num; k++) {
1513                 std::string fieldname;
1514                 *pkt >> fieldname;
1515                 fields[fieldname] = pkt->readLongString();
1516         }
1517
1518         Player *player = m_env->getPlayer(pkt->getPeerId());
1519         if (player == NULL) {
1520                 errorstream << "Server::ProcessData(): Canceling: "
1521                                 "No player for peer_id=" << pkt->getPeerId()
1522                                 << " disconnecting peer!" << std::endl;
1523                 m_con.DisconnectPeer(pkt->getPeerId());
1524                 return;
1525         }
1526
1527         PlayerSAO *playersao = player->getPlayerSAO();
1528         if (playersao == NULL) {
1529                 errorstream << "Server::ProcessData(): Canceling: "
1530                                 "No player object for peer_id=" << pkt->getPeerId()
1531                                 << " disconnecting peer!" << std::endl;
1532                 m_con.DisconnectPeer(pkt->getPeerId());
1533                 return;
1534         }
1535
1536         m_script->on_playerReceiveFields(playersao, formname, fields);
1537 }