- else if(command == TOSERVER_CHAT_MESSAGE)
- {
- /*
- u16 command
- u16 length
- wstring message
- */
- u8 buf[6];
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- // Read stuff
- is.read((char*)buf, 2);
- u16 len = readU16(buf);
-
- std::wstring message;
- for(u16 i=0; i<len; i++)
- {
- is.read((char*)buf, 2);
- message += (wchar_t)readU16(buf);
- }
-
- // If something goes wrong, this player is to blame
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- // Get player name of this client
- std::wstring name = narrow_to_wide(player->getName());
-
- // Run script hook
- bool ate = m_script->on_chat_message(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_only = false;
-
- // Commands are implemented in Lua, so only catch invalid
- // commands that were not "eaten" and send an error back
- if(message[0] == L'/')
- {
- message = message.substr(1);
- send_to_sender_only = true;
- if(message.length() == 0)
- line += L"-!- Empty command";
- else
- line += L"-!- Invalid command: " + str_split(message, L' ')[0];
- }
- else
- {
- if(checkPriv(player->getName(), "shout")){
- line += L"<";
- line += name;
- line += L"> ";
- line += message;
- } else {
- line += L"-!- You don't have permission to shout.";
- send_to_sender_only = true;
- }
- }
-
- if(line != L"")
- {
- /*
- Send the message to sender
- */
- if (send_to_sender_only)
- {
- SendChatMessage(peer_id, line);
- }
- /*
- Send the message to others
- */
- else
- {
- actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
-
- std::list<u16> clients = m_clients.getClientIDs();
-
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
- if (*i != peer_id)
- SendChatMessage(*i, 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;
-
- playersao->setHP(playersao->getHP() - damage);
-
- if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
- DiePlayer(peer_id);
-
- if(playersao->m_hp_not_sent)
- SendPlayerHP(peer_id);
- }
- }
- else if(command == TOSERVER_BREATH)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- u16 breath = readU16(is);
- playersao->setBreath(breath);
- m_script->player_event(playersao,"breath_changed");
- }
- 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;
- }
-
- if(!base64_is_valid(newpwd)){
- infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
- // Wrong old password supplied!!
- SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
- return;
- }
-
- infostream<<"Server: Client requests a password change from "
- <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
-
- std::string playername = player->getName();
-
- std::string checkpwd;
- m_script->getAuth(playername, &checkpwd, NULL);
-
- 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;
- }
-
- bool success = m_script->setPassword(playername, newpwd);
- if(success){
- actionstream<<player->getName()<<" changes password"<<std::endl;
- SendChatMessage(peer_id, L"Password change successful.");
- } else {
- actionstream<<player->getName()<<" tries to change password but "
- <<"it fails"<<std::endl;
- SendChatMessage(peer_id, L"Password change failed or inavailable.");
- }
- }
- else if(command == TOSERVER_PLAYERITEM)
- {
- if (datasize < 2+2)
- return;
-
- u16 item = readU16(&data[2]);
- playersao->setWieldIndex(item);
- }
- else if(command == TOSERVER_RESPAWN)
- {
- if(player->hp != 0 || !g_settings->getBool("enable_damage"))
- return;
-
- RespawnPlayer(peer_id);
-
- actionstream<<player->getName()<<" respawns at "
- <<PP(player->getPosition()/BS)<<std::endl;
-
- // ActiveObject is added to environment in AsyncRunStep after
- // the previous addition has been succesfully removed
- }
- 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);
-
- verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
- <<item_i<<", pointed="<<pointed.dump()<<std::endl;
-
- if(player->hp == 0)
- {
- verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
- <<" tried to interact, but is dead!"<<std::endl;
- return;
- }
-
- v3f player_pos = playersao->getLastGoodPosition();
-
- // Update wielded item
- playersao->setWieldIndex(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)
- {
- verbosestream<<"TOSERVER_INTERACT: "
- "pointed object is NULL"<<std::endl;
- return;
- }
-
- }
-
- v3f pointed_pos_under = player_pos;
- v3f pointed_pos_above = player_pos;
- if(pointed.type == POINTEDTHING_NODE)
- {
- pointed_pos_under = intToFloat(p_under, BS);
- pointed_pos_above = intToFloat(p_above, BS);
- }
- else if(pointed.type == POINTEDTHING_OBJECT)
- {
- pointed_pos_under = pointed_object->getBasePosition();
- pointed_pos_above = pointed_pos_under;
- }
-
- /*
- Check that target is reasonably close
- (only when digging or placing things)
- */
- if(action == 0 || action == 2 || action == 3)
- {
- float d = player_pos.getDistanceFrom(pointed_pos_under);
- float max_d = BS * 14; // 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_under, BS));
- client->SetBlockNotSent(blockpos);
- // Call callbacks
- m_script->on_cheat(playersao, "interacted_too_far");
- // Do nothing else
- return;
- }
- }
-
- /*
- Make sure the player is allowed to do it
- */
- if(!checkPriv(player->getName(), "interact"))
- {
- actionstream<<player->getName()<<" attempted to interact with "
- <<pointed.dump()<<" without 'interact' privilege"
- <<std::endl;
- // Re-send block to revert change on client-side
- RemoteClient *client = getClient(peer_id);
- // Digging completed -> under
- if(action == 2){
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- client->SetBlockNotSent(blockpos);
- }
- // Placement -> above
- if(action == 3){
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
- client->SetBlockNotSent(blockpos);
- }
- return;
- }
-
- /*
- If something goes wrong, this player is to blame
- */
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- /*
- 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.
- */
- 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->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
- }
- if(n.getContent() != CONTENT_IGNORE)
- m_script->node_on_punch(p_under, n, playersao, pointed);
- // Cheat prevention
- playersao->noCheatDigStart(p_under);
- }
- else if(pointed.type == POINTEDTHING_OBJECT)
- {
- // Skip if object has been removed
- if(pointed_object->m_removed)
- return;
-
- actionstream<<player->getName()<<" punches object "
- <<pointed.object_id<<": "
- <<pointed_object->getDescription()<<std::endl;
-
- ItemStack punchitem = playersao->getWieldedItem();
- ToolCapabilities toolcap =
- punchitem.getToolCapabilities(m_itemdef);
- v3f dir = (pointed_object->getBasePosition() -
- (player->getPosition() + player->getEyeOffset())
- ).normalize();
- float time_from_last_punch =
- playersao->resetTimeFromLastPunch();
- pointed_object->punch(dir, &toolcap, playersao,
- time_from_last_punch);
- }
-
- } // action == 0
-
- /*
- 1: stop digging
- */
- else if(action == 1)
- {
- } // action == 1
-
- /*
- 2: Digging completed
- */
- else if(action == 2)
- {
- // Only digging of nodes
- if(pointed.type == POINTEDTHING_NODE)
- {
- MapNode n(CONTENT_IGNORE);
- try
- {
- n = m_env->getMap().getNode(p_under);
- }
- catch(InvalidPositionException &e)
- {
- infostream<<"Server: Not finishing digging: Node not found."
- <<" Adding block to emerge queue."
- <<std::endl;
- m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
- }
-
- /* Cheat prevention */
- bool is_valid_dig = true;
- if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
- {
- v3s16 nocheat_p = playersao->getNoCheatDigPos();
- float nocheat_t = playersao->getNoCheatDigTime();
- playersao->noCheatDigEnd();
- // If player didn't start digging this, ignore dig
- if(nocheat_p != p_under){
- infostream<<"Server: NoCheat: "<<player->getName()
- <<" started digging "
- <<PP(nocheat_p)<<" and completed digging "
- <<PP(p_under)<<"; not digging."<<std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "finished_unknown_dig");
- }
- // Get player's wielded item
- ItemStack playeritem;
- InventoryList *mlist = playersao->getInventory()->getList("main");
- if(mlist != NULL)
- playeritem = mlist->getItem(playersao->getWieldIndex());
- ToolCapabilities playeritem_toolcap =
- playeritem.getToolCapabilities(m_itemdef);
- // Get diggability and expected digging time
- DigParams params = getDigParams(m_nodedef->get(n).groups,
- &playeritem_toolcap);
- // If can't dig, try hand
- if(!params.diggable){
- const ItemDefinition &hand = m_itemdef->get("");
- const ToolCapabilities *tp = hand.tool_capabilities;
- if(tp)
- params = getDigParams(m_nodedef->get(n).groups, tp);
- }
- // If can't dig, ignore dig
- if(!params.diggable){
- infostream<<"Server: NoCheat: "<<player->getName()
- <<" completed digging "<<PP(p_under)
- <<", which is not diggable with tool. not digging."
- <<std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "dug_unbreakable");
- }
- // Check digging time
- // If already invalidated, we don't have to
- if(!is_valid_dig){
- // Well not our problem then
- }
- // Clean and long dig
- else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
- // All is good, but grab time from pool; don't care if
- // it's actually available
- playersao->getDigPool().grab(params.time);
- }
- // Short or laggy dig
- // Try getting the time from pool
- else if(playersao->getDigPool().grab(params.time)){
- // All is good
- }
- // Dig not possible
- else{
- infostream<<"Server: NoCheat: "<<player->getName()
- <<" completed digging "<<PP(p_under)
- <<"too fast; not digging."<<std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "dug_too_fast");
- }
- }
-
- /* Actually dig node */
-
- if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
- m_script->node_on_dig(p_under, n, playersao);
-
- // Send unusual result (that is, node not being removed)
- if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
- {
- // Re-send block to revert change on client-side
- RemoteClient *client = getClient(peer_id);
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- client->SetBlockNotSent(blockpos);
- }
- }
- } // action == 2
-
- /*
- 3: place block or right-click object
- */
- else if(action == 3)
- {
- ItemStack item = playersao->getWieldedItem();
-
- // Reset build time counter
- if(pointed.type == POINTEDTHING_NODE &&
- item.getDefinition(m_itemdef).type == ITEM_NODE)
- getClient(peer_id)->m_time_from_building = 0.0;
-
- if(pointed.type == POINTEDTHING_OBJECT)
- {
- // Right click object
-
- // Skip if object has been removed
- if(pointed_object->m_removed)
- return;
-
- actionstream<<player->getName()<<" right-clicks object "
- <<pointed.object_id<<": "
- <<pointed_object->getDescription()<<std::endl;
-
- // Do stuff
- pointed_object->rightClick(playersao);
- }
- else if(m_script->item_OnPlace(
- item, playersao, pointed))
- {
- // Placement was handled in lua
-
- // Apply returned ItemStack
- playersao->setWieldedItem(item);
- }
-
- // If item has node placement prediction, always send the
- // blocks to make sure the client knows what exactly happened
- if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
- RemoteClient *client = getClient(peer_id);
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
- client->SetBlockNotSent(blockpos);
- v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- if(blockpos2 != blockpos){
- client->SetBlockNotSent(blockpos2);
- }
- }
- } // action == 3
-
- /*
- 4: use
- */
- else if(action == 4)
- {
- ItemStack item = playersao->getWieldedItem();
-
- actionstream<<player->getName()<<" uses "<<item.name
- <<", pointing at "<<pointed.dump()<<std::endl;
-
- if(m_script->item_OnUse(
- item, playersao, pointed))
- {
- // Apply returned ItemStack
- playersao->setWieldedItem(item);
- }
-
- } // action == 4
-
-
- /*
- Catch invalid actions
- */
- else
- {
- infostream<<"WARNING: Server: Invalid action "
- <<action<<std::endl;
- }
- }
- else if(command == TOSERVER_REMOVED_SOUNDS)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- int num = readU16(is);
- for(int k=0; k<num; k++){
- s32 id = readS32(is);
- std::map<s32, ServerPlayingSound>::iterator i =
- m_playing_sounds.find(id);
- if(i == m_playing_sounds.end())
- continue;
- ServerPlayingSound &psound = i->second;
- psound.clients.erase(peer_id);
- if(psound.clients.size() == 0)
- m_playing_sounds.erase(i++);
- }
- }
- else if(command == TOSERVER_NODEMETA_FIELDS)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- v3s16 p = readV3S16(is);
- std::string formname = deSerializeString(is);
- int num = readU16(is);
- std::map<std::string, std::string> fields;
- for(int k=0; k<num; k++){
- std::string fieldname = deSerializeString(is);
- std::string fieldvalue = deSerializeLongString(is);
- fields[fieldname] = fieldvalue;
- }
-
- // If something goes wrong, this player is to blame
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- // Check the target node for rollback data; leave others unnoticed
- RollbackNode rn_old(&m_env->getMap(), p, this);
-
- m_script->node_on_receive_fields(p, formname, fields,playersao);
-
- // Report rollback data
- RollbackNode rn_new(&m_env->getMap(), p, this);
- if(rollback() && rn_new != rn_old){
- RollbackAction action;
- action.setSetNode(p, rn_old, rn_new);
- rollback()->reportAction(action);
- }
- }
- else if(command == TOSERVER_INVENTORY_FIELDS)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- std::string formname = deSerializeString(is);
- int num = readU16(is);
- std::map<std::string, std::string> fields;
- for(int k=0; k<num; k++){
- std::string fieldname = deSerializeString(is);
- std::string fieldvalue = deSerializeLongString(is);
- fields[fieldname] = fieldvalue;
- }
-
- m_script->on_playerReceiveFields(playersao, formname, fields);
- }
- else
- {
- infostream<<"Server::ProcessData(): Ignoring "
- "unknown command "<<command<<std::endl;
- }
-
- } //try
- catch(SendFailedException &e)
- {
- errorstream<<"Server::ProcessData(): SendFailedException: "
- <<"what="<<e.what()
- <<std::endl;
- }
-}
-
-void Server::setTimeOfDay(u32 time)
-{
- m_env->setTimeOfDay(time);
- m_time_of_day_send_timer = 0;
-}
-
-void Server::onMapEditEvent(MapEditEvent *event)
-{
- //infostream<<"Server::onMapEditEvent()"<<std::endl;
- if(m_ignore_map_edit_events)
- return;
- if(m_ignore_map_edit_events_area.contains(event->getArea()))
- return;
- MapEditEvent *e = event->clone();
- m_unsent_map_edit_queue.push_back(e);
-}
-
-Inventory* Server::getInventory(const InventoryLocation &loc)
-{
- switch(loc.type){
- case InventoryLocation::UNDEFINED:
- {}
- break;
- case InventoryLocation::CURRENT_PLAYER:
- {}
- break;
- case InventoryLocation::PLAYER:
- {
- Player *player = m_env->getPlayer(loc.name.c_str());
- if(!player)
- return NULL;
- PlayerSAO *playersao = player->getPlayerSAO();
- if(!playersao)
- return NULL;
- return playersao->getInventory();
- }
- break;
- case InventoryLocation::NODEMETA:
- {
- NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
- if(!meta)
- return NULL;
- return meta->getInventory();
- }
- break;
- case InventoryLocation::DETACHED:
- {
- if(m_detached_inventories.count(loc.name) == 0)
- return NULL;
- return m_detached_inventories[loc.name];
- }
- break;
- default:
- assert(0);
- }
- return NULL;
-}
-void Server::setInventoryModified(const InventoryLocation &loc)
-{
- switch(loc.type){
- case InventoryLocation::UNDEFINED:
- {}
- break;
- case InventoryLocation::PLAYER:
- {
- Player *player = m_env->getPlayer(loc.name.c_str());
- if(!player)
- return;
- PlayerSAO *playersao = player->getPlayerSAO();
- if(!playersao)
- return;
- playersao->m_inventory_not_sent = true;
- playersao->m_wielded_item_not_sent = true;
- }
- break;