+ // Get player name of this client
+ std::wstring name = narrow_to_wide(player->getName());
+
+ // Run script hook
+ bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
+ wide_to_narrow(message));
+ // If script ate the message, don't proceed
+ if(ate)
+ return;
+
+ // Line to send to players
+ std::wstring line;
+ // Whether to send to the player that sent the line
+ bool send_to_sender = false;
+ // Whether to send to other players
+ bool send_to_others = false;
+
+ // Local player gets all privileges regardless of
+ // what's set on their account.
+ u64 privs = getPlayerPrivs(player);
+
+ // Parse commands
+ if(message[0] == L'/')
+ {
+ size_t strip_size = 1;
+ if (message[1] == L'#') // support old-style commans
+ ++strip_size;
+ message = message.substr(strip_size);
+
+ WStrfnd f1(message);
+ f1.next(L" "); // Skip over /#whatever
+ std::wstring paramstring = f1.next(L"");
+
+ ServerCommandContext *ctx = new ServerCommandContext(
+ str_split(message, L' '),
+ paramstring,
+ this,
+ m_env,
+ player,
+ privs);
+
+ std::wstring reply(processServerCommand(ctx));
+ send_to_sender = ctx->flags & SEND_TO_SENDER;
+ send_to_others = ctx->flags & SEND_TO_OTHERS;
+
+ if (ctx->flags & SEND_NO_PREFIX)
+ line += reply;
+ else
+ line += L"Server: " + reply;
+
+ delete ctx;
+
+ }
+ else
+ {
+ if(privs & PRIV_SHOUT)
+ {
+ line += L"<";
+ line += name;
+ line += L"> ";
+ line += message;
+ send_to_others = true;
+ }
+ else
+ {
+ line += L"Server: You are not allowed to shout";
+ send_to_sender = true;
+ }
+ }
+
+ if(line != L"")
+ {
+ if(send_to_others)
+ actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
+
+ /*
+ Send the message to clients
+ */
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Filter recipient
+ bool sender_selected = (peer_id == client->peer_id);
+ if(sender_selected == true && send_to_sender == false)
+ continue;
+ if(sender_selected == false && send_to_others == false)
+ continue;
+
+ SendChatMessage(client->peer_id, line);
+ }
+ }
+ }
+ else if(command == TOSERVER_DAMAGE)
+ {
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+ u8 damage = readU8(is);
+
+ if(g_settings->getBool("enable_damage"))
+ {
+ actionstream<<player->getName()<<" damaged by "
+ <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
+ <<std::endl;
+
+ HandlePlayerHP(player, damage);
+ }
+ else
+ {
+ SendPlayerHP(player);
+ }
+ }
+ else if(command == TOSERVER_PASSWORD)
+ {
+ /*
+ [0] u16 TOSERVER_PASSWORD
+ [2] u8[28] old password
+ [30] u8[28] new password
+ */
+
+ if(datasize != 2+PASSWORD_SIZE*2)
+ return;
+ /*char password[PASSWORD_SIZE];
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ password[i] = data[2+i];
+ password[PASSWORD_SIZE-1] = 0;*/
+ std::string oldpwd;
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ {
+ char c = data[2+i];
+ if(c == 0)
+ break;
+ oldpwd += c;
+ }
+ std::string newpwd;
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ {
+ char c = data[2+PASSWORD_SIZE+i];
+ if(c == 0)
+ break;
+ newpwd += c;
+ }
+
+ infostream<<"Server: Client requests a password change from "
+ <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+
+ std::string playername = player->getName();
+
+ if(m_authmanager.exists(playername) == false)
+ {
+ infostream<<"Server: playername not found in authmanager"<<std::endl;
+ // Wrong old password supplied!!
+ SendChatMessage(peer_id, L"playername not found in authmanager");
+ return;
+ }
+
+ std::string checkpwd = m_authmanager.getPassword(playername);
+
+ if(oldpwd != checkpwd)
+ {
+ infostream<<"Server: invalid old password"<<std::endl;
+ // Wrong old password supplied!!
+ SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
+ return;
+ }
+
+ actionstream<<player->getName()<<" changes password"<<std::endl;
+
+ m_authmanager.setPassword(playername, newpwd);
+
+ infostream<<"Server: password change successful for "<<playername
+ <<std::endl;
+ SendChatMessage(peer_id, L"Password change successful");
+ }
+ else if(command == TOSERVER_PLAYERITEM)
+ {
+ if (datasize < 2+2)
+ return;
+
+ u16 item = readU16(&data[2]);
+ player->wieldItem(item);
+ SendWieldedItem(player);
+ }
+ else if(command == TOSERVER_RESPAWN)
+ {
+ if(player->hp != 0)
+ return;
+
+ RespawnPlayer(player);
+
+ actionstream<<player->getName()<<" respawns at "
+ <<PP(player->getPosition()/BS)<<std::endl;
+ }
+ else if(command == TOSERVER_INTERACT)
+ {
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ /*
+ [0] u16 command
+ [2] u8 action
+ [3] u16 item
+ [5] u32 length of the next item
+ [9] serialized PointedThing
+ actions:
+ 0: start digging (from undersurface) or use
+ 1: stop digging (all parameters ignored)
+ 2: digging completed
+ 3: place block or item (to abovesurface)
+ 4: use item
+ */
+ u8 action = readU8(is);
+ u16 item_i = readU16(is);
+ std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+ PointedThing pointed;
+ pointed.deSerialize(tmp_is);
+
+ infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
+
+ v3f player_pos = srp->m_last_good_position;
+
+ // Update wielded item
+ srp->wieldItem(item_i);
+
+ // Get pointed to node (undefined if not POINTEDTYPE_NODE)
+ v3s16 p_under = pointed.node_undersurface;
+ v3s16 p_above = pointed.node_abovesurface;
+
+ // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
+ ServerActiveObject *pointed_object = NULL;
+ if(pointed.type == POINTEDTHING_OBJECT)
+ {
+ pointed_object = m_env->getActiveObject(pointed.object_id);
+ if(pointed_object == NULL)
+ {
+ infostream<<"TOSERVER_INTERACT: "
+ "pointed object is NULL"<<std::endl;
+ return;
+ }
+
+ }
+
+ /*
+ Check that target is reasonably close
+ (only when digging or placing things)
+ */
+ if(action == 0 || action == 2 || action == 3)
+ {
+ v3f pointed_pos = player_pos;
+ if(pointed.type == POINTEDTHING_NODE)
+ {
+ pointed_pos = intToFloat(p_under, BS);
+ }
+ else if(pointed.type == POINTEDTHING_OBJECT)
+ {
+ pointed_pos = pointed_object->getBasePosition();
+ }
+
+ float d = player_pos.getDistanceFrom(pointed_pos);
+ float max_d = BS * 10; // Just some large enough value
+ if(d > max_d){
+ actionstream<<"Player "<<player->getName()
+ <<" tried to access "<<pointed.dump()
+ <<" from too far: "
+ <<"d="<<d<<", max_d="<<max_d
+ <<". ignoring."<<std::endl;
+ // Re-send block to revert change on client-side
+ RemoteClient *client = getClient(peer_id);
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
+ client->SetBlockNotSent(blockpos);
+ // Do nothing else
+ return;
+ }
+ }
+
+ /*
+ Make sure the player is allowed to do it
+ */
+ bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
+ if(!build_priv)
+ {
+ infostream<<"Ignoring interaction from player "<<player->getName()
+ <<" because privileges are "<<getPlayerPrivs(player)
+ <<std::endl;
+ // NOTE: no return; here, fall through
+ }
+
+ /*
+ 0: start digging or punch object
+ */
+ if(action == 0)
+ {
+ if(pointed.type == POINTEDTHING_NODE)
+ {
+ /*
+ NOTE: This can be used in the future to check if
+ somebody is cheating, by checking the timing.
+ */
+ bool cannot_punch_node = !build_priv;
+
+ MapNode n(CONTENT_IGNORE);
+
+ try
+ {
+ n = m_env->getMap().getNode(p_under);
+ }
+ catch(InvalidPositionException &e)
+ {
+ infostream<<"Server: Not punching: Node not found."
+ <<" Adding block to emerge queue."
+ <<std::endl;
+ m_emerge_queue.addBlock(peer_id,
+ getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+ cannot_punch_node = true;
+ }
+
+ if(cannot_punch_node)
+ return;
+
+ /*
+ Run script hook
+ */
+ scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
+ }
+ else if(pointed.type == POINTEDTHING_OBJECT)
+ {
+ if(!build_priv)
+ return;
+
+ // Skip if object has been removed
+ if(pointed_object->m_removed)
+ return;
+
+ actionstream<<player->getName()<<" punches object "
+ <<pointed.object_id<<std::endl;
+
+ // Do stuff
+ pointed_object->punch(srp);
+ }
+
+ } // action == 0
+
+ /*
+ 1: stop digging
+ */
+ else if(action == 1)
+ {
+ } // action == 1
+
+ /*
+ 2: Digging completed
+ */
+ else if(action == 2)
+ {
+ // Only complete digging of nodes
+ if(pointed.type != POINTEDTHING_NODE)
+ return;
+
+ // Mandatory parameter; actually used for nothing
+ core::map<v3s16, MapBlock*> modified_blocks;
+
+ content_t material = CONTENT_IGNORE;
+ u8 mineral = MINERAL_NONE;
+
+ bool cannot_remove_node = !build_priv;
+
+ MapNode n(CONTENT_IGNORE);
+ try
+ {
+ n = m_env->getMap().getNode(p_under);
+ // Get mineral
+ mineral = n.getMineral(m_nodedef);
+ // Get material at position
+ material = n.getContent();
+ // If not yet cancelled
+ if(cannot_remove_node == false)
+ {
+ // If it's not diggable, do nothing
+ if(m_nodedef->get(material).diggable == false)
+ {
+ infostream<<"Server: Not finishing digging: "
+ <<"Node not diggable"
+ <<std::endl;
+ cannot_remove_node = true;
+ }
+ }
+ // If not yet cancelled
+ if(cannot_remove_node == false)
+ {
+ // Get node metadata
+ NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
+ if(meta && meta->nodeRemovalDisabled() == true)
+ {
+ infostream<<"Server: Not finishing digging: "
+ <<"Node metadata disables removal"
+ <<std::endl;
+ cannot_remove_node = true;
+ }
+ }
+ }
+ catch(InvalidPositionException &e)
+ {
+ infostream<<"Server: Not finishing digging: Node not found."
+ <<" Adding block to emerge queue."
+ <<std::endl;
+ m_emerge_queue.addBlock(peer_id,
+ getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+ cannot_remove_node = true;
+ }
+
+ /*
+ If node can't be removed, set block to be re-sent to
+ client and quit.
+ */
+ if(cannot_remove_node)
+ {
+ infostream<<"Server: Not finishing digging."<<std::endl;
+
+ // Client probably has wrong data.
+ // Set block not sent, so that client will get
+ // a valid one.
+ infostream<<"Client "<<peer_id<<" tried to dig "
+ <<"node; but node cannot be removed."
+ <<" setting MapBlock not sent."<<std::endl;
+ RemoteClient *client = getClient(peer_id);
+ v3s16 blockpos = getNodeBlockPos(p_under);
+ client->SetBlockNotSent(blockpos);
+