3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU 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.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
45 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
47 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
49 class MapEditEventIgnorer
52 MapEditEventIgnorer(bool *flag):
61 ~MapEditEventIgnorer()
74 void * ServerThread::Thread()
78 log_register_thread("ServerThread");
80 DSTACK(__FUNCTION_NAME);
82 BEGIN_DEBUG_EXCEPTION_HANDLER
87 //TimeTaker timer("AsyncRunStep() + Receive()");
90 //TimeTaker timer("AsyncRunStep()");
91 m_server->AsyncRunStep();
94 //infostream<<"Running m_server->Receive()"<<std::endl;
97 catch(con::NoIncomingDataException &e)
100 catch(con::PeerNotFoundException &e)
102 infostream<<"Server: PeerNotFoundException"<<std::endl;
106 END_DEBUG_EXCEPTION_HANDLER(errorstream)
111 void * EmergeThread::Thread()
115 log_register_thread("EmergeThread");
117 DSTACK(__FUNCTION_NAME);
119 BEGIN_DEBUG_EXCEPTION_HANDLER
121 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
124 Get block info from queue, emerge them and send them
127 After queue is empty, exit.
131 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
135 SharedPtr<QueuedBlockEmerge> q(qptr);
141 Do not generate over-limit
143 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
144 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
145 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
146 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
151 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
153 //TimeTaker timer("block emerge");
156 Try to emerge it from somewhere.
158 If it is only wanted as optional, only loading from disk
163 Check if any peer wants it as non-optional. In that case it
166 Also decrement the emerge queue count in clients.
169 bool only_from_disk = true;
172 core::map<u16, u8>::Iterator i;
173 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
175 //u16 peer_id = i.getNode()->getKey();
178 u8 flags = i.getNode()->getValue();
179 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
180 only_from_disk = false;
185 if(enable_mapgen_debug_info)
186 infostream<<"EmergeThread: p="
187 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
188 <<"only_from_disk="<<only_from_disk<<std::endl;
190 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
192 //core::map<v3s16, MapBlock*> changed_blocks;
193 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
195 MapBlock *block = NULL;
196 bool got_block = true;
197 core::map<v3s16, MapBlock*> modified_blocks;
200 Fetch block from map or generate a single block
203 JMutexAutoLock envlock(m_server->m_env_mutex);
205 // Load sector if it isn't loaded
206 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
207 //map.loadSectorFull(p2d);
208 map.loadSectorMeta(p2d);
210 block = map.getBlockNoCreateNoEx(p);
211 if(!block || block->isDummy() || !block->isGenerated())
213 if(enable_mapgen_debug_info)
214 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
216 // Get, load or create sector
217 /*ServerMapSector *sector =
218 (ServerMapSector*)map.createSector(p2d);*/
220 // Load/generate block
222 /*block = map.emergeBlock(p, sector, changed_blocks,
223 lighting_invalidated_blocks);*/
225 block = map.loadBlock(p);
227 if(only_from_disk == false)
229 if(block == NULL || block->isGenerated() == false)
231 if(enable_mapgen_debug_info)
232 infostream<<"EmergeThread: generating"<<std::endl;
233 block = map.generateBlock(p, modified_blocks);
237 if(enable_mapgen_debug_info)
238 infostream<<"EmergeThread: ended up with: "
239 <<analyze_block(block)<<std::endl;
248 Ignore map edit events, they will not need to be
249 sent to anybody because the block hasn't been sent
252 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
254 // Activate objects and stuff
255 m_server->m_env->activateBlock(block, 3600);
260 /*if(block->getLightingExpired()){
261 lighting_invalidated_blocks[block->getPos()] = block;
265 // TODO: Some additional checking and lighting updating,
270 JMutexAutoLock envlock(m_server->m_env_mutex);
275 Collect a list of blocks that have been modified in
276 addition to the fetched one.
280 if(lighting_invalidated_blocks.size() > 0)
282 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
283 <<" blocks"<<std::endl;*/
285 // 50-100ms for single block generation
286 //TimeTaker timer("** EmergeThread updateLighting");
288 // Update lighting without locking the environment mutex,
289 // add modified blocks to changed blocks
290 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
293 // Add all from changed_blocks to modified_blocks
294 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
295 i.atEnd() == false; i++)
297 MapBlock *block = i.getNode()->getValue();
298 modified_blocks.insert(block->getPos(), block);
302 // If we got no block, there should be no invalidated blocks
305 //assert(lighting_invalidated_blocks.size() == 0);
311 Set sent status of modified blocks on clients
314 // NOTE: Server's clients are also behind the connection mutex
315 JMutexAutoLock lock(m_server->m_con_mutex);
318 Add the originally fetched block to the modified list
322 modified_blocks.insert(p, block);
326 Set the modified blocks unsent for all the clients
329 for(core::map<u16, RemoteClient*>::Iterator
330 i = m_server->m_clients.getIterator();
331 i.atEnd() == false; i++)
333 RemoteClient *client = i.getNode()->getValue();
335 if(modified_blocks.size() > 0)
337 // Remove block from sent history
338 client->SetBlocksNotSent(modified_blocks);
344 END_DEBUG_EXCEPTION_HANDLER(errorstream)
349 void RemoteClient::GetNextBlocks(Server *server, float dtime,
350 core::array<PrioritySortedBlockTransfer> &dest)
352 DSTACK(__FUNCTION_NAME);
355 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
358 m_nothing_to_send_pause_timer -= dtime;
359 m_nearest_unsent_reset_timer += dtime;
361 if(m_nothing_to_send_pause_timer >= 0)
366 // Won't send anything if already sending
367 if(m_blocks_sending.size() >= g_settings->getU16
368 ("max_simultaneous_block_sends_per_client"))
370 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
374 //TimeTaker timer("RemoteClient::GetNextBlocks");
376 Player *player = server->m_env->getPlayer(peer_id);
378 assert(player != NULL);
380 v3f playerpos = player->getPosition();
381 v3f playerspeed = player->getSpeed();
382 v3f playerspeeddir(0,0,0);
383 if(playerspeed.getLength() > 1.0*BS)
384 playerspeeddir = playerspeed / playerspeed.getLength();
385 // Predict to next block
386 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
388 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
390 v3s16 center = getNodeBlockPos(center_nodepos);
392 // Camera position and direction
393 v3f camera_pos = player->getEyePosition();
394 v3f camera_dir = v3f(0,0,1);
395 camera_dir.rotateYZBy(player->getPitch());
396 camera_dir.rotateXZBy(player->getYaw());
398 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
399 <<camera_dir.Z<<")"<<std::endl;*/
402 Get the starting value of the block finder radius.
405 if(m_last_center != center)
407 m_nearest_unsent_d = 0;
408 m_last_center = center;
411 /*infostream<<"m_nearest_unsent_reset_timer="
412 <<m_nearest_unsent_reset_timer<<std::endl;*/
414 // Reset periodically to workaround for some bugs or stuff
415 if(m_nearest_unsent_reset_timer > 20.0)
417 m_nearest_unsent_reset_timer = 0;
418 m_nearest_unsent_d = 0;
419 //infostream<<"Resetting m_nearest_unsent_d for "
420 // <<server->getPlayerName(peer_id)<<std::endl;
423 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
424 s16 d_start = m_nearest_unsent_d;
426 //infostream<<"d_start="<<d_start<<std::endl;
428 u16 max_simul_sends_setting = g_settings->getU16
429 ("max_simultaneous_block_sends_per_client");
430 u16 max_simul_sends_usually = max_simul_sends_setting;
433 Check the time from last addNode/removeNode.
435 Decrease send rate if player is building stuff.
437 m_time_from_building += dtime;
438 if(m_time_from_building < g_settings->getFloat(
439 "full_block_send_enable_min_time_from_building"))
441 max_simul_sends_usually
442 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
446 Number of blocks sending + number of blocks selected for sending
448 u32 num_blocks_selected = m_blocks_sending.size();
451 next time d will be continued from the d from which the nearest
452 unsent block was found this time.
454 This is because not necessarily any of the blocks found this
455 time are actually sent.
457 s32 new_nearest_unsent_d = -1;
459 s16 d_max = g_settings->getS16("max_block_send_distance");
460 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
462 // Don't loop very much at a time
463 s16 max_d_increment_at_time = 2;
464 if(d_max > d_start + max_d_increment_at_time)
465 d_max = d_start + max_d_increment_at_time;
466 /*if(d_max_gen > d_start+2)
467 d_max_gen = d_start+2;*/
469 //infostream<<"Starting from "<<d_start<<std::endl;
471 s32 nearest_emerged_d = -1;
472 s32 nearest_emergefull_d = -1;
473 s32 nearest_sent_d = -1;
474 bool queue_is_full = false;
477 for(d = d_start; d <= d_max; d++)
479 /*errorstream<<"checking d="<<d<<" for "
480 <<server->getPlayerName(peer_id)<<std::endl;*/
481 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
484 If m_nearest_unsent_d was changed by the EmergeThread
485 (it can change it to 0 through SetBlockNotSent),
487 Else update m_nearest_unsent_d
489 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
491 d = m_nearest_unsent_d;
492 last_nearest_unsent_d = m_nearest_unsent_d;
496 Get the border/face dot coordinates of a "d-radiused"
499 core::list<v3s16> list;
500 getFacePositions(list, d);
502 core::list<v3s16>::Iterator li;
503 for(li=list.begin(); li!=list.end(); li++)
505 v3s16 p = *li + center;
509 - Don't allow too many simultaneous transfers
510 - EXCEPT when the blocks are very close
512 Also, don't send blocks that are already flying.
515 // Start with the usual maximum
516 u16 max_simul_dynamic = max_simul_sends_usually;
518 // If block is very close, allow full maximum
519 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
520 max_simul_dynamic = max_simul_sends_setting;
522 // Don't select too many blocks for sending
523 if(num_blocks_selected >= max_simul_dynamic)
525 queue_is_full = true;
526 goto queue_full_break;
529 // Don't send blocks that are currently being transferred
530 if(m_blocks_sending.find(p) != NULL)
536 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
537 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
538 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
539 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
544 // If this is true, inexistent block will be made from scratch
545 bool generate = d <= d_max_gen;
548 /*// Limit the generating area vertically to 2/3
549 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
552 // Limit the send area vertically to 1/2
553 if(abs(p.Y - center.Y) > d_max / 2)
559 If block is far away, don't generate it unless it is
565 // Block center y in nodes
566 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
567 // Don't generate if it's very high or very low
568 if(y < -64 || y > 64)
572 v2s16 p2d_nodes_center(
576 // Get ground height in nodes
577 s16 gh = server->m_env->getServerMap().findGroundLevel(
580 // If differs a lot, don't generate
581 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
583 // Actually, don't even send it
589 //infostream<<"d="<<d<<std::endl;
592 Don't generate or send if not in sight
593 FIXME This only works if the client uses a small enough
594 FOV setting. The default of 72 degrees is fine.
597 float camera_fov = (72.0*PI/180) * 4./3.;
598 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
604 Don't send already sent blocks
607 if(m_blocks_sent.find(p) != NULL)
614 Check if map has this block
616 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
618 bool surely_not_found_on_disk = false;
619 bool block_is_invalid = false;
622 // Reset usage timer, this block will be of use in the future.
623 block->resetUsageTimer();
625 // Block is dummy if data doesn't exist.
626 // It means it has been not found from disk and not generated
629 surely_not_found_on_disk = true;
632 // Block is valid if lighting is up-to-date and data exists
633 if(block->isValid() == false)
635 block_is_invalid = true;
638 /*if(block->isFullyGenerated() == false)
640 block_is_invalid = true;
645 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
646 v2s16 chunkpos = map->sector_to_chunk(p2d);
647 if(map->chunkNonVolatile(chunkpos) == false)
648 block_is_invalid = true;
650 if(block->isGenerated() == false)
651 block_is_invalid = true;
654 If block is not close, don't send it unless it is near
657 Block is near ground level if night-time mesh
658 differs from day-time mesh.
662 if(block->dayNightDiffed() == false)
669 If block has been marked to not exist on disk (dummy)
670 and generating new ones is not wanted, skip block.
672 if(generate == false && surely_not_found_on_disk == true)
679 Add inexistent block to emerge queue.
681 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
683 //TODO: Get value from somewhere
684 // Allow only one block in emerge queue
685 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
686 // Allow two blocks in queue per client
687 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
688 if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
690 //infostream<<"Adding block to emerge queue"<<std::endl;
692 // Add it to the emerge queue and trigger the thread
695 if(generate == false)
696 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
698 server->m_emerge_queue.addBlock(peer_id, p, flags);
699 server->m_emergethread.trigger();
701 if(nearest_emerged_d == -1)
702 nearest_emerged_d = d;
704 if(nearest_emergefull_d == -1)
705 nearest_emergefull_d = d;
712 if(nearest_sent_d == -1)
716 Add block to send queue
719 /*errorstream<<"sending from d="<<d<<" to "
720 <<server->getPlayerName(peer_id)<<std::endl;*/
722 PrioritySortedBlockTransfer q((float)d, p, peer_id);
726 num_blocks_selected += 1;
731 //infostream<<"Stopped at "<<d<<std::endl;
733 // If nothing was found for sending and nothing was queued for
734 // emerging, continue next time browsing from here
735 if(nearest_emerged_d != -1){
736 new_nearest_unsent_d = nearest_emerged_d;
737 } else if(nearest_emergefull_d != -1){
738 new_nearest_unsent_d = nearest_emergefull_d;
740 if(d > g_settings->getS16("max_block_send_distance")){
741 new_nearest_unsent_d = 0;
742 m_nothing_to_send_pause_timer = 2.0;
743 /*infostream<<"GetNextBlocks(): d wrapped around for "
744 <<server->getPlayerName(peer_id)
745 <<"; setting to 0 and pausing"<<std::endl;*/
747 if(nearest_sent_d != -1)
748 new_nearest_unsent_d = nearest_sent_d;
750 new_nearest_unsent_d = d;
754 if(new_nearest_unsent_d != -1)
755 m_nearest_unsent_d = new_nearest_unsent_d;
757 /*timer_result = timer.stop(true);
758 if(timer_result != 0)
759 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
762 void RemoteClient::SendObjectData(
765 core::map<v3s16, bool> &stepped_blocks
768 DSTACK(__FUNCTION_NAME);
770 // Can't send anything without knowing version
771 if(serialization_version == SER_FMT_VER_INVALID)
773 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
779 Send a TOCLIENT_OBJECTDATA packet.
783 u16 number of player positions
795 std::ostringstream os(std::ios_base::binary);
799 writeU16(buf, TOCLIENT_OBJECTDATA);
800 os.write((char*)buf, 2);
803 Get and write player data
806 // Get connected players
807 core::list<Player*> players = server->m_env->getPlayers(true);
809 // Write player count
810 u16 playercount = players.size();
811 writeU16(buf, playercount);
812 os.write((char*)buf, 2);
814 core::list<Player*>::Iterator i;
815 for(i = players.begin();
816 i != players.end(); i++)
820 v3f pf = player->getPosition();
821 v3f sf = player->getSpeed();
823 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
824 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
825 s32 pitch_i (player->getPitch() * 100);
826 s32 yaw_i (player->getYaw() * 100);
828 writeU16(buf, player->peer_id);
829 os.write((char*)buf, 2);
830 writeV3S32(buf, position_i);
831 os.write((char*)buf, 12);
832 writeV3S32(buf, speed_i);
833 os.write((char*)buf, 12);
834 writeS32(buf, pitch_i);
835 os.write((char*)buf, 4);
836 writeS32(buf, yaw_i);
837 os.write((char*)buf, 4);
841 Get and write object data (dummy, for compatibility)
846 os.write((char*)buf, 2);
852 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
855 std::string s = os.str();
856 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
857 // Send as unreliable
858 server->m_con.Send(peer_id, 0, data, false);
861 void RemoteClient::GotBlock(v3s16 p)
863 if(m_blocks_sending.find(p) != NULL)
864 m_blocks_sending.remove(p);
867 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
868 " m_blocks_sending"<<std::endl;*/
869 m_excess_gotblocks++;
871 m_blocks_sent.insert(p, true);
874 void RemoteClient::SentBlock(v3s16 p)
876 if(m_blocks_sending.find(p) == NULL)
877 m_blocks_sending.insert(p, 0.0);
879 infostream<<"RemoteClient::SentBlock(): Sent block"
880 " already in m_blocks_sending"<<std::endl;
883 void RemoteClient::SetBlockNotSent(v3s16 p)
885 m_nearest_unsent_d = 0;
887 if(m_blocks_sending.find(p) != NULL)
888 m_blocks_sending.remove(p);
889 if(m_blocks_sent.find(p) != NULL)
890 m_blocks_sent.remove(p);
893 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
895 m_nearest_unsent_d = 0;
897 for(core::map<v3s16, MapBlock*>::Iterator
898 i = blocks.getIterator();
899 i.atEnd()==false; i++)
901 v3s16 p = i.getNode()->getKey();
903 if(m_blocks_sending.find(p) != NULL)
904 m_blocks_sending.remove(p);
905 if(m_blocks_sent.find(p) != NULL)
906 m_blocks_sent.remove(p);
914 PlayerInfo::PlayerInfo()
920 void PlayerInfo::PrintLine(std::ostream *s)
923 (*s)<<"\""<<name<<"\" ("
924 <<(position.X/10)<<","<<(position.Y/10)
925 <<","<<(position.Z/10)<<") ";
927 (*s)<<" avg_rtt="<<avg_rtt;
931 u32 PIChecksum(core::list<PlayerInfo> &l)
933 core::list<PlayerInfo>::Iterator i;
936 for(i=l.begin(); i!=l.end(); i++)
938 checksum += a * (i->id+1);
939 checksum ^= 0x435aafcd;
950 std::string mapsavedir,
951 std::string configpath
954 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
955 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
956 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
960 m_emergethread(this),
962 m_time_of_day_send_timer(0),
964 m_mapsavedir(mapsavedir),
965 m_configpath(configpath),
966 m_shutdown_requested(false),
967 m_ignore_map_edit_events(false),
968 m_ignore_map_edit_events_peer_id(0)
970 m_liquid_transform_timer = 0.0;
971 m_print_info_timer = 0.0;
972 m_objectdata_timer = 0.0;
973 m_emergethread_trigger_timer = 0.0;
974 m_savemap_timer = 0.0;
978 m_step_dtime_mutex.Init();
981 JMutexAutoLock envlock(m_env_mutex);
982 JMutexAutoLock conlock(m_con_mutex);
984 // Initialize scripting
986 infostream<<"Server: Initializing scripting"<<std::endl;
987 m_lua = script_init();
990 scriptapi_export(m_lua, this);
991 // Load and run scripts
992 std::string defaultscript = porting::path_data + DIR_DELIM
993 + "scripts" + DIR_DELIM + "default.lua";
994 bool success = script_load(m_lua, defaultscript.c_str());
996 errorstream<<"Server: Failed to load and run "
997 <<defaultscript<<std::endl;
1001 // Initialize Environment
1003 m_env = new ServerEnvironment(new ServerMap(mapsavedir), m_lua);
1005 // Register us to receive map edit events
1006 m_env->getMap().addEventReceiver(this);
1008 // If file exists, load environment metadata
1009 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1011 infostream<<"Server: Loading environment metadata"<<std::endl;
1012 m_env->loadMeta(m_mapsavedir);
1016 infostream<<"Server: Loading players"<<std::endl;
1017 m_env->deSerializePlayers(m_mapsavedir);
1022 infostream<<"Server::~Server()"<<std::endl;
1025 Send shutdown message
1028 JMutexAutoLock conlock(m_con_mutex);
1030 std::wstring line = L"*** Server shutting down";
1033 Send the message to clients
1035 for(core::map<u16, RemoteClient*>::Iterator
1036 i = m_clients.getIterator();
1037 i.atEnd() == false; i++)
1039 // Get client and check that it is valid
1040 RemoteClient *client = i.getNode()->getValue();
1041 assert(client->peer_id == i.getNode()->getKey());
1042 if(client->serialization_version == SER_FMT_VER_INVALID)
1046 SendChatMessage(client->peer_id, line);
1048 catch(con::PeerNotFoundException &e)
1054 JMutexAutoLock envlock(m_env_mutex);
1059 infostream<<"Server: Saving players"<<std::endl;
1060 m_env->serializePlayers(m_mapsavedir);
1063 Save environment metadata
1065 infostream<<"Server: Saving environment metadata"<<std::endl;
1066 m_env->saveMeta(m_mapsavedir);
1078 JMutexAutoLock clientslock(m_con_mutex);
1080 for(core::map<u16, RemoteClient*>::Iterator
1081 i = m_clients.getIterator();
1082 i.atEnd() == false; i++)
1085 // NOTE: These are removed by env destructor
1087 u16 peer_id = i.getNode()->getKey();
1088 JMutexAutoLock envlock(m_env_mutex);
1089 m_env->removePlayer(peer_id);
1093 delete i.getNode()->getValue();
1097 // Delete Environment
1100 // Deinitialize scripting
1101 infostream<<"Server: Deinitializing scripting"<<std::endl;
1102 script_deinit(m_lua);
1105 void Server::start(unsigned short port)
1107 DSTACK(__FUNCTION_NAME);
1108 // Stop thread if already running
1111 // Initialize connection
1112 m_con.SetTimeoutMs(30);
1116 m_thread.setRun(true);
1119 infostream<<"Server: Started on port "<<port<<std::endl;
1124 DSTACK(__FUNCTION_NAME);
1126 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1128 // Stop threads (set run=false first so both start stopping)
1129 m_thread.setRun(false);
1130 m_emergethread.setRun(false);
1132 m_emergethread.stop();
1134 infostream<<"Server: Threads stopped"<<std::endl;
1137 void Server::step(float dtime)
1139 DSTACK(__FUNCTION_NAME);
1144 JMutexAutoLock lock(m_step_dtime_mutex);
1145 m_step_dtime += dtime;
1149 void Server::AsyncRunStep()
1151 DSTACK(__FUNCTION_NAME);
1153 g_profiler->add("Server::AsyncRunStep (num)", 1);
1157 JMutexAutoLock lock1(m_step_dtime_mutex);
1158 dtime = m_step_dtime;
1162 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1163 // Send blocks to clients
1170 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1172 //infostream<<"Server steps "<<dtime<<std::endl;
1173 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1176 JMutexAutoLock lock1(m_step_dtime_mutex);
1177 m_step_dtime -= dtime;
1184 m_uptime.set(m_uptime.get() + dtime);
1188 // Process connection's timeouts
1189 JMutexAutoLock lock2(m_con_mutex);
1190 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1191 m_con.RunTimeouts(dtime);
1195 // This has to be called so that the client list gets synced
1196 // with the peer list of the connection
1197 handlePeerChanges();
1201 Update m_time_of_day and overall game time
1204 JMutexAutoLock envlock(m_env_mutex);
1206 m_time_counter += dtime;
1207 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1208 u32 units = (u32)(m_time_counter*speed);
1209 m_time_counter -= (f32)units / speed;
1211 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1213 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1216 Send to clients at constant intervals
1219 m_time_of_day_send_timer -= dtime;
1220 if(m_time_of_day_send_timer < 0.0)
1222 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1224 //JMutexAutoLock envlock(m_env_mutex);
1225 JMutexAutoLock conlock(m_con_mutex);
1227 for(core::map<u16, RemoteClient*>::Iterator
1228 i = m_clients.getIterator();
1229 i.atEnd() == false; i++)
1231 RemoteClient *client = i.getNode()->getValue();
1232 //Player *player = m_env->getPlayer(client->peer_id);
1234 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1235 m_env->getTimeOfDay());
1237 m_con.Send(client->peer_id, 0, data, true);
1243 JMutexAutoLock lock(m_env_mutex);
1245 ScopeProfiler sp(g_profiler, "SEnv step");
1246 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1250 const float map_timer_and_unload_dtime = 5.15;
1251 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1253 JMutexAutoLock lock(m_env_mutex);
1254 // Run Map's timers and unload unused data
1255 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1256 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1257 g_settings->getFloat("server_unload_unused_data_timeout"));
1267 m_liquid_transform_timer += dtime;
1268 if(m_liquid_transform_timer >= 1.00)
1270 m_liquid_transform_timer -= 1.00;
1272 JMutexAutoLock lock(m_env_mutex);
1274 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1276 core::map<v3s16, MapBlock*> modified_blocks;
1277 m_env->getMap().transformLiquids(modified_blocks);
1282 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1283 ServerMap &map = ((ServerMap&)m_env->getMap());
1284 map.updateLighting(modified_blocks, lighting_modified_blocks);
1286 // Add blocks modified by lighting to modified_blocks
1287 for(core::map<v3s16, MapBlock*>::Iterator
1288 i = lighting_modified_blocks.getIterator();
1289 i.atEnd() == false; i++)
1291 MapBlock *block = i.getNode()->getValue();
1292 modified_blocks.insert(block->getPos(), block);
1296 Set the modified blocks unsent for all the clients
1299 JMutexAutoLock lock2(m_con_mutex);
1301 for(core::map<u16, RemoteClient*>::Iterator
1302 i = m_clients.getIterator();
1303 i.atEnd() == false; i++)
1305 RemoteClient *client = i.getNode()->getValue();
1307 if(modified_blocks.size() > 0)
1309 // Remove block from sent history
1310 client->SetBlocksNotSent(modified_blocks);
1315 // Periodically print some info
1317 float &counter = m_print_info_timer;
1323 JMutexAutoLock lock2(m_con_mutex);
1325 if(m_clients.size() != 0)
1326 infostream<<"Players:"<<std::endl;
1327 for(core::map<u16, RemoteClient*>::Iterator
1328 i = m_clients.getIterator();
1329 i.atEnd() == false; i++)
1331 //u16 peer_id = i.getNode()->getKey();
1332 RemoteClient *client = i.getNode()->getValue();
1333 Player *player = m_env->getPlayer(client->peer_id);
1336 infostream<<"* "<<player->getName()<<"\t";
1337 client->PrintInfo(infostream);
1342 //if(g_settings->getBool("enable_experimental"))
1346 Check added and deleted active objects
1349 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1350 JMutexAutoLock envlock(m_env_mutex);
1351 JMutexAutoLock conlock(m_con_mutex);
1353 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1355 // Radius inside which objects are active
1356 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1357 radius *= MAP_BLOCKSIZE;
1359 for(core::map<u16, RemoteClient*>::Iterator
1360 i = m_clients.getIterator();
1361 i.atEnd() == false; i++)
1363 RemoteClient *client = i.getNode()->getValue();
1364 Player *player = m_env->getPlayer(client->peer_id);
1367 // This can happen if the client timeouts somehow
1368 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1370 <<" has no associated player"<<std::endl;*/
1373 v3s16 pos = floatToInt(player->getPosition(), BS);
1375 core::map<u16, bool> removed_objects;
1376 core::map<u16, bool> added_objects;
1377 m_env->getRemovedActiveObjects(pos, radius,
1378 client->m_known_objects, removed_objects);
1379 m_env->getAddedActiveObjects(pos, radius,
1380 client->m_known_objects, added_objects);
1382 // Ignore if nothing happened
1383 if(removed_objects.size() == 0 && added_objects.size() == 0)
1385 //infostream<<"active objects: none changed"<<std::endl;
1389 std::string data_buffer;
1393 // Handle removed objects
1394 writeU16((u8*)buf, removed_objects.size());
1395 data_buffer.append(buf, 2);
1396 for(core::map<u16, bool>::Iterator
1397 i = removed_objects.getIterator();
1398 i.atEnd()==false; i++)
1401 u16 id = i.getNode()->getKey();
1402 ServerActiveObject* obj = m_env->getActiveObject(id);
1404 // Add to data buffer for sending
1405 writeU16((u8*)buf, i.getNode()->getKey());
1406 data_buffer.append(buf, 2);
1408 // Remove from known objects
1409 client->m_known_objects.remove(i.getNode()->getKey());
1411 if(obj && obj->m_known_by_count > 0)
1412 obj->m_known_by_count--;
1415 // Handle added objects
1416 writeU16((u8*)buf, added_objects.size());
1417 data_buffer.append(buf, 2);
1418 for(core::map<u16, bool>::Iterator
1419 i = added_objects.getIterator();
1420 i.atEnd()==false; i++)
1423 u16 id = i.getNode()->getKey();
1424 ServerActiveObject* obj = m_env->getActiveObject(id);
1427 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1429 infostream<<"WARNING: "<<__FUNCTION_NAME
1430 <<": NULL object"<<std::endl;
1432 type = obj->getType();
1434 // Add to data buffer for sending
1435 writeU16((u8*)buf, id);
1436 data_buffer.append(buf, 2);
1437 writeU8((u8*)buf, type);
1438 data_buffer.append(buf, 1);
1441 data_buffer.append(serializeLongString(
1442 obj->getClientInitializationData()));
1444 data_buffer.append(serializeLongString(""));
1446 // Add to known objects
1447 client->m_known_objects.insert(i.getNode()->getKey(), false);
1450 obj->m_known_by_count++;
1454 SharedBuffer<u8> reply(2 + data_buffer.size());
1455 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1456 memcpy((char*)&reply[2], data_buffer.c_str(),
1457 data_buffer.size());
1459 m_con.Send(client->peer_id, 0, reply, true);
1461 infostream<<"Server: Sent object remove/add: "
1462 <<removed_objects.size()<<" removed, "
1463 <<added_objects.size()<<" added, "
1464 <<"packet size is "<<reply.getSize()<<std::endl;
1469 Collect a list of all the objects known by the clients
1470 and report it back to the environment.
1473 core::map<u16, bool> all_known_objects;
1475 for(core::map<u16, RemoteClient*>::Iterator
1476 i = m_clients.getIterator();
1477 i.atEnd() == false; i++)
1479 RemoteClient *client = i.getNode()->getValue();
1480 // Go through all known objects of client
1481 for(core::map<u16, bool>::Iterator
1482 i = client->m_known_objects.getIterator();
1483 i.atEnd()==false; i++)
1485 u16 id = i.getNode()->getKey();
1486 all_known_objects[id] = true;
1490 m_env->setKnownActiveObjects(whatever);
1496 Send object messages
1499 JMutexAutoLock envlock(m_env_mutex);
1500 JMutexAutoLock conlock(m_con_mutex);
1502 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1505 // Value = data sent by object
1506 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1508 // Get active object messages from environment
1511 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1515 core::list<ActiveObjectMessage>* message_list = NULL;
1516 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1517 n = buffered_messages.find(aom.id);
1520 message_list = new core::list<ActiveObjectMessage>;
1521 buffered_messages.insert(aom.id, message_list);
1525 message_list = n->getValue();
1527 message_list->push_back(aom);
1530 // Route data to every client
1531 for(core::map<u16, RemoteClient*>::Iterator
1532 i = m_clients.getIterator();
1533 i.atEnd()==false; i++)
1535 RemoteClient *client = i.getNode()->getValue();
1536 std::string reliable_data;
1537 std::string unreliable_data;
1538 // Go through all objects in message buffer
1539 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1540 j = buffered_messages.getIterator();
1541 j.atEnd()==false; j++)
1543 // If object is not known by client, skip it
1544 u16 id = j.getNode()->getKey();
1545 if(client->m_known_objects.find(id) == NULL)
1547 // Get message list of object
1548 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1549 // Go through every message
1550 for(core::list<ActiveObjectMessage>::Iterator
1551 k = list->begin(); k != list->end(); k++)
1553 // Compose the full new data with header
1554 ActiveObjectMessage aom = *k;
1555 std::string new_data;
1558 writeU16((u8*)&buf[0], aom.id);
1559 new_data.append(buf, 2);
1561 new_data += serializeString(aom.datastring);
1562 // Add data to buffer
1564 reliable_data += new_data;
1566 unreliable_data += new_data;
1570 reliable_data and unreliable_data are now ready.
1573 if(reliable_data.size() > 0)
1575 SharedBuffer<u8> reply(2 + reliable_data.size());
1576 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1577 memcpy((char*)&reply[2], reliable_data.c_str(),
1578 reliable_data.size());
1580 m_con.Send(client->peer_id, 0, reply, true);
1582 if(unreliable_data.size() > 0)
1584 SharedBuffer<u8> reply(2 + unreliable_data.size());
1585 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1586 memcpy((char*)&reply[2], unreliable_data.c_str(),
1587 unreliable_data.size());
1588 // Send as unreliable
1589 m_con.Send(client->peer_id, 0, reply, false);
1592 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1594 infostream<<"Server: Size of object message data: "
1595 <<"reliable: "<<reliable_data.size()
1596 <<", unreliable: "<<unreliable_data.size()
1601 // Clear buffered_messages
1602 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1603 i = buffered_messages.getIterator();
1604 i.atEnd()==false; i++)
1606 delete i.getNode()->getValue();
1610 } // enable_experimental
1613 Send queued-for-sending map edit events.
1616 // Don't send too many at a time
1619 // Single change sending is disabled if queue size is not small
1620 bool disable_single_change_sending = false;
1621 if(m_unsent_map_edit_queue.size() >= 4)
1622 disable_single_change_sending = true;
1624 bool got_any_events = false;
1626 // We'll log the amount of each
1629 while(m_unsent_map_edit_queue.size() != 0)
1631 got_any_events = true;
1633 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1635 // Players far away from the change are stored here.
1636 // Instead of sending the changes, MapBlocks are set not sent
1638 core::list<u16> far_players;
1640 if(event->type == MEET_ADDNODE)
1642 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1643 prof.add("MEET_ADDNODE", 1);
1644 if(disable_single_change_sending)
1645 sendAddNode(event->p, event->n, event->already_known_by_peer,
1648 sendAddNode(event->p, event->n, event->already_known_by_peer,
1651 else if(event->type == MEET_REMOVENODE)
1653 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1654 prof.add("MEET_REMOVENODE", 1);
1655 if(disable_single_change_sending)
1656 sendRemoveNode(event->p, event->already_known_by_peer,
1659 sendRemoveNode(event->p, event->already_known_by_peer,
1662 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1664 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1665 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1666 setBlockNotSent(event->p);
1668 else if(event->type == MEET_OTHER)
1670 infostream<<"Server: MEET_OTHER"<<std::endl;
1671 prof.add("MEET_OTHER", 1);
1672 for(core::map<v3s16, bool>::Iterator
1673 i = event->modified_blocks.getIterator();
1674 i.atEnd()==false; i++)
1676 v3s16 p = i.getNode()->getKey();
1682 prof.add("unknown", 1);
1683 infostream<<"WARNING: Server: Unknown MapEditEvent "
1684 <<((u32)event->type)<<std::endl;
1688 Set blocks not sent to far players
1690 if(far_players.size() > 0)
1692 // Convert list format to that wanted by SetBlocksNotSent
1693 core::map<v3s16, MapBlock*> modified_blocks2;
1694 for(core::map<v3s16, bool>::Iterator
1695 i = event->modified_blocks.getIterator();
1696 i.atEnd()==false; i++)
1698 v3s16 p = i.getNode()->getKey();
1699 modified_blocks2.insert(p,
1700 m_env->getMap().getBlockNoCreateNoEx(p));
1702 // Set blocks not sent
1703 for(core::list<u16>::Iterator
1704 i = far_players.begin();
1705 i != far_players.end(); i++)
1708 RemoteClient *client = getClient(peer_id);
1711 client->SetBlocksNotSent(modified_blocks2);
1717 /*// Don't send too many at a time
1719 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1725 infostream<<"Server: MapEditEvents:"<<std::endl;
1726 prof.print(infostream);
1732 Send object positions
1735 float &counter = m_objectdata_timer;
1737 if(counter >= g_settings->getFloat("objectdata_interval"))
1739 JMutexAutoLock lock1(m_env_mutex);
1740 JMutexAutoLock lock2(m_con_mutex);
1742 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1744 SendObjectData(counter);
1751 Trigger emergethread (it somehow gets to a non-triggered but
1752 bysy state sometimes)
1755 float &counter = m_emergethread_trigger_timer;
1761 m_emergethread.trigger();
1765 // Save map, players and auth stuff
1767 float &counter = m_savemap_timer;
1769 if(counter >= g_settings->getFloat("server_map_save_interval"))
1773 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1776 if(m_authmanager.isModified())
1777 m_authmanager.save();
1780 if(m_banmanager.isModified())
1781 m_banmanager.save();
1784 JMutexAutoLock lock(m_env_mutex);
1786 /*// Unload unused data (delete from memory)
1787 m_env->getMap().unloadUnusedData(
1788 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1790 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1791 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1794 // Save only changed parts
1795 m_env->getMap().save(true);
1797 /*if(deleted_count > 0)
1799 infostream<<"Server: Unloaded "<<deleted_count
1800 <<" blocks from memory"<<std::endl;
1804 m_env->serializePlayers(m_mapsavedir);
1806 // Save environment metadata
1807 m_env->saveMeta(m_mapsavedir);
1812 void Server::Receive()
1814 DSTACK(__FUNCTION_NAME);
1815 SharedBuffer<u8> data;
1820 JMutexAutoLock conlock(m_con_mutex);
1821 datasize = m_con.Receive(peer_id, data);
1824 // This has to be called so that the client list gets synced
1825 // with the peer list of the connection
1826 handlePeerChanges();
1828 ProcessData(*data, datasize, peer_id);
1830 catch(con::InvalidIncomingDataException &e)
1832 infostream<<"Server::Receive(): "
1833 "InvalidIncomingDataException: what()="
1834 <<e.what()<<std::endl;
1836 catch(con::PeerNotFoundException &e)
1838 //NOTE: This is not needed anymore
1840 // The peer has been disconnected.
1841 // Find the associated player and remove it.
1843 /*JMutexAutoLock envlock(m_env_mutex);
1845 infostream<<"ServerThread: peer_id="<<peer_id
1846 <<" has apparently closed connection. "
1847 <<"Removing player."<<std::endl;
1849 m_env->removePlayer(peer_id);*/
1853 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1855 DSTACK(__FUNCTION_NAME);
1856 // Environment is locked first.
1857 JMutexAutoLock envlock(m_env_mutex);
1858 JMutexAutoLock conlock(m_con_mutex);
1861 Address address = m_con.GetPeerAddress(peer_id);
1863 // drop player if is ip is banned
1864 if(m_banmanager.isIpBanned(address.serializeString())){
1865 SendAccessDenied(m_con, peer_id,
1866 L"Your ip is banned. Banned name was "
1867 +narrow_to_wide(m_banmanager.getBanName(
1868 address.serializeString())));
1869 m_con.DeletePeer(peer_id);
1873 catch(con::PeerNotFoundException &e)
1875 infostream<<"Server::ProcessData(): Cancelling: peer "
1876 <<peer_id<<" not found"<<std::endl;
1880 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1888 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1890 if(command == TOSERVER_INIT)
1892 // [0] u16 TOSERVER_INIT
1893 // [2] u8 SER_FMT_VER_HIGHEST
1894 // [3] u8[20] player_name
1895 // [23] u8[28] password <--- can be sent without this, from old versions
1897 if(datasize < 2+1+PLAYERNAME_SIZE)
1900 infostream<<"Server: Got TOSERVER_INIT from "
1901 <<peer_id<<std::endl;
1903 // First byte after command is maximum supported
1904 // serialization version
1905 u8 client_max = data[2];
1906 u8 our_max = SER_FMT_VER_HIGHEST;
1907 // Use the highest version supported by both
1908 u8 deployed = core::min_(client_max, our_max);
1909 // If it's lower than the lowest supported, give up.
1910 if(deployed < SER_FMT_VER_LOWEST)
1911 deployed = SER_FMT_VER_INVALID;
1913 //peer->serialization_version = deployed;
1914 getClient(peer_id)->pending_serialization_version = deployed;
1916 if(deployed == SER_FMT_VER_INVALID)
1918 infostream<<"Server: Cannot negotiate "
1919 "serialization version with peer "
1920 <<peer_id<<std::endl;
1921 SendAccessDenied(m_con, peer_id,
1922 L"Your client is too old (map format)");
1927 Read and check network protocol version
1930 u16 net_proto_version = 0;
1931 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1933 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1936 getClient(peer_id)->net_proto_version = net_proto_version;
1938 if(net_proto_version == 0)
1940 SendAccessDenied(m_con, peer_id,
1941 L"Your client is too old. Please upgrade.");
1945 /* Uhh... this should actually be a warning but let's do it like this */
1946 if(g_settings->getBool("strict_protocol_version_checking"))
1948 if(net_proto_version < PROTOCOL_VERSION)
1950 SendAccessDenied(m_con, peer_id,
1951 L"Your client is too old. Please upgrade.");
1961 char playername[PLAYERNAME_SIZE];
1962 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1964 playername[i] = data[3+i];
1966 playername[PLAYERNAME_SIZE-1] = 0;
1968 if(playername[0]=='\0')
1970 infostream<<"Server: Player has empty name"<<std::endl;
1971 SendAccessDenied(m_con, peer_id,
1976 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1978 infostream<<"Server: Player has invalid name"<<std::endl;
1979 SendAccessDenied(m_con, peer_id,
1980 L"Name contains unallowed characters");
1985 char password[PASSWORD_SIZE];
1986 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1988 // old version - assume blank password
1993 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1995 password[i] = data[23+i];
1997 password[PASSWORD_SIZE-1] = 0;
2000 std::string checkpwd;
2001 if(m_authmanager.exists(playername))
2003 checkpwd = m_authmanager.getPassword(playername);
2007 checkpwd = g_settings->get("default_password");
2010 /*infostream<<"Server: Client gave password '"<<password
2011 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2013 if(password != checkpwd && m_authmanager.exists(playername))
2015 infostream<<"Server: peer_id="<<peer_id
2016 <<": supplied invalid password for "
2017 <<playername<<std::endl;
2018 SendAccessDenied(m_con, peer_id, L"Invalid password");
2022 // Add player to auth manager
2023 if(m_authmanager.exists(playername) == false)
2025 infostream<<"Server: adding player "<<playername
2026 <<" to auth manager"<<std::endl;
2027 m_authmanager.add(playername);
2028 m_authmanager.setPassword(playername, checkpwd);
2029 m_authmanager.setPrivs(playername,
2030 stringToPrivs(g_settings->get("default_privs")));
2031 m_authmanager.save();
2034 // Enforce user limit.
2035 // Don't enforce for users that have some admin right
2036 if(m_clients.size() >= g_settings->getU16("max_users") &&
2037 (m_authmanager.getPrivs(playername)
2038 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2039 playername != g_settings->get("name"))
2041 SendAccessDenied(m_con, peer_id, L"Too many users.");
2046 Player *player = emergePlayer(playername, password, peer_id);
2048 // If failed, cancel
2051 infostream<<"Server: peer_id="<<peer_id
2052 <<": failed to emerge player"<<std::endl;
2057 Answer with a TOCLIENT_INIT
2060 SharedBuffer<u8> reply(2+1+6+8);
2061 writeU16(&reply[0], TOCLIENT_INIT);
2062 writeU8(&reply[2], deployed);
2063 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2064 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2067 m_con.Send(peer_id, 0, reply, true);
2071 Send complete position information
2073 SendMovePlayer(player);
2078 if(command == TOSERVER_INIT2)
2080 infostream<<"Server: Got TOSERVER_INIT2 from "
2081 <<peer_id<<std::endl;
2084 getClient(peer_id)->serialization_version
2085 = getClient(peer_id)->pending_serialization_version;
2088 Send some initialization data
2091 // Send player info to all players
2094 // Send inventory to player
2095 UpdateCrafting(peer_id);
2096 SendInventory(peer_id);
2098 // Send player items to all players
2101 Player *player = m_env->getPlayer(peer_id);
2104 SendPlayerHP(player);
2108 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2109 m_env->getTimeOfDay());
2110 m_con.Send(peer_id, 0, data, true);
2113 // Send information about server to player in chat
2114 SendChatMessage(peer_id, getStatusString());
2116 // Send information about joining in chat
2118 std::wstring name = L"unknown";
2119 Player *player = m_env->getPlayer(peer_id);
2121 name = narrow_to_wide(player->getName());
2123 std::wstring message;
2126 message += L" joined game";
2127 BroadcastChatMessage(message);
2130 // Warnings about protocol version can be issued here
2131 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2133 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2137 Check HP, respawn if necessary
2139 HandlePlayerHP(player, 0);
2145 std::ostringstream os(std::ios_base::binary);
2146 for(core::map<u16, RemoteClient*>::Iterator
2147 i = m_clients.getIterator();
2148 i.atEnd() == false; i++)
2150 RemoteClient *client = i.getNode()->getValue();
2151 assert(client->peer_id == i.getNode()->getKey());
2152 if(client->serialization_version == SER_FMT_VER_INVALID)
2155 Player *player = m_env->getPlayer(client->peer_id);
2158 // Get name of player
2159 os<<player->getName()<<" ";
2162 actionstream<<player->getName()<<" joins game. List of players: "
2163 <<os.str()<<std::endl;
2169 if(peer_ser_ver == SER_FMT_VER_INVALID)
2171 infostream<<"Server::ProcessData(): Cancelling: Peer"
2172 " serialization format invalid or not initialized."
2173 " Skipping incoming command="<<command<<std::endl;
2177 Player *player = m_env->getPlayer(peer_id);
2180 infostream<<"Server::ProcessData(): Cancelling: "
2181 "No player for peer_id="<<peer_id
2185 if(command == TOSERVER_PLAYERPOS)
2187 if(datasize < 2+12+12+4+4)
2191 v3s32 ps = readV3S32(&data[start+2]);
2192 v3s32 ss = readV3S32(&data[start+2+12]);
2193 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2194 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2195 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2196 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2197 pitch = wrapDegrees(pitch);
2198 yaw = wrapDegrees(yaw);
2200 player->setPosition(position);
2201 player->setSpeed(speed);
2202 player->setPitch(pitch);
2203 player->setYaw(yaw);
2205 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2206 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2207 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2209 else if(command == TOSERVER_GOTBLOCKS)
2222 u16 count = data[2];
2223 for(u16 i=0; i<count; i++)
2225 if((s16)datasize < 2+1+(i+1)*6)
2226 throw con::InvalidIncomingDataException
2227 ("GOTBLOCKS length is too short");
2228 v3s16 p = readV3S16(&data[2+1+i*6]);
2229 /*infostream<<"Server: GOTBLOCKS ("
2230 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2231 RemoteClient *client = getClient(peer_id);
2232 client->GotBlock(p);
2235 else if(command == TOSERVER_DELETEDBLOCKS)
2248 u16 count = data[2];
2249 for(u16 i=0; i<count; i++)
2251 if((s16)datasize < 2+1+(i+1)*6)
2252 throw con::InvalidIncomingDataException
2253 ("DELETEDBLOCKS length is too short");
2254 v3s16 p = readV3S16(&data[2+1+i*6]);
2255 /*infostream<<"Server: DELETEDBLOCKS ("
2256 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2257 RemoteClient *client = getClient(peer_id);
2258 client->SetBlockNotSent(p);
2261 else if(command == TOSERVER_CLICK_OBJECT)
2263 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2266 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2271 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2277 [2] u8 button (0=left, 1=right)
2281 u8 button = readU8(&data[2]);
2282 u16 id = readS16(&data[3]);
2283 u16 item_i = readU16(&data[5]);
2285 ServerActiveObject *obj = m_env->getActiveObject(id);
2289 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2294 // Skip if object has been removed
2298 //TODO: Check that object is reasonably close
2300 // Left click, pick object up (usually)
2304 Try creating inventory item
2306 InventoryItem *item = obj->createPickedUpItem();
2310 InventoryList *ilist = player->inventory.getList("main");
2313 actionstream<<player->getName()<<" picked up "
2314 <<item->getName()<<std::endl;
2315 if(g_settings->getBool("creative_mode") == false)
2317 // Skip if inventory has no free space
2318 if(ilist->roomForItem(item) == false)
2320 infostream<<"Player inventory has no free space"<<std::endl;
2324 // Add to inventory and send inventory
2325 ilist->addItem(item);
2326 UpdateCrafting(player->peer_id);
2327 SendInventory(player->peer_id);
2330 // Remove object from environment
2331 obj->m_removed = true;
2337 Item cannot be picked up. Punch it instead.
2340 actionstream<<player->getName()<<" punches object "
2341 <<obj->getId()<<std::endl;
2343 ToolItem *titem = NULL;
2344 std::string toolname = "";
2346 InventoryList *mlist = player->inventory.getList("main");
2349 InventoryItem *item = mlist->getItem(item_i);
2350 if(item && (std::string)item->getName() == "ToolItem")
2352 titem = (ToolItem*)item;
2353 toolname = titem->getToolName();
2357 v3f playerpos = player->getPosition();
2358 v3f objpos = obj->getBasePosition();
2359 v3f dir = (objpos - playerpos).normalize();
2361 u16 wear = obj->punch(toolname, dir, player->getName());
2365 bool weared_out = titem->addWear(wear);
2367 mlist->deleteItem(item_i);
2368 SendInventory(player->peer_id);
2372 // Right click, do something with object
2375 actionstream<<player->getName()<<" right clicks object "
2376 <<obj->getId()<<std::endl;
2378 // Track hp changes super-crappily
2379 u16 oldhp = player->hp;
2382 obj->rightClick(player);
2385 if(player->hp != oldhp)
2387 SendPlayerHP(player);
2391 else if(command == TOSERVER_GROUND_ACTION)
2399 [3] v3s16 nodepos_undersurface
2400 [9] v3s16 nodepos_abovesurface
2405 2: stop digging (all parameters ignored)
2406 3: digging completed
2408 u8 action = readU8(&data[2]);
2410 p_under.X = readS16(&data[3]);
2411 p_under.Y = readS16(&data[5]);
2412 p_under.Z = readS16(&data[7]);
2414 p_over.X = readS16(&data[9]);
2415 p_over.Y = readS16(&data[11]);
2416 p_over.Z = readS16(&data[13]);
2417 u16 item_i = readU16(&data[15]);
2419 //TODO: Check that target is reasonably close
2427 NOTE: This can be used in the future to check if
2428 somebody is cheating, by checking the timing.
2435 else if(action == 2)
2438 RemoteClient *client = getClient(peer_id);
2439 JMutexAutoLock digmutex(client->m_dig_mutex);
2440 client->m_dig_tool_item = -1;
2445 3: Digging completed
2447 else if(action == 3)
2449 // Mandatory parameter; actually used for nothing
2450 core::map<v3s16, MapBlock*> modified_blocks;
2452 content_t material = CONTENT_IGNORE;
2453 u8 mineral = MINERAL_NONE;
2455 bool cannot_remove_node = false;
2459 MapNode n = m_env->getMap().getNode(p_under);
2461 mineral = n.getMineral();
2462 // Get material at position
2463 material = n.getContent();
2464 // If not yet cancelled
2465 if(cannot_remove_node == false)
2467 // If it's not diggable, do nothing
2468 if(content_diggable(material) == false)
2470 infostream<<"Server: Not finishing digging: "
2471 <<"Node not diggable"
2473 cannot_remove_node = true;
2476 // If not yet cancelled
2477 if(cannot_remove_node == false)
2479 // Get node metadata
2480 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2481 if(meta && meta->nodeRemovalDisabled() == true)
2483 infostream<<"Server: Not finishing digging: "
2484 <<"Node metadata disables removal"
2486 cannot_remove_node = true;
2490 catch(InvalidPositionException &e)
2492 infostream<<"Server: Not finishing digging: Node not found."
2493 <<" Adding block to emerge queue."
2495 m_emerge_queue.addBlock(peer_id,
2496 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2497 cannot_remove_node = true;
2500 // Make sure the player is allowed to do it
2501 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2503 infostream<<"Player "<<player->getName()<<" cannot remove node"
2504 <<" because privileges are "<<getPlayerPrivs(player)
2506 cannot_remove_node = true;
2510 If node can't be removed, set block to be re-sent to
2513 if(cannot_remove_node)
2515 infostream<<"Server: Not finishing digging."<<std::endl;
2517 // Client probably has wrong data.
2518 // Set block not sent, so that client will get
2520 infostream<<"Client "<<peer_id<<" tried to dig "
2521 <<"node; but node cannot be removed."
2522 <<" setting MapBlock not sent."<<std::endl;
2523 RemoteClient *client = getClient(peer_id);
2524 v3s16 blockpos = getNodeBlockPos(p_under);
2525 client->SetBlockNotSent(blockpos);
2530 actionstream<<player->getName()<<" digs "<<PP(p_under)
2531 <<", gets material "<<(int)material<<", mineral "
2532 <<(int)mineral<<std::endl;
2535 Send the removal to all close-by players.
2536 - If other player is close, send REMOVENODE
2537 - Otherwise set blocks not sent
2539 core::list<u16> far_players;
2540 sendRemoveNode(p_under, peer_id, &far_players, 30);
2543 Update and send inventory
2546 if(g_settings->getBool("creative_mode") == false)
2551 InventoryList *mlist = player->inventory.getList("main");
2554 InventoryItem *item = mlist->getItem(item_i);
2555 if(item && (std::string)item->getName() == "ToolItem")
2557 ToolItem *titem = (ToolItem*)item;
2558 std::string toolname = titem->getToolName();
2560 // Get digging properties for material and tool
2561 DiggingProperties prop =
2562 getDiggingProperties(material, toolname);
2564 if(prop.diggable == false)
2566 infostream<<"Server: WARNING: Player digged"
2567 <<" with impossible material + tool"
2568 <<" combination"<<std::endl;
2571 bool weared_out = titem->addWear(prop.wear);
2575 mlist->deleteItem(item_i);
2581 Add dug item to inventory
2584 InventoryItem *item = NULL;
2586 if(mineral != MINERAL_NONE)
2587 item = getDiggedMineralItem(mineral);
2592 std::string &dug_s = content_features(material).dug_item;
2595 std::istringstream is(dug_s, std::ios::binary);
2596 item = InventoryItem::deSerialize(is);
2602 // Add a item to inventory
2603 player->inventory.addItem("main", item);
2606 UpdateCrafting(player->peer_id);
2607 SendInventory(player->peer_id);
2612 if(mineral != MINERAL_NONE)
2613 item = getDiggedMineralItem(mineral);
2618 std::string &extra_dug_s = content_features(material).extra_dug_item;
2619 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2620 if(extra_dug_s != "" && extra_rarity != 0
2621 && myrand() % extra_rarity == 0)
2623 std::istringstream is(extra_dug_s, std::ios::binary);
2624 item = InventoryItem::deSerialize(is);
2630 // Add a item to inventory
2631 player->inventory.addItem("main", item);
2634 UpdateCrafting(player->peer_id);
2635 SendInventory(player->peer_id);
2641 (this takes some time so it is done after the quick stuff)
2644 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2646 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2649 Set blocks not sent to far players
2651 for(core::list<u16>::Iterator
2652 i = far_players.begin();
2653 i != far_players.end(); i++)
2656 RemoteClient *client = getClient(peer_id);
2659 client->SetBlocksNotSent(modified_blocks);
2666 else if(action == 1)
2669 InventoryList *ilist = player->inventory.getList("main");
2674 InventoryItem *item = ilist->getItem(item_i);
2676 // If there is no item, it is not possible to add it anywhere
2681 Handle material items
2683 if(std::string("MaterialItem") == item->getName())
2686 // Don't add a node if this is not a free space
2687 MapNode n2 = m_env->getMap().getNode(p_over);
2688 bool no_enough_privs =
2689 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2691 infostream<<"Player "<<player->getName()<<" cannot add node"
2692 <<" because privileges are "<<getPlayerPrivs(player)
2695 if(content_features(n2).buildable_to == false
2698 // Client probably has wrong data.
2699 // Set block not sent, so that client will get
2701 infostream<<"Client "<<peer_id<<" tried to place"
2702 <<" node in invalid position; setting"
2703 <<" MapBlock not sent."<<std::endl;
2704 RemoteClient *client = getClient(peer_id);
2705 v3s16 blockpos = getNodeBlockPos(p_over);
2706 client->SetBlockNotSent(blockpos);
2710 catch(InvalidPositionException &e)
2712 infostream<<"Server: Ignoring ADDNODE: Node not found"
2713 <<" Adding block to emerge queue."
2715 m_emerge_queue.addBlock(peer_id,
2716 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2720 // Reset build time counter
2721 getClient(peer_id)->m_time_from_building = 0.0;
2724 MaterialItem *mitem = (MaterialItem*)item;
2726 n.setContent(mitem->getMaterial());
2728 actionstream<<player->getName()<<" places material "
2729 <<(int)mitem->getMaterial()
2730 <<" at "<<PP(p_under)<<std::endl;
2732 // Calculate direction for wall mounted stuff
2733 if(content_features(n).wall_mounted)
2734 n.param2 = packDir(p_under - p_over);
2736 // Calculate the direction for furnaces and chests and stuff
2737 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2739 v3f playerpos = player->getPosition();
2740 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2741 blockpos = blockpos.normalize();
2743 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2757 Send to all close-by players
2759 core::list<u16> far_players;
2760 sendAddNode(p_over, n, 0, &far_players, 30);
2765 InventoryList *ilist = player->inventory.getList("main");
2766 if(g_settings->getBool("creative_mode") == false && ilist)
2768 // Remove from inventory and send inventory
2769 if(mitem->getCount() == 1)
2770 ilist->deleteItem(item_i);
2774 UpdateCrafting(peer_id);
2775 SendInventory(peer_id);
2781 This takes some time so it is done after the quick stuff
2783 core::map<v3s16, MapBlock*> modified_blocks;
2785 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2787 std::string p_name = std::string(player->getName());
2788 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2791 Set blocks not sent to far players
2793 for(core::list<u16>::Iterator
2794 i = far_players.begin();
2795 i != far_players.end(); i++)
2798 RemoteClient *client = getClient(peer_id);
2801 client->SetBlocksNotSent(modified_blocks);
2805 Calculate special events
2808 /*if(n.d == CONTENT_MESE)
2811 for(s16 z=-1; z<=1; z++)
2812 for(s16 y=-1; y<=1; y++)
2813 for(s16 x=-1; x<=1; x++)
2820 Place other item (not a block)
2824 v3s16 blockpos = getNodeBlockPos(p_over);
2827 Check that the block is loaded so that the item
2828 can properly be added to the static list too
2830 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2833 infostream<<"Error while placing object: "
2834 "block not found"<<std::endl;
2839 If in creative mode, item dropping is disabled unless
2840 player has build privileges
2842 if(g_settings->getBool("creative_mode") &&
2843 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2845 infostream<<"Not allowing player to drop item: "
2846 "creative mode and no build privs"<<std::endl;
2850 // Calculate a position for it
2851 v3f pos = intToFloat(p_over, BS);
2853 pos.Y -= BS*0.25; // let it drop a bit
2855 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2856 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2861 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2865 infostream<<"WARNING: item resulted in NULL object, "
2866 <<"not placing onto map"
2871 actionstream<<player->getName()<<" places "<<item->getName()
2872 <<" at "<<PP(p_over)<<std::endl;
2874 // Add the object to the environment
2875 m_env->addActiveObject(obj);
2877 infostream<<"Placed object"<<std::endl;
2879 if(g_settings->getBool("creative_mode") == false)
2881 // Delete the right amount of items from the slot
2882 u16 dropcount = item->getDropCount();
2884 // Delete item if all gone
2885 if(item->getCount() <= dropcount)
2887 if(item->getCount() < dropcount)
2888 infostream<<"WARNING: Server: dropped more items"
2889 <<" than the slot contains"<<std::endl;
2891 InventoryList *ilist = player->inventory.getList("main");
2893 // Remove from inventory and send inventory
2894 ilist->deleteItem(item_i);
2896 // Else decrement it
2898 item->remove(dropcount);
2901 UpdateCrafting(peer_id);
2902 SendInventory(peer_id);
2910 Catch invalid actions
2914 infostream<<"WARNING: Server: Invalid action "
2915 <<action<<std::endl;
2919 else if(command == TOSERVER_RELEASE)
2928 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2931 else if(command == TOSERVER_SIGNTEXT)
2933 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2937 else if(command == TOSERVER_SIGNNODETEXT)
2939 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2947 std::string datastring((char*)&data[2], datasize-2);
2948 std::istringstream is(datastring, std::ios_base::binary);
2951 is.read((char*)buf, 6);
2952 v3s16 p = readV3S16(buf);
2953 is.read((char*)buf, 2);
2954 u16 textlen = readU16(buf);
2956 for(u16 i=0; i<textlen; i++)
2958 is.read((char*)buf, 1);
2959 text += (char)buf[0];
2962 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2965 if(meta->typeId() != CONTENT_SIGN_WALL)
2967 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2968 signmeta->setText(text);
2970 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
2971 <<" at "<<PP(p)<<std::endl;
2973 v3s16 blockpos = getNodeBlockPos(p);
2974 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2977 block->setChangedFlag();
2980 for(core::map<u16, RemoteClient*>::Iterator
2981 i = m_clients.getIterator();
2982 i.atEnd()==false; i++)
2984 RemoteClient *client = i.getNode()->getValue();
2985 client->SetBlockNotSent(blockpos);
2988 else if(command == TOSERVER_INVENTORY_ACTION)
2990 /*// Ignore inventory changes if in creative mode
2991 if(g_settings->getBool("creative_mode") == true)
2993 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2997 // Strip command and create a stream
2998 std::string datastring((char*)&data[2], datasize-2);
2999 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3000 std::istringstream is(datastring, std::ios_base::binary);
3002 InventoryAction *a = InventoryAction::deSerialize(is);
3007 c.current_player = player;
3010 Handle craftresult specially if not in creative mode
3012 bool disable_action = false;
3013 if(a->getType() == IACTION_MOVE
3014 && g_settings->getBool("creative_mode") == false)
3016 IMoveAction *ma = (IMoveAction*)a;
3017 if(ma->to_inv == "current_player" &&
3018 ma->from_inv == "current_player")
3020 InventoryList *rlist = player->inventory.getList("craftresult");
3022 InventoryList *clist = player->inventory.getList("craft");
3024 InventoryList *mlist = player->inventory.getList("main");
3027 Craftresult is no longer preview if something
3030 if(ma->to_list == "craftresult"
3031 && ma->from_list != "craftresult")
3033 // If it currently is a preview, remove
3035 if(player->craftresult_is_preview)
3037 rlist->deleteItem(0);
3039 player->craftresult_is_preview = false;
3042 Crafting takes place if this condition is true.
3044 if(player->craftresult_is_preview &&
3045 ma->from_list == "craftresult")
3047 player->craftresult_is_preview = false;
3048 clist->decrementMaterials(1);
3050 /* Print out action */
3051 InventoryList *list =
3052 player->inventory.getList("craftresult");
3054 InventoryItem *item = list->getItem(0);
3055 std::string itemname = "NULL";
3057 itemname = item->getName();
3058 actionstream<<player->getName()<<" crafts "
3059 <<itemname<<std::endl;
3062 If the craftresult is placed on itself, move it to
3063 main inventory instead of doing the action
3065 if(ma->to_list == "craftresult"
3066 && ma->from_list == "craftresult")
3068 disable_action = true;
3070 InventoryItem *item1 = rlist->changeItem(0, NULL);
3071 mlist->addItem(item1);
3074 // Disallow moving items if not allowed to build
3075 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3079 // if it's a locking chest, only allow the owner or server admins to move items
3080 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3082 Strfnd fn(ma->from_inv);
3083 std::string id0 = fn.next(":");
3084 if(id0 == "nodemeta")
3087 p.X = stoi(fn.next(","));
3088 p.Y = stoi(fn.next(","));
3089 p.Z = stoi(fn.next(","));
3090 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3091 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3092 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3093 if (lcm->getOwner() != player->getName())
3098 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3100 Strfnd fn(ma->to_inv);
3101 std::string id0 = fn.next(":");
3102 if(id0 == "nodemeta")
3105 p.X = stoi(fn.next(","));
3106 p.Y = stoi(fn.next(","));
3107 p.Z = stoi(fn.next(","));
3108 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3109 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3110 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3111 if (lcm->getOwner() != player->getName())
3118 if(disable_action == false)
3120 // Feed action to player inventory
3128 UpdateCrafting(player->peer_id);
3129 SendInventory(player->peer_id);
3134 infostream<<"TOSERVER_INVENTORY_ACTION: "
3135 <<"InventoryAction::deSerialize() returned NULL"
3139 else if(command == TOSERVER_CHAT_MESSAGE)
3147 std::string datastring((char*)&data[2], datasize-2);
3148 std::istringstream is(datastring, std::ios_base::binary);
3151 is.read((char*)buf, 2);
3152 u16 len = readU16(buf);
3154 std::wstring message;
3155 for(u16 i=0; i<len; i++)
3157 is.read((char*)buf, 2);
3158 message += (wchar_t)readU16(buf);
3161 // Get player name of this client
3162 std::wstring name = narrow_to_wide(player->getName());
3164 // Line to send to players
3166 // Whether to send to the player that sent the line
3167 bool send_to_sender = false;
3168 // Whether to send to other players
3169 bool send_to_others = false;
3171 // Local player gets all privileges regardless of
3172 // what's set on their account.
3173 u64 privs = getPlayerPrivs(player);
3176 if(message[0] == L'/')
3178 size_t strip_size = 1;
3179 if (message[1] == L'#') // support old-style commans
3181 message = message.substr(strip_size);
3183 WStrfnd f1(message);
3184 f1.next(L" "); // Skip over /#whatever
3185 std::wstring paramstring = f1.next(L"");
3187 ServerCommandContext *ctx = new ServerCommandContext(
3188 str_split(message, L' '),
3195 std::wstring reply(processServerCommand(ctx));
3196 send_to_sender = ctx->flags & SEND_TO_SENDER;
3197 send_to_others = ctx->flags & SEND_TO_OTHERS;
3199 if (ctx->flags & SEND_NO_PREFIX)
3202 line += L"Server: " + reply;
3209 if(privs & PRIV_SHOUT)
3215 send_to_others = true;
3219 line += L"Server: You are not allowed to shout";
3220 send_to_sender = true;
3227 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3230 Send the message to clients
3232 for(core::map<u16, RemoteClient*>::Iterator
3233 i = m_clients.getIterator();
3234 i.atEnd() == false; i++)
3236 // Get client and check that it is valid
3237 RemoteClient *client = i.getNode()->getValue();
3238 assert(client->peer_id == i.getNode()->getKey());
3239 if(client->serialization_version == SER_FMT_VER_INVALID)
3243 bool sender_selected = (peer_id == client->peer_id);
3244 if(sender_selected == true && send_to_sender == false)
3246 if(sender_selected == false && send_to_others == false)
3249 SendChatMessage(client->peer_id, line);
3253 else if(command == TOSERVER_DAMAGE)
3255 std::string datastring((char*)&data[2], datasize-2);
3256 std::istringstream is(datastring, std::ios_base::binary);
3257 u8 damage = readU8(is);
3259 if(g_settings->getBool("enable_damage"))
3261 actionstream<<player->getName()<<" damaged by "
3262 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3265 HandlePlayerHP(player, damage);
3269 SendPlayerHP(player);
3272 else if(command == TOSERVER_PASSWORD)
3275 [0] u16 TOSERVER_PASSWORD
3276 [2] u8[28] old password
3277 [30] u8[28] new password
3280 if(datasize != 2+PASSWORD_SIZE*2)
3282 /*char password[PASSWORD_SIZE];
3283 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3284 password[i] = data[2+i];
3285 password[PASSWORD_SIZE-1] = 0;*/
3287 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3295 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3297 char c = data[2+PASSWORD_SIZE+i];
3303 infostream<<"Server: Client requests a password change from "
3304 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3306 std::string playername = player->getName();
3308 if(m_authmanager.exists(playername) == false)
3310 infostream<<"Server: playername not found in authmanager"<<std::endl;
3311 // Wrong old password supplied!!
3312 SendChatMessage(peer_id, L"playername not found in authmanager");
3316 std::string checkpwd = m_authmanager.getPassword(playername);
3318 if(oldpwd != checkpwd)
3320 infostream<<"Server: invalid old password"<<std::endl;
3321 // Wrong old password supplied!!
3322 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3326 actionstream<<player->getName()<<" changes password"<<std::endl;
3328 m_authmanager.setPassword(playername, newpwd);
3330 infostream<<"Server: password change successful for "<<playername
3332 SendChatMessage(peer_id, L"Password change successful");
3334 else if(command == TOSERVER_PLAYERITEM)
3339 u16 item = readU16(&data[2]);
3340 player->wieldItem(item);
3341 SendWieldedItem(player);
3343 else if(command == TOSERVER_RESPAWN)
3348 RespawnPlayer(player);
3350 actionstream<<player->getName()<<" respawns at "
3351 <<PP(player->getPosition()/BS)<<std::endl;
3355 infostream<<"Server::ProcessData(): Ignoring "
3356 "unknown command "<<command<<std::endl;
3360 catch(SendFailedException &e)
3362 errorstream<<"Server::ProcessData(): SendFailedException: "
3368 void Server::onMapEditEvent(MapEditEvent *event)
3370 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3371 if(m_ignore_map_edit_events)
3373 MapEditEvent *e = event->clone();
3374 m_unsent_map_edit_queue.push_back(e);
3377 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3379 if(id == "current_player")
3381 assert(c->current_player);
3382 return &(c->current_player->inventory);
3386 std::string id0 = fn.next(":");
3388 if(id0 == "nodemeta")
3391 p.X = stoi(fn.next(","));
3392 p.Y = stoi(fn.next(","));
3393 p.Z = stoi(fn.next(","));
3394 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3396 return meta->getInventory();
3397 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3398 <<"no metadata found"<<std::endl;
3402 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3405 void Server::inventoryModified(InventoryContext *c, std::string id)
3407 if(id == "current_player")
3409 assert(c->current_player);
3411 UpdateCrafting(c->current_player->peer_id);
3412 SendInventory(c->current_player->peer_id);
3417 std::string id0 = fn.next(":");
3419 if(id0 == "nodemeta")
3422 p.X = stoi(fn.next(","));
3423 p.Y = stoi(fn.next(","));
3424 p.Z = stoi(fn.next(","));
3425 v3s16 blockpos = getNodeBlockPos(p);
3427 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3429 meta->inventoryModified();
3431 for(core::map<u16, RemoteClient*>::Iterator
3432 i = m_clients.getIterator();
3433 i.atEnd()==false; i++)
3435 RemoteClient *client = i.getNode()->getValue();
3436 client->SetBlockNotSent(blockpos);
3442 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3445 core::list<PlayerInfo> Server::getPlayerInfo()
3447 DSTACK(__FUNCTION_NAME);
3448 JMutexAutoLock envlock(m_env_mutex);
3449 JMutexAutoLock conlock(m_con_mutex);
3451 core::list<PlayerInfo> list;
3453 core::list<Player*> players = m_env->getPlayers();
3455 core::list<Player*>::Iterator i;
3456 for(i = players.begin();
3457 i != players.end(); i++)
3461 Player *player = *i;
3464 // Copy info from connection to info struct
3465 info.id = player->peer_id;
3466 info.address = m_con.GetPeerAddress(player->peer_id);
3467 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3469 catch(con::PeerNotFoundException &e)
3471 // Set dummy peer info
3473 info.address = Address(0,0,0,0,0);
3477 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3478 info.position = player->getPosition();
3480 list.push_back(info);
3487 void Server::peerAdded(con::Peer *peer)
3489 DSTACK(__FUNCTION_NAME);
3490 infostream<<"Server::peerAdded(): peer->id="
3491 <<peer->id<<std::endl;
3494 c.type = PEER_ADDED;
3495 c.peer_id = peer->id;
3497 m_peer_change_queue.push_back(c);
3500 void Server::deletingPeer(con::Peer *peer, bool timeout)
3502 DSTACK(__FUNCTION_NAME);
3503 infostream<<"Server::deletingPeer(): peer->id="
3504 <<peer->id<<", timeout="<<timeout<<std::endl;
3507 c.type = PEER_REMOVED;
3508 c.peer_id = peer->id;
3509 c.timeout = timeout;
3510 m_peer_change_queue.push_back(c);
3517 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3519 DSTACK(__FUNCTION_NAME);
3520 std::ostringstream os(std::ios_base::binary);
3522 writeU16(os, TOCLIENT_HP);
3526 std::string s = os.str();
3527 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3529 con.Send(peer_id, 0, data, true);
3532 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3533 const std::wstring &reason)
3535 DSTACK(__FUNCTION_NAME);
3536 std::ostringstream os(std::ios_base::binary);
3538 writeU16(os, TOCLIENT_ACCESS_DENIED);
3539 os<<serializeWideString(reason);
3542 std::string s = os.str();
3543 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3545 con.Send(peer_id, 0, data, true);
3548 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3549 bool set_camera_point_target, v3f camera_point_target)
3551 DSTACK(__FUNCTION_NAME);
3552 std::ostringstream os(std::ios_base::binary);
3554 writeU16(os, TOCLIENT_DEATHSCREEN);
3555 writeU8(os, set_camera_point_target);
3556 writeV3F1000(os, camera_point_target);
3559 std::string s = os.str();
3560 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3562 con.Send(peer_id, 0, data, true);
3566 Non-static send methods
3569 void Server::SendObjectData(float dtime)
3571 DSTACK(__FUNCTION_NAME);
3573 core::map<v3s16, bool> stepped_blocks;
3575 for(core::map<u16, RemoteClient*>::Iterator
3576 i = m_clients.getIterator();
3577 i.atEnd() == false; i++)
3579 u16 peer_id = i.getNode()->getKey();
3580 RemoteClient *client = i.getNode()->getValue();
3581 assert(client->peer_id == peer_id);
3583 if(client->serialization_version == SER_FMT_VER_INVALID)
3586 client->SendObjectData(this, dtime, stepped_blocks);
3590 void Server::SendPlayerInfos()
3592 DSTACK(__FUNCTION_NAME);
3594 //JMutexAutoLock envlock(m_env_mutex);
3596 // Get connected players
3597 core::list<Player*> players = m_env->getPlayers(true);
3599 u32 player_count = players.getSize();
3600 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3602 SharedBuffer<u8> data(datasize);
3603 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3606 core::list<Player*>::Iterator i;
3607 for(i = players.begin();
3608 i != players.end(); i++)
3610 Player *player = *i;
3612 /*infostream<<"Server sending player info for player with "
3613 "peer_id="<<player->peer_id<<std::endl;*/
3615 writeU16(&data[start], player->peer_id);
3616 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3617 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3618 start += 2+PLAYERNAME_SIZE;
3621 //JMutexAutoLock conlock(m_con_mutex);
3624 m_con.SendToAll(0, data, true);
3627 void Server::SendInventory(u16 peer_id)
3629 DSTACK(__FUNCTION_NAME);
3631 Player* player = m_env->getPlayer(peer_id);
3638 std::ostringstream os;
3639 //os.imbue(std::locale("C"));
3641 player->inventory.serialize(os);
3643 std::string s = os.str();
3645 SharedBuffer<u8> data(s.size()+2);
3646 writeU16(&data[0], TOCLIENT_INVENTORY);
3647 memcpy(&data[2], s.c_str(), s.size());
3650 m_con.Send(peer_id, 0, data, true);
3653 std::string getWieldedItemString(const Player *player)
3655 const InventoryItem *item = player->getWieldItem();
3657 return std::string("");
3658 std::ostringstream os(std::ios_base::binary);
3659 item->serialize(os);
3663 void Server::SendWieldedItem(const Player* player)
3665 DSTACK(__FUNCTION_NAME);
3669 std::ostringstream os(std::ios_base::binary);
3671 writeU16(os, TOCLIENT_PLAYERITEM);
3673 writeU16(os, player->peer_id);
3674 os<<serializeString(getWieldedItemString(player));
3677 std::string s = os.str();
3678 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3680 m_con.SendToAll(0, data, true);
3683 void Server::SendPlayerItems()
3685 DSTACK(__FUNCTION_NAME);
3687 std::ostringstream os(std::ios_base::binary);
3688 core::list<Player *> players = m_env->getPlayers(true);
3690 writeU16(os, TOCLIENT_PLAYERITEM);
3691 writeU16(os, players.size());
3692 core::list<Player *>::Iterator i;
3693 for(i = players.begin(); i != players.end(); ++i)
3696 writeU16(os, p->peer_id);
3697 os<<serializeString(getWieldedItemString(p));
3701 std::string s = os.str();
3702 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3704 m_con.SendToAll(0, data, true);
3707 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3709 DSTACK(__FUNCTION_NAME);
3711 std::ostringstream os(std::ios_base::binary);
3715 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3716 os.write((char*)buf, 2);
3719 writeU16(buf, message.size());
3720 os.write((char*)buf, 2);
3723 for(u32 i=0; i<message.size(); i++)
3727 os.write((char*)buf, 2);
3731 std::string s = os.str();
3732 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3734 m_con.Send(peer_id, 0, data, true);
3737 void Server::BroadcastChatMessage(const std::wstring &message)
3739 for(core::map<u16, RemoteClient*>::Iterator
3740 i = m_clients.getIterator();
3741 i.atEnd() == false; i++)
3743 // Get client and check that it is valid
3744 RemoteClient *client = i.getNode()->getValue();
3745 assert(client->peer_id == i.getNode()->getKey());
3746 if(client->serialization_version == SER_FMT_VER_INVALID)
3749 SendChatMessage(client->peer_id, message);
3753 void Server::SendPlayerHP(Player *player)
3755 SendHP(m_con, player->peer_id, player->hp);
3758 void Server::SendMovePlayer(Player *player)
3760 DSTACK(__FUNCTION_NAME);
3761 std::ostringstream os(std::ios_base::binary);
3763 writeU16(os, TOCLIENT_MOVE_PLAYER);
3764 writeV3F1000(os, player->getPosition());
3765 writeF1000(os, player->getPitch());
3766 writeF1000(os, player->getYaw());
3769 v3f pos = player->getPosition();
3770 f32 pitch = player->getPitch();
3771 f32 yaw = player->getYaw();
3772 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3773 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3780 std::string s = os.str();
3781 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3783 m_con.Send(player->peer_id, 0, data, true);
3786 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3787 core::list<u16> *far_players, float far_d_nodes)
3789 float maxd = far_d_nodes*BS;
3790 v3f p_f = intToFloat(p, BS);
3794 SharedBuffer<u8> reply(replysize);
3795 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3796 writeS16(&reply[2], p.X);
3797 writeS16(&reply[4], p.Y);
3798 writeS16(&reply[6], p.Z);
3800 for(core::map<u16, RemoteClient*>::Iterator
3801 i = m_clients.getIterator();
3802 i.atEnd() == false; i++)
3804 // Get client and check that it is valid
3805 RemoteClient *client = i.getNode()->getValue();
3806 assert(client->peer_id == i.getNode()->getKey());
3807 if(client->serialization_version == SER_FMT_VER_INVALID)
3810 // Don't send if it's the same one
3811 if(client->peer_id == ignore_id)
3817 Player *player = m_env->getPlayer(client->peer_id);
3820 // If player is far away, only set modified blocks not sent
3821 v3f player_pos = player->getPosition();
3822 if(player_pos.getDistanceFrom(p_f) > maxd)
3824 far_players->push_back(client->peer_id);
3831 m_con.Send(client->peer_id, 0, reply, true);
3835 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3836 core::list<u16> *far_players, float far_d_nodes)
3838 float maxd = far_d_nodes*BS;
3839 v3f p_f = intToFloat(p, BS);
3841 for(core::map<u16, RemoteClient*>::Iterator
3842 i = m_clients.getIterator();
3843 i.atEnd() == false; i++)
3845 // Get client and check that it is valid
3846 RemoteClient *client = i.getNode()->getValue();
3847 assert(client->peer_id == i.getNode()->getKey());
3848 if(client->serialization_version == SER_FMT_VER_INVALID)
3851 // Don't send if it's the same one
3852 if(client->peer_id == ignore_id)
3858 Player *player = m_env->getPlayer(client->peer_id);
3861 // If player is far away, only set modified blocks not sent
3862 v3f player_pos = player->getPosition();
3863 if(player_pos.getDistanceFrom(p_f) > maxd)
3865 far_players->push_back(client->peer_id);
3872 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3873 SharedBuffer<u8> reply(replysize);
3874 writeU16(&reply[0], TOCLIENT_ADDNODE);
3875 writeS16(&reply[2], p.X);
3876 writeS16(&reply[4], p.Y);
3877 writeS16(&reply[6], p.Z);
3878 n.serialize(&reply[8], client->serialization_version);
3881 m_con.Send(client->peer_id, 0, reply, true);
3885 void Server::setBlockNotSent(v3s16 p)
3887 for(core::map<u16, RemoteClient*>::Iterator
3888 i = m_clients.getIterator();
3889 i.atEnd()==false; i++)
3891 RemoteClient *client = i.getNode()->getValue();
3892 client->SetBlockNotSent(p);
3896 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3898 DSTACK(__FUNCTION_NAME);
3900 v3s16 p = block->getPos();
3904 bool completely_air = true;
3905 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3906 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3907 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3909 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3911 completely_air = false;
3912 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3917 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3919 infostream<<"[completely air] ";
3920 infostream<<std::endl;
3924 Create a packet with the block in the right format
3927 std::ostringstream os(std::ios_base::binary);
3928 block->serialize(os, ver);
3929 std::string s = os.str();
3930 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3932 u32 replysize = 8 + blockdata.getSize();
3933 SharedBuffer<u8> reply(replysize);
3934 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3935 writeS16(&reply[2], p.X);
3936 writeS16(&reply[4], p.Y);
3937 writeS16(&reply[6], p.Z);
3938 memcpy(&reply[8], *blockdata, blockdata.getSize());
3940 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3941 <<": \tpacket size: "<<replysize<<std::endl;*/
3946 m_con.Send(peer_id, 1, reply, true);
3949 void Server::SendBlocks(float dtime)
3951 DSTACK(__FUNCTION_NAME);
3953 JMutexAutoLock envlock(m_env_mutex);
3954 JMutexAutoLock conlock(m_con_mutex);
3956 //TimeTaker timer("Server::SendBlocks");
3958 core::array<PrioritySortedBlockTransfer> queue;
3960 s32 total_sending = 0;
3963 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3965 for(core::map<u16, RemoteClient*>::Iterator
3966 i = m_clients.getIterator();
3967 i.atEnd() == false; i++)
3969 RemoteClient *client = i.getNode()->getValue();
3970 assert(client->peer_id == i.getNode()->getKey());
3972 total_sending += client->SendingCount();
3974 if(client->serialization_version == SER_FMT_VER_INVALID)
3977 client->GetNextBlocks(this, dtime, queue);
3982 // Lowest priority number comes first.
3983 // Lowest is most important.
3986 for(u32 i=0; i<queue.size(); i++)
3988 //TODO: Calculate limit dynamically
3989 if(total_sending >= g_settings->getS32
3990 ("max_simultaneous_block_sends_server_total"))
3993 PrioritySortedBlockTransfer q = queue[i];
3995 MapBlock *block = NULL;
3998 block = m_env->getMap().getBlockNoCreate(q.pos);
4000 catch(InvalidPositionException &e)
4005 RemoteClient *client = getClient(q.peer_id);
4007 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4009 client->SentBlock(q.pos);
4019 void Server::HandlePlayerHP(Player *player, s16 damage)
4021 if(player->hp > damage)
4023 player->hp -= damage;
4024 SendPlayerHP(player);
4028 infostream<<"Server::HandlePlayerHP(): Player "
4029 <<player->getName()<<" dies"<<std::endl;
4033 //TODO: Throw items around
4035 // Handle players that are not connected
4036 if(player->peer_id == PEER_ID_INEXISTENT){
4037 RespawnPlayer(player);
4041 SendPlayerHP(player);
4043 RemoteClient *client = getClient(player->peer_id);
4044 if(client->net_proto_version >= 3)
4046 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4050 RespawnPlayer(player);
4055 void Server::RespawnPlayer(Player *player)
4057 v3f pos = findSpawnPos(m_env->getServerMap());
4058 player->setPosition(pos);
4060 SendMovePlayer(player);
4061 SendPlayerHP(player);
4064 void Server::UpdateCrafting(u16 peer_id)
4066 DSTACK(__FUNCTION_NAME);
4068 Player* player = m_env->getPlayer(peer_id);
4072 Calculate crafting stuff
4074 if(g_settings->getBool("creative_mode") == false)
4076 InventoryList *clist = player->inventory.getList("craft");
4077 InventoryList *rlist = player->inventory.getList("craftresult");
4079 if(rlist && rlist->getUsedSlots() == 0)
4080 player->craftresult_is_preview = true;
4082 if(rlist && player->craftresult_is_preview)
4084 rlist->clearItems();
4086 if(clist && rlist && player->craftresult_is_preview)
4088 InventoryItem *items[9];
4089 for(u16 i=0; i<9; i++)
4091 items[i] = clist->getItem(i);
4094 // Get result of crafting grid
4095 InventoryItem *result = craft_get_result(items);
4097 rlist->addItem(result);
4100 } // if creative_mode == false
4103 RemoteClient* Server::getClient(u16 peer_id)
4105 DSTACK(__FUNCTION_NAME);
4106 //JMutexAutoLock lock(m_con_mutex);
4107 core::map<u16, RemoteClient*>::Node *n;
4108 n = m_clients.find(peer_id);
4109 // A client should exist for all peers
4111 return n->getValue();
4114 std::wstring Server::getStatusString()
4116 std::wostringstream os(std::ios_base::binary);
4119 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4121 os<<L", uptime="<<m_uptime.get();
4122 // Information about clients
4124 for(core::map<u16, RemoteClient*>::Iterator
4125 i = m_clients.getIterator();
4126 i.atEnd() == false; i++)
4128 // Get client and check that it is valid
4129 RemoteClient *client = i.getNode()->getValue();
4130 assert(client->peer_id == i.getNode()->getKey());
4131 if(client->serialization_version == SER_FMT_VER_INVALID)
4134 Player *player = m_env->getPlayer(client->peer_id);
4135 // Get name of player
4136 std::wstring name = L"unknown";
4138 name = narrow_to_wide(player->getName());
4139 // Add name to information string
4143 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4144 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4145 if(g_settings->get("motd") != "")
4146 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4150 // Saves g_settings to configpath given at initialization
4151 void Server::saveConfig()
4153 if(m_configpath != "")
4154 g_settings->updateConfigFile(m_configpath.c_str());
4157 void Server::notifyPlayer(const char *name, const std::wstring msg)
4159 Player *player = m_env->getPlayer(name);
4162 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4165 void Server::notifyPlayers(const std::wstring msg)
4167 BroadcastChatMessage(msg);
4170 v3f findSpawnPos(ServerMap &map)
4172 //return v3f(50,50,50)*BS;
4177 nodepos = v2s16(0,0);
4182 // Try to find a good place a few times
4183 for(s32 i=0; i<1000; i++)
4186 // We're going to try to throw the player to this position
4187 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4188 -range + (myrand()%(range*2)));
4189 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4190 // Get ground height at point (fallbacks to heightmap function)
4191 s16 groundheight = map.findGroundLevel(nodepos2d);
4192 // Don't go underwater
4193 if(groundheight < WATER_LEVEL)
4195 //infostream<<"-> Underwater"<<std::endl;
4198 // Don't go to high places
4199 if(groundheight > WATER_LEVEL + 4)
4201 //infostream<<"-> Underwater"<<std::endl;
4205 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4206 bool is_good = false;
4208 for(s32 i=0; i<10; i++){
4209 v3s16 blockpos = getNodeBlockPos(nodepos);
4210 map.emergeBlock(blockpos, true);
4211 MapNode n = map.getNodeNoEx(nodepos);
4212 if(n.getContent() == CONTENT_AIR){
4223 // Found a good place
4224 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4230 return intToFloat(nodepos, BS);
4233 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4236 Try to get an existing player
4238 Player *player = m_env->getPlayer(name);
4241 // If player is already connected, cancel
4242 if(player->peer_id != 0)
4244 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4249 player->peer_id = peer_id;
4251 // Reset inventory to creative if in creative mode
4252 if(g_settings->getBool("creative_mode"))
4254 // Warning: double code below
4255 // Backup actual inventory
4256 player->inventory_backup = new Inventory();
4257 *(player->inventory_backup) = player->inventory;
4258 // Set creative inventory
4259 craft_set_creative_inventory(player);
4266 If player with the wanted peer_id already exists, cancel.
4268 if(m_env->getPlayer(peer_id) != NULL)
4270 infostream<<"emergePlayer(): Player with wrong name but same"
4271 " peer_id already exists"<<std::endl;
4279 player = new ServerRemotePlayer();
4280 //player->peer_id = c.peer_id;
4281 //player->peer_id = PEER_ID_INEXISTENT;
4282 player->peer_id = peer_id;
4283 player->updateName(name);
4284 m_authmanager.add(name);
4285 m_authmanager.setPassword(name, password);
4286 m_authmanager.setPrivs(name,
4287 stringToPrivs(g_settings->get("default_privs")));
4293 infostream<<"Server: Finding spawn place for player \""
4294 <<player->getName()<<"\""<<std::endl;
4296 v3f pos = findSpawnPos(m_env->getServerMap());
4298 player->setPosition(pos);
4301 Add player to environment
4304 m_env->addPlayer(player);
4307 Add stuff to inventory
4310 if(g_settings->getBool("creative_mode"))
4312 // Warning: double code above
4313 // Backup actual inventory
4314 player->inventory_backup = new Inventory();
4315 *(player->inventory_backup) = player->inventory;
4316 // Set creative inventory
4317 craft_set_creative_inventory(player);
4319 else if(g_settings->getBool("give_initial_stuff"))
4321 craft_give_initial_stuff(player);
4326 } // create new player
4329 void Server::handlePeerChange(PeerChange &c)
4331 JMutexAutoLock envlock(m_env_mutex);
4332 JMutexAutoLock conlock(m_con_mutex);
4334 if(c.type == PEER_ADDED)
4341 core::map<u16, RemoteClient*>::Node *n;
4342 n = m_clients.find(c.peer_id);
4343 // The client shouldn't already exist
4347 RemoteClient *client = new RemoteClient();
4348 client->peer_id = c.peer_id;
4349 m_clients.insert(client->peer_id, client);
4352 else if(c.type == PEER_REMOVED)
4359 core::map<u16, RemoteClient*>::Node *n;
4360 n = m_clients.find(c.peer_id);
4361 // The client should exist
4365 Mark objects to be not known by the client
4367 RemoteClient *client = n->getValue();
4369 for(core::map<u16, bool>::Iterator
4370 i = client->m_known_objects.getIterator();
4371 i.atEnd()==false; i++)
4374 u16 id = i.getNode()->getKey();
4375 ServerActiveObject* obj = m_env->getActiveObject(id);
4377 if(obj && obj->m_known_by_count > 0)
4378 obj->m_known_by_count--;
4381 // Collect information about leaving in chat
4382 std::wstring message;
4384 Player *player = m_env->getPlayer(c.peer_id);
4387 std::wstring name = narrow_to_wide(player->getName());
4390 message += L" left game";
4392 message += L" (timed out)";
4398 m_env->removePlayer(c.peer_id);
4401 // Set player client disconnected
4403 Player *player = m_env->getPlayer(c.peer_id);
4405 player->peer_id = 0;
4412 std::ostringstream os(std::ios_base::binary);
4413 for(core::map<u16, RemoteClient*>::Iterator
4414 i = m_clients.getIterator();
4415 i.atEnd() == false; i++)
4417 RemoteClient *client = i.getNode()->getValue();
4418 assert(client->peer_id == i.getNode()->getKey());
4419 if(client->serialization_version == SER_FMT_VER_INVALID)
4422 Player *player = m_env->getPlayer(client->peer_id);
4425 // Get name of player
4426 os<<player->getName()<<" ";
4429 actionstream<<player->getName()<<" "
4430 <<(c.timeout?"times out.":"leaves game.")
4431 <<" List of players: "
4432 <<os.str()<<std::endl;
4437 delete m_clients[c.peer_id];
4438 m_clients.remove(c.peer_id);
4440 // Send player info to all remaining clients
4443 // Send leave chat message to all remaining clients
4444 BroadcastChatMessage(message);
4453 void Server::handlePeerChanges()
4455 while(m_peer_change_queue.size() > 0)
4457 PeerChange c = m_peer_change_queue.pop_front();
4459 infostream<<"Server: Handling peer change: "
4460 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4463 handlePeerChange(c);
4467 u64 Server::getPlayerPrivs(Player *player)
4471 std::string playername = player->getName();
4472 // Local player gets all privileges regardless of
4473 // what's set on their account.
4474 if(g_settings->get("name") == playername)
4480 return getPlayerAuthPrivs(playername);
4484 void dedicated_server_loop(Server &server, bool &kill)
4486 DSTACK(__FUNCTION_NAME);
4488 infostream<<DTIME<<std::endl;
4489 infostream<<"========================"<<std::endl;
4490 infostream<<"Running dedicated server"<<std::endl;
4491 infostream<<"========================"<<std::endl;
4492 infostream<<std::endl;
4494 IntervalLimiter m_profiler_interval;
4498 // This is kind of a hack but can be done like this
4499 // because server.step() is very light
4501 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4506 if(server.getShutdownRequested() || kill)
4508 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4515 float profiler_print_interval =
4516 g_settings->getFloat("profiler_print_interval");
4517 if(profiler_print_interval != 0)
4519 if(m_profiler_interval.step(0.030, profiler_print_interval))
4521 infostream<<"Profiler:"<<std::endl;
4522 g_profiler->print(infostream);
4523 g_profiler->clear();
4530 static int counter = 0;
4536 core::list<PlayerInfo> list = server.getPlayerInfo();
4537 core::list<PlayerInfo>::Iterator i;
4538 static u32 sum_old = 0;
4539 u32 sum = PIChecksum(list);
4542 infostream<<DTIME<<"Player info:"<<std::endl;
4543 for(i=list.begin(); i!=list.end(); i++)
4545 i->PrintLine(&infostream);