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"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
52 class MapEditEventIgnorer
55 MapEditEventIgnorer(bool *flag):
64 ~MapEditEventIgnorer()
77 void * ServerThread::Thread()
81 log_register_thread("ServerThread");
83 DSTACK(__FUNCTION_NAME);
85 BEGIN_DEBUG_EXCEPTION_HANDLER
90 //TimeTaker timer("AsyncRunStep() + Receive()");
93 //TimeTaker timer("AsyncRunStep()");
94 m_server->AsyncRunStep();
97 //infostream<<"Running m_server->Receive()"<<std::endl;
100 catch(con::NoIncomingDataException &e)
103 catch(con::PeerNotFoundException &e)
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
109 END_DEBUG_EXCEPTION_HANDLER(errorstream)
114 void * EmergeThread::Thread()
118 log_register_thread("EmergeThread");
120 DSTACK(__FUNCTION_NAME);
122 BEGIN_DEBUG_EXCEPTION_HANDLER
124 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
127 Get block info from queue, emerge them and send them
130 After queue is empty, exit.
134 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
138 SharedPtr<QueuedBlockEmerge> q(qptr);
144 Do not generate over-limit
146 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
151 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 //core::map<v3s16, MapBlock*> changed_blocks;
196 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
198 MapBlock *block = NULL;
199 bool got_block = true;
200 core::map<v3s16, MapBlock*> modified_blocks;
203 Fetch block from map or generate a single block
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 //map.loadSectorFull(p2d);
211 map.loadSectorMeta(p2d);
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
219 // Get, load or create sector
220 /*ServerMapSector *sector =
221 (ServerMapSector*)map.createSector(p2d);*/
223 // Load/generate block
225 /*block = map.emergeBlock(p, sector, changed_blocks,
226 lighting_invalidated_blocks);*/
228 block = map.loadBlock(p);
230 if(only_from_disk == false)
232 if(block == NULL || block->isGenerated() == false)
234 if(enable_mapgen_debug_info)
235 infostream<<"EmergeThread: generating"<<std::endl;
236 block = map.generateBlock(p, modified_blocks);
240 if(enable_mapgen_debug_info)
241 infostream<<"EmergeThread: ended up with: "
242 <<analyze_block(block)<<std::endl;
251 Ignore map edit events, they will not need to be
252 sent to anybody because the block hasn't been sent
255 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
257 // Activate objects and stuff
258 m_server->m_env->activateBlock(block, 3600);
263 /*if(block->getLightingExpired()){
264 lighting_invalidated_blocks[block->getPos()] = block;
268 // TODO: Some additional checking and lighting updating,
273 JMutexAutoLock envlock(m_server->m_env_mutex);
278 Collect a list of blocks that have been modified in
279 addition to the fetched one.
283 if(lighting_invalidated_blocks.size() > 0)
285 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
286 <<" blocks"<<std::endl;*/
288 // 50-100ms for single block generation
289 //TimeTaker timer("** EmergeThread updateLighting");
291 // Update lighting without locking the environment mutex,
292 // add modified blocks to changed blocks
293 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
296 // Add all from changed_blocks to modified_blocks
297 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
298 i.atEnd() == false; i++)
300 MapBlock *block = i.getNode()->getValue();
301 modified_blocks.insert(block->getPos(), block);
305 // If we got no block, there should be no invalidated blocks
308 //assert(lighting_invalidated_blocks.size() == 0);
314 Set sent status of modified blocks on clients
317 // NOTE: Server's clients are also behind the connection mutex
318 JMutexAutoLock lock(m_server->m_con_mutex);
321 Add the originally fetched block to the modified list
325 modified_blocks.insert(p, block);
329 Set the modified blocks unsent for all the clients
332 for(core::map<u16, RemoteClient*>::Iterator
333 i = m_server->m_clients.getIterator();
334 i.atEnd() == false; i++)
336 RemoteClient *client = i.getNode()->getValue();
338 if(modified_blocks.size() > 0)
340 // Remove block from sent history
341 client->SetBlocksNotSent(modified_blocks);
347 END_DEBUG_EXCEPTION_HANDLER(errorstream)
352 void RemoteClient::GetNextBlocks(Server *server, float dtime,
353 core::array<PrioritySortedBlockTransfer> &dest)
355 DSTACK(__FUNCTION_NAME);
358 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
361 m_nothing_to_send_pause_timer -= dtime;
362 m_nearest_unsent_reset_timer += dtime;
364 if(m_nothing_to_send_pause_timer >= 0)
369 // Won't send anything if already sending
370 if(m_blocks_sending.size() >= g_settings->getU16
371 ("max_simultaneous_block_sends_per_client"))
373 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
377 //TimeTaker timer("RemoteClient::GetNextBlocks");
379 Player *player = server->m_env->getPlayer(peer_id);
381 assert(player != NULL);
383 v3f playerpos = player->getPosition();
384 v3f playerspeed = player->getSpeed();
385 v3f playerspeeddir(0,0,0);
386 if(playerspeed.getLength() > 1.0*BS)
387 playerspeeddir = playerspeed / playerspeed.getLength();
388 // Predict to next block
389 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
391 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
393 v3s16 center = getNodeBlockPos(center_nodepos);
395 // Camera position and direction
396 v3f camera_pos = player->getEyePosition();
397 v3f camera_dir = v3f(0,0,1);
398 camera_dir.rotateYZBy(player->getPitch());
399 camera_dir.rotateXZBy(player->getYaw());
401 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
402 <<camera_dir.Z<<")"<<std::endl;*/
405 Get the starting value of the block finder radius.
408 if(m_last_center != center)
410 m_nearest_unsent_d = 0;
411 m_last_center = center;
414 /*infostream<<"m_nearest_unsent_reset_timer="
415 <<m_nearest_unsent_reset_timer<<std::endl;*/
417 // Reset periodically to workaround for some bugs or stuff
418 if(m_nearest_unsent_reset_timer > 20.0)
420 m_nearest_unsent_reset_timer = 0;
421 m_nearest_unsent_d = 0;
422 //infostream<<"Resetting m_nearest_unsent_d for "
423 // <<server->getPlayerName(peer_id)<<std::endl;
426 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
427 s16 d_start = m_nearest_unsent_d;
429 //infostream<<"d_start="<<d_start<<std::endl;
431 u16 max_simul_sends_setting = g_settings->getU16
432 ("max_simultaneous_block_sends_per_client");
433 u16 max_simul_sends_usually = max_simul_sends_setting;
436 Check the time from last addNode/removeNode.
438 Decrease send rate if player is building stuff.
440 m_time_from_building += dtime;
441 if(m_time_from_building < g_settings->getFloat(
442 "full_block_send_enable_min_time_from_building"))
444 max_simul_sends_usually
445 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
449 Number of blocks sending + number of blocks selected for sending
451 u32 num_blocks_selected = m_blocks_sending.size();
454 next time d will be continued from the d from which the nearest
455 unsent block was found this time.
457 This is because not necessarily any of the blocks found this
458 time are actually sent.
460 s32 new_nearest_unsent_d = -1;
462 s16 d_max = g_settings->getS16("max_block_send_distance");
463 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
465 // Don't loop very much at a time
466 s16 max_d_increment_at_time = 2;
467 if(d_max > d_start + max_d_increment_at_time)
468 d_max = d_start + max_d_increment_at_time;
469 /*if(d_max_gen > d_start+2)
470 d_max_gen = d_start+2;*/
472 //infostream<<"Starting from "<<d_start<<std::endl;
474 s32 nearest_emerged_d = -1;
475 s32 nearest_emergefull_d = -1;
476 s32 nearest_sent_d = -1;
477 bool queue_is_full = false;
480 for(d = d_start; d <= d_max; d++)
482 /*errorstream<<"checking d="<<d<<" for "
483 <<server->getPlayerName(peer_id)<<std::endl;*/
484 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
487 If m_nearest_unsent_d was changed by the EmergeThread
488 (it can change it to 0 through SetBlockNotSent),
490 Else update m_nearest_unsent_d
492 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
494 d = m_nearest_unsent_d;
495 last_nearest_unsent_d = m_nearest_unsent_d;
499 Get the border/face dot coordinates of a "d-radiused"
502 core::list<v3s16> list;
503 getFacePositions(list, d);
505 core::list<v3s16>::Iterator li;
506 for(li=list.begin(); li!=list.end(); li++)
508 v3s16 p = *li + center;
512 - Don't allow too many simultaneous transfers
513 - EXCEPT when the blocks are very close
515 Also, don't send blocks that are already flying.
518 // Start with the usual maximum
519 u16 max_simul_dynamic = max_simul_sends_usually;
521 // If block is very close, allow full maximum
522 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
523 max_simul_dynamic = max_simul_sends_setting;
525 // Don't select too many blocks for sending
526 if(num_blocks_selected >= max_simul_dynamic)
528 queue_is_full = true;
529 goto queue_full_break;
532 // Don't send blocks that are currently being transferred
533 if(m_blocks_sending.find(p) != NULL)
539 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
544 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
547 // If this is true, inexistent block will be made from scratch
548 bool generate = d <= d_max_gen;
551 /*// Limit the generating area vertically to 2/3
552 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
555 // Limit the send area vertically to 1/2
556 if(abs(p.Y - center.Y) > d_max / 2)
562 If block is far away, don't generate it unless it is
568 // Block center y in nodes
569 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
570 // Don't generate if it's very high or very low
571 if(y < -64 || y > 64)
575 v2s16 p2d_nodes_center(
579 // Get ground height in nodes
580 s16 gh = server->m_env->getServerMap().findGroundLevel(
583 // If differs a lot, don't generate
584 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
586 // Actually, don't even send it
592 //infostream<<"d="<<d<<std::endl;
595 Don't generate or send if not in sight
596 FIXME This only works if the client uses a small enough
597 FOV setting. The default of 72 degrees is fine.
600 float camera_fov = (72.0*PI/180) * 4./3.;
601 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
607 Don't send already sent blocks
610 if(m_blocks_sent.find(p) != NULL)
617 Check if map has this block
619 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
621 bool surely_not_found_on_disk = false;
622 bool block_is_invalid = false;
625 // Reset usage timer, this block will be of use in the future.
626 block->resetUsageTimer();
628 // Block is dummy if data doesn't exist.
629 // It means it has been not found from disk and not generated
632 surely_not_found_on_disk = true;
635 // Block is valid if lighting is up-to-date and data exists
636 if(block->isValid() == false)
638 block_is_invalid = true;
641 /*if(block->isFullyGenerated() == false)
643 block_is_invalid = true;
648 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
649 v2s16 chunkpos = map->sector_to_chunk(p2d);
650 if(map->chunkNonVolatile(chunkpos) == false)
651 block_is_invalid = true;
653 if(block->isGenerated() == false)
654 block_is_invalid = true;
657 If block is not close, don't send it unless it is near
660 Block is near ground level if night-time mesh
661 differs from day-time mesh.
665 if(block->dayNightDiffed() == false)
672 If block has been marked to not exist on disk (dummy)
673 and generating new ones is not wanted, skip block.
675 if(generate == false && surely_not_found_on_disk == true)
682 Add inexistent block to emerge queue.
684 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
686 //TODO: Get value from somewhere
687 // Allow only one block in emerge queue
688 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
689 // Allow two blocks in queue per client
690 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
692 // Make it more responsive when needing to generate stuff
693 if(surely_not_found_on_disk)
695 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
697 //infostream<<"Adding block to emerge queue"<<std::endl;
699 // Add it to the emerge queue and trigger the thread
702 if(generate == false)
703 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
705 server->m_emerge_queue.addBlock(peer_id, p, flags);
706 server->m_emergethread.trigger();
708 if(nearest_emerged_d == -1)
709 nearest_emerged_d = d;
711 if(nearest_emergefull_d == -1)
712 nearest_emergefull_d = d;
719 if(nearest_sent_d == -1)
723 Add block to send queue
726 /*errorstream<<"sending from d="<<d<<" to "
727 <<server->getPlayerName(peer_id)<<std::endl;*/
729 PrioritySortedBlockTransfer q((float)d, p, peer_id);
733 num_blocks_selected += 1;
738 //infostream<<"Stopped at "<<d<<std::endl;
740 // If nothing was found for sending and nothing was queued for
741 // emerging, continue next time browsing from here
742 if(nearest_emerged_d != -1){
743 new_nearest_unsent_d = nearest_emerged_d;
744 } else if(nearest_emergefull_d != -1){
745 new_nearest_unsent_d = nearest_emergefull_d;
747 if(d > g_settings->getS16("max_block_send_distance")){
748 new_nearest_unsent_d = 0;
749 m_nothing_to_send_pause_timer = 2.0;
750 /*infostream<<"GetNextBlocks(): d wrapped around for "
751 <<server->getPlayerName(peer_id)
752 <<"; setting to 0 and pausing"<<std::endl;*/
754 if(nearest_sent_d != -1)
755 new_nearest_unsent_d = nearest_sent_d;
757 new_nearest_unsent_d = d;
761 if(new_nearest_unsent_d != -1)
762 m_nearest_unsent_d = new_nearest_unsent_d;
764 /*timer_result = timer.stop(true);
765 if(timer_result != 0)
766 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
769 void RemoteClient::SendObjectData(
772 core::map<v3s16, bool> &stepped_blocks
775 DSTACK(__FUNCTION_NAME);
777 // Can't send anything without knowing version
778 if(serialization_version == SER_FMT_VER_INVALID)
780 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
786 Send a TOCLIENT_OBJECTDATA packet.
790 u16 number of player positions
802 std::ostringstream os(std::ios_base::binary);
806 writeU16(buf, TOCLIENT_OBJECTDATA);
807 os.write((char*)buf, 2);
810 Get and write player data
813 // Get connected players
814 core::list<Player*> players = server->m_env->getPlayers(true);
816 // Write player count
817 u16 playercount = players.size();
818 writeU16(buf, playercount);
819 os.write((char*)buf, 2);
821 core::list<Player*>::Iterator i;
822 for(i = players.begin();
823 i != players.end(); i++)
827 v3f pf = player->getPosition();
828 v3f sf = player->getSpeed();
830 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
831 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
832 s32 pitch_i (player->getPitch() * 100);
833 s32 yaw_i (player->getYaw() * 100);
835 writeU16(buf, player->peer_id);
836 os.write((char*)buf, 2);
837 writeV3S32(buf, position_i);
838 os.write((char*)buf, 12);
839 writeV3S32(buf, speed_i);
840 os.write((char*)buf, 12);
841 writeS32(buf, pitch_i);
842 os.write((char*)buf, 4);
843 writeS32(buf, yaw_i);
844 os.write((char*)buf, 4);
848 Get and write object data (dummy, for compatibility)
853 os.write((char*)buf, 2);
859 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
862 std::string s = os.str();
863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
864 // Send as unreliable
865 server->m_con.Send(peer_id, 0, data, false);
868 void RemoteClient::GotBlock(v3s16 p)
870 if(m_blocks_sending.find(p) != NULL)
871 m_blocks_sending.remove(p);
874 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
875 " m_blocks_sending"<<std::endl;*/
876 m_excess_gotblocks++;
878 m_blocks_sent.insert(p, true);
881 void RemoteClient::SentBlock(v3s16 p)
883 if(m_blocks_sending.find(p) == NULL)
884 m_blocks_sending.insert(p, 0.0);
886 infostream<<"RemoteClient::SentBlock(): Sent block"
887 " already in m_blocks_sending"<<std::endl;
890 void RemoteClient::SetBlockNotSent(v3s16 p)
892 m_nearest_unsent_d = 0;
894 if(m_blocks_sending.find(p) != NULL)
895 m_blocks_sending.remove(p);
896 if(m_blocks_sent.find(p) != NULL)
897 m_blocks_sent.remove(p);
900 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
902 m_nearest_unsent_d = 0;
904 for(core::map<v3s16, MapBlock*>::Iterator
905 i = blocks.getIterator();
906 i.atEnd()==false; i++)
908 v3s16 p = i.getNode()->getKey();
910 if(m_blocks_sending.find(p) != NULL)
911 m_blocks_sending.remove(p);
912 if(m_blocks_sent.find(p) != NULL)
913 m_blocks_sent.remove(p);
921 PlayerInfo::PlayerInfo()
927 void PlayerInfo::PrintLine(std::ostream *s)
930 (*s)<<"\""<<name<<"\" ("
931 <<(position.X/10)<<","<<(position.Y/10)
932 <<","<<(position.Z/10)<<") ";
934 (*s)<<" avg_rtt="<<avg_rtt;
938 u32 PIChecksum(core::list<PlayerInfo> &l)
940 core::list<PlayerInfo>::Iterator i;
943 for(i=l.begin(); i!=l.end(); i++)
945 checksum += a * (i->id+1);
946 checksum ^= 0x435aafcd;
957 ModSpec(const std::string &name_="", const std::string path_=""):
963 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
965 core::list<ModSpec> mods;
966 for(core::list<std::string>::Iterator i = modspaths.begin();
967 i != modspaths.end(); i++){
968 std::string modspath = *i;
969 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
970 for(u32 j=0; j<dirlist.size(); j++){
973 std::string modname = dirlist[j].name;
974 std::string modpath = modspath + DIR_DELIM + modname;
975 mods.push_back(ModSpec(modname, modpath));
986 std::string mapsavedir,
987 std::string configpath
990 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
991 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
992 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
994 m_toolmgr(createToolDefManager()),
995 m_nodedef(createNodeDefManager()),
996 m_craftdef(createCraftDefManager()),
998 m_emergethread(this),
1000 m_time_of_day_send_timer(0),
1002 m_mapsavedir(mapsavedir),
1003 m_configpath(configpath),
1004 m_shutdown_requested(false),
1005 m_ignore_map_edit_events(false),
1006 m_ignore_map_edit_events_peer_id(0)
1008 m_liquid_transform_timer = 0.0;
1009 m_print_info_timer = 0.0;
1010 m_objectdata_timer = 0.0;
1011 m_emergethread_trigger_timer = 0.0;
1012 m_savemap_timer = 0.0;
1016 m_step_dtime_mutex.Init();
1019 JMutexAutoLock envlock(m_env_mutex);
1020 JMutexAutoLock conlock(m_con_mutex);
1022 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1024 // Initialize default node definitions
1025 content_mapnode_init(m_nodedef);
1027 // Path to builtin.lua
1028 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1029 // Add default global mod path
1030 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1032 // Initialize scripting
1034 infostream<<"Server: Initializing scripting"<<std::endl;
1035 m_lua = script_init();
1038 scriptapi_export(m_lua, this);
1039 // Load and run builtin.lua
1040 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1042 bool success = script_load(m_lua, builtinpath.c_str());
1044 errorstream<<"Server: Failed to load and run "
1045 <<builtinpath<<std::endl;
1048 // Load and run "mod" scripts
1049 core::list<ModSpec> mods = getMods(m_modspaths);
1050 for(core::list<ModSpec>::Iterator i = mods.begin();
1051 i != mods.end(); i++){
1053 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1054 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1055 bool success = script_load(m_lua, scriptpath.c_str());
1057 errorstream<<"Server: Failed to load and run "
1058 <<scriptpath<<std::endl;
1063 // Initialize Environment
1065 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1067 // Give environment reference to scripting api
1068 scriptapi_add_environment(m_lua, m_env);
1070 // Register us to receive map edit events
1071 m_env->getMap().addEventReceiver(this);
1073 // If file exists, load environment metadata
1074 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1076 infostream<<"Server: Loading environment metadata"<<std::endl;
1077 m_env->loadMeta(m_mapsavedir);
1081 infostream<<"Server: Loading players"<<std::endl;
1082 m_env->deSerializePlayers(m_mapsavedir);
1087 infostream<<"Server::~Server()"<<std::endl;
1090 Send shutdown message
1093 JMutexAutoLock conlock(m_con_mutex);
1095 std::wstring line = L"*** Server shutting down";
1098 Send the message to clients
1100 for(core::map<u16, RemoteClient*>::Iterator
1101 i = m_clients.getIterator();
1102 i.atEnd() == false; i++)
1104 // Get client and check that it is valid
1105 RemoteClient *client = i.getNode()->getValue();
1106 assert(client->peer_id == i.getNode()->getKey());
1107 if(client->serialization_version == SER_FMT_VER_INVALID)
1111 SendChatMessage(client->peer_id, line);
1113 catch(con::PeerNotFoundException &e)
1119 JMutexAutoLock envlock(m_env_mutex);
1124 infostream<<"Server: Saving players"<<std::endl;
1125 m_env->serializePlayers(m_mapsavedir);
1128 Save environment metadata
1130 infostream<<"Server: Saving environment metadata"<<std::endl;
1131 m_env->saveMeta(m_mapsavedir);
1143 JMutexAutoLock clientslock(m_con_mutex);
1145 for(core::map<u16, RemoteClient*>::Iterator
1146 i = m_clients.getIterator();
1147 i.atEnd() == false; i++)
1150 // NOTE: These are removed by env destructor
1152 u16 peer_id = i.getNode()->getKey();
1153 JMutexAutoLock envlock(m_env_mutex);
1154 m_env->removePlayer(peer_id);
1158 delete i.getNode()->getValue();
1162 // Delete Environment
1168 // Deinitialize scripting
1169 infostream<<"Server: Deinitializing scripting"<<std::endl;
1170 script_deinit(m_lua);
1173 void Server::start(unsigned short port)
1175 DSTACK(__FUNCTION_NAME);
1176 // Stop thread if already running
1179 // Initialize connection
1180 m_con.SetTimeoutMs(30);
1184 m_thread.setRun(true);
1187 infostream<<"Server: Started on port "<<port<<std::endl;
1192 DSTACK(__FUNCTION_NAME);
1194 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1196 // Stop threads (set run=false first so both start stopping)
1197 m_thread.setRun(false);
1198 m_emergethread.setRun(false);
1200 m_emergethread.stop();
1202 infostream<<"Server: Threads stopped"<<std::endl;
1205 void Server::step(float dtime)
1207 DSTACK(__FUNCTION_NAME);
1212 JMutexAutoLock lock(m_step_dtime_mutex);
1213 m_step_dtime += dtime;
1217 void Server::AsyncRunStep()
1219 DSTACK(__FUNCTION_NAME);
1221 g_profiler->add("Server::AsyncRunStep (num)", 1);
1225 JMutexAutoLock lock1(m_step_dtime_mutex);
1226 dtime = m_step_dtime;
1230 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1231 // Send blocks to clients
1238 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1240 //infostream<<"Server steps "<<dtime<<std::endl;
1241 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1244 JMutexAutoLock lock1(m_step_dtime_mutex);
1245 m_step_dtime -= dtime;
1252 m_uptime.set(m_uptime.get() + dtime);
1256 // Process connection's timeouts
1257 JMutexAutoLock lock2(m_con_mutex);
1258 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1259 m_con.RunTimeouts(dtime);
1263 // This has to be called so that the client list gets synced
1264 // with the peer list of the connection
1265 handlePeerChanges();
1269 Update m_time_of_day and overall game time
1272 JMutexAutoLock envlock(m_env_mutex);
1274 m_time_counter += dtime;
1275 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1276 u32 units = (u32)(m_time_counter*speed);
1277 m_time_counter -= (f32)units / speed;
1279 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1281 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1284 Send to clients at constant intervals
1287 m_time_of_day_send_timer -= dtime;
1288 if(m_time_of_day_send_timer < 0.0)
1290 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1292 //JMutexAutoLock envlock(m_env_mutex);
1293 JMutexAutoLock conlock(m_con_mutex);
1295 for(core::map<u16, RemoteClient*>::Iterator
1296 i = m_clients.getIterator();
1297 i.atEnd() == false; i++)
1299 RemoteClient *client = i.getNode()->getValue();
1300 //Player *player = m_env->getPlayer(client->peer_id);
1302 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1303 m_env->getTimeOfDay());
1305 m_con.Send(client->peer_id, 0, data, true);
1311 JMutexAutoLock lock(m_env_mutex);
1313 ScopeProfiler sp(g_profiler, "SEnv step");
1314 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1318 const float map_timer_and_unload_dtime = 2.92;
1319 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1321 JMutexAutoLock lock(m_env_mutex);
1322 // Run Map's timers and unload unused data
1323 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1324 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1325 g_settings->getFloat("server_unload_unused_data_timeout"));
1335 m_liquid_transform_timer += dtime;
1336 if(m_liquid_transform_timer >= 1.00)
1338 m_liquid_transform_timer -= 1.00;
1340 JMutexAutoLock lock(m_env_mutex);
1342 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1344 core::map<v3s16, MapBlock*> modified_blocks;
1345 m_env->getMap().transformLiquids(modified_blocks);
1350 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1351 ServerMap &map = ((ServerMap&)m_env->getMap());
1352 map.updateLighting(modified_blocks, lighting_modified_blocks);
1354 // Add blocks modified by lighting to modified_blocks
1355 for(core::map<v3s16, MapBlock*>::Iterator
1356 i = lighting_modified_blocks.getIterator();
1357 i.atEnd() == false; i++)
1359 MapBlock *block = i.getNode()->getValue();
1360 modified_blocks.insert(block->getPos(), block);
1364 Set the modified blocks unsent for all the clients
1367 JMutexAutoLock lock2(m_con_mutex);
1369 for(core::map<u16, RemoteClient*>::Iterator
1370 i = m_clients.getIterator();
1371 i.atEnd() == false; i++)
1373 RemoteClient *client = i.getNode()->getValue();
1375 if(modified_blocks.size() > 0)
1377 // Remove block from sent history
1378 client->SetBlocksNotSent(modified_blocks);
1383 // Periodically print some info
1385 float &counter = m_print_info_timer;
1391 JMutexAutoLock lock2(m_con_mutex);
1393 if(m_clients.size() != 0)
1394 infostream<<"Players:"<<std::endl;
1395 for(core::map<u16, RemoteClient*>::Iterator
1396 i = m_clients.getIterator();
1397 i.atEnd() == false; i++)
1399 //u16 peer_id = i.getNode()->getKey();
1400 RemoteClient *client = i.getNode()->getValue();
1401 Player *player = m_env->getPlayer(client->peer_id);
1404 infostream<<"* "<<player->getName()<<"\t";
1405 client->PrintInfo(infostream);
1410 //if(g_settings->getBool("enable_experimental"))
1414 Check added and deleted active objects
1417 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1418 JMutexAutoLock envlock(m_env_mutex);
1419 JMutexAutoLock conlock(m_con_mutex);
1421 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1423 // Radius inside which objects are active
1424 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1425 radius *= MAP_BLOCKSIZE;
1427 for(core::map<u16, RemoteClient*>::Iterator
1428 i = m_clients.getIterator();
1429 i.atEnd() == false; i++)
1431 RemoteClient *client = i.getNode()->getValue();
1432 Player *player = m_env->getPlayer(client->peer_id);
1435 // This can happen if the client timeouts somehow
1436 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1438 <<" has no associated player"<<std::endl;*/
1441 v3s16 pos = floatToInt(player->getPosition(), BS);
1443 core::map<u16, bool> removed_objects;
1444 core::map<u16, bool> added_objects;
1445 m_env->getRemovedActiveObjects(pos, radius,
1446 client->m_known_objects, removed_objects);
1447 m_env->getAddedActiveObjects(pos, radius,
1448 client->m_known_objects, added_objects);
1450 // Ignore if nothing happened
1451 if(removed_objects.size() == 0 && added_objects.size() == 0)
1453 //infostream<<"active objects: none changed"<<std::endl;
1457 std::string data_buffer;
1461 // Handle removed objects
1462 writeU16((u8*)buf, removed_objects.size());
1463 data_buffer.append(buf, 2);
1464 for(core::map<u16, bool>::Iterator
1465 i = removed_objects.getIterator();
1466 i.atEnd()==false; i++)
1469 u16 id = i.getNode()->getKey();
1470 ServerActiveObject* obj = m_env->getActiveObject(id);
1472 // Add to data buffer for sending
1473 writeU16((u8*)buf, i.getNode()->getKey());
1474 data_buffer.append(buf, 2);
1476 // Remove from known objects
1477 client->m_known_objects.remove(i.getNode()->getKey());
1479 if(obj && obj->m_known_by_count > 0)
1480 obj->m_known_by_count--;
1483 // Handle added objects
1484 writeU16((u8*)buf, added_objects.size());
1485 data_buffer.append(buf, 2);
1486 for(core::map<u16, bool>::Iterator
1487 i = added_objects.getIterator();
1488 i.atEnd()==false; i++)
1491 u16 id = i.getNode()->getKey();
1492 ServerActiveObject* obj = m_env->getActiveObject(id);
1495 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1497 infostream<<"WARNING: "<<__FUNCTION_NAME
1498 <<": NULL object"<<std::endl;
1500 type = obj->getType();
1502 // Add to data buffer for sending
1503 writeU16((u8*)buf, id);
1504 data_buffer.append(buf, 2);
1505 writeU8((u8*)buf, type);
1506 data_buffer.append(buf, 1);
1509 data_buffer.append(serializeLongString(
1510 obj->getClientInitializationData()));
1512 data_buffer.append(serializeLongString(""));
1514 // Add to known objects
1515 client->m_known_objects.insert(i.getNode()->getKey(), false);
1518 obj->m_known_by_count++;
1522 SharedBuffer<u8> reply(2 + data_buffer.size());
1523 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1524 memcpy((char*)&reply[2], data_buffer.c_str(),
1525 data_buffer.size());
1527 m_con.Send(client->peer_id, 0, reply, true);
1529 infostream<<"Server: Sent object remove/add: "
1530 <<removed_objects.size()<<" removed, "
1531 <<added_objects.size()<<" added, "
1532 <<"packet size is "<<reply.getSize()<<std::endl;
1537 Collect a list of all the objects known by the clients
1538 and report it back to the environment.
1541 core::map<u16, bool> all_known_objects;
1543 for(core::map<u16, RemoteClient*>::Iterator
1544 i = m_clients.getIterator();
1545 i.atEnd() == false; i++)
1547 RemoteClient *client = i.getNode()->getValue();
1548 // Go through all known objects of client
1549 for(core::map<u16, bool>::Iterator
1550 i = client->m_known_objects.getIterator();
1551 i.atEnd()==false; i++)
1553 u16 id = i.getNode()->getKey();
1554 all_known_objects[id] = true;
1558 m_env->setKnownActiveObjects(whatever);
1564 Send object messages
1567 JMutexAutoLock envlock(m_env_mutex);
1568 JMutexAutoLock conlock(m_con_mutex);
1570 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1573 // Value = data sent by object
1574 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1576 // Get active object messages from environment
1579 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1583 core::list<ActiveObjectMessage>* message_list = NULL;
1584 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1585 n = buffered_messages.find(aom.id);
1588 message_list = new core::list<ActiveObjectMessage>;
1589 buffered_messages.insert(aom.id, message_list);
1593 message_list = n->getValue();
1595 message_list->push_back(aom);
1598 // Route data to every client
1599 for(core::map<u16, RemoteClient*>::Iterator
1600 i = m_clients.getIterator();
1601 i.atEnd()==false; i++)
1603 RemoteClient *client = i.getNode()->getValue();
1604 std::string reliable_data;
1605 std::string unreliable_data;
1606 // Go through all objects in message buffer
1607 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1608 j = buffered_messages.getIterator();
1609 j.atEnd()==false; j++)
1611 // If object is not known by client, skip it
1612 u16 id = j.getNode()->getKey();
1613 if(client->m_known_objects.find(id) == NULL)
1615 // Get message list of object
1616 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1617 // Go through every message
1618 for(core::list<ActiveObjectMessage>::Iterator
1619 k = list->begin(); k != list->end(); k++)
1621 // Compose the full new data with header
1622 ActiveObjectMessage aom = *k;
1623 std::string new_data;
1626 writeU16((u8*)&buf[0], aom.id);
1627 new_data.append(buf, 2);
1629 new_data += serializeString(aom.datastring);
1630 // Add data to buffer
1632 reliable_data += new_data;
1634 unreliable_data += new_data;
1638 reliable_data and unreliable_data are now ready.
1641 if(reliable_data.size() > 0)
1643 SharedBuffer<u8> reply(2 + reliable_data.size());
1644 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1645 memcpy((char*)&reply[2], reliable_data.c_str(),
1646 reliable_data.size());
1648 m_con.Send(client->peer_id, 0, reply, true);
1650 if(unreliable_data.size() > 0)
1652 SharedBuffer<u8> reply(2 + unreliable_data.size());
1653 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1654 memcpy((char*)&reply[2], unreliable_data.c_str(),
1655 unreliable_data.size());
1656 // Send as unreliable
1657 m_con.Send(client->peer_id, 0, reply, false);
1660 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1662 infostream<<"Server: Size of object message data: "
1663 <<"reliable: "<<reliable_data.size()
1664 <<", unreliable: "<<unreliable_data.size()
1669 // Clear buffered_messages
1670 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1671 i = buffered_messages.getIterator();
1672 i.atEnd()==false; i++)
1674 delete i.getNode()->getValue();
1678 } // enable_experimental
1681 Send queued-for-sending map edit events.
1684 // Don't send too many at a time
1687 // Single change sending is disabled if queue size is not small
1688 bool disable_single_change_sending = false;
1689 if(m_unsent_map_edit_queue.size() >= 4)
1690 disable_single_change_sending = true;
1692 bool got_any_events = false;
1694 // We'll log the amount of each
1697 while(m_unsent_map_edit_queue.size() != 0)
1699 got_any_events = true;
1701 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1703 // Players far away from the change are stored here.
1704 // Instead of sending the changes, MapBlocks are set not sent
1706 core::list<u16> far_players;
1708 if(event->type == MEET_ADDNODE)
1710 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1711 prof.add("MEET_ADDNODE", 1);
1712 if(disable_single_change_sending)
1713 sendAddNode(event->p, event->n, event->already_known_by_peer,
1716 sendAddNode(event->p, event->n, event->already_known_by_peer,
1719 else if(event->type == MEET_REMOVENODE)
1721 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1722 prof.add("MEET_REMOVENODE", 1);
1723 if(disable_single_change_sending)
1724 sendRemoveNode(event->p, event->already_known_by_peer,
1727 sendRemoveNode(event->p, event->already_known_by_peer,
1730 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1732 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1733 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1734 setBlockNotSent(event->p);
1736 else if(event->type == MEET_OTHER)
1738 infostream<<"Server: MEET_OTHER"<<std::endl;
1739 prof.add("MEET_OTHER", 1);
1740 for(core::map<v3s16, bool>::Iterator
1741 i = event->modified_blocks.getIterator();
1742 i.atEnd()==false; i++)
1744 v3s16 p = i.getNode()->getKey();
1750 prof.add("unknown", 1);
1751 infostream<<"WARNING: Server: Unknown MapEditEvent "
1752 <<((u32)event->type)<<std::endl;
1756 Set blocks not sent to far players
1758 if(far_players.size() > 0)
1760 // Convert list format to that wanted by SetBlocksNotSent
1761 core::map<v3s16, MapBlock*> modified_blocks2;
1762 for(core::map<v3s16, bool>::Iterator
1763 i = event->modified_blocks.getIterator();
1764 i.atEnd()==false; i++)
1766 v3s16 p = i.getNode()->getKey();
1767 modified_blocks2.insert(p,
1768 m_env->getMap().getBlockNoCreateNoEx(p));
1770 // Set blocks not sent
1771 for(core::list<u16>::Iterator
1772 i = far_players.begin();
1773 i != far_players.end(); i++)
1776 RemoteClient *client = getClient(peer_id);
1779 client->SetBlocksNotSent(modified_blocks2);
1785 /*// Don't send too many at a time
1787 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1793 infostream<<"Server: MapEditEvents:"<<std::endl;
1794 prof.print(infostream);
1800 Send object positions
1803 float &counter = m_objectdata_timer;
1805 if(counter >= g_settings->getFloat("objectdata_interval"))
1807 JMutexAutoLock lock1(m_env_mutex);
1808 JMutexAutoLock lock2(m_con_mutex);
1810 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1812 SendObjectData(counter);
1819 Trigger emergethread (it somehow gets to a non-triggered but
1820 bysy state sometimes)
1823 float &counter = m_emergethread_trigger_timer;
1829 m_emergethread.trigger();
1833 // Save map, players and auth stuff
1835 float &counter = m_savemap_timer;
1837 if(counter >= g_settings->getFloat("server_map_save_interval"))
1841 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1844 if(m_authmanager.isModified())
1845 m_authmanager.save();
1848 if(m_banmanager.isModified())
1849 m_banmanager.save();
1852 JMutexAutoLock lock(m_env_mutex);
1854 /*// Unload unused data (delete from memory)
1855 m_env->getMap().unloadUnusedData(
1856 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1858 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1859 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1862 // Save only changed parts
1863 m_env->getMap().save(true);
1865 /*if(deleted_count > 0)
1867 infostream<<"Server: Unloaded "<<deleted_count
1868 <<" blocks from memory"<<std::endl;
1872 m_env->serializePlayers(m_mapsavedir);
1874 // Save environment metadata
1875 m_env->saveMeta(m_mapsavedir);
1880 void Server::Receive()
1882 DSTACK(__FUNCTION_NAME);
1883 SharedBuffer<u8> data;
1888 JMutexAutoLock conlock(m_con_mutex);
1889 datasize = m_con.Receive(peer_id, data);
1892 // This has to be called so that the client list gets synced
1893 // with the peer list of the connection
1894 handlePeerChanges();
1896 ProcessData(*data, datasize, peer_id);
1898 catch(con::InvalidIncomingDataException &e)
1900 infostream<<"Server::Receive(): "
1901 "InvalidIncomingDataException: what()="
1902 <<e.what()<<std::endl;
1904 catch(con::PeerNotFoundException &e)
1906 //NOTE: This is not needed anymore
1908 // The peer has been disconnected.
1909 // Find the associated player and remove it.
1911 /*JMutexAutoLock envlock(m_env_mutex);
1913 infostream<<"ServerThread: peer_id="<<peer_id
1914 <<" has apparently closed connection. "
1915 <<"Removing player."<<std::endl;
1917 m_env->removePlayer(peer_id);*/
1921 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1923 DSTACK(__FUNCTION_NAME);
1924 // Environment is locked first.
1925 JMutexAutoLock envlock(m_env_mutex);
1926 JMutexAutoLock conlock(m_con_mutex);
1929 Address address = m_con.GetPeerAddress(peer_id);
1931 // drop player if is ip is banned
1932 if(m_banmanager.isIpBanned(address.serializeString())){
1933 SendAccessDenied(m_con, peer_id,
1934 L"Your ip is banned. Banned name was "
1935 +narrow_to_wide(m_banmanager.getBanName(
1936 address.serializeString())));
1937 m_con.DeletePeer(peer_id);
1941 catch(con::PeerNotFoundException &e)
1943 infostream<<"Server::ProcessData(): Cancelling: peer "
1944 <<peer_id<<" not found"<<std::endl;
1948 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1956 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1958 if(command == TOSERVER_INIT)
1960 // [0] u16 TOSERVER_INIT
1961 // [2] u8 SER_FMT_VER_HIGHEST
1962 // [3] u8[20] player_name
1963 // [23] u8[28] password <--- can be sent without this, from old versions
1965 if(datasize < 2+1+PLAYERNAME_SIZE)
1968 infostream<<"Server: Got TOSERVER_INIT from "
1969 <<peer_id<<std::endl;
1971 // First byte after command is maximum supported
1972 // serialization version
1973 u8 client_max = data[2];
1974 u8 our_max = SER_FMT_VER_HIGHEST;
1975 // Use the highest version supported by both
1976 u8 deployed = core::min_(client_max, our_max);
1977 // If it's lower than the lowest supported, give up.
1978 if(deployed < SER_FMT_VER_LOWEST)
1979 deployed = SER_FMT_VER_INVALID;
1981 //peer->serialization_version = deployed;
1982 getClient(peer_id)->pending_serialization_version = deployed;
1984 if(deployed == SER_FMT_VER_INVALID)
1986 infostream<<"Server: Cannot negotiate "
1987 "serialization version with peer "
1988 <<peer_id<<std::endl;
1989 SendAccessDenied(m_con, peer_id,
1990 L"Your client is too old (map format)");
1995 Read and check network protocol version
1998 u16 net_proto_version = 0;
1999 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2001 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2004 getClient(peer_id)->net_proto_version = net_proto_version;
2006 if(net_proto_version == 0)
2008 SendAccessDenied(m_con, peer_id,
2009 L"Your client is too old. Please upgrade.");
2013 /* Uhh... this should actually be a warning but let's do it like this */
2014 if(g_settings->getBool("strict_protocol_version_checking"))
2016 if(net_proto_version < PROTOCOL_VERSION)
2018 SendAccessDenied(m_con, peer_id,
2019 L"Your client is too old. Please upgrade.");
2029 char playername[PLAYERNAME_SIZE];
2030 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2032 playername[i] = data[3+i];
2034 playername[PLAYERNAME_SIZE-1] = 0;
2036 if(playername[0]=='\0')
2038 infostream<<"Server: Player has empty name"<<std::endl;
2039 SendAccessDenied(m_con, peer_id,
2044 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2046 infostream<<"Server: Player has invalid name"<<std::endl;
2047 SendAccessDenied(m_con, peer_id,
2048 L"Name contains unallowed characters");
2053 char password[PASSWORD_SIZE];
2054 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2056 // old version - assume blank password
2061 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2063 password[i] = data[23+i];
2065 password[PASSWORD_SIZE-1] = 0;
2068 std::string checkpwd;
2069 if(m_authmanager.exists(playername))
2071 checkpwd = m_authmanager.getPassword(playername);
2075 checkpwd = g_settings->get("default_password");
2078 /*infostream<<"Server: Client gave password '"<<password
2079 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2081 if(password != checkpwd && m_authmanager.exists(playername))
2083 infostream<<"Server: peer_id="<<peer_id
2084 <<": supplied invalid password for "
2085 <<playername<<std::endl;
2086 SendAccessDenied(m_con, peer_id, L"Invalid password");
2090 // Add player to auth manager
2091 if(m_authmanager.exists(playername) == false)
2093 infostream<<"Server: adding player "<<playername
2094 <<" to auth manager"<<std::endl;
2095 m_authmanager.add(playername);
2096 m_authmanager.setPassword(playername, checkpwd);
2097 m_authmanager.setPrivs(playername,
2098 stringToPrivs(g_settings->get("default_privs")));
2099 m_authmanager.save();
2102 // Enforce user limit.
2103 // Don't enforce for users that have some admin right
2104 if(m_clients.size() >= g_settings->getU16("max_users") &&
2105 (m_authmanager.getPrivs(playername)
2106 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2107 playername != g_settings->get("name"))
2109 SendAccessDenied(m_con, peer_id, L"Too many users.");
2114 Player *player = emergePlayer(playername, password, peer_id);
2116 // If failed, cancel
2119 infostream<<"Server: peer_id="<<peer_id
2120 <<": failed to emerge player"<<std::endl;
2125 Answer with a TOCLIENT_INIT
2128 SharedBuffer<u8> reply(2+1+6+8);
2129 writeU16(&reply[0], TOCLIENT_INIT);
2130 writeU8(&reply[2], deployed);
2131 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2132 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2135 m_con.Send(peer_id, 0, reply, true);
2139 Send complete position information
2141 SendMovePlayer(player);
2146 if(command == TOSERVER_INIT2)
2148 infostream<<"Server: Got TOSERVER_INIT2 from "
2149 <<peer_id<<std::endl;
2152 getClient(peer_id)->serialization_version
2153 = getClient(peer_id)->pending_serialization_version;
2156 Send some initialization data
2159 // Send tool definitions
2160 SendToolDef(m_con, peer_id, m_toolmgr);
2162 // Send node definitions
2163 SendNodeDef(m_con, peer_id, m_nodedef);
2166 SendTextures(peer_id);
2168 // Send player info to all players
2171 // Send inventory to player
2172 UpdateCrafting(peer_id);
2173 SendInventory(peer_id);
2175 // Send player items to all players
2178 Player *player = m_env->getPlayer(peer_id);
2181 SendPlayerHP(player);
2185 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2186 m_env->getTimeOfDay());
2187 m_con.Send(peer_id, 0, data, true);
2190 // Send information about server to player in chat
2191 SendChatMessage(peer_id, getStatusString());
2193 // Send information about joining in chat
2195 std::wstring name = L"unknown";
2196 Player *player = m_env->getPlayer(peer_id);
2198 name = narrow_to_wide(player->getName());
2200 std::wstring message;
2203 message += L" joined game";
2204 BroadcastChatMessage(message);
2207 // Warnings about protocol version can be issued here
2208 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2210 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2214 Check HP, respawn if necessary
2216 HandlePlayerHP(player, 0);
2222 std::ostringstream os(std::ios_base::binary);
2223 for(core::map<u16, RemoteClient*>::Iterator
2224 i = m_clients.getIterator();
2225 i.atEnd() == false; i++)
2227 RemoteClient *client = i.getNode()->getValue();
2228 assert(client->peer_id == i.getNode()->getKey());
2229 if(client->serialization_version == SER_FMT_VER_INVALID)
2232 Player *player = m_env->getPlayer(client->peer_id);
2235 // Get name of player
2236 os<<player->getName()<<" ";
2239 actionstream<<player->getName()<<" joins game. List of players: "
2240 <<os.str()<<std::endl;
2246 if(peer_ser_ver == SER_FMT_VER_INVALID)
2248 infostream<<"Server::ProcessData(): Cancelling: Peer"
2249 " serialization format invalid or not initialized."
2250 " Skipping incoming command="<<command<<std::endl;
2254 Player *player = m_env->getPlayer(peer_id);
2257 infostream<<"Server::ProcessData(): Cancelling: "
2258 "No player for peer_id="<<peer_id
2262 if(command == TOSERVER_PLAYERPOS)
2264 if(datasize < 2+12+12+4+4)
2268 v3s32 ps = readV3S32(&data[start+2]);
2269 v3s32 ss = readV3S32(&data[start+2+12]);
2270 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2271 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2272 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2273 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2274 pitch = wrapDegrees(pitch);
2275 yaw = wrapDegrees(yaw);
2277 player->setPosition(position);
2278 player->setSpeed(speed);
2279 player->setPitch(pitch);
2280 player->setYaw(yaw);
2282 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2283 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2284 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2286 else if(command == TOSERVER_GOTBLOCKS)
2299 u16 count = data[2];
2300 for(u16 i=0; i<count; i++)
2302 if((s16)datasize < 2+1+(i+1)*6)
2303 throw con::InvalidIncomingDataException
2304 ("GOTBLOCKS length is too short");
2305 v3s16 p = readV3S16(&data[2+1+i*6]);
2306 /*infostream<<"Server: GOTBLOCKS ("
2307 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2308 RemoteClient *client = getClient(peer_id);
2309 client->GotBlock(p);
2312 else if(command == TOSERVER_DELETEDBLOCKS)
2325 u16 count = data[2];
2326 for(u16 i=0; i<count; i++)
2328 if((s16)datasize < 2+1+(i+1)*6)
2329 throw con::InvalidIncomingDataException
2330 ("DELETEDBLOCKS length is too short");
2331 v3s16 p = readV3S16(&data[2+1+i*6]);
2332 /*infostream<<"Server: DELETEDBLOCKS ("
2333 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2334 RemoteClient *client = getClient(peer_id);
2335 client->SetBlockNotSent(p);
2338 else if(command == TOSERVER_CLICK_OBJECT)
2340 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2343 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2348 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2354 [2] u8 button (0=left, 1=right)
2358 u8 button = readU8(&data[2]);
2359 u16 id = readS16(&data[3]);
2360 u16 item_i = readU16(&data[5]);
2362 ServerActiveObject *obj = m_env->getActiveObject(id);
2366 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2371 // Skip if object has been removed
2375 //TODO: Check that object is reasonably close
2377 // Get ServerRemotePlayer
2378 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2380 // Update wielded item
2381 srp->wieldItem(item_i);
2383 // Left click, pick/punch
2386 actionstream<<player->getName()<<" punches object "
2387 <<obj->getId()<<std::endl;
2394 Try creating inventory item
2396 InventoryItem *item = obj->createPickedUpItem();
2400 InventoryList *ilist = player->inventory.getList("main");
2403 actionstream<<player->getName()<<" picked up "
2404 <<item->getName()<<std::endl;
2405 if(g_settings->getBool("creative_mode") == false)
2407 // Skip if inventory has no free space
2408 if(ilist->roomForItem(item) == false)
2410 infostream<<"Player inventory has no free space"<<std::endl;
2414 // Add to inventory and send inventory
2415 ilist->addItem(item);
2416 UpdateCrafting(player->peer_id);
2417 SendInventory(player->peer_id);
2420 // Remove object from environment
2421 obj->m_removed = true;
2427 Item cannot be picked up. Punch it instead.
2430 actionstream<<player->getName()<<" punches object "
2431 <<obj->getId()<<std::endl;
2433 ToolItem *titem = NULL;
2434 std::string toolname = "";
2436 InventoryList *mlist = player->inventory.getList("main");
2439 InventoryItem *item = mlist->getItem(item_i);
2440 if(item && (std::string)item->getName() == "ToolItem")
2442 titem = (ToolItem*)item;
2443 toolname = titem->getToolName();
2447 v3f playerpos = player->getPosition();
2448 v3f objpos = obj->getBasePosition();
2449 v3f dir = (objpos - playerpos).normalize();
2451 u16 wear = obj->punch(toolname, dir, player->getName());
2455 bool weared_out = titem->addWear(wear);
2457 mlist->deleteItem(item_i);
2458 SendInventory(player->peer_id);
2463 // Right click, do something with object
2466 actionstream<<player->getName()<<" right clicks object "
2467 <<obj->getId()<<std::endl;
2470 obj->rightClick(srp);
2474 Update player state to client
2476 SendPlayerHP(player);
2477 UpdateCrafting(player->peer_id);
2478 SendInventory(player->peer_id);
2480 else if(command == TOSERVER_GROUND_ACTION)
2488 [3] v3s16 nodepos_undersurface
2489 [9] v3s16 nodepos_abovesurface
2494 2: stop digging (all parameters ignored)
2495 3: digging completed
2497 u8 action = readU8(&data[2]);
2499 p_under.X = readS16(&data[3]);
2500 p_under.Y = readS16(&data[5]);
2501 p_under.Z = readS16(&data[7]);
2503 p_over.X = readS16(&data[9]);
2504 p_over.Y = readS16(&data[11]);
2505 p_over.Z = readS16(&data[13]);
2506 u16 item_i = readU16(&data[15]);
2508 //TODO: Check that target is reasonably close
2516 NOTE: This can be used in the future to check if
2517 somebody is cheating, by checking the timing.
2519 bool cannot_punch_node = false;
2521 MapNode n(CONTENT_IGNORE);
2525 n = m_env->getMap().getNode(p_under);
2527 catch(InvalidPositionException &e)
2529 infostream<<"Server: Not punching: Node not found."
2530 <<" Adding block to emerge queue."
2532 m_emerge_queue.addBlock(peer_id,
2533 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2534 cannot_punch_node = true;
2537 if(cannot_punch_node)
2543 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2544 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2551 else if(action == 2)
2554 RemoteClient *client = getClient(peer_id);
2555 JMutexAutoLock digmutex(client->m_dig_mutex);
2556 client->m_dig_tool_item = -1;
2561 3: Digging completed
2563 else if(action == 3)
2565 // Mandatory parameter; actually used for nothing
2566 core::map<v3s16, MapBlock*> modified_blocks;
2568 content_t material = CONTENT_IGNORE;
2569 u8 mineral = MINERAL_NONE;
2571 bool cannot_remove_node = false;
2573 MapNode n(CONTENT_IGNORE);
2576 n = m_env->getMap().getNode(p_under);
2578 mineral = n.getMineral(m_nodedef);
2579 // Get material at position
2580 material = n.getContent();
2581 // If not yet cancelled
2582 if(cannot_remove_node == false)
2584 // If it's not diggable, do nothing
2585 if(m_nodedef->get(material).diggable == false)
2587 infostream<<"Server: Not finishing digging: "
2588 <<"Node not diggable"
2590 cannot_remove_node = true;
2593 // If not yet cancelled
2594 if(cannot_remove_node == false)
2596 // Get node metadata
2597 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2598 if(meta && meta->nodeRemovalDisabled() == true)
2600 infostream<<"Server: Not finishing digging: "
2601 <<"Node metadata disables removal"
2603 cannot_remove_node = true;
2607 catch(InvalidPositionException &e)
2609 infostream<<"Server: Not finishing digging: Node not found."
2610 <<" Adding block to emerge queue."
2612 m_emerge_queue.addBlock(peer_id,
2613 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2614 cannot_remove_node = true;
2617 // Make sure the player is allowed to do it
2618 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2620 infostream<<"Player "<<player->getName()<<" cannot remove node"
2621 <<" because privileges are "<<getPlayerPrivs(player)
2623 cannot_remove_node = true;
2627 If node can't be removed, set block to be re-sent to
2630 if(cannot_remove_node)
2632 infostream<<"Server: Not finishing digging."<<std::endl;
2634 // Client probably has wrong data.
2635 // Set block not sent, so that client will get
2637 infostream<<"Client "<<peer_id<<" tried to dig "
2638 <<"node; but node cannot be removed."
2639 <<" setting MapBlock not sent."<<std::endl;
2640 RemoteClient *client = getClient(peer_id);
2641 v3s16 blockpos = getNodeBlockPos(p_under);
2642 client->SetBlockNotSent(blockpos);
2647 actionstream<<player->getName()<<" digs "<<PP(p_under)
2648 <<", gets material "<<(int)material<<", mineral "
2649 <<(int)mineral<<std::endl;
2652 Send the removal to all close-by players.
2653 - If other player is close, send REMOVENODE
2654 - Otherwise set blocks not sent
2656 core::list<u16> far_players;
2657 sendRemoveNode(p_under, peer_id, &far_players, 30);
2660 Update and send inventory
2663 if(g_settings->getBool("creative_mode") == false)
2668 InventoryList *mlist = player->inventory.getList("main");
2671 InventoryItem *item = mlist->getItem(item_i);
2672 if(item && (std::string)item->getName() == "ToolItem")
2674 ToolItem *titem = (ToolItem*)item;
2675 std::string toolname = titem->getToolName();
2677 // Get digging properties for material and tool
2678 ToolDiggingProperties tp =
2679 m_toolmgr->getDiggingProperties(toolname);
2680 DiggingProperties prop =
2681 getDiggingProperties(material, &tp, m_nodedef);
2683 if(prop.diggable == false)
2685 infostream<<"Server: WARNING: Player digged"
2686 <<" with impossible material + tool"
2687 <<" combination"<<std::endl;
2690 bool weared_out = titem->addWear(prop.wear);
2694 mlist->deleteItem(item_i);
2700 Add dug item to inventory
2703 InventoryItem *item = NULL;
2705 if(mineral != MINERAL_NONE)
2706 item = getDiggedMineralItem(mineral, this);
2711 const std::string &dug_s = m_nodedef->get(material).dug_item;
2714 std::istringstream is(dug_s, std::ios::binary);
2715 item = InventoryItem::deSerialize(is, this);
2721 // Add a item to inventory
2722 player->inventory.addItem("main", item);
2725 UpdateCrafting(player->peer_id);
2726 SendInventory(player->peer_id);
2731 if(mineral != MINERAL_NONE)
2732 item = getDiggedMineralItem(mineral, this);
2737 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2738 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2739 if(extra_dug_s != "" && extra_rarity != 0
2740 && myrand() % extra_rarity == 0)
2742 std::istringstream is(extra_dug_s, std::ios::binary);
2743 item = InventoryItem::deSerialize(is, this);
2749 // Add a item to inventory
2750 player->inventory.addItem("main", item);
2753 UpdateCrafting(player->peer_id);
2754 SendInventory(player->peer_id);
2760 (this takes some time so it is done after the quick stuff)
2763 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2765 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2768 Set blocks not sent to far players
2770 for(core::list<u16>::Iterator
2771 i = far_players.begin();
2772 i != far_players.end(); i++)
2775 RemoteClient *client = getClient(peer_id);
2778 client->SetBlocksNotSent(modified_blocks);
2784 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2785 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2791 else if(action == 1)
2794 InventoryList *ilist = player->inventory.getList("main");
2799 InventoryItem *item = ilist->getItem(item_i);
2801 // If there is no item, it is not possible to add it anywhere
2806 Handle material items
2808 if(std::string("MaterialItem") == item->getName())
2811 // Don't add a node if this is not a free space
2812 MapNode n2 = m_env->getMap().getNode(p_over);
2813 bool no_enough_privs =
2814 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2816 infostream<<"Player "<<player->getName()<<" cannot add node"
2817 <<" because privileges are "<<getPlayerPrivs(player)
2820 if(m_nodedef->get(n2).buildable_to == false
2823 // Client probably has wrong data.
2824 // Set block not sent, so that client will get
2826 infostream<<"Client "<<peer_id<<" tried to place"
2827 <<" node in invalid position; setting"
2828 <<" MapBlock not sent."<<std::endl;
2829 RemoteClient *client = getClient(peer_id);
2830 v3s16 blockpos = getNodeBlockPos(p_over);
2831 client->SetBlockNotSent(blockpos);
2835 catch(InvalidPositionException &e)
2837 infostream<<"Server: Ignoring ADDNODE: Node not found"
2838 <<" Adding block to emerge queue."
2840 m_emerge_queue.addBlock(peer_id,
2841 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2845 // Reset build time counter
2846 getClient(peer_id)->m_time_from_building = 0.0;
2849 MaterialItem *mitem = (MaterialItem*)item;
2851 n.setContent(mitem->getMaterial());
2853 actionstream<<player->getName()<<" places material "
2854 <<(int)mitem->getMaterial()
2855 <<" at "<<PP(p_under)<<std::endl;
2857 // Calculate direction for wall mounted stuff
2858 if(m_nodedef->get(n).wall_mounted)
2859 n.param2 = packDir(p_under - p_over);
2861 // Calculate the direction for furnaces and chests and stuff
2862 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2864 v3f playerpos = player->getPosition();
2865 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2866 blockpos = blockpos.normalize();
2868 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2882 Send to all close-by players
2884 core::list<u16> far_players;
2885 sendAddNode(p_over, n, 0, &far_players, 30);
2890 InventoryList *ilist = player->inventory.getList("main");
2891 if(g_settings->getBool("creative_mode") == false && ilist)
2893 // Remove from inventory and send inventory
2894 if(mitem->getCount() == 1)
2895 ilist->deleteItem(item_i);
2899 UpdateCrafting(peer_id);
2900 SendInventory(peer_id);
2906 This takes some time so it is done after the quick stuff
2908 core::map<v3s16, MapBlock*> modified_blocks;
2910 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2912 std::string p_name = std::string(player->getName());
2913 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2916 Set blocks not sent to far players
2918 for(core::list<u16>::Iterator
2919 i = far_players.begin();
2920 i != far_players.end(); i++)
2923 RemoteClient *client = getClient(peer_id);
2926 client->SetBlocksNotSent(modified_blocks);
2932 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2933 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
2936 Calculate special events
2939 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
2942 for(s16 z=-1; z<=1; z++)
2943 for(s16 y=-1; y<=1; y++)
2944 for(s16 x=-1; x<=1; x++)
2951 Place other item (not a block)
2955 v3s16 blockpos = getNodeBlockPos(p_over);
2958 Check that the block is loaded so that the item
2959 can properly be added to the static list too
2961 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2964 infostream<<"Error while placing object: "
2965 "block not found"<<std::endl;
2970 If in creative mode, item dropping is disabled unless
2971 player has build privileges
2973 if(g_settings->getBool("creative_mode") &&
2974 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2976 infostream<<"Not allowing player to drop item: "
2977 "creative mode and no build privs"<<std::endl;
2981 // Calculate a position for it
2982 v3f pos = intToFloat(p_over, BS);
2984 /*pos.Y -= BS*0.25; // let it drop a bit
2986 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2987 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2992 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2996 infostream<<"WARNING: item resulted in NULL object, "
2997 <<"not placing onto map"
3002 actionstream<<player->getName()<<" places "<<item->getName()
3003 <<" at "<<PP(p_over)<<std::endl;
3005 // Add the object to the environment
3006 m_env->addActiveObject(obj);
3008 infostream<<"Placed object"<<std::endl;
3010 if(g_settings->getBool("creative_mode") == false)
3012 // Delete the right amount of items from the slot
3013 u16 dropcount = item->getDropCount();
3015 // Delete item if all gone
3016 if(item->getCount() <= dropcount)
3018 if(item->getCount() < dropcount)
3019 infostream<<"WARNING: Server: dropped more items"
3020 <<" than the slot contains"<<std::endl;
3022 InventoryList *ilist = player->inventory.getList("main");
3024 // Remove from inventory and send inventory
3025 ilist->deleteItem(item_i);
3027 // Else decrement it
3029 item->remove(dropcount);
3032 UpdateCrafting(peer_id);
3033 SendInventory(peer_id);
3041 Catch invalid actions
3045 infostream<<"WARNING: Server: Invalid action "
3046 <<action<<std::endl;
3050 else if(command == TOSERVER_RELEASE)
3059 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3062 else if(command == TOSERVER_SIGNTEXT)
3064 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3068 else if(command == TOSERVER_SIGNNODETEXT)
3070 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3078 std::string datastring((char*)&data[2], datasize-2);
3079 std::istringstream is(datastring, std::ios_base::binary);
3082 is.read((char*)buf, 6);
3083 v3s16 p = readV3S16(buf);
3084 is.read((char*)buf, 2);
3085 u16 textlen = readU16(buf);
3087 for(u16 i=0; i<textlen; i++)
3089 is.read((char*)buf, 1);
3090 text += (char)buf[0];
3093 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3096 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3098 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3099 signmeta->setText(text);
3101 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3102 <<" at "<<PP(p)<<std::endl;
3104 v3s16 blockpos = getNodeBlockPos(p);
3105 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3108 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3112 setBlockNotSent(blockpos);
3114 else if(command == TOSERVER_INVENTORY_ACTION)
3116 /*// Ignore inventory changes if in creative mode
3117 if(g_settings->getBool("creative_mode") == true)
3119 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3123 // Strip command and create a stream
3124 std::string datastring((char*)&data[2], datasize-2);
3125 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3126 std::istringstream is(datastring, std::ios_base::binary);
3128 InventoryAction *a = InventoryAction::deSerialize(is);
3133 c.current_player = player;
3136 Handle craftresult specially if not in creative mode
3138 bool disable_action = false;
3139 if(a->getType() == IACTION_MOVE
3140 && g_settings->getBool("creative_mode") == false)
3142 IMoveAction *ma = (IMoveAction*)a;
3143 if(ma->to_inv == "current_player" &&
3144 ma->from_inv == "current_player")
3146 InventoryList *rlist = player->inventory.getList("craftresult");
3148 InventoryList *clist = player->inventory.getList("craft");
3150 InventoryList *mlist = player->inventory.getList("main");
3153 Craftresult is no longer preview if something
3156 if(ma->to_list == "craftresult"
3157 && ma->from_list != "craftresult")
3159 // If it currently is a preview, remove
3161 if(player->craftresult_is_preview)
3163 rlist->deleteItem(0);
3165 player->craftresult_is_preview = false;
3168 Crafting takes place if this condition is true.
3170 if(player->craftresult_is_preview &&
3171 ma->from_list == "craftresult")
3173 player->craftresult_is_preview = false;
3174 clist->decrementMaterials(1);
3176 /* Print out action */
3177 InventoryList *list =
3178 player->inventory.getList("craftresult");
3180 InventoryItem *item = list->getItem(0);
3181 std::string itemname = "NULL";
3183 itemname = item->getName();
3184 actionstream<<player->getName()<<" crafts "
3185 <<itemname<<std::endl;
3188 If the craftresult is placed on itself, move it to
3189 main inventory instead of doing the action
3191 if(ma->to_list == "craftresult"
3192 && ma->from_list == "craftresult")
3194 disable_action = true;
3196 InventoryItem *item1 = rlist->changeItem(0, NULL);
3197 mlist->addItem(item1);
3200 // Disallow moving items if not allowed to build
3201 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3205 // if it's a locking chest, only allow the owner or server admins to move items
3206 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3208 Strfnd fn(ma->from_inv);
3209 std::string id0 = fn.next(":");
3210 if(id0 == "nodemeta")
3213 p.X = stoi(fn.next(","));
3214 p.Y = stoi(fn.next(","));
3215 p.Z = stoi(fn.next(","));
3216 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3217 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3218 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3219 if (lcm->getOwner() != player->getName())
3224 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3226 Strfnd fn(ma->to_inv);
3227 std::string id0 = fn.next(":");
3228 if(id0 == "nodemeta")
3231 p.X = stoi(fn.next(","));
3232 p.Y = stoi(fn.next(","));
3233 p.Z = stoi(fn.next(","));
3234 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3235 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3236 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3237 if (lcm->getOwner() != player->getName())
3244 if(disable_action == false)
3246 // Feed action to player inventory
3254 UpdateCrafting(player->peer_id);
3255 SendInventory(player->peer_id);
3260 infostream<<"TOSERVER_INVENTORY_ACTION: "
3261 <<"InventoryAction::deSerialize() returned NULL"
3265 else if(command == TOSERVER_CHAT_MESSAGE)
3273 std::string datastring((char*)&data[2], datasize-2);
3274 std::istringstream is(datastring, std::ios_base::binary);
3277 is.read((char*)buf, 2);
3278 u16 len = readU16(buf);
3280 std::wstring message;
3281 for(u16 i=0; i<len; i++)
3283 is.read((char*)buf, 2);
3284 message += (wchar_t)readU16(buf);
3287 // Get player name of this client
3288 std::wstring name = narrow_to_wide(player->getName());
3290 // Line to send to players
3292 // Whether to send to the player that sent the line
3293 bool send_to_sender = false;
3294 // Whether to send to other players
3295 bool send_to_others = false;
3297 // Local player gets all privileges regardless of
3298 // what's set on their account.
3299 u64 privs = getPlayerPrivs(player);
3302 if(message[0] == L'/')
3304 size_t strip_size = 1;
3305 if (message[1] == L'#') // support old-style commans
3307 message = message.substr(strip_size);
3309 WStrfnd f1(message);
3310 f1.next(L" "); // Skip over /#whatever
3311 std::wstring paramstring = f1.next(L"");
3313 ServerCommandContext *ctx = new ServerCommandContext(
3314 str_split(message, L' '),
3321 std::wstring reply(processServerCommand(ctx));
3322 send_to_sender = ctx->flags & SEND_TO_SENDER;
3323 send_to_others = ctx->flags & SEND_TO_OTHERS;
3325 if (ctx->flags & SEND_NO_PREFIX)
3328 line += L"Server: " + reply;
3335 if(privs & PRIV_SHOUT)
3341 send_to_others = true;
3345 line += L"Server: You are not allowed to shout";
3346 send_to_sender = true;
3353 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3356 Send the message to clients
3358 for(core::map<u16, RemoteClient*>::Iterator
3359 i = m_clients.getIterator();
3360 i.atEnd() == false; i++)
3362 // Get client and check that it is valid
3363 RemoteClient *client = i.getNode()->getValue();
3364 assert(client->peer_id == i.getNode()->getKey());
3365 if(client->serialization_version == SER_FMT_VER_INVALID)
3369 bool sender_selected = (peer_id == client->peer_id);
3370 if(sender_selected == true && send_to_sender == false)
3372 if(sender_selected == false && send_to_others == false)
3375 SendChatMessage(client->peer_id, line);
3379 else if(command == TOSERVER_DAMAGE)
3381 std::string datastring((char*)&data[2], datasize-2);
3382 std::istringstream is(datastring, std::ios_base::binary);
3383 u8 damage = readU8(is);
3385 if(g_settings->getBool("enable_damage"))
3387 actionstream<<player->getName()<<" damaged by "
3388 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3391 HandlePlayerHP(player, damage);
3395 SendPlayerHP(player);
3398 else if(command == TOSERVER_PASSWORD)
3401 [0] u16 TOSERVER_PASSWORD
3402 [2] u8[28] old password
3403 [30] u8[28] new password
3406 if(datasize != 2+PASSWORD_SIZE*2)
3408 /*char password[PASSWORD_SIZE];
3409 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3410 password[i] = data[2+i];
3411 password[PASSWORD_SIZE-1] = 0;*/
3413 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3421 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3423 char c = data[2+PASSWORD_SIZE+i];
3429 infostream<<"Server: Client requests a password change from "
3430 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3432 std::string playername = player->getName();
3434 if(m_authmanager.exists(playername) == false)
3436 infostream<<"Server: playername not found in authmanager"<<std::endl;
3437 // Wrong old password supplied!!
3438 SendChatMessage(peer_id, L"playername not found in authmanager");
3442 std::string checkpwd = m_authmanager.getPassword(playername);
3444 if(oldpwd != checkpwd)
3446 infostream<<"Server: invalid old password"<<std::endl;
3447 // Wrong old password supplied!!
3448 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3452 actionstream<<player->getName()<<" changes password"<<std::endl;
3454 m_authmanager.setPassword(playername, newpwd);
3456 infostream<<"Server: password change successful for "<<playername
3458 SendChatMessage(peer_id, L"Password change successful");
3460 else if(command == TOSERVER_PLAYERITEM)
3465 u16 item = readU16(&data[2]);
3466 player->wieldItem(item);
3467 SendWieldedItem(player);
3469 else if(command == TOSERVER_RESPAWN)
3474 RespawnPlayer(player);
3476 actionstream<<player->getName()<<" respawns at "
3477 <<PP(player->getPosition()/BS)<<std::endl;
3481 infostream<<"Server::ProcessData(): Ignoring "
3482 "unknown command "<<command<<std::endl;
3486 catch(SendFailedException &e)
3488 errorstream<<"Server::ProcessData(): SendFailedException: "
3494 void Server::onMapEditEvent(MapEditEvent *event)
3496 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3497 if(m_ignore_map_edit_events)
3499 MapEditEvent *e = event->clone();
3500 m_unsent_map_edit_queue.push_back(e);
3503 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3505 if(id == "current_player")
3507 assert(c->current_player);
3508 return &(c->current_player->inventory);
3512 std::string id0 = fn.next(":");
3514 if(id0 == "nodemeta")
3517 p.X = stoi(fn.next(","));
3518 p.Y = stoi(fn.next(","));
3519 p.Z = stoi(fn.next(","));
3520 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3522 return meta->getInventory();
3523 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3524 <<"no metadata found"<<std::endl;
3528 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3531 void Server::inventoryModified(InventoryContext *c, std::string id)
3533 if(id == "current_player")
3535 assert(c->current_player);
3537 UpdateCrafting(c->current_player->peer_id);
3538 SendInventory(c->current_player->peer_id);
3543 std::string id0 = fn.next(":");
3545 if(id0 == "nodemeta")
3548 p.X = stoi(fn.next(","));
3549 p.Y = stoi(fn.next(","));
3550 p.Z = stoi(fn.next(","));
3551 v3s16 blockpos = getNodeBlockPos(p);
3553 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3555 meta->inventoryModified();
3557 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3559 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3561 setBlockNotSent(blockpos);
3566 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3569 core::list<PlayerInfo> Server::getPlayerInfo()
3571 DSTACK(__FUNCTION_NAME);
3572 JMutexAutoLock envlock(m_env_mutex);
3573 JMutexAutoLock conlock(m_con_mutex);
3575 core::list<PlayerInfo> list;
3577 core::list<Player*> players = m_env->getPlayers();
3579 core::list<Player*>::Iterator i;
3580 for(i = players.begin();
3581 i != players.end(); i++)
3585 Player *player = *i;
3588 // Copy info from connection to info struct
3589 info.id = player->peer_id;
3590 info.address = m_con.GetPeerAddress(player->peer_id);
3591 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3593 catch(con::PeerNotFoundException &e)
3595 // Set dummy peer info
3597 info.address = Address(0,0,0,0,0);
3601 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3602 info.position = player->getPosition();
3604 list.push_back(info);
3611 void Server::peerAdded(con::Peer *peer)
3613 DSTACK(__FUNCTION_NAME);
3614 infostream<<"Server::peerAdded(): peer->id="
3615 <<peer->id<<std::endl;
3618 c.type = PEER_ADDED;
3619 c.peer_id = peer->id;
3621 m_peer_change_queue.push_back(c);
3624 void Server::deletingPeer(con::Peer *peer, bool timeout)
3626 DSTACK(__FUNCTION_NAME);
3627 infostream<<"Server::deletingPeer(): peer->id="
3628 <<peer->id<<", timeout="<<timeout<<std::endl;
3631 c.type = PEER_REMOVED;
3632 c.peer_id = peer->id;
3633 c.timeout = timeout;
3634 m_peer_change_queue.push_back(c);
3641 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3643 DSTACK(__FUNCTION_NAME);
3644 std::ostringstream os(std::ios_base::binary);
3646 writeU16(os, TOCLIENT_HP);
3650 std::string s = os.str();
3651 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3653 con.Send(peer_id, 0, data, true);
3656 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3657 const std::wstring &reason)
3659 DSTACK(__FUNCTION_NAME);
3660 std::ostringstream os(std::ios_base::binary);
3662 writeU16(os, TOCLIENT_ACCESS_DENIED);
3663 os<<serializeWideString(reason);
3666 std::string s = os.str();
3667 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3669 con.Send(peer_id, 0, data, true);
3672 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3673 bool set_camera_point_target, v3f camera_point_target)
3675 DSTACK(__FUNCTION_NAME);
3676 std::ostringstream os(std::ios_base::binary);
3678 writeU16(os, TOCLIENT_DEATHSCREEN);
3679 writeU8(os, set_camera_point_target);
3680 writeV3F1000(os, camera_point_target);
3683 std::string s = os.str();
3684 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3686 con.Send(peer_id, 0, data, true);
3689 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3690 IToolDefManager *tooldef)
3692 DSTACK(__FUNCTION_NAME);
3693 std::ostringstream os(std::ios_base::binary);
3697 u32 length of the next item
3698 serialized ToolDefManager
3700 writeU16(os, TOCLIENT_TOOLDEF);
3701 std::ostringstream tmp_os(std::ios::binary);
3702 tooldef->serialize(tmp_os);
3703 os<<serializeLongString(tmp_os.str());
3706 std::string s = os.str();
3707 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3708 <<s.size()<<std::endl;
3709 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3711 con.Send(peer_id, 0, data, true);
3714 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3715 INodeDefManager *nodedef)
3717 DSTACK(__FUNCTION_NAME);
3718 std::ostringstream os(std::ios_base::binary);
3722 u32 length of the next item
3723 serialized NodeDefManager
3725 writeU16(os, TOCLIENT_NODEDEF);
3726 std::ostringstream tmp_os(std::ios::binary);
3727 nodedef->serialize(tmp_os);
3728 os<<serializeLongString(tmp_os.str());
3731 std::string s = os.str();
3732 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3733 <<s.size()<<std::endl;
3734 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3736 con.Send(peer_id, 0, data, true);
3740 Non-static send methods
3743 void Server::SendObjectData(float dtime)
3745 DSTACK(__FUNCTION_NAME);
3747 core::map<v3s16, bool> stepped_blocks;
3749 for(core::map<u16, RemoteClient*>::Iterator
3750 i = m_clients.getIterator();
3751 i.atEnd() == false; i++)
3753 u16 peer_id = i.getNode()->getKey();
3754 RemoteClient *client = i.getNode()->getValue();
3755 assert(client->peer_id == peer_id);
3757 if(client->serialization_version == SER_FMT_VER_INVALID)
3760 client->SendObjectData(this, dtime, stepped_blocks);
3764 void Server::SendPlayerInfos()
3766 DSTACK(__FUNCTION_NAME);
3768 //JMutexAutoLock envlock(m_env_mutex);
3770 // Get connected players
3771 core::list<Player*> players = m_env->getPlayers(true);
3773 u32 player_count = players.getSize();
3774 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3776 SharedBuffer<u8> data(datasize);
3777 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3780 core::list<Player*>::Iterator i;
3781 for(i = players.begin();
3782 i != players.end(); i++)
3784 Player *player = *i;
3786 /*infostream<<"Server sending player info for player with "
3787 "peer_id="<<player->peer_id<<std::endl;*/
3789 writeU16(&data[start], player->peer_id);
3790 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3791 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3792 start += 2+PLAYERNAME_SIZE;
3795 //JMutexAutoLock conlock(m_con_mutex);
3798 m_con.SendToAll(0, data, true);
3801 void Server::SendInventory(u16 peer_id)
3803 DSTACK(__FUNCTION_NAME);
3805 Player* player = m_env->getPlayer(peer_id);
3812 std::ostringstream os;
3813 //os.imbue(std::locale("C"));
3815 player->inventory.serialize(os);
3817 std::string s = os.str();
3819 SharedBuffer<u8> data(s.size()+2);
3820 writeU16(&data[0], TOCLIENT_INVENTORY);
3821 memcpy(&data[2], s.c_str(), s.size());
3824 m_con.Send(peer_id, 0, data, true);
3827 std::string getWieldedItemString(const Player *player)
3829 const InventoryItem *item = player->getWieldItem();
3831 return std::string("");
3832 std::ostringstream os(std::ios_base::binary);
3833 item->serialize(os);
3837 void Server::SendWieldedItem(const Player* player)
3839 DSTACK(__FUNCTION_NAME);
3843 std::ostringstream os(std::ios_base::binary);
3845 writeU16(os, TOCLIENT_PLAYERITEM);
3847 writeU16(os, player->peer_id);
3848 os<<serializeString(getWieldedItemString(player));
3851 std::string s = os.str();
3852 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3854 m_con.SendToAll(0, data, true);
3857 void Server::SendPlayerItems()
3859 DSTACK(__FUNCTION_NAME);
3861 std::ostringstream os(std::ios_base::binary);
3862 core::list<Player *> players = m_env->getPlayers(true);
3864 writeU16(os, TOCLIENT_PLAYERITEM);
3865 writeU16(os, players.size());
3866 core::list<Player *>::Iterator i;
3867 for(i = players.begin(); i != players.end(); ++i)
3870 writeU16(os, p->peer_id);
3871 os<<serializeString(getWieldedItemString(p));
3875 std::string s = os.str();
3876 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3878 m_con.SendToAll(0, data, true);
3881 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3883 DSTACK(__FUNCTION_NAME);
3885 std::ostringstream os(std::ios_base::binary);
3889 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3890 os.write((char*)buf, 2);
3893 writeU16(buf, message.size());
3894 os.write((char*)buf, 2);
3897 for(u32 i=0; i<message.size(); i++)
3901 os.write((char*)buf, 2);
3905 std::string s = os.str();
3906 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3908 m_con.Send(peer_id, 0, data, true);
3911 void Server::BroadcastChatMessage(const std::wstring &message)
3913 for(core::map<u16, RemoteClient*>::Iterator
3914 i = m_clients.getIterator();
3915 i.atEnd() == false; i++)
3917 // Get client and check that it is valid
3918 RemoteClient *client = i.getNode()->getValue();
3919 assert(client->peer_id == i.getNode()->getKey());
3920 if(client->serialization_version == SER_FMT_VER_INVALID)
3923 SendChatMessage(client->peer_id, message);
3927 void Server::SendPlayerHP(Player *player)
3929 SendHP(m_con, player->peer_id, player->hp);
3932 void Server::SendMovePlayer(Player *player)
3934 DSTACK(__FUNCTION_NAME);
3935 std::ostringstream os(std::ios_base::binary);
3937 writeU16(os, TOCLIENT_MOVE_PLAYER);
3938 writeV3F1000(os, player->getPosition());
3939 writeF1000(os, player->getPitch());
3940 writeF1000(os, player->getYaw());
3943 v3f pos = player->getPosition();
3944 f32 pitch = player->getPitch();
3945 f32 yaw = player->getYaw();
3946 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3947 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3954 std::string s = os.str();
3955 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3957 m_con.Send(player->peer_id, 0, data, true);
3960 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3961 core::list<u16> *far_players, float far_d_nodes)
3963 float maxd = far_d_nodes*BS;
3964 v3f p_f = intToFloat(p, BS);
3968 SharedBuffer<u8> reply(replysize);
3969 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3970 writeS16(&reply[2], p.X);
3971 writeS16(&reply[4], p.Y);
3972 writeS16(&reply[6], p.Z);
3974 for(core::map<u16, RemoteClient*>::Iterator
3975 i = m_clients.getIterator();
3976 i.atEnd() == false; i++)
3978 // Get client and check that it is valid
3979 RemoteClient *client = i.getNode()->getValue();
3980 assert(client->peer_id == i.getNode()->getKey());
3981 if(client->serialization_version == SER_FMT_VER_INVALID)
3984 // Don't send if it's the same one
3985 if(client->peer_id == ignore_id)
3991 Player *player = m_env->getPlayer(client->peer_id);
3994 // If player is far away, only set modified blocks not sent
3995 v3f player_pos = player->getPosition();
3996 if(player_pos.getDistanceFrom(p_f) > maxd)
3998 far_players->push_back(client->peer_id);
4005 m_con.Send(client->peer_id, 0, reply, true);
4009 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4010 core::list<u16> *far_players, float far_d_nodes)
4012 float maxd = far_d_nodes*BS;
4013 v3f p_f = intToFloat(p, BS);
4015 for(core::map<u16, RemoteClient*>::Iterator
4016 i = m_clients.getIterator();
4017 i.atEnd() == false; i++)
4019 // Get client and check that it is valid
4020 RemoteClient *client = i.getNode()->getValue();
4021 assert(client->peer_id == i.getNode()->getKey());
4022 if(client->serialization_version == SER_FMT_VER_INVALID)
4025 // Don't send if it's the same one
4026 if(client->peer_id == ignore_id)
4032 Player *player = m_env->getPlayer(client->peer_id);
4035 // If player is far away, only set modified blocks not sent
4036 v3f player_pos = player->getPosition();
4037 if(player_pos.getDistanceFrom(p_f) > maxd)
4039 far_players->push_back(client->peer_id);
4046 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4047 SharedBuffer<u8> reply(replysize);
4048 writeU16(&reply[0], TOCLIENT_ADDNODE);
4049 writeS16(&reply[2], p.X);
4050 writeS16(&reply[4], p.Y);
4051 writeS16(&reply[6], p.Z);
4052 n.serialize(&reply[8], client->serialization_version);
4055 m_con.Send(client->peer_id, 0, reply, true);
4059 void Server::setBlockNotSent(v3s16 p)
4061 for(core::map<u16, RemoteClient*>::Iterator
4062 i = m_clients.getIterator();
4063 i.atEnd()==false; i++)
4065 RemoteClient *client = i.getNode()->getValue();
4066 client->SetBlockNotSent(p);
4070 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4072 DSTACK(__FUNCTION_NAME);
4074 v3s16 p = block->getPos();
4078 bool completely_air = true;
4079 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4080 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4081 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4083 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4085 completely_air = false;
4086 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4091 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4093 infostream<<"[completely air] ";
4094 infostream<<std::endl;
4098 Create a packet with the block in the right format
4101 std::ostringstream os(std::ios_base::binary);
4102 block->serialize(os, ver);
4103 std::string s = os.str();
4104 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4106 u32 replysize = 8 + blockdata.getSize();
4107 SharedBuffer<u8> reply(replysize);
4108 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4109 writeS16(&reply[2], p.X);
4110 writeS16(&reply[4], p.Y);
4111 writeS16(&reply[6], p.Z);
4112 memcpy(&reply[8], *blockdata, blockdata.getSize());
4114 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4115 <<": \tpacket size: "<<replysize<<std::endl;*/
4120 m_con.Send(peer_id, 1, reply, true);
4123 void Server::SendBlocks(float dtime)
4125 DSTACK(__FUNCTION_NAME);
4127 JMutexAutoLock envlock(m_env_mutex);
4128 JMutexAutoLock conlock(m_con_mutex);
4130 //TimeTaker timer("Server::SendBlocks");
4132 core::array<PrioritySortedBlockTransfer> queue;
4134 s32 total_sending = 0;
4137 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4139 for(core::map<u16, RemoteClient*>::Iterator
4140 i = m_clients.getIterator();
4141 i.atEnd() == false; i++)
4143 RemoteClient *client = i.getNode()->getValue();
4144 assert(client->peer_id == i.getNode()->getKey());
4146 total_sending += client->SendingCount();
4148 if(client->serialization_version == SER_FMT_VER_INVALID)
4151 client->GetNextBlocks(this, dtime, queue);
4156 // Lowest priority number comes first.
4157 // Lowest is most important.
4160 for(u32 i=0; i<queue.size(); i++)
4162 //TODO: Calculate limit dynamically
4163 if(total_sending >= g_settings->getS32
4164 ("max_simultaneous_block_sends_server_total"))
4167 PrioritySortedBlockTransfer q = queue[i];
4169 MapBlock *block = NULL;
4172 block = m_env->getMap().getBlockNoCreate(q.pos);
4174 catch(InvalidPositionException &e)
4179 RemoteClient *client = getClient(q.peer_id);
4181 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4183 client->SentBlock(q.pos);
4189 struct SendableTexture
4195 SendableTexture(const std::string &name_="", const std::string path_="",
4196 const std::string &data_=""):
4203 void Server::SendTextures(u16 peer_id)
4205 DSTACK(__FUNCTION_NAME);
4207 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4211 // Put 5kB in one bunch (this is not accurate)
4212 u32 bytes_per_bunch = 5000;
4214 core::array< core::list<SendableTexture> > texture_bunches;
4215 texture_bunches.push_back(core::list<SendableTexture>());
4217 u32 texture_size_bunch_total = 0;
4218 core::list<ModSpec> mods = getMods(m_modspaths);
4219 for(core::list<ModSpec>::Iterator i = mods.begin();
4220 i != mods.end(); i++){
4222 std::string texturepath = mod.path + DIR_DELIM + "textures";
4223 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4224 for(u32 j=0; j<dirlist.size(); j++){
4225 if(dirlist[j].dir) // Ignode dirs
4227 std::string tname = dirlist[j].name;
4228 std::string tpath = texturepath + DIR_DELIM + tname;
4230 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4231 if(fis.good() == false){
4232 errorstream<<"Server::SendTextures(): Could not open \""
4233 <<tname<<"\" for reading"<<std::endl;
4236 std::ostringstream tmp_os(std::ios_base::binary);
4240 fis.read(buf, 1024);
4241 std::streamsize len = fis.gcount();
4242 tmp_os.write(buf, len);
4243 texture_size_bunch_total += len;
4252 errorstream<<"Server::SendTextures(): Failed to read \""
4253 <<tname<<"\""<<std::endl;
4256 /*infostream<<"Server::SendTextures(): Loaded \""
4257 <<tname<<"\""<<std::endl;*/
4259 texture_bunches[texture_bunches.size()-1].push_back(
4260 SendableTexture(tname, tpath, tmp_os.str()));
4262 // Start next bunch if got enough data
4263 if(texture_size_bunch_total >= bytes_per_bunch){
4264 texture_bunches.push_back(core::list<SendableTexture>());
4265 texture_size_bunch_total = 0;
4270 /* Create and send packets */
4272 u32 num_bunches = texture_bunches.size();
4273 for(u32 i=0; i<num_bunches; i++)
4277 u16 total number of texture bunches
4278 u16 index of this bunch
4279 u32 number of textures in this bunch
4287 std::ostringstream os(std::ios_base::binary);
4289 writeU16(os, TOCLIENT_TEXTURES);
4290 writeU16(os, num_bunches);
4292 writeU32(os, texture_bunches[i].size());
4294 for(core::list<SendableTexture>::Iterator
4295 j = texture_bunches[i].begin();
4296 j != texture_bunches[i].end(); j++){
4297 os<<serializeString(j->name);
4298 os<<serializeLongString(j->data);
4302 std::string s = os.str();
4303 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4304 <<" textures="<<texture_bunches[i].size()
4305 <<" size=" <<s.size()<<std::endl;
4306 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4308 m_con.Send(peer_id, 0, data, true);
4316 void Server::HandlePlayerHP(Player *player, s16 damage)
4318 if(player->hp > damage)
4320 player->hp -= damage;
4321 SendPlayerHP(player);
4325 infostream<<"Server::HandlePlayerHP(): Player "
4326 <<player->getName()<<" dies"<<std::endl;
4330 //TODO: Throw items around
4332 // Handle players that are not connected
4333 if(player->peer_id == PEER_ID_INEXISTENT){
4334 RespawnPlayer(player);
4338 SendPlayerHP(player);
4340 RemoteClient *client = getClient(player->peer_id);
4341 if(client->net_proto_version >= 3)
4343 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4347 RespawnPlayer(player);
4352 void Server::RespawnPlayer(Player *player)
4354 v3f pos = findSpawnPos(m_env->getServerMap());
4355 player->setPosition(pos);
4357 SendMovePlayer(player);
4358 SendPlayerHP(player);
4361 void Server::UpdateCrafting(u16 peer_id)
4363 DSTACK(__FUNCTION_NAME);
4365 Player* player = m_env->getPlayer(peer_id);
4369 Calculate crafting stuff
4371 if(g_settings->getBool("creative_mode") == false)
4373 InventoryList *clist = player->inventory.getList("craft");
4374 InventoryList *rlist = player->inventory.getList("craftresult");
4376 if(rlist && rlist->getUsedSlots() == 0)
4377 player->craftresult_is_preview = true;
4379 if(rlist && player->craftresult_is_preview)
4381 rlist->clearItems();
4383 if(clist && rlist && player->craftresult_is_preview)
4385 // Get result of crafting grid
4387 std::vector<InventoryItem*> items;
4388 for(u16 i=0; i<9; i++){
4389 if(clist->getItem(i) == NULL)
4390 items.push_back(NULL);
4392 items.push_back(clist->getItem(i)->clone());
4394 CraftPointerInput cpi(3, items);
4396 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4397 //InventoryItem *result = craft_get_result(items, this);
4399 rlist->addItem(result);
4402 } // if creative_mode == false
4405 RemoteClient* Server::getClient(u16 peer_id)
4407 DSTACK(__FUNCTION_NAME);
4408 //JMutexAutoLock lock(m_con_mutex);
4409 core::map<u16, RemoteClient*>::Node *n;
4410 n = m_clients.find(peer_id);
4411 // A client should exist for all peers
4413 return n->getValue();
4416 std::wstring Server::getStatusString()
4418 std::wostringstream os(std::ios_base::binary);
4421 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4423 os<<L", uptime="<<m_uptime.get();
4424 // Information about clients
4426 for(core::map<u16, RemoteClient*>::Iterator
4427 i = m_clients.getIterator();
4428 i.atEnd() == false; i++)
4430 // Get client and check that it is valid
4431 RemoteClient *client = i.getNode()->getValue();
4432 assert(client->peer_id == i.getNode()->getKey());
4433 if(client->serialization_version == SER_FMT_VER_INVALID)
4436 Player *player = m_env->getPlayer(client->peer_id);
4437 // Get name of player
4438 std::wstring name = L"unknown";
4440 name = narrow_to_wide(player->getName());
4441 // Add name to information string
4445 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4446 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4447 if(g_settings->get("motd") != "")
4448 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4452 // Saves g_settings to configpath given at initialization
4453 void Server::saveConfig()
4455 if(m_configpath != "")
4456 g_settings->updateConfigFile(m_configpath.c_str());
4459 void Server::notifyPlayer(const char *name, const std::wstring msg)
4461 Player *player = m_env->getPlayer(name);
4464 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4467 void Server::notifyPlayers(const std::wstring msg)
4469 BroadcastChatMessage(msg);
4472 // IGameDef interface
4474 IToolDefManager* Server::getToolDefManager()
4478 INodeDefManager* Server::getNodeDefManager()
4482 ICraftDefManager* Server::getCraftDefManager()
4486 ITextureSource* Server::getTextureSource()
4490 u16 Server::allocateUnknownNodeId(const std::string &name)
4492 return m_nodedef->allocateDummy(name);
4495 IWritableToolDefManager* Server::getWritableToolDefManager()
4499 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4503 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4508 v3f findSpawnPos(ServerMap &map)
4510 //return v3f(50,50,50)*BS;
4515 nodepos = v2s16(0,0);
4520 // Try to find a good place a few times
4521 for(s32 i=0; i<1000; i++)
4524 // We're going to try to throw the player to this position
4525 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4526 -range + (myrand()%(range*2)));
4527 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4528 // Get ground height at point (fallbacks to heightmap function)
4529 s16 groundheight = map.findGroundLevel(nodepos2d);
4530 // Don't go underwater
4531 if(groundheight < WATER_LEVEL)
4533 //infostream<<"-> Underwater"<<std::endl;
4536 // Don't go to high places
4537 if(groundheight > WATER_LEVEL + 4)
4539 //infostream<<"-> Underwater"<<std::endl;
4543 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4544 bool is_good = false;
4546 for(s32 i=0; i<10; i++){
4547 v3s16 blockpos = getNodeBlockPos(nodepos);
4548 map.emergeBlock(blockpos, true);
4549 MapNode n = map.getNodeNoEx(nodepos);
4550 if(n.getContent() == CONTENT_AIR){
4561 // Found a good place
4562 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4568 return intToFloat(nodepos, BS);
4571 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4574 Try to get an existing player
4576 Player *player = m_env->getPlayer(name);
4579 // If player is already connected, cancel
4580 if(player->peer_id != 0)
4582 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4587 player->peer_id = peer_id;
4589 // Reset inventory to creative if in creative mode
4590 if(g_settings->getBool("creative_mode"))
4592 // Warning: double code below
4593 // Backup actual inventory
4594 player->inventory_backup = new Inventory();
4595 *(player->inventory_backup) = player->inventory;
4596 // Set creative inventory
4597 craft_set_creative_inventory(player, this);
4604 If player with the wanted peer_id already exists, cancel.
4606 if(m_env->getPlayer(peer_id) != NULL)
4608 infostream<<"emergePlayer(): Player with wrong name but same"
4609 " peer_id already exists"<<std::endl;
4617 // Add authentication stuff
4618 m_authmanager.add(name);
4619 m_authmanager.setPassword(name, password);
4620 m_authmanager.setPrivs(name,
4621 stringToPrivs(g_settings->get("default_privs")));
4627 infostream<<"Server: Finding spawn place for player \""
4628 <<name<<"\""<<std::endl;
4630 v3f pos = findSpawnPos(m_env->getServerMap());
4632 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4635 Add player to environment
4638 m_env->addPlayer(player);
4641 Add stuff to inventory
4644 if(g_settings->getBool("creative_mode"))
4646 // Warning: double code above
4647 // Backup actual inventory
4648 player->inventory_backup = new Inventory();
4649 *(player->inventory_backup) = player->inventory;
4650 // Set creative inventory
4651 craft_set_creative_inventory(player, this);
4653 else if(g_settings->getBool("give_initial_stuff"))
4655 craft_give_initial_stuff(player, this);
4660 } // create new player
4663 void Server::handlePeerChange(PeerChange &c)
4665 JMutexAutoLock envlock(m_env_mutex);
4666 JMutexAutoLock conlock(m_con_mutex);
4668 if(c.type == PEER_ADDED)
4675 core::map<u16, RemoteClient*>::Node *n;
4676 n = m_clients.find(c.peer_id);
4677 // The client shouldn't already exist
4681 RemoteClient *client = new RemoteClient();
4682 client->peer_id = c.peer_id;
4683 m_clients.insert(client->peer_id, client);
4686 else if(c.type == PEER_REMOVED)
4693 core::map<u16, RemoteClient*>::Node *n;
4694 n = m_clients.find(c.peer_id);
4695 // The client should exist
4699 Mark objects to be not known by the client
4701 RemoteClient *client = n->getValue();
4703 for(core::map<u16, bool>::Iterator
4704 i = client->m_known_objects.getIterator();
4705 i.atEnd()==false; i++)
4708 u16 id = i.getNode()->getKey();
4709 ServerActiveObject* obj = m_env->getActiveObject(id);
4711 if(obj && obj->m_known_by_count > 0)
4712 obj->m_known_by_count--;
4715 // Collect information about leaving in chat
4716 std::wstring message;
4718 Player *player = m_env->getPlayer(c.peer_id);
4721 std::wstring name = narrow_to_wide(player->getName());
4724 message += L" left game";
4726 message += L" (timed out)";
4732 m_env->removePlayer(c.peer_id);
4735 // Set player client disconnected
4737 Player *player = m_env->getPlayer(c.peer_id);
4739 player->peer_id = 0;
4746 std::ostringstream os(std::ios_base::binary);
4747 for(core::map<u16, RemoteClient*>::Iterator
4748 i = m_clients.getIterator();
4749 i.atEnd() == false; i++)
4751 RemoteClient *client = i.getNode()->getValue();
4752 assert(client->peer_id == i.getNode()->getKey());
4753 if(client->serialization_version == SER_FMT_VER_INVALID)
4756 Player *player = m_env->getPlayer(client->peer_id);
4759 // Get name of player
4760 os<<player->getName()<<" ";
4763 actionstream<<player->getName()<<" "
4764 <<(c.timeout?"times out.":"leaves game.")
4765 <<" List of players: "
4766 <<os.str()<<std::endl;
4771 delete m_clients[c.peer_id];
4772 m_clients.remove(c.peer_id);
4774 // Send player info to all remaining clients
4777 // Send leave chat message to all remaining clients
4778 BroadcastChatMessage(message);
4787 void Server::handlePeerChanges()
4789 while(m_peer_change_queue.size() > 0)
4791 PeerChange c = m_peer_change_queue.pop_front();
4793 infostream<<"Server: Handling peer change: "
4794 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4797 handlePeerChange(c);
4801 u64 Server::getPlayerPrivs(Player *player)
4805 std::string playername = player->getName();
4806 // Local player gets all privileges regardless of
4807 // what's set on their account.
4808 if(g_settings->get("name") == playername)
4814 return getPlayerAuthPrivs(playername);
4818 void dedicated_server_loop(Server &server, bool &kill)
4820 DSTACK(__FUNCTION_NAME);
4822 infostream<<DTIME<<std::endl;
4823 infostream<<"========================"<<std::endl;
4824 infostream<<"Running dedicated server"<<std::endl;
4825 infostream<<"========================"<<std::endl;
4826 infostream<<std::endl;
4828 IntervalLimiter m_profiler_interval;
4832 // This is kind of a hack but can be done like this
4833 // because server.step() is very light
4835 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4840 if(server.getShutdownRequested() || kill)
4842 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4849 float profiler_print_interval =
4850 g_settings->getFloat("profiler_print_interval");
4851 if(profiler_print_interval != 0)
4853 if(m_profiler_interval.step(0.030, profiler_print_interval))
4855 infostream<<"Profiler:"<<std::endl;
4856 g_profiler->print(infostream);
4857 g_profiler->clear();
4864 static int counter = 0;
4870 core::list<PlayerInfo> list = server.getPlayerInfo();
4871 core::list<PlayerInfo>::Iterator i;
4872 static u32 sum_old = 0;
4873 u32 sum = PIChecksum(list);
4876 infostream<<DTIME<<"Player info:"<<std::endl;
4877 for(i=list.begin(); i!=list.end(); i++)
4879 i->PrintLine(&infostream);