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.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
30 #include "materials.h"
33 #include "servercommand.h"
35 #include "content_mapnode.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
47 #include "craftitemdef.h"
49 #include "content_abm.h"
54 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
56 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
58 class MapEditEventIgnorer
61 MapEditEventIgnorer(bool *flag):
70 ~MapEditEventIgnorer()
83 void * ServerThread::Thread()
87 log_register_thread("ServerThread");
89 DSTACK(__FUNCTION_NAME);
91 BEGIN_DEBUG_EXCEPTION_HANDLER
96 //TimeTaker timer("AsyncRunStep() + Receive()");
99 //TimeTaker timer("AsyncRunStep()");
100 m_server->AsyncRunStep();
103 //infostream<<"Running m_server->Receive()"<<std::endl;
106 catch(con::NoIncomingDataException &e)
109 catch(con::PeerNotFoundException &e)
111 infostream<<"Server: PeerNotFoundException"<<std::endl;
115 END_DEBUG_EXCEPTION_HANDLER(errorstream)
120 void * EmergeThread::Thread()
124 log_register_thread("EmergeThread");
126 DSTACK(__FUNCTION_NAME);
128 BEGIN_DEBUG_EXCEPTION_HANDLER
130 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
133 Get block info from queue, emerge them and send them
136 After queue is empty, exit.
140 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
144 SharedPtr<QueuedBlockEmerge> q(qptr);
150 Do not generate over-limit
152 if(blockpos_over_limit(p))
155 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
157 //TimeTaker timer("block emerge");
160 Try to emerge it from somewhere.
162 If it is only wanted as optional, only loading from disk
167 Check if any peer wants it as non-optional. In that case it
170 Also decrement the emerge queue count in clients.
173 bool only_from_disk = true;
176 core::map<u16, u8>::Iterator i;
177 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
179 //u16 peer_id = i.getNode()->getKey();
182 u8 flags = i.getNode()->getValue();
183 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
184 only_from_disk = false;
189 if(enable_mapgen_debug_info)
190 infostream<<"EmergeThread: p="
191 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
192 <<"only_from_disk="<<only_from_disk<<std::endl;
194 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
196 MapBlock *block = NULL;
197 bool got_block = true;
198 core::map<v3s16, MapBlock*> modified_blocks;
201 Try to fetch block from memory or disk.
202 If not found and asked to generate, initialize generator.
205 bool started_generate = false;
206 mapgen::BlockMakeData data;
209 JMutexAutoLock envlock(m_server->m_env_mutex);
211 // Load sector if it isn't loaded
212 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
213 map.loadSectorMeta(p2d);
215 // Attempt to load block
216 block = map.getBlockNoCreateNoEx(p);
217 if(!block || block->isDummy() || !block->isGenerated())
219 if(enable_mapgen_debug_info)
220 infostream<<"EmergeThread: not in memory, "
221 <<"attempting to load from disk"<<std::endl;
223 block = map.loadBlock(p);
226 // If could not load and allowed to generate, start generation
227 // inside this same envlock
228 if(only_from_disk == false &&
229 (block == NULL || block->isGenerated() == false)){
230 if(enable_mapgen_debug_info)
231 infostream<<"EmergeThread: generating"<<std::endl;
232 started_generate = true;
234 map.initBlockMake(&data, p);
239 If generator was initialized, generate now when envlock is free.
244 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
246 TimeTaker t("mapgen::make_block()");
248 mapgen::make_block(&data);
250 if(enable_mapgen_debug_info == false)
251 t.stop(true); // Hide output
255 // Lock environment again to access the map
256 JMutexAutoLock envlock(m_server->m_env_mutex);
258 ScopeProfiler sp(g_profiler, "EmergeThread: after "
259 "mapgen::make_block (envlock)", SPT_AVG);
261 // Blit data back on map, update lighting, add mobs and
262 // whatever this does
263 map.finishBlockMake(&data, modified_blocks);
266 block = map.getBlockNoCreateNoEx(p);
268 // If block doesn't exist, don't try doing anything with it
269 // This happens if the block is not in generation boundaries
274 Do some post-generate stuff
277 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
278 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
279 scriptapi_environment_on_generated(m_server->m_lua,
282 if(enable_mapgen_debug_info)
283 infostream<<"EmergeThread: ended up with: "
284 <<analyze_block(block)<<std::endl;
287 Ignore map edit events, they will not need to be
288 sent to anybody because the block hasn't been sent
291 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
293 // Activate objects and stuff
294 m_server->m_env->activateBlock(block, 0);
302 Set sent status of modified blocks on clients
305 // NOTE: Server's clients are also behind the connection mutex
306 JMutexAutoLock lock(m_server->m_con_mutex);
309 Add the originally fetched block to the modified list
313 modified_blocks.insert(p, block);
317 Set the modified blocks unsent for all the clients
320 for(core::map<u16, RemoteClient*>::Iterator
321 i = m_server->m_clients.getIterator();
322 i.atEnd() == false; i++)
324 RemoteClient *client = i.getNode()->getValue();
326 if(modified_blocks.size() > 0)
328 // Remove block from sent history
329 client->SetBlocksNotSent(modified_blocks);
335 END_DEBUG_EXCEPTION_HANDLER(errorstream)
337 log_deregister_thread();
342 void RemoteClient::GetNextBlocks(Server *server, float dtime,
343 core::array<PrioritySortedBlockTransfer> &dest)
345 DSTACK(__FUNCTION_NAME);
348 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
351 m_nothing_to_send_pause_timer -= dtime;
352 m_nearest_unsent_reset_timer += dtime;
354 if(m_nothing_to_send_pause_timer >= 0)
359 // Won't send anything if already sending
360 if(m_blocks_sending.size() >= g_settings->getU16
361 ("max_simultaneous_block_sends_per_client"))
363 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
367 //TimeTaker timer("RemoteClient::GetNextBlocks");
369 Player *player = server->m_env->getPlayer(peer_id);
371 assert(player != NULL);
373 v3f playerpos = player->getPosition();
374 v3f playerspeed = player->getSpeed();
375 v3f playerspeeddir(0,0,0);
376 if(playerspeed.getLength() > 1.0*BS)
377 playerspeeddir = playerspeed / playerspeed.getLength();
378 // Predict to next block
379 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
381 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
383 v3s16 center = getNodeBlockPos(center_nodepos);
385 // Camera position and direction
386 v3f camera_pos = player->getEyePosition();
387 v3f camera_dir = v3f(0,0,1);
388 camera_dir.rotateYZBy(player->getPitch());
389 camera_dir.rotateXZBy(player->getYaw());
391 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
392 <<camera_dir.Z<<")"<<std::endl;*/
395 Get the starting value of the block finder radius.
398 if(m_last_center != center)
400 m_nearest_unsent_d = 0;
401 m_last_center = center;
404 /*infostream<<"m_nearest_unsent_reset_timer="
405 <<m_nearest_unsent_reset_timer<<std::endl;*/
407 // Reset periodically to workaround for some bugs or stuff
408 if(m_nearest_unsent_reset_timer > 20.0)
410 m_nearest_unsent_reset_timer = 0;
411 m_nearest_unsent_d = 0;
412 //infostream<<"Resetting m_nearest_unsent_d for "
413 // <<server->getPlayerName(peer_id)<<std::endl;
416 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
417 s16 d_start = m_nearest_unsent_d;
419 //infostream<<"d_start="<<d_start<<std::endl;
421 u16 max_simul_sends_setting = g_settings->getU16
422 ("max_simultaneous_block_sends_per_client");
423 u16 max_simul_sends_usually = max_simul_sends_setting;
426 Check the time from last addNode/removeNode.
428 Decrease send rate if player is building stuff.
430 m_time_from_building += dtime;
431 if(m_time_from_building < g_settings->getFloat(
432 "full_block_send_enable_min_time_from_building"))
434 max_simul_sends_usually
435 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
439 Number of blocks sending + number of blocks selected for sending
441 u32 num_blocks_selected = m_blocks_sending.size();
444 next time d will be continued from the d from which the nearest
445 unsent block was found this time.
447 This is because not necessarily any of the blocks found this
448 time are actually sent.
450 s32 new_nearest_unsent_d = -1;
452 s16 d_max = g_settings->getS16("max_block_send_distance");
453 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
455 // Don't loop very much at a time
456 s16 max_d_increment_at_time = 2;
457 if(d_max > d_start + max_d_increment_at_time)
458 d_max = d_start + max_d_increment_at_time;
459 /*if(d_max_gen > d_start+2)
460 d_max_gen = d_start+2;*/
462 //infostream<<"Starting from "<<d_start<<std::endl;
464 s32 nearest_emerged_d = -1;
465 s32 nearest_emergefull_d = -1;
466 s32 nearest_sent_d = -1;
467 bool queue_is_full = false;
470 for(d = d_start; d <= d_max; d++)
472 /*errorstream<<"checking d="<<d<<" for "
473 <<server->getPlayerName(peer_id)<<std::endl;*/
474 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
477 If m_nearest_unsent_d was changed by the EmergeThread
478 (it can change it to 0 through SetBlockNotSent),
480 Else update m_nearest_unsent_d
482 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
484 d = m_nearest_unsent_d;
485 last_nearest_unsent_d = m_nearest_unsent_d;
489 Get the border/face dot coordinates of a "d-radiused"
492 core::list<v3s16> list;
493 getFacePositions(list, d);
495 core::list<v3s16>::Iterator li;
496 for(li=list.begin(); li!=list.end(); li++)
498 v3s16 p = *li + center;
502 - Don't allow too many simultaneous transfers
503 - EXCEPT when the blocks are very close
505 Also, don't send blocks that are already flying.
508 // Start with the usual maximum
509 u16 max_simul_dynamic = max_simul_sends_usually;
511 // If block is very close, allow full maximum
512 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
513 max_simul_dynamic = max_simul_sends_setting;
515 // Don't select too many blocks for sending
516 if(num_blocks_selected >= max_simul_dynamic)
518 queue_is_full = true;
519 goto queue_full_break;
522 // Don't send blocks that are currently being transferred
523 if(m_blocks_sending.find(p) != NULL)
529 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
534 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
537 // If this is true, inexistent block will be made from scratch
538 bool generate = d <= d_max_gen;
541 /*// Limit the generating area vertically to 2/3
542 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
545 // Limit the send area vertically to 1/2
546 if(abs(p.Y - center.Y) > d_max / 2)
552 If block is far away, don't generate it unless it is
558 // Block center y in nodes
559 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
560 // Don't generate if it's very high or very low
561 if(y < -64 || y > 64)
565 v2s16 p2d_nodes_center(
569 // Get ground height in nodes
570 s16 gh = server->m_env->getServerMap().findGroundLevel(
573 // If differs a lot, don't generate
574 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
576 // Actually, don't even send it
582 //infostream<<"d="<<d<<std::endl;
585 Don't generate or send if not in sight
586 FIXME This only works if the client uses a small enough
587 FOV setting. The default of 72 degrees is fine.
590 float camera_fov = (72.0*PI/180) * 4./3.;
591 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
597 Don't send already sent blocks
600 if(m_blocks_sent.find(p) != NULL)
607 Check if map has this block
609 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
611 bool surely_not_found_on_disk = false;
612 bool block_is_invalid = false;
615 // Reset usage timer, this block will be of use in the future.
616 block->resetUsageTimer();
618 // Block is dummy if data doesn't exist.
619 // It means it has been not found from disk and not generated
622 surely_not_found_on_disk = true;
625 // Block is valid if lighting is up-to-date and data exists
626 if(block->isValid() == false)
628 block_is_invalid = true;
631 /*if(block->isFullyGenerated() == false)
633 block_is_invalid = true;
638 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
639 v2s16 chunkpos = map->sector_to_chunk(p2d);
640 if(map->chunkNonVolatile(chunkpos) == false)
641 block_is_invalid = true;
643 if(block->isGenerated() == false)
644 block_is_invalid = true;
647 If block is not close, don't send it unless it is near
650 Block is near ground level if night-time mesh
651 differs from day-time mesh.
655 if(block->dayNightDiffed() == false)
662 If block has been marked to not exist on disk (dummy)
663 and generating new ones is not wanted, skip block.
665 if(generate == false && surely_not_found_on_disk == true)
672 Add inexistent block to emerge queue.
674 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
676 //TODO: Get value from somewhere
677 // Allow only one block in emerge queue
678 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
679 // Allow two blocks in queue per client
680 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
682 // Make it more responsive when needing to generate stuff
683 if(surely_not_found_on_disk)
685 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
687 //infostream<<"Adding block to emerge queue"<<std::endl;
689 // Add it to the emerge queue and trigger the thread
692 if(generate == false)
693 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
695 server->m_emerge_queue.addBlock(peer_id, p, flags);
696 server->m_emergethread.trigger();
698 if(nearest_emerged_d == -1)
699 nearest_emerged_d = d;
701 if(nearest_emergefull_d == -1)
702 nearest_emergefull_d = d;
709 if(nearest_sent_d == -1)
713 Add block to send queue
716 /*errorstream<<"sending from d="<<d<<" to "
717 <<server->getPlayerName(peer_id)<<std::endl;*/
719 PrioritySortedBlockTransfer q((float)d, p, peer_id);
723 num_blocks_selected += 1;
728 //infostream<<"Stopped at "<<d<<std::endl;
730 // If nothing was found for sending and nothing was queued for
731 // emerging, continue next time browsing from here
732 if(nearest_emerged_d != -1){
733 new_nearest_unsent_d = nearest_emerged_d;
734 } else if(nearest_emergefull_d != -1){
735 new_nearest_unsent_d = nearest_emergefull_d;
737 if(d > g_settings->getS16("max_block_send_distance")){
738 new_nearest_unsent_d = 0;
739 m_nothing_to_send_pause_timer = 2.0;
740 /*infostream<<"GetNextBlocks(): d wrapped around for "
741 <<server->getPlayerName(peer_id)
742 <<"; setting to 0 and pausing"<<std::endl;*/
744 if(nearest_sent_d != -1)
745 new_nearest_unsent_d = nearest_sent_d;
747 new_nearest_unsent_d = d;
751 if(new_nearest_unsent_d != -1)
752 m_nearest_unsent_d = new_nearest_unsent_d;
754 /*timer_result = timer.stop(true);
755 if(timer_result != 0)
756 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
759 void RemoteClient::GotBlock(v3s16 p)
761 if(m_blocks_sending.find(p) != NULL)
762 m_blocks_sending.remove(p);
765 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
766 " m_blocks_sending"<<std::endl;*/
767 m_excess_gotblocks++;
769 m_blocks_sent.insert(p, true);
772 void RemoteClient::SentBlock(v3s16 p)
774 if(m_blocks_sending.find(p) == NULL)
775 m_blocks_sending.insert(p, 0.0);
777 infostream<<"RemoteClient::SentBlock(): Sent block"
778 " already in m_blocks_sending"<<std::endl;
781 void RemoteClient::SetBlockNotSent(v3s16 p)
783 m_nearest_unsent_d = 0;
785 if(m_blocks_sending.find(p) != NULL)
786 m_blocks_sending.remove(p);
787 if(m_blocks_sent.find(p) != NULL)
788 m_blocks_sent.remove(p);
791 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
793 m_nearest_unsent_d = 0;
795 for(core::map<v3s16, MapBlock*>::Iterator
796 i = blocks.getIterator();
797 i.atEnd()==false; i++)
799 v3s16 p = i.getNode()->getKey();
801 if(m_blocks_sending.find(p) != NULL)
802 m_blocks_sending.remove(p);
803 if(m_blocks_sent.find(p) != NULL)
804 m_blocks_sent.remove(p);
812 PlayerInfo::PlayerInfo()
818 void PlayerInfo::PrintLine(std::ostream *s)
821 (*s)<<"\""<<name<<"\" ("
822 <<(position.X/10)<<","<<(position.Y/10)
823 <<","<<(position.Z/10)<<") ";
825 (*s)<<" avg_rtt="<<avg_rtt;
829 u32 PIChecksum(core::list<PlayerInfo> &l)
831 core::list<PlayerInfo>::Iterator i;
834 for(i=l.begin(); i!=l.end(); i++)
836 checksum += a * (i->id+1);
837 checksum ^= 0x435aafcd;
848 std::string mapsavedir,
849 std::string configpath
852 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
853 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
854 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
856 m_toolmgr(createToolDefManager()),
857 m_nodedef(createNodeDefManager()),
858 m_craftdef(createCraftDefManager()),
859 m_craftitemdef(createCraftItemDefManager()),
861 m_emergethread(this),
863 m_time_of_day_send_timer(0),
865 m_mapsavedir(mapsavedir),
866 m_configpath(configpath),
867 m_shutdown_requested(false),
868 m_ignore_map_edit_events(false),
869 m_ignore_map_edit_events_peer_id(0)
871 m_liquid_transform_timer = 0.0;
872 m_print_info_timer = 0.0;
873 m_objectdata_timer = 0.0;
874 m_emergethread_trigger_timer = 0.0;
875 m_savemap_timer = 0.0;
879 m_step_dtime_mutex.Init();
882 JMutexAutoLock envlock(m_env_mutex);
883 JMutexAutoLock conlock(m_con_mutex);
885 // Path to builtin.lua
886 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
888 // Add default global mod search path
889 m_modspaths.push_front(porting::path_data + DIR_DELIM + "mods");
890 // Add world mod search path
891 m_modspaths.push_front(mapsavedir + DIR_DELIM + "worldmods");
892 // Add user mod search path
893 m_modspaths.push_front(porting::path_userdata + DIR_DELIM + "usermods");
895 // Print out mod search paths
896 infostream<<"Mod search paths:"<<std::endl;
897 for(core::list<std::string>::Iterator i = m_modspaths.begin();
898 i != m_modspaths.end(); i++){
899 std::string modspath = *i;
900 infostream<<" "<<modspath<<std::endl;
903 // Initialize scripting
905 infostream<<"Server: Initializing scripting"<<std::endl;
906 m_lua = script_init();
909 scriptapi_export(m_lua, this);
910 // Load and run builtin.lua
911 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
913 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
915 errorstream<<"Server: Failed to load and run "
916 <<builtinpath<<std::endl;
917 throw ModError("Failed to load and run "+builtinpath);
919 // Load and run "mod" scripts
920 m_mods = getMods(m_modspaths);
921 for(core::list<ModSpec>::Iterator i = m_mods.begin();
922 i != m_mods.end(); i++){
923 const ModSpec &mod = *i;
924 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
925 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
926 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
928 errorstream<<"Server: Failed to load and run "
929 <<scriptpath<<std::endl;
930 throw ModError("Failed to load and run "+scriptpath);
934 // Read Textures and calculate sha1 sums
937 // Initialize Environment
939 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
942 // Give environment reference to scripting api
943 scriptapi_add_environment(m_lua, m_env);
945 // Register us to receive map edit events
946 m_env->getMap().addEventReceiver(this);
948 // If file exists, load environment metadata
949 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
951 infostream<<"Server: Loading environment metadata"<<std::endl;
952 m_env->loadMeta(m_mapsavedir);
956 infostream<<"Server: Loading players"<<std::endl;
957 m_env->deSerializePlayers(m_mapsavedir);
960 Add some test ActiveBlockModifiers to environment
962 add_legacy_abms(m_env, m_nodedef);
967 infostream<<"Server::~Server()"<<std::endl;
970 Send shutdown message
973 JMutexAutoLock conlock(m_con_mutex);
975 std::wstring line = L"*** Server shutting down";
978 Send the message to clients
980 for(core::map<u16, RemoteClient*>::Iterator
981 i = m_clients.getIterator();
982 i.atEnd() == false; i++)
984 // Get client and check that it is valid
985 RemoteClient *client = i.getNode()->getValue();
986 assert(client->peer_id == i.getNode()->getKey());
987 if(client->serialization_version == SER_FMT_VER_INVALID)
991 SendChatMessage(client->peer_id, line);
993 catch(con::PeerNotFoundException &e)
999 JMutexAutoLock envlock(m_env_mutex);
1004 infostream<<"Server: Saving players"<<std::endl;
1005 m_env->serializePlayers(m_mapsavedir);
1008 Save environment metadata
1010 infostream<<"Server: Saving environment metadata"<<std::endl;
1011 m_env->saveMeta(m_mapsavedir);
1023 JMutexAutoLock clientslock(m_con_mutex);
1025 for(core::map<u16, RemoteClient*>::Iterator
1026 i = m_clients.getIterator();
1027 i.atEnd() == false; i++)
1030 // NOTE: These are removed by env destructor
1032 u16 peer_id = i.getNode()->getKey();
1033 JMutexAutoLock envlock(m_env_mutex);
1034 m_env->removePlayer(peer_id);
1038 delete i.getNode()->getValue();
1042 // Delete Environment
1048 delete m_craftitemdef;
1050 // Deinitialize scripting
1051 infostream<<"Server: Deinitializing scripting"<<std::endl;
1052 script_deinit(m_lua);
1055 void Server::start(unsigned short port)
1057 DSTACK(__FUNCTION_NAME);
1058 // Stop thread if already running
1061 // Initialize connection
1062 m_con.SetTimeoutMs(30);
1066 m_thread.setRun(true);
1069 infostream<<"Server: Started on port "<<port<<std::endl;
1074 DSTACK(__FUNCTION_NAME);
1076 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1078 // Stop threads (set run=false first so both start stopping)
1079 m_thread.setRun(false);
1080 m_emergethread.setRun(false);
1082 m_emergethread.stop();
1084 infostream<<"Server: Threads stopped"<<std::endl;
1087 void Server::step(float dtime)
1089 DSTACK(__FUNCTION_NAME);
1094 JMutexAutoLock lock(m_step_dtime_mutex);
1095 m_step_dtime += dtime;
1099 void Server::AsyncRunStep()
1101 DSTACK(__FUNCTION_NAME);
1103 g_profiler->add("Server::AsyncRunStep (num)", 1);
1107 JMutexAutoLock lock1(m_step_dtime_mutex);
1108 dtime = m_step_dtime;
1112 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1113 // Send blocks to clients
1120 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1122 //infostream<<"Server steps "<<dtime<<std::endl;
1123 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1126 JMutexAutoLock lock1(m_step_dtime_mutex);
1127 m_step_dtime -= dtime;
1134 m_uptime.set(m_uptime.get() + dtime);
1138 // Process connection's timeouts
1139 JMutexAutoLock lock2(m_con_mutex);
1140 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1141 m_con.RunTimeouts(dtime);
1145 // This has to be called so that the client list gets synced
1146 // with the peer list of the connection
1147 handlePeerChanges();
1151 Update m_time_of_day and overall game time
1154 JMutexAutoLock envlock(m_env_mutex);
1156 m_time_counter += dtime;
1157 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1158 u32 units = (u32)(m_time_counter*speed);
1159 m_time_counter -= (f32)units / speed;
1161 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1163 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1166 Send to clients at constant intervals
1169 m_time_of_day_send_timer -= dtime;
1170 if(m_time_of_day_send_timer < 0.0)
1172 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1174 //JMutexAutoLock envlock(m_env_mutex);
1175 JMutexAutoLock conlock(m_con_mutex);
1177 for(core::map<u16, RemoteClient*>::Iterator
1178 i = m_clients.getIterator();
1179 i.atEnd() == false; i++)
1181 RemoteClient *client = i.getNode()->getValue();
1182 //Player *player = m_env->getPlayer(client->peer_id);
1184 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1185 m_env->getTimeOfDay());
1187 m_con.Send(client->peer_id, 0, data, true);
1193 JMutexAutoLock lock(m_env_mutex);
1195 ScopeProfiler sp(g_profiler, "SEnv step");
1196 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1200 const float map_timer_and_unload_dtime = 2.92;
1201 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1203 JMutexAutoLock lock(m_env_mutex);
1204 // Run Map's timers and unload unused data
1205 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1206 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1207 g_settings->getFloat("server_unload_unused_data_timeout"));
1218 JMutexAutoLock lock(m_env_mutex);
1219 JMutexAutoLock lock2(m_con_mutex);
1221 ScopeProfiler sp(g_profiler, "Server: handle players");
1223 //float player_max_speed = BS * 4.0; // Normal speed
1224 float player_max_speed = BS * 20; // Fast speed
1225 float player_max_speed_up = BS * 20;
1227 player_max_speed *= 2.5; // Tolerance
1228 player_max_speed_up *= 2.5;
1230 for(core::map<u16, RemoteClient*>::Iterator
1231 i = m_clients.getIterator();
1232 i.atEnd() == false; i++)
1234 RemoteClient *client = i.getNode()->getValue();
1235 ServerRemotePlayer *player =
1236 static_cast<ServerRemotePlayer*>
1237 (m_env->getPlayer(client->peer_id));
1242 Check player movements
1244 NOTE: Actually the server should handle player physics like the
1245 client does and compare player's position to what is calculated
1246 on our side. This is required when eg. players fly due to an
1249 player->m_last_good_position_age += dtime;
1250 if(player->m_last_good_position_age >= 2.0){
1251 float age = player->m_last_good_position_age;
1252 v3f diff = (player->getPosition() - player->m_last_good_position);
1253 float d_vert = diff.Y;
1255 float d_horiz = diff.getLength();
1256 /*infostream<<player->getName()<<"'s horizontal speed is "
1257 <<(d_horiz/age)<<std::endl;*/
1258 if(d_horiz <= age * player_max_speed &&
1259 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1260 player->m_last_good_position = player->getPosition();
1262 actionstream<<"Player "<<player->getName()
1263 <<" moved too fast; resetting position"
1265 player->setPosition(player->m_last_good_position);
1266 SendMovePlayer(player);
1268 player->m_last_good_position_age = 0;
1272 Handle player HPs (die if hp=0)
1274 HandlePlayerHP(player, 0);
1277 Send player inventories and HPs if necessary
1279 if(player->m_inventory_not_sent){
1280 UpdateCrafting(player->peer_id);
1281 SendInventory(player->peer_id);
1283 if(player->m_hp_not_sent){
1284 SendPlayerHP(player);
1288 Add to environment if is not in respawn screen
1290 if(!player->m_is_in_environment && !player->m_respawn_active){
1291 player->m_removed = false;
1293 m_env->addActiveObject(player);
1298 /* Transform liquids */
1299 m_liquid_transform_timer += dtime;
1300 if(m_liquid_transform_timer >= 1.00)
1302 m_liquid_transform_timer -= 1.00;
1304 JMutexAutoLock lock(m_env_mutex);
1306 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1308 core::map<v3s16, MapBlock*> modified_blocks;
1309 m_env->getMap().transformLiquids(modified_blocks);
1314 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1315 ServerMap &map = ((ServerMap&)m_env->getMap());
1316 map.updateLighting(modified_blocks, lighting_modified_blocks);
1318 // Add blocks modified by lighting to modified_blocks
1319 for(core::map<v3s16, MapBlock*>::Iterator
1320 i = lighting_modified_blocks.getIterator();
1321 i.atEnd() == false; i++)
1323 MapBlock *block = i.getNode()->getValue();
1324 modified_blocks.insert(block->getPos(), block);
1328 Set the modified blocks unsent for all the clients
1331 JMutexAutoLock lock2(m_con_mutex);
1333 for(core::map<u16, RemoteClient*>::Iterator
1334 i = m_clients.getIterator();
1335 i.atEnd() == false; i++)
1337 RemoteClient *client = i.getNode()->getValue();
1339 if(modified_blocks.size() > 0)
1341 // Remove block from sent history
1342 client->SetBlocksNotSent(modified_blocks);
1347 // Periodically print some info
1349 float &counter = m_print_info_timer;
1355 JMutexAutoLock lock2(m_con_mutex);
1357 if(m_clients.size() != 0)
1358 infostream<<"Players:"<<std::endl;
1359 for(core::map<u16, RemoteClient*>::Iterator
1360 i = m_clients.getIterator();
1361 i.atEnd() == false; i++)
1363 //u16 peer_id = i.getNode()->getKey();
1364 RemoteClient *client = i.getNode()->getValue();
1365 Player *player = m_env->getPlayer(client->peer_id);
1368 infostream<<"* "<<player->getName()<<"\t";
1369 client->PrintInfo(infostream);
1374 //if(g_settings->getBool("enable_experimental"))
1378 Check added and deleted active objects
1381 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1382 JMutexAutoLock envlock(m_env_mutex);
1383 JMutexAutoLock conlock(m_con_mutex);
1385 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1387 // Radius inside which objects are active
1388 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1389 radius *= MAP_BLOCKSIZE;
1391 for(core::map<u16, RemoteClient*>::Iterator
1392 i = m_clients.getIterator();
1393 i.atEnd() == false; i++)
1395 RemoteClient *client = i.getNode()->getValue();
1397 // If definitions and textures have not been sent, don't
1398 // send objects either
1399 if(!client->definitions_sent)
1402 Player *player = m_env->getPlayer(client->peer_id);
1405 // This can happen if the client timeouts somehow
1406 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1408 <<" has no associated player"<<std::endl;*/
1411 v3s16 pos = floatToInt(player->getPosition(), BS);
1413 core::map<u16, bool> removed_objects;
1414 core::map<u16, bool> added_objects;
1415 m_env->getRemovedActiveObjects(pos, radius,
1416 client->m_known_objects, removed_objects);
1417 m_env->getAddedActiveObjects(pos, radius,
1418 client->m_known_objects, added_objects);
1420 // Ignore if nothing happened
1421 if(removed_objects.size() == 0 && added_objects.size() == 0)
1423 //infostream<<"active objects: none changed"<<std::endl;
1427 std::string data_buffer;
1431 // Handle removed objects
1432 writeU16((u8*)buf, removed_objects.size());
1433 data_buffer.append(buf, 2);
1434 for(core::map<u16, bool>::Iterator
1435 i = removed_objects.getIterator();
1436 i.atEnd()==false; i++)
1439 u16 id = i.getNode()->getKey();
1440 ServerActiveObject* obj = m_env->getActiveObject(id);
1442 // Add to data buffer for sending
1443 writeU16((u8*)buf, i.getNode()->getKey());
1444 data_buffer.append(buf, 2);
1446 // Remove from known objects
1447 client->m_known_objects.remove(i.getNode()->getKey());
1449 if(obj && obj->m_known_by_count > 0)
1450 obj->m_known_by_count--;
1453 // Handle added objects
1454 writeU16((u8*)buf, added_objects.size());
1455 data_buffer.append(buf, 2);
1456 for(core::map<u16, bool>::Iterator
1457 i = added_objects.getIterator();
1458 i.atEnd()==false; i++)
1461 u16 id = i.getNode()->getKey();
1462 ServerActiveObject* obj = m_env->getActiveObject(id);
1465 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1467 infostream<<"WARNING: "<<__FUNCTION_NAME
1468 <<": NULL object"<<std::endl;
1470 type = obj->getType();
1472 // Add to data buffer for sending
1473 writeU16((u8*)buf, id);
1474 data_buffer.append(buf, 2);
1475 writeU8((u8*)buf, type);
1476 data_buffer.append(buf, 1);
1479 data_buffer.append(serializeLongString(
1480 obj->getClientInitializationData()));
1482 data_buffer.append(serializeLongString(""));
1484 // Add to known objects
1485 client->m_known_objects.insert(i.getNode()->getKey(), false);
1488 obj->m_known_by_count++;
1492 SharedBuffer<u8> reply(2 + data_buffer.size());
1493 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1494 memcpy((char*)&reply[2], data_buffer.c_str(),
1495 data_buffer.size());
1497 m_con.Send(client->peer_id, 0, reply, true);
1499 infostream<<"Server: Sent object remove/add: "
1500 <<removed_objects.size()<<" removed, "
1501 <<added_objects.size()<<" added, "
1502 <<"packet size is "<<reply.getSize()<<std::endl;
1507 Collect a list of all the objects known by the clients
1508 and report it back to the environment.
1511 core::map<u16, bool> all_known_objects;
1513 for(core::map<u16, RemoteClient*>::Iterator
1514 i = m_clients.getIterator();
1515 i.atEnd() == false; i++)
1517 RemoteClient *client = i.getNode()->getValue();
1518 // Go through all known objects of client
1519 for(core::map<u16, bool>::Iterator
1520 i = client->m_known_objects.getIterator();
1521 i.atEnd()==false; i++)
1523 u16 id = i.getNode()->getKey();
1524 all_known_objects[id] = true;
1528 m_env->setKnownActiveObjects(whatever);
1534 Send object messages
1537 JMutexAutoLock envlock(m_env_mutex);
1538 JMutexAutoLock conlock(m_con_mutex);
1540 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1543 // Value = data sent by object
1544 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1546 // Get active object messages from environment
1549 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1553 core::list<ActiveObjectMessage>* message_list = NULL;
1554 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1555 n = buffered_messages.find(aom.id);
1558 message_list = new core::list<ActiveObjectMessage>;
1559 buffered_messages.insert(aom.id, message_list);
1563 message_list = n->getValue();
1565 message_list->push_back(aom);
1568 // Route data to every client
1569 for(core::map<u16, RemoteClient*>::Iterator
1570 i = m_clients.getIterator();
1571 i.atEnd()==false; i++)
1573 RemoteClient *client = i.getNode()->getValue();
1574 std::string reliable_data;
1575 std::string unreliable_data;
1576 // Go through all objects in message buffer
1577 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1578 j = buffered_messages.getIterator();
1579 j.atEnd()==false; j++)
1581 // If object is not known by client, skip it
1582 u16 id = j.getNode()->getKey();
1583 if(client->m_known_objects.find(id) == NULL)
1585 // Get message list of object
1586 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1587 // Go through every message
1588 for(core::list<ActiveObjectMessage>::Iterator
1589 k = list->begin(); k != list->end(); k++)
1591 // Compose the full new data with header
1592 ActiveObjectMessage aom = *k;
1593 std::string new_data;
1596 writeU16((u8*)&buf[0], aom.id);
1597 new_data.append(buf, 2);
1599 new_data += serializeString(aom.datastring);
1600 // Add data to buffer
1602 reliable_data += new_data;
1604 unreliable_data += new_data;
1608 reliable_data and unreliable_data are now ready.
1611 if(reliable_data.size() > 0)
1613 SharedBuffer<u8> reply(2 + reliable_data.size());
1614 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1615 memcpy((char*)&reply[2], reliable_data.c_str(),
1616 reliable_data.size());
1618 m_con.Send(client->peer_id, 0, reply, true);
1620 if(unreliable_data.size() > 0)
1622 SharedBuffer<u8> reply(2 + unreliable_data.size());
1623 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1624 memcpy((char*)&reply[2], unreliable_data.c_str(),
1625 unreliable_data.size());
1626 // Send as unreliable
1627 m_con.Send(client->peer_id, 0, reply, false);
1630 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1632 infostream<<"Server: Size of object message data: "
1633 <<"reliable: "<<reliable_data.size()
1634 <<", unreliable: "<<unreliable_data.size()
1639 // Clear buffered_messages
1640 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1641 i = buffered_messages.getIterator();
1642 i.atEnd()==false; i++)
1644 delete i.getNode()->getValue();
1648 } // enable_experimental
1651 Send queued-for-sending map edit events.
1654 // Don't send too many at a time
1657 // Single change sending is disabled if queue size is not small
1658 bool disable_single_change_sending = false;
1659 if(m_unsent_map_edit_queue.size() >= 4)
1660 disable_single_change_sending = true;
1662 bool got_any_events = false;
1664 // We'll log the amount of each
1667 while(m_unsent_map_edit_queue.size() != 0)
1669 got_any_events = true;
1671 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1673 // Players far away from the change are stored here.
1674 // Instead of sending the changes, MapBlocks are set not sent
1676 core::list<u16> far_players;
1678 if(event->type == MEET_ADDNODE)
1680 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1681 prof.add("MEET_ADDNODE", 1);
1682 if(disable_single_change_sending)
1683 sendAddNode(event->p, event->n, event->already_known_by_peer,
1686 sendAddNode(event->p, event->n, event->already_known_by_peer,
1689 else if(event->type == MEET_REMOVENODE)
1691 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1692 prof.add("MEET_REMOVENODE", 1);
1693 if(disable_single_change_sending)
1694 sendRemoveNode(event->p, event->already_known_by_peer,
1697 sendRemoveNode(event->p, event->already_known_by_peer,
1700 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1702 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1703 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1704 setBlockNotSent(event->p);
1706 else if(event->type == MEET_OTHER)
1708 infostream<<"Server: MEET_OTHER"<<std::endl;
1709 prof.add("MEET_OTHER", 1);
1710 for(core::map<v3s16, bool>::Iterator
1711 i = event->modified_blocks.getIterator();
1712 i.atEnd()==false; i++)
1714 v3s16 p = i.getNode()->getKey();
1720 prof.add("unknown", 1);
1721 infostream<<"WARNING: Server: Unknown MapEditEvent "
1722 <<((u32)event->type)<<std::endl;
1726 Set blocks not sent to far players
1728 if(far_players.size() > 0)
1730 // Convert list format to that wanted by SetBlocksNotSent
1731 core::map<v3s16, MapBlock*> modified_blocks2;
1732 for(core::map<v3s16, bool>::Iterator
1733 i = event->modified_blocks.getIterator();
1734 i.atEnd()==false; i++)
1736 v3s16 p = i.getNode()->getKey();
1737 modified_blocks2.insert(p,
1738 m_env->getMap().getBlockNoCreateNoEx(p));
1740 // Set blocks not sent
1741 for(core::list<u16>::Iterator
1742 i = far_players.begin();
1743 i != far_players.end(); i++)
1746 RemoteClient *client = getClient(peer_id);
1749 client->SetBlocksNotSent(modified_blocks2);
1755 /*// Don't send too many at a time
1757 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1763 infostream<<"Server: MapEditEvents:"<<std::endl;
1764 prof.print(infostream);
1770 Trigger emergethread (it somehow gets to a non-triggered but
1771 bysy state sometimes)
1774 float &counter = m_emergethread_trigger_timer;
1780 m_emergethread.trigger();
1784 // Save map, players and auth stuff
1786 float &counter = m_savemap_timer;
1788 if(counter >= g_settings->getFloat("server_map_save_interval"))
1792 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1795 if(m_authmanager.isModified())
1796 m_authmanager.save();
1799 if(m_banmanager.isModified())
1800 m_banmanager.save();
1803 JMutexAutoLock lock(m_env_mutex);
1805 // Save changed parts of map
1806 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1809 m_env->serializePlayers(m_mapsavedir);
1811 // Save environment metadata
1812 m_env->saveMeta(m_mapsavedir);
1817 void Server::Receive()
1819 DSTACK(__FUNCTION_NAME);
1820 SharedBuffer<u8> data;
1825 JMutexAutoLock conlock(m_con_mutex);
1826 datasize = m_con.Receive(peer_id, data);
1829 // This has to be called so that the client list gets synced
1830 // with the peer list of the connection
1831 handlePeerChanges();
1833 ProcessData(*data, datasize, peer_id);
1835 catch(con::InvalidIncomingDataException &e)
1837 infostream<<"Server::Receive(): "
1838 "InvalidIncomingDataException: what()="
1839 <<e.what()<<std::endl;
1841 catch(con::PeerNotFoundException &e)
1843 //NOTE: This is not needed anymore
1845 // The peer has been disconnected.
1846 // Find the associated player and remove it.
1848 /*JMutexAutoLock envlock(m_env_mutex);
1850 infostream<<"ServerThread: peer_id="<<peer_id
1851 <<" has apparently closed connection. "
1852 <<"Removing player."<<std::endl;
1854 m_env->removePlayer(peer_id);*/
1858 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1860 DSTACK(__FUNCTION_NAME);
1861 // Environment is locked first.
1862 JMutexAutoLock envlock(m_env_mutex);
1863 JMutexAutoLock conlock(m_con_mutex);
1866 Address address = m_con.GetPeerAddress(peer_id);
1868 // drop player if is ip is banned
1869 if(m_banmanager.isIpBanned(address.serializeString())){
1870 SendAccessDenied(m_con, peer_id,
1871 L"Your ip is banned. Banned name was "
1872 +narrow_to_wide(m_banmanager.getBanName(
1873 address.serializeString())));
1874 m_con.DeletePeer(peer_id);
1878 catch(con::PeerNotFoundException &e)
1880 infostream<<"Server::ProcessData(): Cancelling: peer "
1881 <<peer_id<<" not found"<<std::endl;
1885 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1893 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1895 if(command == TOSERVER_INIT)
1897 // [0] u16 TOSERVER_INIT
1898 // [2] u8 SER_FMT_VER_HIGHEST
1899 // [3] u8[20] player_name
1900 // [23] u8[28] password <--- can be sent without this, from old versions
1902 if(datasize < 2+1+PLAYERNAME_SIZE)
1905 infostream<<"Server: Got TOSERVER_INIT from "
1906 <<peer_id<<std::endl;
1908 // First byte after command is maximum supported
1909 // serialization version
1910 u8 client_max = data[2];
1911 u8 our_max = SER_FMT_VER_HIGHEST;
1912 // Use the highest version supported by both
1913 u8 deployed = core::min_(client_max, our_max);
1914 // If it's lower than the lowest supported, give up.
1915 if(deployed < SER_FMT_VER_LOWEST)
1916 deployed = SER_FMT_VER_INVALID;
1918 //peer->serialization_version = deployed;
1919 getClient(peer_id)->pending_serialization_version = deployed;
1921 if(deployed == SER_FMT_VER_INVALID)
1923 infostream<<"Server: Cannot negotiate "
1924 "serialization version with peer "
1925 <<peer_id<<std::endl;
1926 SendAccessDenied(m_con, peer_id, std::wstring(
1927 L"Your client's version is not supported.\n"
1928 L"Server version is ")
1929 + narrow_to_wide(VERSION_STRING) + L"."
1935 Read and check network protocol version
1938 u16 net_proto_version = 0;
1939 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1941 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1944 getClient(peer_id)->net_proto_version = net_proto_version;
1946 if(net_proto_version == 0)
1948 SendAccessDenied(m_con, peer_id, std::wstring(
1949 L"Your client's version is not supported.\n"
1950 L"Server version is ")
1951 + narrow_to_wide(VERSION_STRING) + L"."
1956 if(g_settings->getBool("strict_protocol_version_checking"))
1958 if(net_proto_version != PROTOCOL_VERSION)
1960 SendAccessDenied(m_con, peer_id, std::wstring(
1961 L"Your client's version is not supported.\n"
1962 L"Server version is ")
1963 + narrow_to_wide(VERSION_STRING) + L",\n"
1964 + L"server's PROTOCOL_VERSION is "
1965 + narrow_to_wide(itos(PROTOCOL_VERSION))
1966 + L", client's PROTOCOL_VERSION is "
1967 + narrow_to_wide(itos(net_proto_version))
1978 char playername[PLAYERNAME_SIZE];
1979 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1981 playername[i] = data[3+i];
1983 playername[PLAYERNAME_SIZE-1] = 0;
1985 if(playername[0]=='\0')
1987 infostream<<"Server: Player has empty name"<<std::endl;
1988 SendAccessDenied(m_con, peer_id,
1993 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1995 infostream<<"Server: Player has invalid name"<<std::endl;
1996 SendAccessDenied(m_con, peer_id,
1997 L"Name contains unallowed characters");
2002 char password[PASSWORD_SIZE];
2003 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2005 // old version - assume blank password
2010 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2012 password[i] = data[23+i];
2014 password[PASSWORD_SIZE-1] = 0;
2017 // Add player to auth manager
2018 if(m_authmanager.exists(playername) == false)
2020 std::wstring default_password =
2021 narrow_to_wide(g_settings->get("default_password"));
2022 std::string translated_default_password =
2023 translatePassword(playername, default_password);
2025 // If default_password is empty, allow any initial password
2026 if (default_password.length() == 0)
2027 translated_default_password = password;
2029 infostream<<"Server: adding player "<<playername
2030 <<" to auth manager"<<std::endl;
2031 m_authmanager.add(playername);
2032 m_authmanager.setPassword(playername, translated_default_password);
2033 m_authmanager.setPrivs(playername,
2034 stringToPrivs(g_settings->get("default_privs")));
2035 m_authmanager.save();
2038 std::string checkpwd = m_authmanager.getPassword(playername);
2040 /*infostream<<"Server: Client gave password '"<<password
2041 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2043 if(password != checkpwd)
2045 infostream<<"Server: peer_id="<<peer_id
2046 <<": supplied invalid password for "
2047 <<playername<<std::endl;
2048 SendAccessDenied(m_con, peer_id, L"Invalid password");
2052 // Enforce user limit.
2053 // Don't enforce for users that have some admin right
2054 if(m_clients.size() >= g_settings->getU16("max_users") &&
2055 (m_authmanager.getPrivs(playername)
2056 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2057 playername != g_settings->get("name"))
2059 SendAccessDenied(m_con, peer_id, L"Too many users.");
2064 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2066 // If failed, cancel
2069 infostream<<"Server: peer_id="<<peer_id
2070 <<": failed to emerge player"<<std::endl;
2075 Answer with a TOCLIENT_INIT
2078 SharedBuffer<u8> reply(2+1+6+8);
2079 writeU16(&reply[0], TOCLIENT_INIT);
2080 writeU8(&reply[2], deployed);
2081 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2082 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2085 m_con.Send(peer_id, 0, reply, true);
2089 Send complete position information
2091 SendMovePlayer(player);
2096 if(command == TOSERVER_INIT2)
2098 infostream<<"Server: Got TOSERVER_INIT2 from "
2099 <<peer_id<<std::endl;
2102 getClient(peer_id)->serialization_version
2103 = getClient(peer_id)->pending_serialization_version;
2106 Send some initialization data
2109 // Send tool definitions
2110 SendToolDef(m_con, peer_id, m_toolmgr);
2112 // Send node definitions
2113 SendNodeDef(m_con, peer_id, m_nodedef);
2115 // Send CraftItem definitions
2116 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2118 // Send texture announcement
2119 SendTextureAnnouncement(peer_id);
2121 // Send player info to all players
2122 //SendPlayerInfos();
2124 // Send inventory to player
2125 UpdateCrafting(peer_id);
2126 SendInventory(peer_id);
2128 // Send player items to all players
2131 Player *player = m_env->getPlayer(peer_id);
2134 SendPlayerHP(player);
2138 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2139 m_env->getTimeOfDay());
2140 m_con.Send(peer_id, 0, data, true);
2143 // Send information about server to player in chat
2144 SendChatMessage(peer_id, getStatusString());
2146 // Send information about joining in chat
2148 std::wstring name = L"unknown";
2149 Player *player = m_env->getPlayer(peer_id);
2151 name = narrow_to_wide(player->getName());
2153 std::wstring message;
2156 message += L" joined game";
2157 BroadcastChatMessage(message);
2160 // Warnings about protocol version can be issued here
2161 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2163 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2167 Check HP, respawn if necessary
2169 HandlePlayerHP(player, 0);
2175 std::ostringstream os(std::ios_base::binary);
2176 for(core::map<u16, RemoteClient*>::Iterator
2177 i = m_clients.getIterator();
2178 i.atEnd() == false; i++)
2180 RemoteClient *client = i.getNode()->getValue();
2181 assert(client->peer_id == i.getNode()->getKey());
2182 if(client->serialization_version == SER_FMT_VER_INVALID)
2185 Player *player = m_env->getPlayer(client->peer_id);
2188 // Get name of player
2189 os<<player->getName()<<" ";
2192 actionstream<<player->getName()<<" joins game. List of players: "
2193 <<os.str()<<std::endl;
2199 if(peer_ser_ver == SER_FMT_VER_INVALID)
2201 infostream<<"Server::ProcessData(): Cancelling: Peer"
2202 " serialization format invalid or not initialized."
2203 " Skipping incoming command="<<command<<std::endl;
2207 Player *player = m_env->getPlayer(peer_id);
2208 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2211 infostream<<"Server::ProcessData(): Cancelling: "
2212 "No player for peer_id="<<peer_id
2216 if(command == TOSERVER_PLAYERPOS)
2218 if(datasize < 2+12+12+4+4)
2222 v3s32 ps = readV3S32(&data[start+2]);
2223 v3s32 ss = readV3S32(&data[start+2+12]);
2224 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2225 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2226 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2227 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2228 pitch = wrapDegrees(pitch);
2229 yaw = wrapDegrees(yaw);
2231 player->setPosition(position);
2232 player->setSpeed(speed);
2233 player->setPitch(pitch);
2234 player->setYaw(yaw);
2236 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2237 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2238 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2240 else if(command == TOSERVER_GOTBLOCKS)
2253 u16 count = data[2];
2254 for(u16 i=0; i<count; i++)
2256 if((s16)datasize < 2+1+(i+1)*6)
2257 throw con::InvalidIncomingDataException
2258 ("GOTBLOCKS length is too short");
2259 v3s16 p = readV3S16(&data[2+1+i*6]);
2260 /*infostream<<"Server: GOTBLOCKS ("
2261 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2262 RemoteClient *client = getClient(peer_id);
2263 client->GotBlock(p);
2266 else if(command == TOSERVER_DELETEDBLOCKS)
2279 u16 count = data[2];
2280 for(u16 i=0; i<count; i++)
2282 if((s16)datasize < 2+1+(i+1)*6)
2283 throw con::InvalidIncomingDataException
2284 ("DELETEDBLOCKS length is too short");
2285 v3s16 p = readV3S16(&data[2+1+i*6]);
2286 /*infostream<<"Server: DELETEDBLOCKS ("
2287 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2288 RemoteClient *client = getClient(peer_id);
2289 client->SetBlockNotSent(p);
2292 else if(command == TOSERVER_CLICK_OBJECT)
2294 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2297 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2299 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2302 else if(command == TOSERVER_GROUND_ACTION)
2304 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2308 else if(command == TOSERVER_RELEASE)
2310 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2313 else if(command == TOSERVER_SIGNTEXT)
2315 infostream<<"Server: SIGNTEXT not supported anymore"
2319 else if(command == TOSERVER_SIGNNODETEXT)
2321 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2329 std::string datastring((char*)&data[2], datasize-2);
2330 std::istringstream is(datastring, std::ios_base::binary);
2333 is.read((char*)buf, 6);
2334 v3s16 p = readV3S16(buf);
2335 is.read((char*)buf, 2);
2336 u16 textlen = readU16(buf);
2338 for(u16 i=0; i<textlen; i++)
2340 is.read((char*)buf, 1);
2341 text += (char)buf[0];
2344 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2348 meta->setText(text);
2350 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2351 <<" at "<<PP(p)<<std::endl;
2353 v3s16 blockpos = getNodeBlockPos(p);
2354 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2357 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2361 setBlockNotSent(blockpos);
2363 else if(command == TOSERVER_INVENTORY_ACTION)
2365 /*// Ignore inventory changes if in creative mode
2366 if(g_settings->getBool("creative_mode") == true)
2368 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2372 // Strip command and create a stream
2373 std::string datastring((char*)&data[2], datasize-2);
2374 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2375 std::istringstream is(datastring, std::ios_base::binary);
2377 InventoryAction *a = InventoryAction::deSerialize(is);
2380 infostream<<"TOSERVER_INVENTORY_ACTION: "
2381 <<"InventoryAction::deSerialize() returned NULL"
2387 c.current_player = player;
2390 Handle restrictions and special cases of the move action
2392 if(a->getType() == IACTION_MOVE
2393 && g_settings->getBool("creative_mode") == false)
2395 InventoryList *rlist = player->inventory.getList("craftresult");
2397 InventoryList *clist = player->inventory.getList("craft");
2399 InventoryList *mlist = player->inventory.getList("main");
2402 IMoveAction *ma = (IMoveAction*)a;
2405 Disable moving items into craftresult from elsewhere
2407 if(ma->to_inv == "current_player"
2408 && ma->to_list == "craftresult"
2409 && (ma->from_inv != "current_player"
2410 || ma->from_list != "craftresult"))
2412 infostream<<"Ignoring IMoveAction from "
2413 <<ma->from_inv<<":"<<ma->from_list
2414 <<" to "<<ma->to_inv<<":"<<ma->to_list
2415 <<" because dst is craftresult"
2416 <<" and src isn't craftresult"<<std::endl;
2422 Handle crafting (source is craftresult, which is preview)
2424 if(ma->from_inv == "current_player"
2425 && ma->from_list == "craftresult"
2426 && player->craftresult_is_preview)
2429 If the craftresult is placed on itself, crafting takes
2430 place and result is moved into main list
2432 if(ma->to_inv == "current_player"
2433 && ma->to_list == "craftresult")
2435 // Except if main list doesn't have free slots
2436 if(mlist->getFreeSlots() == 0){
2437 infostream<<"Cannot craft: Main list doesn't have"
2438 <<" free slots"<<std::endl;
2443 player->craftresult_is_preview = false;
2444 clist->decrementMaterials(1);
2446 InventoryItem *item1 = rlist->changeItem(0, NULL);
2447 mlist->addItem(item1);
2449 srp->m_inventory_not_sent = true;
2455 Disable action if there are no free slots in
2458 If the item is placed on an item that is not of the
2459 same kind, the existing item will be first moved to
2460 craftresult and immediately moved to the free slot.
2463 Inventory *inv_to = InventoryManager::getInventory(&c, ma->to_inv);
2465 InventoryList *list_to = inv_to->getList(ma->to_list);
2467 if(list_to->getFreeSlots() == 0){
2468 infostream<<"Cannot craft: Destination doesn't have"
2469 <<" free slots"<<std::endl;
2473 }while(0); // Allow break
2478 player->craftresult_is_preview = false;
2479 clist->decrementMaterials(1);
2481 /* Print out action */
2482 InventoryItem *item = rlist->getItem(0);
2483 std::string itemstring = "NULL";
2485 itemstring = item->getItemString();
2486 actionstream<<player->getName()<<" crafts "
2487 <<itemstring<<std::endl;
2490 a->apply(&c, this, m_env);
2500 // Disallow moving items in elsewhere than player's inventory
2501 // if not allowed to build
2502 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
2503 && (ma->from_inv != "current_player"
2504 || ma->to_inv != "current_player"))
2506 infostream<<"Cannot move outside of player's inventory: "
2507 <<"No build privilege"<<std::endl;
2512 // If player is not an admin, check for ownership of src
2513 if(ma->from_inv != "current_player"
2514 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2516 Strfnd fn(ma->from_inv);
2517 std::string id0 = fn.next(":");
2518 if(id0 == "nodemeta")
2521 p.X = stoi(fn.next(","));
2522 p.Y = stoi(fn.next(","));
2523 p.Z = stoi(fn.next(","));
2524 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2525 if(meta->getOwner() != "" &&
2526 meta->getOwner() != player->getName())
2528 infostream<<"Cannot move item: "
2529 "not owner of metadata"
2536 // If player is not an admin, check for ownership of dst
2537 if(ma->to_inv != "current_player"
2538 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2540 Strfnd fn(ma->to_inv);
2541 std::string id0 = fn.next(":");
2542 if(id0 == "nodemeta")
2545 p.X = stoi(fn.next(","));
2546 p.Y = stoi(fn.next(","));
2547 p.Z = stoi(fn.next(","));
2548 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2549 if(meta->getOwner() != "" &&
2550 meta->getOwner() != player->getName())
2552 infostream<<"Cannot move item: "
2553 "not owner of metadata"
2562 Handle restrictions and special cases of the drop action
2564 else if(a->getType() == IACTION_DROP)
2566 IDropAction *da = (IDropAction*)a;
2567 // Disallow dropping items if not allowed to build
2568 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2573 // If player is not an admin, check for ownership
2574 else if (da->from_inv != "current_player"
2575 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2577 Strfnd fn(da->from_inv);
2578 std::string id0 = fn.next(":");
2579 if(id0 == "nodemeta")
2582 p.X = stoi(fn.next(","));
2583 p.Y = stoi(fn.next(","));
2584 p.Z = stoi(fn.next(","));
2585 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2586 if(meta->getOwner() != "" &&
2587 meta->getOwner() != player->getName())
2589 infostream<<"Cannot move item: "
2590 "not owner of metadata"
2600 a->apply(&c, this, m_env);
2604 else if(command == TOSERVER_CHAT_MESSAGE)
2612 std::string datastring((char*)&data[2], datasize-2);
2613 std::istringstream is(datastring, std::ios_base::binary);
2616 is.read((char*)buf, 2);
2617 u16 len = readU16(buf);
2619 std::wstring message;
2620 for(u16 i=0; i<len; i++)
2622 is.read((char*)buf, 2);
2623 message += (wchar_t)readU16(buf);
2626 // Get player name of this client
2627 std::wstring name = narrow_to_wide(player->getName());
2630 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2631 wide_to_narrow(message));
2632 // If script ate the message, don't proceed
2636 // Line to send to players
2638 // Whether to send to the player that sent the line
2639 bool send_to_sender = false;
2640 // Whether to send to other players
2641 bool send_to_others = false;
2643 // Local player gets all privileges regardless of
2644 // what's set on their account.
2645 u64 privs = getPlayerPrivs(player);
2648 if(message[0] == L'/')
2650 size_t strip_size = 1;
2651 if (message[1] == L'#') // support old-style commans
2653 message = message.substr(strip_size);
2655 WStrfnd f1(message);
2656 f1.next(L" "); // Skip over /#whatever
2657 std::wstring paramstring = f1.next(L"");
2659 ServerCommandContext *ctx = new ServerCommandContext(
2660 str_split(message, L' '),
2667 std::wstring reply(processServerCommand(ctx));
2668 send_to_sender = ctx->flags & SEND_TO_SENDER;
2669 send_to_others = ctx->flags & SEND_TO_OTHERS;
2671 if (ctx->flags & SEND_NO_PREFIX)
2674 line += L"Server: " + reply;
2681 if(privs & PRIV_SHOUT)
2687 send_to_others = true;
2691 line += L"Server: You are not allowed to shout";
2692 send_to_sender = true;
2699 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2702 Send the message to clients
2704 for(core::map<u16, RemoteClient*>::Iterator
2705 i = m_clients.getIterator();
2706 i.atEnd() == false; i++)
2708 // Get client and check that it is valid
2709 RemoteClient *client = i.getNode()->getValue();
2710 assert(client->peer_id == i.getNode()->getKey());
2711 if(client->serialization_version == SER_FMT_VER_INVALID)
2715 bool sender_selected = (peer_id == client->peer_id);
2716 if(sender_selected == true && send_to_sender == false)
2718 if(sender_selected == false && send_to_others == false)
2721 SendChatMessage(client->peer_id, line);
2725 else if(command == TOSERVER_DAMAGE)
2727 std::string datastring((char*)&data[2], datasize-2);
2728 std::istringstream is(datastring, std::ios_base::binary);
2729 u8 damage = readU8(is);
2731 if(g_settings->getBool("enable_damage"))
2733 actionstream<<player->getName()<<" damaged by "
2734 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2737 HandlePlayerHP(player, damage);
2741 SendPlayerHP(player);
2744 else if(command == TOSERVER_PASSWORD)
2747 [0] u16 TOSERVER_PASSWORD
2748 [2] u8[28] old password
2749 [30] u8[28] new password
2752 if(datasize != 2+PASSWORD_SIZE*2)
2754 /*char password[PASSWORD_SIZE];
2755 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2756 password[i] = data[2+i];
2757 password[PASSWORD_SIZE-1] = 0;*/
2759 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2767 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2769 char c = data[2+PASSWORD_SIZE+i];
2775 infostream<<"Server: Client requests a password change from "
2776 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2778 std::string playername = player->getName();
2780 if(m_authmanager.exists(playername) == false)
2782 infostream<<"Server: playername not found in authmanager"<<std::endl;
2783 // Wrong old password supplied!!
2784 SendChatMessage(peer_id, L"playername not found in authmanager");
2788 std::string checkpwd = m_authmanager.getPassword(playername);
2790 if(oldpwd != checkpwd)
2792 infostream<<"Server: invalid old password"<<std::endl;
2793 // Wrong old password supplied!!
2794 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2798 actionstream<<player->getName()<<" changes password"<<std::endl;
2800 m_authmanager.setPassword(playername, newpwd);
2802 infostream<<"Server: password change successful for "<<playername
2804 SendChatMessage(peer_id, L"Password change successful");
2806 else if(command == TOSERVER_PLAYERITEM)
2811 u16 item = readU16(&data[2]);
2812 player->wieldItem(item);
2813 SendWieldedItem(player);
2815 else if(command == TOSERVER_RESPAWN)
2820 srp->m_respawn_active = false;
2822 RespawnPlayer(player);
2824 actionstream<<player->getName()<<" respawns at "
2825 <<PP(player->getPosition()/BS)<<std::endl;
2827 // ActiveObject is added to environment in AsyncRunStep after
2828 // the previous addition has been succesfully removed
2830 else if(command == TOSERVER_REQUEST_TEXTURES) {
2831 std::string datastring((char*)&data[2], datasize-2);
2832 std::istringstream is(datastring, std::ios_base::binary);
2834 infostream<<"TOSERVER_REQUEST_TEXTURES: "<<std::endl;
2836 core::list<TextureRequest> tosend;
2838 u16 numtextures = readU16(is);
2840 for(int i = 0; i < numtextures; i++) {
2842 std::string name = deSerializeString(is);
2844 tosend.push_back(TextureRequest(name));
2845 infostream<<"TOSERVER_REQUEST_TEXTURES: requested texture " << name <<std::endl;
2848 SendTexturesRequested(peer_id, tosend);
2850 // Now the client should know about everything
2851 // (definitions and textures)
2852 getClient(peer_id)->definitions_sent = true;
2854 else if(command == TOSERVER_INTERACT)
2856 std::string datastring((char*)&data[2], datasize-2);
2857 std::istringstream is(datastring, std::ios_base::binary);
2863 [5] u32 length of the next item
2864 [9] serialized PointedThing
2866 0: start digging (from undersurface) or use
2867 1: stop digging (all parameters ignored)
2868 2: digging completed
2869 3: place block or item (to abovesurface)
2872 u8 action = readU8(is);
2873 u16 item_i = readU16(is);
2874 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2875 PointedThing pointed;
2876 pointed.deSerialize(tmp_is);
2878 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2880 v3f player_pos = srp->m_last_good_position;
2882 // Update wielded item
2883 srp->wieldItem(item_i);
2885 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2886 v3s16 p_under = pointed.node_undersurface;
2887 v3s16 p_above = pointed.node_abovesurface;
2889 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2890 ServerActiveObject *pointed_object = NULL;
2891 if(pointed.type == POINTEDTHING_OBJECT)
2893 pointed_object = m_env->getActiveObject(pointed.object_id);
2894 if(pointed_object == NULL)
2896 infostream<<"TOSERVER_INTERACT: "
2897 "pointed object is NULL"<<std::endl;
2904 Check that target is reasonably close
2905 (only when digging or placing things)
2907 if(action == 0 || action == 2 || action == 3)
2909 v3f pointed_pos = player_pos;
2910 if(pointed.type == POINTEDTHING_NODE)
2912 pointed_pos = intToFloat(p_under, BS);
2914 else if(pointed.type == POINTEDTHING_OBJECT)
2916 pointed_pos = pointed_object->getBasePosition();
2919 float d = player_pos.getDistanceFrom(pointed_pos);
2920 float max_d = BS * 10; // Just some large enough value
2922 actionstream<<"Player "<<player->getName()
2923 <<" tried to access "<<pointed.dump()
2925 <<"d="<<d<<", max_d="<<max_d
2926 <<". ignoring."<<std::endl;
2927 // Re-send block to revert change on client-side
2928 RemoteClient *client = getClient(peer_id);
2929 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2930 client->SetBlockNotSent(blockpos);
2937 Make sure the player is allowed to do it
2939 bool build_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
2942 infostream<<"Ignoring interaction from player "<<player->getName()
2943 <<" because privileges are "<<getPlayerPrivs(player)
2945 // NOTE: no return; here, fall through
2949 0: start digging or punch object
2953 if(pointed.type == POINTEDTHING_NODE)
2956 NOTE: This can be used in the future to check if
2957 somebody is cheating, by checking the timing.
2959 bool cannot_punch_node = !build_priv;
2961 MapNode n(CONTENT_IGNORE);
2965 n = m_env->getMap().getNode(p_under);
2967 catch(InvalidPositionException &e)
2969 infostream<<"Server: Not punching: Node not found."
2970 <<" Adding block to emerge queue."
2972 m_emerge_queue.addBlock(peer_id,
2973 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2974 cannot_punch_node = true;
2977 if(cannot_punch_node)
2983 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2985 else if(pointed.type == POINTEDTHING_OBJECT)
2990 // Skip if object has been removed
2991 if(pointed_object->m_removed)
2994 actionstream<<player->getName()<<" punches object "
2995 <<pointed.object_id<<std::endl;
2998 pointed_object->punch(srp, srp->m_time_from_last_punch);
2999 srp->m_time_from_last_punch = 0;
3007 else if(action == 1)
3012 2: Digging completed
3014 else if(action == 2)
3016 // Only complete digging of nodes
3017 if(pointed.type != POINTEDTHING_NODE)
3020 // Mandatory parameter; actually used for nothing
3021 core::map<v3s16, MapBlock*> modified_blocks;
3023 content_t material = CONTENT_IGNORE;
3024 u8 mineral = MINERAL_NONE;
3026 bool cannot_remove_node = !build_priv;
3028 MapNode n(CONTENT_IGNORE);
3031 n = m_env->getMap().getNode(p_under);
3033 mineral = n.getMineral(m_nodedef);
3034 // Get material at position
3035 material = n.getContent();
3036 // If not yet cancelled
3037 if(cannot_remove_node == false)
3039 // If it's not diggable, do nothing
3040 if(m_nodedef->get(material).diggable == false)
3042 infostream<<"Server: Not finishing digging: "
3043 <<"Node not diggable"
3045 cannot_remove_node = true;
3048 // If not yet cancelled
3049 if(cannot_remove_node == false)
3051 // Get node metadata
3052 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3053 if(meta && meta->nodeRemovalDisabled() == true)
3055 infostream<<"Server: Not finishing digging: "
3056 <<"Node metadata disables removal"
3058 cannot_remove_node = true;
3062 catch(InvalidPositionException &e)
3064 infostream<<"Server: Not finishing digging: Node not found."
3065 <<" Adding block to emerge queue."
3067 m_emerge_queue.addBlock(peer_id,
3068 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3069 cannot_remove_node = true;
3073 If node can't be removed, set block to be re-sent to
3076 if(cannot_remove_node)
3078 infostream<<"Server: Not finishing digging."<<std::endl;
3080 // Client probably has wrong data.
3081 // Set block not sent, so that client will get
3083 infostream<<"Client "<<peer_id<<" tried to dig "
3084 <<"node; but node cannot be removed."
3085 <<" setting MapBlock not sent."<<std::endl;
3086 RemoteClient *client = getClient(peer_id);
3087 v3s16 blockpos = getNodeBlockPos(p_under);
3088 client->SetBlockNotSent(blockpos);
3093 actionstream<<player->getName()<<" digs "<<PP(p_under)
3094 <<", gets material "<<(int)material<<", mineral "
3095 <<(int)mineral<<std::endl;
3098 Send the removal to all close-by players.
3099 - If other player is close, send REMOVENODE
3100 - Otherwise set blocks not sent
3102 core::list<u16> far_players;
3103 sendRemoveNode(p_under, peer_id, &far_players, 30);
3106 Update and send inventory
3109 if(g_settings->getBool("creative_mode") == false)
3114 InventoryList *mlist = player->inventory.getList("main");
3117 InventoryItem *item = mlist->getItem(item_i);
3118 if(item && (std::string)item->getName() == "ToolItem")
3120 ToolItem *titem = (ToolItem*)item;
3121 std::string toolname = titem->getToolName();
3123 // Get digging properties for material and tool
3124 ToolDiggingProperties tp =
3125 m_toolmgr->getDiggingProperties(toolname);
3126 DiggingProperties prop =
3127 getDiggingProperties(material, &tp, m_nodedef);
3129 if(prop.diggable == false)
3131 infostream<<"Server: WARNING: Player digged"
3132 <<" with impossible material + tool"
3133 <<" combination"<<std::endl;
3136 bool weared_out = titem->addWear(prop.wear);
3140 mlist->deleteItem(item_i);
3143 srp->m_inventory_not_sent = true;
3148 Add dug item to inventory
3151 InventoryItem *item = NULL;
3153 if(mineral != MINERAL_NONE)
3154 item = getDiggedMineralItem(mineral, this);
3159 const std::string &dug_s = m_nodedef->get(material).dug_item;
3162 std::istringstream is(dug_s, std::ios::binary);
3163 item = InventoryItem::deSerialize(is, this);
3169 // Add a item to inventory
3170 player->inventory.addItem("main", item);
3171 srp->m_inventory_not_sent = true;
3177 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3178 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3179 if(extra_dug_s != "" && extra_rarity != 0
3180 && myrand() % extra_rarity == 0)
3182 std::istringstream is(extra_dug_s, std::ios::binary);
3183 item = InventoryItem::deSerialize(is, this);
3189 // Add a item to inventory
3190 player->inventory.addItem("main", item);
3191 srp->m_inventory_not_sent = true;
3197 (this takes some time so it is done after the quick stuff)
3200 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3202 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3205 Set blocks not sent to far players
3207 for(core::list<u16>::Iterator
3208 i = far_players.begin();
3209 i != far_players.end(); i++)
3212 RemoteClient *client = getClient(peer_id);
3215 client->SetBlocksNotSent(modified_blocks);
3221 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3225 3: place block or right-click object
3227 else if(action == 3)
3229 if(pointed.type == POINTEDTHING_NODE)
3231 InventoryList *ilist = player->inventory.getList("main");
3236 InventoryItem *item = ilist->getItem(item_i);
3238 // If there is no item, it is not possible to add it anywhere
3243 Handle material items
3245 if(std::string("MaterialItem") == item->getName())
3247 bool cannot_place_node = !build_priv;
3250 // Don't add a node if this is not a free space
3251 MapNode n2 = m_env->getMap().getNode(p_above);
3252 if(m_nodedef->get(n2).buildable_to == false)
3254 infostream<<"Client "<<peer_id<<" tried to place"
3255 <<" node in invalid position."<<std::endl;
3256 cannot_place_node = true;
3259 catch(InvalidPositionException &e)
3261 infostream<<"Server: Ignoring ADDNODE: Node not found"
3262 <<" Adding block to emerge queue."
3264 m_emerge_queue.addBlock(peer_id,
3265 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3266 cannot_place_node = true;
3269 if(cannot_place_node)
3271 // Client probably has wrong data.
3272 // Set block not sent, so that client will get
3274 RemoteClient *client = getClient(peer_id);
3275 v3s16 blockpos = getNodeBlockPos(p_above);
3276 client->SetBlockNotSent(blockpos);
3280 // Reset build time counter
3281 getClient(peer_id)->m_time_from_building = 0.0;
3284 MaterialItem *mitem = (MaterialItem*)item;
3286 n.setContent(mitem->getMaterial());
3288 actionstream<<player->getName()<<" places material "
3289 <<(int)mitem->getMaterial()
3290 <<" at "<<PP(p_under)<<std::endl;
3292 // Calculate direction for wall mounted stuff
3293 if(m_nodedef->get(n).wall_mounted)
3294 n.param2 = packDir(p_under - p_above);
3296 // Calculate the direction for furnaces and chests and stuff
3297 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3299 v3f playerpos = player->getPosition();
3300 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3301 blockpos = blockpos.normalize();
3303 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3317 Send to all close-by players
3319 core::list<u16> far_players;
3320 sendAddNode(p_above, n, 0, &far_players, 30);
3325 InventoryList *ilist = player->inventory.getList("main");
3326 if(g_settings->getBool("creative_mode") == false && ilist)
3328 // Remove from inventory and send inventory
3329 if(mitem->getCount() <= 1)
3330 ilist->deleteItem(item_i);
3333 srp->m_inventory_not_sent = true;
3339 This takes some time so it is done after the quick stuff
3341 core::map<v3s16, MapBlock*> modified_blocks;
3343 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3345 std::string p_name = std::string(player->getName());
3346 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3349 Set blocks not sent to far players
3351 for(core::list<u16>::Iterator
3352 i = far_players.begin();
3353 i != far_players.end(); i++)
3356 RemoteClient *client = getClient(peer_id);
3359 client->SetBlocksNotSent(modified_blocks);
3365 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3368 Calculate special events
3371 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3374 for(s16 z=-1; z<=1; z++)
3375 for(s16 y=-1; y<=1; y++)
3376 for(s16 x=-1; x<=1; x++)
3383 Place other item (not a block)
3389 infostream<<"Not allowing player to place item: "
3390 "no build privileges"<<std::endl;
3394 // Calculate a position for it
3395 v3f pos = player_pos;
3396 if(pointed.type == POINTEDTHING_NOTHING)
3398 infostream<<"Not allowing player to place item: "
3399 "pointing to nothing"<<std::endl;
3402 else if(pointed.type == POINTEDTHING_NODE)
3404 pos = intToFloat(p_above, BS);
3406 else if(pointed.type == POINTEDTHING_OBJECT)
3408 pos = pointed_object->getBasePosition();
3411 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3412 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3416 //pos.Y -= BS*0.25; // let it drop a bit
3419 Check that the block is loaded so that the item
3420 can properly be added to the static list too
3422 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3423 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3426 infostream<<"Error while placing item: "
3427 "block not found"<<std::endl;
3431 actionstream<<player->getName()<<" places "<<item->getName()
3432 <<" at "<<PP(pos)<<std::endl;
3437 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3438 if(remove && g_settings->getBool("creative_mode") == false)
3440 InventoryList *ilist = player->inventory.getList("main");
3442 // Remove from inventory and send inventory
3443 ilist->deleteItem(item_i);
3444 srp->m_inventory_not_sent = true;
3449 else if(pointed.type == POINTEDTHING_OBJECT)
3451 // Right click object
3456 // Skip if object has been removed
3457 if(pointed_object->m_removed)
3460 actionstream<<player->getName()<<" right-clicks object "
3461 <<pointed.object_id<<std::endl;
3464 pointed_object->rightClick(srp);
3472 else if(action == 4)
3474 InventoryList *ilist = player->inventory.getList("main");
3479 InventoryItem *item = ilist->getItem(item_i);
3481 // If there is no item, it is not possible to add it anywhere
3485 // Requires build privs
3488 infostream<<"Not allowing player to use item: "
3489 "no build privileges"<<std::endl;
3493 actionstream<<player->getName()<<" uses "<<item->getName()
3494 <<", pointing at "<<pointed.dump()<<std::endl;
3496 bool remove = item->use(m_env, srp, pointed);
3498 if(remove && g_settings->getBool("creative_mode") == false)
3500 InventoryList *ilist = player->inventory.getList("main");
3502 // Remove from inventory and send inventory
3503 ilist->deleteItem(item_i);
3504 srp->m_inventory_not_sent = true;
3511 Catch invalid actions
3515 infostream<<"WARNING: Server: Invalid action "
3516 <<action<<std::endl;
3519 // Complete add_to_inventory_later
3520 srp->completeAddToInventoryLater(item_i);
3524 infostream<<"Server::ProcessData(): Ignoring "
3525 "unknown command "<<command<<std::endl;
3529 catch(SendFailedException &e)
3531 errorstream<<"Server::ProcessData(): SendFailedException: "
3537 void Server::onMapEditEvent(MapEditEvent *event)
3539 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3540 if(m_ignore_map_edit_events)
3542 MapEditEvent *e = event->clone();
3543 m_unsent_map_edit_queue.push_back(e);
3546 Inventory* Server::getInventory(const InventoryLocation &loc)
3549 case InventoryLocation::UNDEFINED:
3552 case InventoryLocation::PLAYER:
3554 Player *player = m_env->getPlayer(loc.name.c_str());
3557 return &player->inventory;
3560 case InventoryLocation::NODEMETA:
3562 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3565 return meta->getInventory();
3573 void Server::setInventoryModified(const InventoryLocation &loc)
3576 case InventoryLocation::UNDEFINED:
3579 case InventoryLocation::PLAYER:
3581 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>
3582 (m_env->getPlayer(loc.name.c_str()));
3585 srp->m_inventory_not_sent = true;
3588 case InventoryLocation::NODEMETA:
3590 v3s16 blockpos = getNodeBlockPos(loc.p);
3592 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3594 meta->inventoryModified();
3596 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3598 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3600 setBlockNotSent(blockpos);
3608 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3610 if(id == "current_player")
3612 assert(c->current_player);
3613 return &(c->current_player->inventory);
3617 std::string id0 = fn.next(":");
3619 if(id0 == "nodemeta")
3622 p.X = stoi(fn.next(","));
3623 p.Y = stoi(fn.next(","));
3624 p.Z = stoi(fn.next(","));
3626 InventoryLocation loc;
3628 return getInventory(loc);
3631 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3634 void Server::inventoryModified(InventoryContext *c, std::string id)
3636 if(id == "current_player")
3638 assert(c->current_player);
3639 ServerRemotePlayer *srp =
3640 static_cast<ServerRemotePlayer*>(c->current_player);
3641 srp->m_inventory_not_sent = true;
3646 std::string id0 = fn.next(":");
3648 if(id0 == "nodemeta")
3651 p.X = stoi(fn.next(","));
3652 p.Y = stoi(fn.next(","));
3653 p.Z = stoi(fn.next(","));
3654 v3s16 blockpos = getNodeBlockPos(p);
3656 InventoryLocation loc;
3658 setInventoryModified(loc);
3662 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3666 core::list<PlayerInfo> Server::getPlayerInfo()
3668 DSTACK(__FUNCTION_NAME);
3669 JMutexAutoLock envlock(m_env_mutex);
3670 JMutexAutoLock conlock(m_con_mutex);
3672 core::list<PlayerInfo> list;
3674 core::list<Player*> players = m_env->getPlayers();
3676 core::list<Player*>::Iterator i;
3677 for(i = players.begin();
3678 i != players.end(); i++)
3682 Player *player = *i;
3685 // Copy info from connection to info struct
3686 info.id = player->peer_id;
3687 info.address = m_con.GetPeerAddress(player->peer_id);
3688 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3690 catch(con::PeerNotFoundException &e)
3692 // Set dummy peer info
3694 info.address = Address(0,0,0,0,0);
3698 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3699 info.position = player->getPosition();
3701 list.push_back(info);
3708 void Server::peerAdded(con::Peer *peer)
3710 DSTACK(__FUNCTION_NAME);
3711 infostream<<"Server::peerAdded(): peer->id="
3712 <<peer->id<<std::endl;
3715 c.type = PEER_ADDED;
3716 c.peer_id = peer->id;
3718 m_peer_change_queue.push_back(c);
3721 void Server::deletingPeer(con::Peer *peer, bool timeout)
3723 DSTACK(__FUNCTION_NAME);
3724 infostream<<"Server::deletingPeer(): peer->id="
3725 <<peer->id<<", timeout="<<timeout<<std::endl;
3728 c.type = PEER_REMOVED;
3729 c.peer_id = peer->id;
3730 c.timeout = timeout;
3731 m_peer_change_queue.push_back(c);
3738 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3740 DSTACK(__FUNCTION_NAME);
3741 std::ostringstream os(std::ios_base::binary);
3743 writeU16(os, TOCLIENT_HP);
3747 std::string s = os.str();
3748 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3750 con.Send(peer_id, 0, data, true);
3753 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3754 const std::wstring &reason)
3756 DSTACK(__FUNCTION_NAME);
3757 std::ostringstream os(std::ios_base::binary);
3759 writeU16(os, TOCLIENT_ACCESS_DENIED);
3760 os<<serializeWideString(reason);
3763 std::string s = os.str();
3764 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3766 con.Send(peer_id, 0, data, true);
3769 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3770 bool set_camera_point_target, v3f camera_point_target)
3772 DSTACK(__FUNCTION_NAME);
3773 std::ostringstream os(std::ios_base::binary);
3775 writeU16(os, TOCLIENT_DEATHSCREEN);
3776 writeU8(os, set_camera_point_target);
3777 writeV3F1000(os, camera_point_target);
3780 std::string s = os.str();
3781 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3783 con.Send(peer_id, 0, data, true);
3786 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3787 IToolDefManager *tooldef)
3789 DSTACK(__FUNCTION_NAME);
3790 std::ostringstream os(std::ios_base::binary);
3794 u32 length of the next item
3795 serialized ToolDefManager
3797 writeU16(os, TOCLIENT_TOOLDEF);
3798 std::ostringstream tmp_os(std::ios::binary);
3799 tooldef->serialize(tmp_os);
3800 os<<serializeLongString(tmp_os.str());
3803 std::string s = os.str();
3804 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3805 <<s.size()<<std::endl;
3806 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3808 con.Send(peer_id, 0, data, true);
3811 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3812 INodeDefManager *nodedef)
3814 DSTACK(__FUNCTION_NAME);
3815 std::ostringstream os(std::ios_base::binary);
3819 u32 length of the next item
3820 serialized NodeDefManager
3822 writeU16(os, TOCLIENT_NODEDEF);
3823 std::ostringstream tmp_os(std::ios::binary);
3824 nodedef->serialize(tmp_os);
3825 os<<serializeLongString(tmp_os.str());
3828 std::string s = os.str();
3829 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3830 <<s.size()<<std::endl;
3831 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3833 con.Send(peer_id, 0, data, true);
3836 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3837 ICraftItemDefManager *craftitemdef)
3839 DSTACK(__FUNCTION_NAME);
3840 std::ostringstream os(std::ios_base::binary);
3844 u32 length of the next item
3845 serialized CraftItemDefManager
3847 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3848 std::ostringstream tmp_os(std::ios::binary);
3849 craftitemdef->serialize(tmp_os);
3850 os<<serializeLongString(tmp_os.str());
3853 std::string s = os.str();
3854 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3855 <<s.size()<<std::endl;
3856 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3858 con.Send(peer_id, 0, data, true);
3862 Non-static send methods
3865 void Server::SendInventory(u16 peer_id)
3867 DSTACK(__FUNCTION_NAME);
3869 ServerRemotePlayer* player =
3870 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3873 player->m_inventory_not_sent = false;
3879 std::ostringstream os;
3880 //os.imbue(std::locale("C"));
3882 player->inventory.serialize(os);
3884 std::string s = os.str();
3886 SharedBuffer<u8> data(s.size()+2);
3887 writeU16(&data[0], TOCLIENT_INVENTORY);
3888 memcpy(&data[2], s.c_str(), s.size());
3891 m_con.Send(peer_id, 0, data, true);
3894 std::string getWieldedItemString(const Player *player)
3896 const InventoryItem *item = player->getWieldItem();
3898 return std::string("");
3899 std::ostringstream os(std::ios_base::binary);
3900 item->serialize(os);
3904 void Server::SendWieldedItem(const Player* player)
3906 DSTACK(__FUNCTION_NAME);
3910 std::ostringstream os(std::ios_base::binary);
3912 writeU16(os, TOCLIENT_PLAYERITEM);
3914 writeU16(os, player->peer_id);
3915 os<<serializeString(getWieldedItemString(player));
3918 std::string s = os.str();
3919 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3921 m_con.SendToAll(0, data, true);
3924 void Server::SendPlayerItems()
3926 DSTACK(__FUNCTION_NAME);
3928 std::ostringstream os(std::ios_base::binary);
3929 core::list<Player *> players = m_env->getPlayers(true);
3931 writeU16(os, TOCLIENT_PLAYERITEM);
3932 writeU16(os, players.size());
3933 core::list<Player *>::Iterator i;
3934 for(i = players.begin(); i != players.end(); ++i)
3937 writeU16(os, p->peer_id);
3938 os<<serializeString(getWieldedItemString(p));
3942 std::string s = os.str();
3943 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3945 m_con.SendToAll(0, data, true);
3948 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3950 DSTACK(__FUNCTION_NAME);
3952 std::ostringstream os(std::ios_base::binary);
3956 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3957 os.write((char*)buf, 2);
3960 writeU16(buf, message.size());
3961 os.write((char*)buf, 2);
3964 for(u32 i=0; i<message.size(); i++)
3968 os.write((char*)buf, 2);
3972 std::string s = os.str();
3973 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3975 m_con.Send(peer_id, 0, data, true);
3978 void Server::BroadcastChatMessage(const std::wstring &message)
3980 for(core::map<u16, RemoteClient*>::Iterator
3981 i = m_clients.getIterator();
3982 i.atEnd() == false; i++)
3984 // Get client and check that it is valid
3985 RemoteClient *client = i.getNode()->getValue();
3986 assert(client->peer_id == i.getNode()->getKey());
3987 if(client->serialization_version == SER_FMT_VER_INVALID)
3990 SendChatMessage(client->peer_id, message);
3994 void Server::SendPlayerHP(Player *player)
3996 SendHP(m_con, player->peer_id, player->hp);
3997 static_cast<ServerRemotePlayer*>(player)->m_hp_not_sent = false;
4000 void Server::SendMovePlayer(Player *player)
4002 DSTACK(__FUNCTION_NAME);
4003 std::ostringstream os(std::ios_base::binary);
4005 writeU16(os, TOCLIENT_MOVE_PLAYER);
4006 writeV3F1000(os, player->getPosition());
4007 writeF1000(os, player->getPitch());
4008 writeF1000(os, player->getYaw());
4011 v3f pos = player->getPosition();
4012 f32 pitch = player->getPitch();
4013 f32 yaw = player->getYaw();
4014 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4015 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4022 std::string s = os.str();
4023 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4025 m_con.Send(player->peer_id, 0, data, true);
4028 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4029 core::list<u16> *far_players, float far_d_nodes)
4031 float maxd = far_d_nodes*BS;
4032 v3f p_f = intToFloat(p, BS);
4036 SharedBuffer<u8> reply(replysize);
4037 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4038 writeS16(&reply[2], p.X);
4039 writeS16(&reply[4], p.Y);
4040 writeS16(&reply[6], p.Z);
4042 for(core::map<u16, RemoteClient*>::Iterator
4043 i = m_clients.getIterator();
4044 i.atEnd() == false; i++)
4046 // Get client and check that it is valid
4047 RemoteClient *client = i.getNode()->getValue();
4048 assert(client->peer_id == i.getNode()->getKey());
4049 if(client->serialization_version == SER_FMT_VER_INVALID)
4052 // Don't send if it's the same one
4053 if(client->peer_id == ignore_id)
4059 Player *player = m_env->getPlayer(client->peer_id);
4062 // If player is far away, only set modified blocks not sent
4063 v3f player_pos = player->getPosition();
4064 if(player_pos.getDistanceFrom(p_f) > maxd)
4066 far_players->push_back(client->peer_id);
4073 m_con.Send(client->peer_id, 0, reply, true);
4077 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4078 core::list<u16> *far_players, float far_d_nodes)
4080 float maxd = far_d_nodes*BS;
4081 v3f p_f = intToFloat(p, BS);
4083 for(core::map<u16, RemoteClient*>::Iterator
4084 i = m_clients.getIterator();
4085 i.atEnd() == false; i++)
4087 // Get client and check that it is valid
4088 RemoteClient *client = i.getNode()->getValue();
4089 assert(client->peer_id == i.getNode()->getKey());
4090 if(client->serialization_version == SER_FMT_VER_INVALID)
4093 // Don't send if it's the same one
4094 if(client->peer_id == ignore_id)
4100 Player *player = m_env->getPlayer(client->peer_id);
4103 // If player is far away, only set modified blocks not sent
4104 v3f player_pos = player->getPosition();
4105 if(player_pos.getDistanceFrom(p_f) > maxd)
4107 far_players->push_back(client->peer_id);
4114 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4115 SharedBuffer<u8> reply(replysize);
4116 writeU16(&reply[0], TOCLIENT_ADDNODE);
4117 writeS16(&reply[2], p.X);
4118 writeS16(&reply[4], p.Y);
4119 writeS16(&reply[6], p.Z);
4120 n.serialize(&reply[8], client->serialization_version);
4123 m_con.Send(client->peer_id, 0, reply, true);
4127 void Server::setBlockNotSent(v3s16 p)
4129 for(core::map<u16, RemoteClient*>::Iterator
4130 i = m_clients.getIterator();
4131 i.atEnd()==false; i++)
4133 RemoteClient *client = i.getNode()->getValue();
4134 client->SetBlockNotSent(p);
4138 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4140 DSTACK(__FUNCTION_NAME);
4142 v3s16 p = block->getPos();
4146 bool completely_air = true;
4147 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4148 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4149 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4151 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4153 completely_air = false;
4154 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4159 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4161 infostream<<"[completely air] ";
4162 infostream<<std::endl;
4166 Create a packet with the block in the right format
4169 std::ostringstream os(std::ios_base::binary);
4170 block->serialize(os, ver);
4171 std::string s = os.str();
4172 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4174 u32 replysize = 8 + blockdata.getSize();
4175 SharedBuffer<u8> reply(replysize);
4176 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4177 writeS16(&reply[2], p.X);
4178 writeS16(&reply[4], p.Y);
4179 writeS16(&reply[6], p.Z);
4180 memcpy(&reply[8], *blockdata, blockdata.getSize());
4182 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4183 <<": \tpacket size: "<<replysize<<std::endl;*/
4188 m_con.Send(peer_id, 1, reply, true);
4191 void Server::SendBlocks(float dtime)
4193 DSTACK(__FUNCTION_NAME);
4195 JMutexAutoLock envlock(m_env_mutex);
4196 JMutexAutoLock conlock(m_con_mutex);
4198 //TimeTaker timer("Server::SendBlocks");
4200 core::array<PrioritySortedBlockTransfer> queue;
4202 s32 total_sending = 0;
4205 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4207 for(core::map<u16, RemoteClient*>::Iterator
4208 i = m_clients.getIterator();
4209 i.atEnd() == false; i++)
4211 RemoteClient *client = i.getNode()->getValue();
4212 assert(client->peer_id == i.getNode()->getKey());
4214 // If definitions and textures have not been sent, don't
4215 // send MapBlocks either
4216 if(!client->definitions_sent)
4219 total_sending += client->SendingCount();
4221 if(client->serialization_version == SER_FMT_VER_INVALID)
4224 client->GetNextBlocks(this, dtime, queue);
4229 // Lowest priority number comes first.
4230 // Lowest is most important.
4233 for(u32 i=0; i<queue.size(); i++)
4235 //TODO: Calculate limit dynamically
4236 if(total_sending >= g_settings->getS32
4237 ("max_simultaneous_block_sends_server_total"))
4240 PrioritySortedBlockTransfer q = queue[i];
4242 MapBlock *block = NULL;
4245 block = m_env->getMap().getBlockNoCreate(q.pos);
4247 catch(InvalidPositionException &e)
4252 RemoteClient *client = getClient(q.peer_id);
4254 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4256 client->SentBlock(q.pos);
4262 void Server::PrepareTextures() {
4263 DSTACK(__FUNCTION_NAME);
4265 infostream<<"Server::PrepareTextures(): Calculate sha1 sums of textures"<<std::endl;
4267 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4268 i != m_mods.end(); i++){
4269 const ModSpec &mod = *i;
4270 std::string texturepath = mod.path + DIR_DELIM + "textures";
4271 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4272 for(u32 j=0; j<dirlist.size(); j++){
4273 if(dirlist[j].dir) // Ignode dirs
4275 std::string tname = dirlist[j].name;
4276 std::string tpath = texturepath + DIR_DELIM + tname;
4278 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4279 if(fis.good() == false){
4280 errorstream<<"Server::PrepareTextures(): Could not open \""
4281 <<tname<<"\" for reading"<<std::endl;
4284 std::ostringstream tmp_os(std::ios_base::binary);
4288 fis.read(buf, 1024);
4289 std::streamsize len = fis.gcount();
4290 tmp_os.write(buf, len);
4299 errorstream<<"Server::PrepareTextures(): Failed to read \""
4300 <<tname<<"\""<<std::endl;
4305 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4307 unsigned char *digest = sha1.getDigest();
4308 std::string digest_string = base64_encode(digest, 20);
4313 this->m_Textures[tname] = TextureInformation(tpath,digest_string);
4314 infostream<<"Server::PrepareTextures(): added sha1 for "<< tname <<std::endl;
4319 struct SendableTextureAnnouncement
4322 std::string sha1_digest;
4324 SendableTextureAnnouncement(const std::string name_="",
4325 const std::string sha1_digest_=""):
4327 sha1_digest(sha1_digest_)
4332 void Server::SendTextureAnnouncement(u16 peer_id){
4333 DSTACK(__FUNCTION_NAME);
4335 infostream<<"Server::SendTextureAnnouncement(): Calculate sha1 sums of textures and send to client"<<std::endl;
4337 core::list<SendableTextureAnnouncement> texture_announcements;
4339 for (std::map<std::string,TextureInformation>::iterator i = m_Textures.begin();i != m_Textures.end(); i++ ) {
4342 texture_announcements.push_back(
4343 SendableTextureAnnouncement(i->first, i->second.sha1_digest));
4346 //send announcements
4350 u32 number of textures
4354 u16 length of digest string
4358 std::ostringstream os(std::ios_base::binary);
4360 writeU16(os, TOCLIENT_ANNOUNCE_TEXTURES);
4361 writeU16(os, texture_announcements.size());
4363 for(core::list<SendableTextureAnnouncement>::Iterator
4364 j = texture_announcements.begin();
4365 j != texture_announcements.end(); j++){
4366 os<<serializeString(j->name);
4367 os<<serializeString(j->sha1_digest);
4371 std::string s = os.str();
4372 infostream<<"Server::SendTextureAnnouncement(): Send to client"<<std::endl;
4373 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4376 m_con.Send(peer_id, 0, data, true);
4380 struct SendableTexture
4386 SendableTexture(const std::string &name_="", const std::string path_="",
4387 const std::string &data_=""):
4394 void Server::SendTexturesRequested(u16 peer_id,core::list<TextureRequest> tosend) {
4395 DSTACK(__FUNCTION_NAME);
4397 infostream<<"Server::SendTexturesRequested(): Sending textures to client"<<std::endl;
4401 // Put 5kB in one bunch (this is not accurate)
4402 u32 bytes_per_bunch = 5000;
4404 core::array< core::list<SendableTexture> > texture_bunches;
4405 texture_bunches.push_back(core::list<SendableTexture>());
4407 u32 texture_size_bunch_total = 0;
4409 for(core::list<TextureRequest>::Iterator i = tosend.begin(); i != tosend.end(); i++) {
4411 //TODO get path + name
4412 std::string tpath = m_Textures[(*i).name].path;
4415 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4416 if(fis.good() == false){
4417 errorstream<<"Server::SendTexturesRequested(): Could not open \""
4418 <<tpath<<"\" for reading"<<std::endl;
4421 std::ostringstream tmp_os(std::ios_base::binary);
4425 fis.read(buf, 1024);
4426 std::streamsize len = fis.gcount();
4427 tmp_os.write(buf, len);
4428 texture_size_bunch_total += len;
4437 errorstream<<"Server::SendTexturesRequested(): Failed to read \""
4438 <<(*i).name<<"\""<<std::endl;
4441 /*infostream<<"Server::SendTexturesRequested(): Loaded \""
4442 <<tname<<"\""<<std::endl;*/
4444 texture_bunches[texture_bunches.size()-1].push_back(
4445 SendableTexture((*i).name, tpath, tmp_os.str()));
4447 // Start next bunch if got enough data
4448 if(texture_size_bunch_total >= bytes_per_bunch){
4449 texture_bunches.push_back(core::list<SendableTexture>());
4450 texture_size_bunch_total = 0;
4455 /* Create and send packets */
4457 u32 num_bunches = texture_bunches.size();
4458 for(u32 i=0; i<num_bunches; i++)
4462 u16 total number of texture bunches
4463 u16 index of this bunch
4464 u32 number of textures in this bunch
4472 std::ostringstream os(std::ios_base::binary);
4474 writeU16(os, TOCLIENT_TEXTURES);
4475 writeU16(os, num_bunches);
4477 writeU32(os, texture_bunches[i].size());
4479 for(core::list<SendableTexture>::Iterator
4480 j = texture_bunches[i].begin();
4481 j != texture_bunches[i].end(); j++){
4482 os<<serializeString(j->name);
4483 os<<serializeLongString(j->data);
4487 std::string s = os.str();
4488 infostream<<"Server::SendTexturesRequested(): bunch "<<i<<"/"<<num_bunches
4489 <<" textures="<<texture_bunches[i].size()
4490 <<" size=" <<s.size()<<std::endl;
4491 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4493 m_con.Send(peer_id, 0, data, true);
4503 void Server::HandlePlayerHP(Player *player, s16 damage)
4505 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4507 if(srp->m_respawn_active)
4510 if(player->hp > damage)
4513 player->hp -= damage;
4514 SendPlayerHP(player);
4519 infostream<<"Server::HandlePlayerHP(): Player "
4520 <<player->getName()<<" dies"<<std::endl;
4524 //TODO: Throw items around
4526 // Handle players that are not connected
4527 if(player->peer_id == PEER_ID_INEXISTENT){
4528 RespawnPlayer(player);
4532 SendPlayerHP(player);
4534 RemoteClient *client = getClient(player->peer_id);
4535 if(client->net_proto_version >= 3)
4537 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4538 srp->m_removed = true;
4539 srp->m_respawn_active = true;
4543 RespawnPlayer(player);
4547 void Server::RespawnPlayer(Player *player)
4550 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4551 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4553 v3f pos = findSpawnPos(m_env->getServerMap());
4554 player->setPosition(pos);
4555 srp->m_last_good_position = pos;
4556 srp->m_last_good_position_age = 0;
4558 SendMovePlayer(player);
4559 SendPlayerHP(player);
4562 void Server::UpdateCrafting(u16 peer_id)
4564 DSTACK(__FUNCTION_NAME);
4566 Player* player = m_env->getPlayer(peer_id);
4568 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4570 // No crafting in creative mode
4571 if(g_settings->getBool("creative_mode"))
4574 // Get the InventoryLists of the player in which we will operate
4575 InventoryList *clist = player->inventory.getList("craft");
4577 InventoryList *rlist = player->inventory.getList("craftresult");
4579 InventoryList *mlist = player->inventory.getList("main");
4582 // If the result list is not a preview and is not empty, try to
4583 // throw the item into main list
4584 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4586 // Grab item out of craftresult
4587 InventoryItem *item = rlist->changeItem(0, NULL);
4588 // Try to put in main
4589 InventoryItem *leftover = mlist->addItem(item);
4590 // If there are leftovers, put them back to craftresult and
4592 delete rlist->addItem(leftover);
4593 // Inventory was modified
4594 srp->m_inventory_not_sent = true;
4597 // If result list is empty, we will make it preview what would be
4599 if(rlist->getUsedSlots() == 0)
4600 player->craftresult_is_preview = true;
4602 // If it is a preview, clear the possible old preview in it
4603 if(player->craftresult_is_preview)
4604 rlist->clearItems();
4606 // If it is a preview, find out what is the crafting result
4608 if(player->craftresult_is_preview)
4610 // Mangle crafting grid to an another format
4611 std::vector<InventoryItem*> items;
4612 for(u16 i=0; i<9; i++){
4613 if(clist->getItem(i) == NULL)
4614 items.push_back(NULL);
4616 items.push_back(clist->getItem(i)->clone());
4618 CraftPointerInput cpi(3, items);
4620 // Find out what is crafted and add it to result item slot
4621 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4623 rlist->addItem(result);
4627 RemoteClient* Server::getClient(u16 peer_id)
4629 DSTACK(__FUNCTION_NAME);
4630 //JMutexAutoLock lock(m_con_mutex);
4631 core::map<u16, RemoteClient*>::Node *n;
4632 n = m_clients.find(peer_id);
4633 // A client should exist for all peers
4635 return n->getValue();
4638 std::wstring Server::getStatusString()
4640 std::wostringstream os(std::ios_base::binary);
4643 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4645 os<<L", uptime="<<m_uptime.get();
4646 // Information about clients
4648 for(core::map<u16, RemoteClient*>::Iterator
4649 i = m_clients.getIterator();
4650 i.atEnd() == false; i++)
4652 // Get client and check that it is valid
4653 RemoteClient *client = i.getNode()->getValue();
4654 assert(client->peer_id == i.getNode()->getKey());
4655 if(client->serialization_version == SER_FMT_VER_INVALID)
4658 Player *player = m_env->getPlayer(client->peer_id);
4659 // Get name of player
4660 std::wstring name = L"unknown";
4662 name = narrow_to_wide(player->getName());
4663 // Add name to information string
4667 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4668 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4669 if(g_settings->get("motd") != "")
4670 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4674 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4676 // Add player to auth manager
4677 if(m_authmanager.exists(name) == false)
4679 infostream<<"Server: adding player "<<name
4680 <<" to auth manager"<<std::endl;
4681 m_authmanager.add(name);
4682 m_authmanager.setPrivs(name,
4683 stringToPrivs(g_settings->get("default_privs")));
4685 // Change password and save
4686 m_authmanager.setPassword(name, translatePassword(name, password));
4687 m_authmanager.save();
4690 // Saves g_settings to configpath given at initialization
4691 void Server::saveConfig()
4693 if(m_configpath != "")
4694 g_settings->updateConfigFile(m_configpath.c_str());
4697 void Server::notifyPlayer(const char *name, const std::wstring msg)
4699 Player *player = m_env->getPlayer(name);
4702 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4705 void Server::notifyPlayers(const std::wstring msg)
4707 BroadcastChatMessage(msg);
4710 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4714 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4715 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4718 // IGameDef interface
4720 IToolDefManager* Server::getToolDefManager()
4724 INodeDefManager* Server::getNodeDefManager()
4728 ICraftDefManager* Server::getCraftDefManager()
4732 ICraftItemDefManager* Server::getCraftItemDefManager()
4734 return m_craftitemdef;
4736 ITextureSource* Server::getTextureSource()
4740 u16 Server::allocateUnknownNodeId(const std::string &name)
4742 return m_nodedef->allocateDummy(name);
4745 IWritableToolDefManager* Server::getWritableToolDefManager()
4749 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4753 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4757 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4759 return m_craftitemdef;
4762 const ModSpec* Server::getModSpec(const std::string &modname)
4764 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4765 i != m_mods.end(); i++){
4766 const ModSpec &mod = *i;
4767 if(mod.name == modname)
4773 v3f findSpawnPos(ServerMap &map)
4775 //return v3f(50,50,50)*BS;
4780 nodepos = v2s16(0,0);
4785 // Try to find a good place a few times
4786 for(s32 i=0; i<1000; i++)
4789 // We're going to try to throw the player to this position
4790 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4791 -range + (myrand()%(range*2)));
4792 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4793 // Get ground height at point (fallbacks to heightmap function)
4794 s16 groundheight = map.findGroundLevel(nodepos2d);
4795 // Don't go underwater
4796 if(groundheight < WATER_LEVEL)
4798 //infostream<<"-> Underwater"<<std::endl;
4801 // Don't go to high places
4802 if(groundheight > WATER_LEVEL + 4)
4804 //infostream<<"-> Underwater"<<std::endl;
4808 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4809 bool is_good = false;
4811 for(s32 i=0; i<10; i++){
4812 v3s16 blockpos = getNodeBlockPos(nodepos);
4813 map.emergeBlock(blockpos, true);
4814 MapNode n = map.getNodeNoEx(nodepos);
4815 if(n.getContent() == CONTENT_AIR){
4826 // Found a good place
4827 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4833 return intToFloat(nodepos, BS);
4836 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4839 Try to get an existing player
4841 ServerRemotePlayer *player =
4842 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4845 // If player is already connected, cancel
4846 if(player->peer_id != 0)
4848 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4853 player->peer_id = peer_id;
4855 // Reset inventory to creative if in creative mode
4856 if(g_settings->getBool("creative_mode"))
4858 // Warning: double code below
4859 // Backup actual inventory
4860 player->inventory_backup = new Inventory();
4861 *(player->inventory_backup) = player->inventory;
4862 // Set creative inventory
4863 player->resetInventory();
4864 scriptapi_get_creative_inventory(m_lua, player);
4871 If player with the wanted peer_id already exists, cancel.
4873 if(m_env->getPlayer(peer_id) != NULL)
4875 infostream<<"emergePlayer(): Player with wrong name but same"
4876 " peer_id already exists"<<std::endl;
4884 /* Set player position */
4886 infostream<<"Server: Finding spawn place for player \""
4887 <<name<<"\""<<std::endl;
4889 v3f pos = findSpawnPos(m_env->getServerMap());
4891 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4893 /* Add player to environment */
4894 m_env->addPlayer(player);
4897 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4898 scriptapi_on_newplayer(m_lua, srp);
4900 /* Add stuff to inventory */
4901 if(g_settings->getBool("creative_mode"))
4903 // Warning: double code above
4904 // Backup actual inventory
4905 player->inventory_backup = new Inventory();
4906 *(player->inventory_backup) = player->inventory;
4907 // Set creative inventory
4908 player->resetInventory();
4909 scriptapi_get_creative_inventory(m_lua, player);
4914 } // create new player
4917 void Server::handlePeerChange(PeerChange &c)
4919 JMutexAutoLock envlock(m_env_mutex);
4920 JMutexAutoLock conlock(m_con_mutex);
4922 if(c.type == PEER_ADDED)
4929 core::map<u16, RemoteClient*>::Node *n;
4930 n = m_clients.find(c.peer_id);
4931 // The client shouldn't already exist
4935 RemoteClient *client = new RemoteClient();
4936 client->peer_id = c.peer_id;
4937 m_clients.insert(client->peer_id, client);
4940 else if(c.type == PEER_REMOVED)
4947 core::map<u16, RemoteClient*>::Node *n;
4948 n = m_clients.find(c.peer_id);
4949 // The client should exist
4953 Mark objects to be not known by the client
4955 RemoteClient *client = n->getValue();
4957 for(core::map<u16, bool>::Iterator
4958 i = client->m_known_objects.getIterator();
4959 i.atEnd()==false; i++)
4962 u16 id = i.getNode()->getKey();
4963 ServerActiveObject* obj = m_env->getActiveObject(id);
4965 if(obj && obj->m_known_by_count > 0)
4966 obj->m_known_by_count--;
4969 ServerRemotePlayer* player =
4970 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4972 // Collect information about leaving in chat
4973 std::wstring message;
4977 std::wstring name = narrow_to_wide(player->getName());
4980 message += L" left game";
4982 message += L" (timed out)";
4986 // Remove from environment
4988 player->m_removed = true;
4990 // Set player client disconnected
4992 player->peer_id = 0;
5000 std::ostringstream os(std::ios_base::binary);
5001 for(core::map<u16, RemoteClient*>::Iterator
5002 i = m_clients.getIterator();
5003 i.atEnd() == false; i++)
5005 RemoteClient *client = i.getNode()->getValue();
5006 assert(client->peer_id == i.getNode()->getKey());
5007 if(client->serialization_version == SER_FMT_VER_INVALID)
5010 Player *player = m_env->getPlayer(client->peer_id);
5013 // Get name of player
5014 os<<player->getName()<<" ";
5017 actionstream<<player->getName()<<" "
5018 <<(c.timeout?"times out.":"leaves game.")
5019 <<" List of players: "
5020 <<os.str()<<std::endl;
5025 delete m_clients[c.peer_id];
5026 m_clients.remove(c.peer_id);
5028 // Send player info to all remaining clients
5029 //SendPlayerInfos();
5031 // Send leave chat message to all remaining clients
5032 if(message.length() != 0)
5033 BroadcastChatMessage(message);
5042 void Server::handlePeerChanges()
5044 while(m_peer_change_queue.size() > 0)
5046 PeerChange c = m_peer_change_queue.pop_front();
5048 infostream<<"Server: Handling peer change: "
5049 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5052 handlePeerChange(c);
5056 u64 Server::getPlayerPrivs(Player *player)
5060 std::string playername = player->getName();
5061 // Local player gets all privileges regardless of
5062 // what's set on their account.
5063 if(g_settings->get("name") == playername)
5069 return getPlayerAuthPrivs(playername);
5073 void dedicated_server_loop(Server &server, bool &kill)
5075 DSTACK(__FUNCTION_NAME);
5077 infostream<<DTIME<<std::endl;
5078 infostream<<"========================"<<std::endl;
5079 infostream<<"Running dedicated server"<<std::endl;
5080 infostream<<"========================"<<std::endl;
5081 infostream<<std::endl;
5083 IntervalLimiter m_profiler_interval;
5087 // This is kind of a hack but can be done like this
5088 // because server.step() is very light
5090 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5095 if(server.getShutdownRequested() || kill)
5097 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
5104 float profiler_print_interval =
5105 g_settings->getFloat("profiler_print_interval");
5106 if(profiler_print_interval != 0)
5108 if(m_profiler_interval.step(0.030, profiler_print_interval))
5110 infostream<<"Profiler:"<<std::endl;
5111 g_profiler->print(infostream);
5112 g_profiler->clear();
5119 static int counter = 0;
5125 core::list<PlayerInfo> list = server.getPlayerInfo();
5126 core::list<PlayerInfo>::Iterator i;
5127 static u32 sum_old = 0;
5128 u32 sum = PIChecksum(list);
5131 infostream<<DTIME<<"Player info:"<<std::endl;
5132 for(i=list.begin(); i!=list.end(); i++)
5134 i->PrintLine(&infostream);